├── .gitignore ├── .kdev4 └── matrix-xmpp-bridge.kdev4 ├── README.md ├── appservice.py ├── matrix-xmpp-bridge.kdev4 ├── mxbridge.conf.example ├── registration.yaml.example ├── schema.sql └── xmpp_component.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *swp 3 | registration.yaml 4 | -------------------------------------------------------------------------------- /.kdev4/matrix-xmpp-bridge.kdev4: -------------------------------------------------------------------------------- 1 | [Buildset] 2 | BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00$\x00m\x00a\x00t\x00r\x00i\x00x\x00-\x00x\x00m\x00p\x00p\x00-\x00b\x00r\x00i\x00d\x00g\x00e) 3 | 4 | [Defines And Includes][Compiler] 5 | Name=GCC 6 | Path=gcc 7 | Type=GCC 8 | 9 | [Project] 10 | VersionControlSupport=kdevgit 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | NOTE: This project is no longer maintained (and never really made it past an experimental stage anyway) 2 | 3 | # Matrix-XMPP Bridge 4 | This project creates a bridge between a Matrix room and an XMPP MUC. It is currently very early in development and only relays messages one way (from XMPP to Matrix). Use it if you wish, but don't blame me if it blows up in your face. 5 | 6 | ## Using 7 | - Add an AS and HS token to registration.yaml and reference it in your homeserver config as described [here](http://matrix.org/blog/2015/03/02/introduction-to-application-services/) 8 | - Edit mxbridge.conf.example with user and room details for the Matrix/XMPP rooms you would like to bridge and save as /etc/mxbridge.conf 9 | - Start appservice.py and xmpp_component.py in a screen session 10 | 11 | ## Dependencies 12 | - sleekxmpp 13 | - configparser 14 | - requests 15 | - flask 16 | -------------------------------------------------------------------------------- /appservice.py: -------------------------------------------------------------------------------- 1 | # app_service.py: 2 | 3 | import json, requests 4 | from flask import Flask, jsonify, request 5 | import xmpp_component as xmpp 6 | import ConfigParser 7 | app = Flask(__name__) 8 | 9 | config = ConfigParser.ConfigParser() 10 | config.read('/etc/mxbridge.conf') 11 | TOKEN = config.get("Matrix", "token") 12 | APIURL = config.get("Matrix", "api_url") 13 | 14 | # Observe all the things forever 15 | # TODO: Send these to XMPP 16 | @app.route("/transactions/", methods=["PUT"]) 17 | def on_receive_events(transaction): 18 | events = request.get_json()["events"] 19 | #if(event['type'] == 'm.room.message'): 20 | #xmpp.sendMessage(event['user_id'] + ': ' + event['content']['body']) 21 | for event in events: 22 | print "User: %s Room: %s" % (event["user_id"], event["room_id"]) 23 | print "Event Type: %s" % event["type"] 24 | print "Content: %s" % event["content"] 25 | return jsonify({}) 26 | 27 | # Create this room if it doesn't exist 28 | @app.route("/rooms/") 29 | def query_alias(alias): 30 | alias_localpart = alias.split(":")[0][1:] 31 | createRoom(TOKEN, alias_localpart) 32 | print('Room alias: ' + alias) 33 | return jsonify({}) 34 | 35 | def createRoom(as_token, alias_localpart): 36 | roomCreateURL = APIURL + "/createRoom?access_token=" + as_token 37 | data = json.dumps({ 38 | "room_alias_name": alias_localpart 39 | }) 40 | resp = requests.post(roomCreateURL, data=data, headers={"Content-Type": "application/json"}) 41 | print('Room creation URL: ' + roomCreateURL) 42 | print('Data: ' + data) 43 | print(resp.text) 44 | 45 | @app.route("/mxbridge/send", methods=["POST"]) 46 | def sendMessage(): 47 | print(request.data) 48 | req = request.get_json() 49 | 50 | sender = req["from"] 51 | #sender = request.args.get('from') 52 | xmppRecipient = req["to"] 53 | #xmppRecipient = request.args.get('to') 54 | message = req["body"] 55 | #message = request.args.get('body') 56 | print('Sending to room: ' + str(xmppRecipient)) 57 | 58 | #recipient = xmppMap(xmppRecipient) 59 | 60 | joinRoom(token=TOKEN, roomid=xmppRecipient) 61 | sendMessageURL = APIURL + "/rooms/" + xmppRecipient + '/send/m.room.message?access_token=' + TOKEN 62 | body = json.dumps({ 63 | "msgtype": "m.text", 64 | "body": message 65 | }) 66 | requests.post(sendMessageURL, data=body, headers={"Content-Type": "application/json"}) 67 | 68 | return jsonify({}) 69 | 70 | 71 | def joinRoom(token, roomid): 72 | if(token != None and roomid != None): 73 | requests.post(APIURL + '/' + roomid + '/join?access_token=' + token) 74 | elif(token == None): 75 | print("Must include access token!") 76 | elif(roomid == None): 77 | print("Must include roomid!") 78 | 79 | 80 | if __name__ == "__main__": 81 | app.config['TRAP_BAD_REQUEST_ERRORS'] = True 82 | app.run(debug=True) 83 | -------------------------------------------------------------------------------- /matrix-xmpp-bridge.kdev4: -------------------------------------------------------------------------------- 1 | [Project] 2 | Manager=KDevGenericManager 3 | Name=matrix-xmpp-bridge 4 | -------------------------------------------------------------------------------- /mxbridge.conf.example: -------------------------------------------------------------------------------- 1 | [Matrix] 2 | token=supersekrettoken 3 | api_url=http://localhost:8008/_matrix/client/api/v1 4 | room_id=!CvcvRuDYDzTOzfKKgh:localhost 5 | as_api_url=http://localhost:5000 6 | 7 | [XMPP] 8 | muc_room=test@conference.example.com 9 | nick=mxbridge 10 | username=test@example.com 11 | #password=supersekret -------------------------------------------------------------------------------- /registration.yaml.example: -------------------------------------------------------------------------------- 1 | # registration.yaml 2 | 3 | # this is the base URL of the application service 4 | url: "http://localhost:5000" 5 | 6 | # This is the token that the AS should use as its access_token when using the Client-Server API 7 | # This can be anything you want. 8 | as_token: supersekret 9 | 10 | # This is the token that the HS will use when sending requests to the AS. 11 | # This can be anything you want. 12 | hs_token: supersekret 13 | 14 | # this is the local part of the desired user ID for this AS (in this case @logging:localhost) 15 | sender_localpart: mxbridge 16 | namespaces: 17 | users: [] 18 | rooms: [] 19 | aliases: 20 | - exclusive: false 21 | regex: "#xmpp.*" 22 | -------------------------------------------------------------------------------- /schema.sql: -------------------------------------------------------------------------------- 1 | drop table if exists virtual_users; 2 | create table usermap ( 3 | id integer primary key autoincrement, 4 | xmpp_user text not null, 5 | matrix_user text not null 6 | ); 7 | create table roommap ( 8 | id integer primary key autoincrement, 9 | xmpp_room text not null, 10 | matrix_room text not null 11 | ); 12 | create table room_membership ( 13 | id integer primary key autoincrement, 14 | room text not null, 15 | room integer not null 16 | ) -------------------------------------------------------------------------------- /xmpp_component.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import logging 5 | import getpass 6 | from optparse import OptionParser 7 | import requests 8 | import json 9 | import ConfigParser 10 | 11 | import sleekxmpp 12 | 13 | config = ConfigParser.ConfigParser() 14 | config.read('/etc/mxbridge.conf') 15 | MXAPI = config.get("Matrix", "as_api_url") 16 | MXROOM = config.get("Matrix", "room_id") 17 | 18 | class BridgeBot(sleekxmpp.ClientXMPP): 19 | def __init__(self, jid, password, room, nick): 20 | sleekxmpp.ClientXMPP.__init__(self, jid, password) 21 | self.room = room 22 | self.nick = nick 23 | 24 | self.add_event_handler("session_start", self.start) 25 | self.add_event_handler("groupchat_message", self.muc_message) 26 | 27 | def start(self, event): 28 | self.get_roster() 29 | self.send_presence() 30 | self.plugin['xep_0045'].joinMUC(self.room, 31 | self.nick, 32 | wait=True) 33 | 34 | def muc_message(self, msg): 35 | if(msg['mucnick'] != self.nick): 36 | data = {"from": "test", "to": MXROOM, "body": msg['mucnick'] + ": " + msg['body']} 37 | requests.post(MXAPI + "/mxbridge/send", data=json.dumps(data), headers={"Content-Type": "application/json"}) 38 | 39 | #if sys.version_info < (3, 0): 40 | #reload(sys) 41 | #sys.setdefaultencoding('utf8') 42 | 43 | 44 | 45 | if(__name__ == '__main__'): 46 | jid = config.get("XMPP", "username") 47 | room = config.get("XMPP", "muc_room") 48 | nick = config.get("XMPP", "nick") 49 | 50 | try: 51 | password = config.get("XMPP", "password") 52 | except ConfigParser.NoOptionError: 53 | password = getpass.getpass("Password: ") 54 | 55 | xmpp = BridgeBot(jid, password, room, nick) 56 | xmpp.register_plugin('xep_0045') 57 | 58 | if xmpp.connect(): 59 | try: 60 | xmpp.process(block=True) 61 | except TypeError: 62 | xmpp.process(threaded=False) # Used for older versions of SleekXMPP 63 | print("Done") 64 | else: 65 | print("Unable to connect.") --------------------------------------------------------------------------------