We can into jitsi now

main
Robert Gerus 2022-01-16 00:41:25 +01:00
parent 9434cb3752
commit 9696835270
4 changed files with 271 additions and 1 deletions

5
go.mod
View File

@ -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
View File

@ -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=

View File

@ -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]

220
jitsi.go Normal file
View File

@ -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)
}