import json import requests import logging from bot import MonkeyBot from pnutlib import Pnut from flask import Flask, jsonify, request, abort from models import * app = Flask(__name__) app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False db.init_app(app) cmdbot = MonkeyBot() txId = 0 @app.route("/transactions/", methods=["PUT"]) def on_receive_events(transaction): global txId events = request.get_json()["events"] for event in events: logging.info(event) user = MatrixUser.query.filter_by(matrix_id=event["user_id"]).first() if (event['type'] == 'm.room.message' and not app.config['MATRIX_PNUT_PREFIX'] in event["user_id"] and not 'pnut-bridge' in event["user_id"]): embed = None chan = MatrixRoom.query.filter_by(room_id=event['room_id']).first() if chan: chan_id = chan.pnut_chan else: adminrm = MatrixAdminRooms.query.filter_by(room_id=event['room_id']).first() if adminrm: cmdbot.on_message(event) return jsonify({}) if (event['content']['msgtype'] == 'm.text' or event['content']['msgtype'] == 'm.notice'): if user: token = user.pnut_token text = event['content']['body'] else: token = app.config['MATRIX_PNUT_TOKEN'] text = "[" + get_displayname(event["user_id"]) + "] " + event['content']['body'] elif event['content']['msgtype'] == 'm.emote': if user: token = user.pnut_token text = "* " + user.pnut_id + " " + event['content']['body'] else: token = app.config['MATRIX_PNUT_TOKEN'] text = "* " + get_displayname(event["user_id"]) + " " + event['content']['body'] elif event['content']['msgtype'] == 'm.image': imgurl = app.config['MATRIX_HOST'] + '/_matrix/media/r0/download/' + event['content']['url'][6:] thmburl = app.config['MATRIX_HOST'] + '/_matrix/media/r0/download/' + event['content']['info']['thumbnail_url'][6:] value = {"type": "photo", "version": "1.0"} value["title"] = event['content']['body'] value["url"] = imgurl value["width"] = event['content']['info']['w'] value["height"] = event['content']['info']['h'] value["thumbnail_width"] = event['content']['info']['thumbnail_info']['w'] value["thumbnail_height"] = event['content']['info']['thumbnail_info']['h'] value["thumbnail_url"] = thmburl rawitem = {"type": "io.pnut.core.oembed", "value": value} embed = [rawitem] if user: token = user.pnut_token text = "" else: token = app.config['MATRIX_PNUT_TOKEN'] text = "[" + get_displayname(event["user_id"]) + "] " text += imgurl else: text = None if user == None and not chan.pnut_write: txId += 1 url = app.config['MATRIX_HOST'] url += '/_matrix/client/r0/rooms/{0}/redact/{1}/{2}'.format( event['room_id'], event['event_id'], txId) url += "?access_token=" + app.config['MATRIX_AS_TOKEN'] requests.put(url, headers={"Content-Type": "application/json"}, data=json.dumps({})) else: if text: r = Pnut(token).channel_post(chan_id, text, embed) if r.status_code == 201: msgid = json.loads(r.text)['data']['id'] if token == app.config['MATRIX_PNUT_TOKEN']: puser = app.config['MATRIX_PNUT_USER'] else: puser = user.pnut_id el = MatrixMsgEvents(event['event_id'], event['room_id'], msgid, puser, chan_id) db.session.add(el) db.session.commit() elif r.status_code == 401: txId += 1 err_notice = event["user_id"] + ": The pnut.io channel this room is connected with is restricted to valid " err_notice += "pnut.io users only. Direct chat this bot to associate your account." body = {'msgtype': 'm.notice', 'body': err_notice} url = app.config['MATRIX_HOST'] url += '/_matrix/client/r0/rooms/{0}/send/m.room.message/{1}'.format(event['room_id'], str(txId)) url += "?access_token=" + app.config['MATRIX_AS_TOKEN'] requests.put(url, headers={"Content-Type": "application/json"}, data=json.dumps(body)) else: logging.debug(r.text) elif event['type'] == 'm.room.redaction': r_event = MatrixMsgEvents.query.filter_by(event_id=event['redacts'],room_id=event['room_id']).first() if r_event: if r_event.pnut_user == app.config['MATRIX_PNUT_USER']: token = token == app.config['MATRIX_PNUT_TOKEN'] else: r_user = MatrixUser.query.filter_by(pnut_id=r_event.pnut_user).first() if r_user: token = r_user.pnut_token else: return jsonify({}) r = Pnut(token).delete_channel_post(r_event.pnut_chan, r_event.pnut_msgid) elif event['type'] == 'm.room.member': if event['state_key'] == app.config['MATRIX_AS_ID']: if event['content']['membership'] == 'invite' and 'is_direct' in event['content'] and event['content']['is_direct'] == True: logging.info('>> GOT PRIVATE INVITE') txId += 1 url = app.config['MATRIX_HOST'] url += '/_matrix/client/r0/join/' + event['room_id'] url += "?access_token=" + app.config['MATRIX_AS_TOKEN'] r = requests.post(url, headers={"Content-Type": "application/json"}, data=json.dumps({})) if r.status_code == 200: addadminrm = MatrixAdminRooms(matrix_id=event['sender'], room_id=event['room_id']) db.session.add(addadminrm) db.session.commit() elif event['content']['membership'] == 'invite': logging.info('>> GOT ROOM INVITE') txId += 1 url = app.config['MATRIX_HOST'] url += '/_matrix/client/r0/join/' + event['room_id'] url += "?access_token=" + app.config['MATRIX_AS_TOKEN'] r = requests.post(url, headers={"Content-Type": "application/json"}, data=json.dumps({})) elif event['membership'] == 'leave': adminrm = MatrixAdminRooms.query.filter_by(room_id=event['room_id']).first() if adminrm: logging.info('>> GOT LEAVE') txId += 1 url = app.config['MATRIX_HOST'] url += '/_matrix/client/r0/rooms/' + event['room_id'] + "/leave" url += "?access_token=" + app.config['MATRIX_AS_TOKEN'] r = requests.post(url, headers={"Content-Type": "application/json"}, data=json.dumps({})) if r.status_code == 200: db.session.delete(adminrm) db.session.commit() return jsonify({}) @app.route("/rooms/") def query_alias(alias): alias_localpart = alias.split(":")[0][1:] try: channel_id = int(alias_localpart.split('_')[1]) r = requests.get('https://api.pnut.io/v0/channels/' + str(channel_id) + '?include_raw=1') if r.status_code == 200: cdata = json.loads(r.text)['data'] if cdata['type'] != 'io.pnut.core.chat': abort(404) if 'is_active' in cdata: if not cdata['is_active']: abort(404) raw = cdata['raw'] for item in raw: if item['type'] == 'io.pnut.core.chat-settings': chan_settings = item['value'] else: abort(404) except: raise abort(404) mroom = {'room_alias_name': alias_localpart} if 'name' in chan_settings: mroom['name'] = chan_settings['name'] if 'description' in chan_settings: mroom['topic'] = chan_settings['description'] if cdata['acl']['read']['any_user']: mroom['preset'] = 'public_chat' mroom['visibility'] = 'public' resp = requests.post( # NB: "TOKEN" is the as_token referred to in registration.yaml "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token=" + app.config['MATRIX_AS_TOKEN'], json.dumps(mroom), headers={"Content-Type":"application/json"} ) if resp.status_code == 200: room_id = json.loads(resp.text)['room_id'] mro = MatrixRoom(room_id, channel_id, cdata['acl']['write']['any_user']) db.session.add(mro) db.session.commit() return jsonify({}) @app.errorhandler(404) def not_found(error): return jsonify({'errcode':'COM.MONKEYSTEW.PNUT_NOT_FOUND'}), 404 def get_displayname(userid): url = "http://localhost:8008/_matrix/client/r0/profile/" + userid r = requests.get(url) if r.status_code == 200: data = json.loads(r.text) if 'displayname' in data: return data["displayname"] return userid