diff options
Diffstat (limited to 'contrib/jitsimeetbridge/jitsimeetbridge.py')
-rw-r--r-- | contrib/jitsimeetbridge/jitsimeetbridge.py | 298 |
1 files changed, 0 insertions, 298 deletions
diff --git a/contrib/jitsimeetbridge/jitsimeetbridge.py b/contrib/jitsimeetbridge/jitsimeetbridge.py deleted file mode 100644 index b3de468687..0000000000 --- a/contrib/jitsimeetbridge/jitsimeetbridge.py +++ /dev/null @@ -1,298 +0,0 @@ -#!/usr/bin/env python - -""" -This is an attempt at bridging matrix clients into a Jitis meet room via Matrix -video call. It uses hard-coded xml strings overg XMPP BOSH. It can display one -of the streams from the Jitsi bridge until the second lot of SDP comes down and -we set the remote SDP at which point the stream ends. Our video never gets to -the bridge. - -Requires: -npm install jquery jsdom -""" -import json -import subprocess -import time - -import gevent -import grequests -from BeautifulSoup import BeautifulSoup - -ACCESS_TOKEN = "" - -MATRIXBASE = "https://matrix.org/_matrix/client/api/v1/" -MYUSERNAME = "@davetest:matrix.org" - -HTTPBIND = "https://meet.jit.si/http-bind" -# HTTPBIND = 'https://jitsi.vuc.me/http-bind' -# ROOMNAME = "matrix" -ROOMNAME = "pibble" - -HOST = "guest.jit.si" -# HOST="jitsi.vuc.me" - -TURNSERVER = "turn.guest.jit.si" -# TURNSERVER="turn.jitsi.vuc.me" - -ROOMDOMAIN = "meet.jit.si" -# ROOMDOMAIN="conference.jitsi.vuc.me" - - -class TrivialMatrixClient: - def __init__(self, access_token): - self.token = None - self.access_token = access_token - - def getEvent(self): - while True: - url = ( - MATRIXBASE - + "events?access_token=" - + self.access_token - + "&timeout=60000" - ) - if self.token: - url += "&from=" + self.token - req = grequests.get(url) - resps = grequests.map([req]) - obj = json.loads(resps[0].content) - print("incoming from matrix", obj) - if "end" not in obj: - continue - self.token = obj["end"] - if len(obj["chunk"]): - return obj["chunk"][0] - - def joinRoom(self, roomId): - url = MATRIXBASE + "rooms/" + roomId + "/join?access_token=" + self.access_token - print(url) - headers = {"Content-Type": "application/json"} - req = grequests.post(url, headers=headers, data="{}") - resps = grequests.map([req]) - obj = json.loads(resps[0].content) - print("response: ", obj) - - def sendEvent(self, roomId, evType, event): - url = ( - MATRIXBASE - + "rooms/" - + roomId - + "/send/" - + evType - + "?access_token=" - + self.access_token - ) - print(url) - print(json.dumps(event)) - headers = {"Content-Type": "application/json"} - req = grequests.post(url, headers=headers, data=json.dumps(event)) - resps = grequests.map([req]) - obj = json.loads(resps[0].content) - print("response: ", obj) - - -xmppClients = {} - - -def matrixLoop(): - while True: - ev = matrixCli.getEvent() - print(ev) - if ev["type"] == "m.room.member": - print("membership event") - if ev["membership"] == "invite" and ev["state_key"] == MYUSERNAME: - roomId = ev["room_id"] - print("joining room %s" % (roomId)) - matrixCli.joinRoom(roomId) - elif ev["type"] == "m.room.message": - if ev["room_id"] in xmppClients: - print("already have a bridge for that user, ignoring") - continue - print("got message, connecting") - xmppClients[ev["room_id"]] = TrivialXmppClient(ev["room_id"], ev["user_id"]) - gevent.spawn(xmppClients[ev["room_id"]].xmppLoop) - elif ev["type"] == "m.call.invite": - print("Incoming call") - # sdp = ev['content']['offer']['sdp'] - # print "sdp: %s" % (sdp) - # xmppClients[ev['room_id']] = TrivialXmppClient(ev['room_id'], ev['user_id']) - # gevent.spawn(xmppClients[ev['room_id']].xmppLoop) - elif ev["type"] == "m.call.answer": - print("Call answered") - sdp = ev["content"]["answer"]["sdp"] - if ev["room_id"] not in xmppClients: - print("We didn't have a call for that room") - continue - # should probably check call ID too - xmppCli = xmppClients[ev["room_id"]] - xmppCli.sendAnswer(sdp) - elif ev["type"] == "m.call.hangup": - if ev["room_id"] in xmppClients: - xmppClients[ev["room_id"]].stop() - del xmppClients[ev["room_id"]] - - -class TrivialXmppClient: - def __init__(self, matrixRoom, userId): - self.rid = 0 - self.matrixRoom = matrixRoom - self.userId = userId - self.running = True - - def stop(self): - self.running = False - - def nextRid(self): - self.rid += 1 - return "%d" % (self.rid) - - def sendIq(self, xml): - fullXml = ( - "<body rid='%s' xmlns='http://jabber.org/protocol/httpbind' sid='%s'>%s</body>" - % (self.nextRid(), self.sid, xml) - ) - # print "\t>>>%s" % (fullXml) - return self.xmppPoke(fullXml) - - def xmppPoke(self, xml): - headers = {"Content-Type": "application/xml"} - req = grequests.post(HTTPBIND, verify=False, headers=headers, data=xml) - resps = grequests.map([req]) - obj = BeautifulSoup(resps[0].content) - return obj - - def sendAnswer(self, answer): - print("sdp from matrix client", answer) - p = subprocess.Popen( - ["node", "unjingle/unjingle.js", "--sdp"], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - ) - jingle, out_err = p.communicate(answer) - jingle = jingle % { - "tojid": self.callfrom, - "action": "session-accept", - "initiator": self.callfrom, - "responder": self.jid, - "sid": self.callsid, - } - print("answer jingle from sdp", jingle) - res = self.sendIq(jingle) - print("reply from answer: ", res) - - self.ssrcs = {} - jingleSoup = BeautifulSoup(jingle) - for cont in jingleSoup.iq.jingle.findAll("content"): - if cont.description: - self.ssrcs[cont["name"]] = cont.description["ssrc"] - print("my ssrcs:", self.ssrcs) - - gevent.joinall([gevent.spawn(self.advertiseSsrcs)]) - - def advertiseSsrcs(self): - time.sleep(7) - print("SSRC spammer started") - while self.running: - ssrcMsg = ( - "<presence to='%(tojid)s' xmlns='jabber:client'><x xmlns='http://jabber.org/protocol/muc'/><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://jitsi.org/jitsimeet' ver='0WkSdhFnAUxrz4ImQQLdB80GFlE='/><nick xmlns='http://jabber.org/protocol/nick'>%(nick)s</nick><stats xmlns='http://jitsi.org/jitmeet/stats'><stat name='bitrate_download' value='175'/><stat name='bitrate_upload' value='176'/><stat name='packetLoss_total' value='0'/><stat name='packetLoss_download' value='0'/><stat name='packetLoss_upload' value='0'/></stats><media xmlns='http://estos.de/ns/mjs'><source type='audio' ssrc='%(assrc)s' direction='sendre'/><source type='video' ssrc='%(vssrc)s' direction='sendre'/></media></presence>" - % { - "tojid": "%s@%s/%s" % (ROOMNAME, ROOMDOMAIN, self.shortJid), - "nick": self.userId, - "assrc": self.ssrcs["audio"], - "vssrc": self.ssrcs["video"], - } - ) - res = self.sendIq(ssrcMsg) - print("reply from ssrc announce: ", res) - time.sleep(10) - - def xmppLoop(self): - self.matrixCallId = time.time() - res = self.xmppPoke( - "<body rid='%s' xmlns='http://jabber.org/protocol/httpbind' to='%s' xml:lang='en' wait='60' hold='1' content='text/xml; charset=utf-8' ver='1.6' xmpp:version='1.0' xmlns:xmpp='urn:xmpp:xbosh'/>" - % (self.nextRid(), HOST) - ) - - print(res) - self.sid = res.body["sid"] - print("sid %s" % (self.sid)) - - res = self.sendIq( - "<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='ANONYMOUS'/>" - ) - - res = self.xmppPoke( - "<body rid='%s' xmlns='http://jabber.org/protocol/httpbind' sid='%s' to='%s' xml:lang='en' xmpp:restart='true' xmlns:xmpp='urn:xmpp:xbosh'/>" - % (self.nextRid(), self.sid, HOST) - ) - - res = self.sendIq( - "<iq type='set' id='_bind_auth_2' xmlns='jabber:client'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/></iq>" - ) - print(res) - - self.jid = res.body.iq.bind.jid.string - print("jid: %s" % (self.jid)) - self.shortJid = self.jid.split("-")[0] - - res = self.sendIq( - "<iq type='set' id='_session_auth_2' xmlns='jabber:client'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>" - ) - - # randomthing = res.body.iq['to'] - # whatsitpart = randomthing.split('-')[0] - - # print "other random bind thing: %s" % (randomthing) - - # advertise preence to the jitsi room, with our nick - res = self.sendIq( - "<iq type='get' to='%s' xmlns='jabber:client' id='1:sendIQ'><services xmlns='urn:xmpp:extdisco:1'><service host='%s'/></services></iq><presence to='%s@%s/d98f6c40' xmlns='jabber:client'><x xmlns='http://jabber.org/protocol/muc'/><c xmlns='http://jabber.org/protocol/caps' hash='sha-1' node='http://jitsi.org/jitsimeet' ver='0WkSdhFnAUxrz4ImQQLdB80GFlE='/><nick xmlns='http://jabber.org/protocol/nick'>%s</nick></presence>" - % (HOST, TURNSERVER, ROOMNAME, ROOMDOMAIN, self.userId) - ) - self.muc = {"users": []} - for p in res.body.findAll("presence"): - u = {} - u["shortJid"] = p["from"].split("/")[1] - if p.c and p.c.nick: - u["nick"] = p.c.nick.string - self.muc["users"].append(u) - print("muc: ", self.muc) - - # wait for stuff - while True: - print("waiting...") - res = self.sendIq("") - print("got from stream: ", res) - if res.body.iq: - jingles = res.body.iq.findAll("jingle") - if len(jingles): - self.callfrom = res.body.iq["from"] - self.handleInvite(jingles[0]) - elif "type" in res.body and res.body["type"] == "terminate": - self.running = False - del xmppClients[self.matrixRoom] - return - - def handleInvite(self, jingle): - self.initiator = jingle["initiator"] - self.callsid = jingle["sid"] - p = subprocess.Popen( - ["node", "unjingle/unjingle.js", "--jingle"], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - ) - print("raw jingle invite", str(jingle)) - sdp, out_err = p.communicate(str(jingle)) - print("transformed remote offer sdp", sdp) - inviteEvent = { - "offer": {"type": "offer", "sdp": sdp}, - "call_id": self.matrixCallId, - "version": 0, - "lifetime": 30000, - } - matrixCli.sendEvent(self.matrixRoom, "m.call.invite", inviteEvent) - - -matrixCli = TrivialMatrixClient(ACCESS_TOKEN) # Undefined name - -gevent.joinall([gevent.spawn(matrixLoop)]) |