We can into jitsi now
parent
9434cb3752
commit
9696835270
5
go.mod
5
go.mod
|
@ -2,4 +2,7 @@ module code.hackerspace.pl/ar/notbot
|
|||
|
||||
go 1.16
|
||||
|
||||
require gopkg.in/irc.v3 v3.1.4
|
||||
require (
|
||||
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d
|
||||
gopkg.in/irc.v3 v3.1.4
|
||||
)
|
||||
|
|
7
go.sum
7
go.sum
|
@ -5,6 +5,13 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d h1:1n1fc535VhN8SYtD4cDUyNlfpAF2ROMM9+11equK3hs=
|
||||
golang.org/x/net v0.0.0-20220114011407-0dd24b26b47d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/irc.v3 v3.1.4 h1:DYGMRFbtseXEh+NadmMUFzMraqyuUj4I3iWYFEzDZPc=
|
||||
gopkg.in/irc.v3 v3.1.4/go.mod h1:shO2gz8+PVeS+4E6GAny88Z0YVVQSxQghdrMVGQsR9s=
|
||||
|
|
|
@ -30,6 +30,46 @@
|
|||
rev = "221dbe5ed46703ee255b1da0dec05086f5035f62"
|
||||
sha256 = "187i5g88sxfy4vxpm7dw1gwv29pa2qaq475lxrdh5livh69wqfjb"
|
||||
|
||||
["golang.org/x/net"]
|
||||
sumVersion = "v0.0.0-20220114011407-0dd24b26b47d"
|
||||
["golang.org/x/net".fetch]
|
||||
type = "git"
|
||||
url = "https://go.googlesource.com/net"
|
||||
rev = "0dd24b26b47d4eb2d45eb3c7a4bcb809d7c1edb8"
|
||||
sha256 = "0k8s5y6nkw7q1g3rdgnv33q96jrxi19ni2p587wy9z48jcgk6w2i"
|
||||
|
||||
["golang.org/x/sys"]
|
||||
sumVersion = "v0.0.0-20210423082822-04245dca01da"
|
||||
["golang.org/x/sys".fetch]
|
||||
type = "git"
|
||||
url = "https://go.googlesource.com/sys"
|
||||
rev = "04245dca01dae530ad36275d662a90d6b8a5ee29"
|
||||
sha256 = "11is2c5cpxk0gf2mxza2wpzfcf71fxb9b3in77f6w2q0pr68ykzx"
|
||||
|
||||
["golang.org/x/term"]
|
||||
sumVersion = "v0.0.0-20201126162022-7de9c90e9dd1"
|
||||
["golang.org/x/term".fetch]
|
||||
type = "git"
|
||||
url = "https://go.googlesource.com/term"
|
||||
rev = "7de9c90e9dd184706b838f536a1cbf40a296ddb7"
|
||||
sha256 = "1ba252xmv6qsvf1w1gcy98mngrj0vd4inbjw0lsklqvva65nljna"
|
||||
|
||||
["golang.org/x/text"]
|
||||
sumVersion = "v0.3.6"
|
||||
["golang.org/x/text".fetch]
|
||||
type = "git"
|
||||
url = "https://go.googlesource.com/text"
|
||||
rev = "e328d63cff14134669501e0e154e4f141c784322"
|
||||
sha256 = "0wzhvdb059vrp2cczqw422ajrb9sbs4l3qd020hlngj33qfhxah0"
|
||||
|
||||
["golang.org/x/tools"]
|
||||
sumVersion = "v0.0.0-20180917221912-90fa682c2a6e"
|
||||
["golang.org/x/tools".fetch]
|
||||
type = "git"
|
||||
url = "https://go.googlesource.com/tools"
|
||||
rev = "90fa682c2a6e6a37b3a1364ce2fe1d5e41af9d6d"
|
||||
sha256 = "03ic2xsy51jw9749wl7gszdbz99iijbd2bckgygl6cm9w5m364ak"
|
||||
|
||||
["gopkg.in/check.v1"]
|
||||
sumVersion = "v0.0.0-20161208181325-20d25e280405"
|
||||
["gopkg.in/check.v1".fetch]
|
||||
|
|
|
@ -0,0 +1,220 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/websocket"
|
||||
"gopkg.in/irc.v3"
|
||||
)
|
||||
|
||||
const pingFrame = "<iq type='get'><ping xmlns='urn:xmpp:ping'/>"
|
||||
|
||||
var (
|
||||
jitsiChannels arrayFlags
|
||||
)
|
||||
|
||||
// we actually only care about .Type, .Nick, and .X.Item.Jid
|
||||
type JitsiPresence struct {
|
||||
XMLName xml.Name `xml:"presence"`
|
||||
Text string `xml:",chardata"`
|
||||
From string `xml:"from,attr"`
|
||||
To string `xml:"to,attr"`
|
||||
Type string `xml:"type,attr"`
|
||||
Xmlns string `xml:"xmlns,attr"`
|
||||
StatsID string `xml:"stats-id"`
|
||||
Region struct {
|
||||
Text string `xml:",chardata"`
|
||||
Xmlns string `xml:"xmlns,attr"`
|
||||
ID string `xml:"id,attr"`
|
||||
} `xml:"region"`
|
||||
C struct {
|
||||
Text string `xml:",chardata"`
|
||||
Ver string `xml:"ver,attr"`
|
||||
Hash string `xml:"hash,attr"`
|
||||
Xmlns string `xml:"xmlns,attr"`
|
||||
Node string `xml:"node,attr"`
|
||||
} `xml:"c"`
|
||||
Features struct {
|
||||
Text string `xml:",chardata"`
|
||||
Feature struct {
|
||||
Text string `xml:",chardata"`
|
||||
Var string `xml:"var,attr"`
|
||||
} `xml:"feature"`
|
||||
} `xml:"features"`
|
||||
JitsiParticipantRegion string `xml:"jitsi_participant_region"`
|
||||
JitsiParticipantCodecType string `xml:"jitsi_participant_codecType"`
|
||||
Nick struct {
|
||||
Text string `xml:",chardata"`
|
||||
Xmlns string `xml:"xmlns,attr"`
|
||||
} `xml:"nick"`
|
||||
JitsiParticipantE2eeIdKey string `xml:"jitsi_participant_e2ee.idKey"`
|
||||
Audiomuted string `xml:"audiomuted"`
|
||||
Videomuted string `xml:"videomuted"`
|
||||
X struct {
|
||||
Text string `xml:",chardata"`
|
||||
Xmlns string `xml:"xmlns,attr"`
|
||||
Photo string `xml:"photo"`
|
||||
Item struct {
|
||||
Text string `xml:",chardata"`
|
||||
Affiliation string `xml:"affiliation,attr"`
|
||||
Role string `xml:"role,attr"`
|
||||
Jid string `xml:"jid,attr"`
|
||||
} `xml:"item"`
|
||||
} `xml:"x"`
|
||||
}
|
||||
|
||||
type JitsiClient struct {
|
||||
nick string
|
||||
server string
|
||||
room string
|
||||
ircChannel string
|
||||
done chan bool
|
||||
users map[string]string // map[jid]nick
|
||||
}
|
||||
|
||||
func (j *JitsiClient) KeepAlive(ws *websocket.Conn) {
|
||||
ticker := time.NewTicker(5 * time.Second)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if _, err := ws.Write([]byte(pingFrame)); err != nil {
|
||||
log.Println("JitsiClient", j.server, j.room, "Error while sending ping", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (j *JitsiClient) Run(c *irc.Client, done chan bool) {
|
||||
var msg = make([]byte, 64*1024)
|
||||
|
||||
origin := "https://" + j.server
|
||||
url := "wss://" + j.server + "/xmpp-websocket?room=" + j.room
|
||||
protocol := "xmpp"
|
||||
var initFrames = []string{
|
||||
"<open to=\"" + j.server + "\" version=\"1.0\" xmlns=\"urn:ietf:params:xml:ns:xmpp-framing\"/>",
|
||||
"<auth mechanism=\"ANONYMOUS\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"/>",
|
||||
"<open to=\"" + j.server + "\" version=\"1.0\" xmlns=\"urn:ietf:params:xml:ns:xmpp-framing\"/>",
|
||||
"<iq id=\"_bind_auth_2\" type=\"set\" xmlns=\"jabber:client\"><bind xmlns=\"urn:ietf:params:xml:ns:xmpp-bind\"/></iq>",
|
||||
"<iq id=\"_session_auth_2\" type=\"set\" xmlns=\"jabber:client\"><session xmlns=\"urn:ietf:params:xml:ns:xmpp-session\"/></iq>",
|
||||
"<presence to=\"" + j.room + "@conference." + j.server + "/3344bf4a\" xmlns=\"jabber:client\"><x xmlns=\"http://jabber.org/protocol/muc\"/>" +
|
||||
"<stats-id>Joy-4gA</stats-id><region id=\"ffmuc-de1\" xmlns=\"http://jitsi.org/jitsi-meet\"/>" +
|
||||
"<c hash=\"sha-1\" node=\"https://jitsi.org/jitsi-meet\" ver=\"ZjoRESHG8S3zyis9xCdYpFmbThk=\" xmlns=\"http://jabber.org/protocol/caps\"/>" +
|
||||
"<jitsi_participant_region>ffmuc-de1</jitsi_participant_region><videomuted>true</videomuted><audiomuted>true</audiomuted>" +
|
||||
"<jitsi_participant_codecType></jitsi_participant_codecType><nick xmlns=\"http://jabber.org/protocol/nick\">" + j.nick + "</nick></presence>",
|
||||
}
|
||||
|
||||
j.users = make(map[string]string)
|
||||
|
||||
for {
|
||||
log.Println("JitsiClient", j.server, j.room, "Initializing")
|
||||
|
||||
ws, err := websocket.Dial(url, protocol, origin)
|
||||
if err != nil {
|
||||
log.Println("JitsiClient", j.server, j.room, "Error connecting to websocket", url, err)
|
||||
goto reconnect
|
||||
}
|
||||
|
||||
for n, frame := range initFrames {
|
||||
if _, err := ws.Write([]byte(frame)); err != nil {
|
||||
log.Println("JitsiClient", j.server, j.room, "Error sending initialization frame", n, err)
|
||||
goto reconnect
|
||||
}
|
||||
}
|
||||
|
||||
log.Println("JitsiClient", j.server, j.room, "Running")
|
||||
go j.KeepAlive(ws)
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
log.Println("JitsiClient", j.server, j.room, "Shutting down")
|
||||
return
|
||||
default:
|
||||
_, err := ws.Read(msg)
|
||||
v := JitsiPresence{}
|
||||
|
||||
if err != nil {
|
||||
log.Println("JitsiClient", j.server, j.room, "Error while reading from websocket", err)
|
||||
goto reconnect
|
||||
}
|
||||
|
||||
err = xml.Unmarshal(msg, &v)
|
||||
if err != nil {
|
||||
// xml parsing errors will be normal here
|
||||
continue
|
||||
}
|
||||
|
||||
if v.Nick.Text != "" { // if presence event has Nick present, it *shouldn't* mean that user has left the chat
|
||||
if v.X.Item.Jid != "" {
|
||||
if knownNick, ok := j.users[v.X.Item.Jid]; ok {
|
||||
if knownNick != v.Nick.Text { // user changed nickname, we don't care about that enough
|
||||
log.Println("JitsiClient", j.server, j.room, "User changed nickname:", knownNick, v.Nick.Text)
|
||||
j.users[v.X.Item.Jid] = v.Nick.Text
|
||||
continue
|
||||
}
|
||||
} else { // new user
|
||||
j.users[v.X.Item.Jid] = v.Nick.Text
|
||||
nickZws := v.Nick.Text[:1] + "\u200B" + v.Nick.Text[1:]
|
||||
ircMsg := fmt.Sprintf("NOTICE %s :jitsi: +%s\n", j.ircChannel, nickZws)
|
||||
log.Println("JitsiClient", j.server, j.room, "User joined:", j.users[v.X.Item.Jid])
|
||||
c.Write(ircMsg)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if v.Type == "unavailable" {
|
||||
if v.X.Item.Jid != "" {
|
||||
if knownNick, ok := j.users[v.X.Item.Jid]; ok {
|
||||
delete(j.users, v.X.Item.Jid)
|
||||
nickZws := knownNick[:1] + "\u200B" + knownNick[1:]
|
||||
ircMsg := fmt.Sprintf("NOTICE %s :jitsi: -%s\n", j.ircChannel, nickZws)
|
||||
log.Println("JitsiClient", j.server, j.room, "User left:", knownNick)
|
||||
c.Write(ircMsg)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reconnect:
|
||||
time.Sleep(1 * time.Second)
|
||||
log.Println("JitsiClient", j.server, j.room, "Reconnecting...")
|
||||
}
|
||||
}
|
||||
|
||||
func JitsiRunWrapper(c *irc.Client, done chan bool) {
|
||||
jitsiDone := make([]chan bool, len(jitsiChannels))
|
||||
|
||||
for i, ch := range jitsiChannels {
|
||||
args := strings.Split(ch, ",")
|
||||
if len(args) != 3 {
|
||||
log.Fatalln("Wrong jitsi channel mapping format", ch, args)
|
||||
}
|
||||
|
||||
j := JitsiClient{
|
||||
nick: nickname,
|
||||
ircChannel: args[0],
|
||||
server: args[1],
|
||||
room: args[2],
|
||||
}
|
||||
|
||||
go j.Run(c, jitsiDone[i])
|
||||
}
|
||||
|
||||
<-done
|
||||
for _, ch := range jitsiDone {
|
||||
ch <- true
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
flag.Var(&jitsiChannels, "jitsi.channels", "ircChannel,jitsiServer,jitsiRoom mapping; may be specified multiple times")
|
||||
|
||||
Runners.Add(JitsiRunWrapper)
|
||||
}
|
Loading…
Reference in New Issue