pnut-matrix/appservice.py
2017-07-28 16:16:36 -07:00

259 lines
11 KiB
Python

import json
import requests
import logging
import re
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/<transaction>", 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 = MatrixRoom2.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:]
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']
if 'thumbnail_info' in event['content']['info']:
thmburl = app.config['MATRIX_HOST'] + '/_matrix/media/r0/download/' + event['content']['info']['thumbnail_url'][6:]
value["thumbnail_width"] = event['content']['info']['thumbnail_info']['w']
value["thumbnail_height"] = event['content']['info']['thumbnail_info']['h']
value["thumbnail_url"] = thmburl
else:
value["thumbnail_width"] = event['content']['info']['w']
value["thumbnail_height"] = event['content']['info']['h']
value["thumbnail_url"] = imgurl
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
cctag = re.search('##$', text)
if cctag:
cname = get_channel_settings(chan_id)['name']
text = text[:-2]
text += '\n\n[' + cname + "](https://patter.chat/room.html?channel=" + chan_id + ")"
r = Pnut(token).post(text)
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. Start a direct chat to link 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['content']['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/<alias>")
def query_alias(alias):
alias_localpart = alias.split(":")[0][1:]
channel_id = int(alias_localpart.split('_')[1])
# prevent room from being created if channel is already plumbed
chroom = MatrixRoom2.query.filter_by(pnut_chan=channel_id).first()
if chroom:
abort(404)
try:
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 = MatrixRoom2(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
def get_channel_settings(channel_id):
chan_settings = {}
r = requests.get('https://api.pnut.io/v0/channels/' + str(channel_id) + '?include_raw=1')
if r.status_code == 200:
cdata = r.json()['data']
raw = cdata['raw']
for item in raw:
if item['type'] == 'io.pnut.core.chat-settings':
chan_settings = item['value']
return chan_settings