pnut-matrix/appservice.py
2018-01-09 17:22:35 -08:00

268 lines
11 KiB
Python

import json
import requests
import logging
import re
import pnutpy
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:
pnutpy.api.add_authorization_token(token)
try:
msg, meta = pnutpy.api.create_message(chan_id, data={'text': text, 'raw': embed})
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, meta = pnutpy.api.create_post(data={'text': text})
el = MatrixMsgEvents(event['event_id'], event['room_id'], msg.id, puser, chan_id)
db.session.add(el)
db.session.commit()
except pnutpy.errors.PnutAuthAPIException:
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))
except:
logging.debug('*** an error occured while posting a message ***')
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({})
pnutpy.api.add_authorization_token(token)
r, meta = pnutpy.api.delete_message(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()
else:
print(r.status_code)
print(r.text)
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