Morgan McMillian
c75b7517ca
When creating of the matrix room is triggered, the pnut attribute channel.acl.read.public flag should be used to determine if the matrix room preset should be set to public or private. Issue #58
841 lines
29 KiB
Python
841 lines
29 KiB
Python
import json
|
|
import requests
|
|
import logging
|
|
import re
|
|
import pnutpy
|
|
import textwrap
|
|
import time
|
|
|
|
from matrix_client.api import MatrixHttpApi
|
|
from matrix_client.api import MatrixError, MatrixRequestError
|
|
from models import Avatars, Rooms, Events, Users, DirectRooms, ControlRooms
|
|
from database import db_session
|
|
from sqlalchemy import and_
|
|
from flask import Flask, jsonify, request, abort
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
app = Flask(__name__)
|
|
|
|
@app.errorhandler(404)
|
|
def not_found(error):
|
|
return jsonify({'errcode':'COM.MONKEYSTEW.PNUT_NOT_FOUND'}), 404
|
|
|
|
@app.errorhandler(403)
|
|
def forbidden(error):
|
|
return jsonify({'errcode':'COM.MONKEYSTEW.PNUT_FORBIDDEN'}), 403
|
|
|
|
@app.teardown_appcontext
|
|
def shutdown_session(exception=None):
|
|
db_session.remove()
|
|
|
|
@app.route("/rooms/<alias>")
|
|
def query_alias(alias):
|
|
alias_localpart = alias.split(":")[0][1:]
|
|
channel_id = int(alias_localpart.split('_')[1])
|
|
|
|
room = Rooms.query.filter(Rooms.pnut_chan == channel_id).one_or_none()
|
|
if room is not None:
|
|
abort(404)
|
|
|
|
token = app.config['MATRIX_PNUT_TOKEN']
|
|
pnutpy.api.add_authorization_token(token)
|
|
try:
|
|
channel, meta = pnutpy.api.get_channel(channel_id, include_raw=1)
|
|
|
|
if 'is_active' in channel and channel.is_active == False:
|
|
logger.debug("-channel isn't active-")
|
|
abort(404)
|
|
|
|
channel_settings = {}
|
|
for item in channel.raw:
|
|
if item.type == 'io.pnut.core.chat-settings':
|
|
channel_settings = item.value
|
|
|
|
# Matrix sdk doesn't include all details in a single call
|
|
room = {'room_alias_name': alias_localpart}
|
|
if 'name' in channel_settings:
|
|
room['name'] = channel_settings['name']
|
|
if 'description' in channel_settings:
|
|
room['topic'] = channel_settings['description']
|
|
if channel.acl.read.public:
|
|
room['preset'] = 'public_chat'
|
|
room['visibility'] = 'public'
|
|
else:
|
|
abort(401)
|
|
|
|
url = app.config['MATRIX_HOST'] + '/_matrix/client/api/v1/createRoom?access_token='
|
|
url += app.config['MATRIX_AS_TOKEN']
|
|
headers = {"Content-Type":"application/json"}
|
|
r = requests.post(url, headers=headers, data=json.dumps(room))
|
|
if r.status_code == 200:
|
|
pnutpy.api.subscribe_channel(channel_id)
|
|
rdata = r.json()
|
|
rr = Rooms(
|
|
room_id=rdata['room_id'],
|
|
pnut_chan=channel_id,
|
|
portal=True
|
|
)
|
|
db_session.add(rr)
|
|
db_session.commit()
|
|
|
|
except pnutpy.errors.PnutPermissionDenied:
|
|
abort(401)
|
|
|
|
except Exception:
|
|
logger.exception("-couldn't get the pnut channel-")
|
|
abort(404)
|
|
|
|
return jsonify({})
|
|
|
|
@app.route("/transactions/<transaction>", methods=["PUT"])
|
|
def on_receive_events(transaction):
|
|
|
|
access_token = request.args.get('access_token', '')
|
|
if access_token != app.config['MATRIX_HS_TOKEN']:
|
|
abort(403)
|
|
|
|
events = request.get_json()["events"]
|
|
for event in events:
|
|
logger.debug(event)
|
|
|
|
if app.config['MATRIX_ADMIN_ROOM'] and app.config['MATRIX_ADMIN_ROOM'] == event['room_id']:
|
|
return on_admin_event(event)
|
|
|
|
user = Users.query.filter(Users.matrix_id == event['user_id']).one_or_none()
|
|
|
|
if event['type'] == 'm.room.message':
|
|
new_message(event, user)
|
|
|
|
elif event['type'] == 'm.room.redaction':
|
|
delete_message(event, user)
|
|
|
|
elif event['type'] == 'm.room.member':
|
|
if 'is_direct' in event['content'] and 'membership' in event['content']:
|
|
if event['content']['membership'] == "invite" and event['content']['is_direct']:
|
|
return on_direct_invite(event)
|
|
|
|
if 'membership' in event['content']:
|
|
if event['content']['membership'] == "leave":
|
|
return on_leave_event(event)
|
|
|
|
logger.debug("----room member event----")
|
|
logger.debug(user)
|
|
logger.debug(event)
|
|
|
|
return jsonify({})
|
|
|
|
def new_message(event, user):
|
|
if app.config['MATRIX_PNUT_PREFIX'] in event['user_id'] or 'pnut-bridge' in event['user_id']:
|
|
logger.debug('-skipping dup event-')
|
|
return
|
|
|
|
if 'msgtype' not in event['content']:
|
|
logger.debug('-unknown message type-')
|
|
return
|
|
|
|
control = ControlRooms.query.filter(ControlRooms.room_id == event['room_id']).one_or_none()
|
|
if control is not None:
|
|
return on_control_message(event)
|
|
|
|
direct = DirectRooms.query.filter(DirectRooms.room_id == event['room_id']).one_or_none()
|
|
if direct is not None:
|
|
return on_direct_message(event, user, direct)
|
|
|
|
room = Rooms.query.filter(Rooms.room_id == event['room_id']).one_or_none()
|
|
if room is None:
|
|
logger.debug('-room not mapped-')
|
|
return
|
|
|
|
if user is not None:
|
|
token = user.pnut_user_token
|
|
prefix = ""
|
|
else:
|
|
token = app.config['MATRIX_PNUT_TOKEN']
|
|
matrix_profile = get_profile(event['user_id'])
|
|
if "displayname" in matrix_profile:
|
|
prefix = "[" + matrix_profile['displayname'] + "] (" + event['user_id'] + ")\n"
|
|
else:
|
|
prefix = "(" + event['user_id'] + ")\n"
|
|
pnutpy.api.add_authorization_token(token)
|
|
|
|
embed = [crosspost_raw(event)]
|
|
evtext, evraw = msg_from_event(event)
|
|
text = prefix + evtext
|
|
if len(evraw) > 0:
|
|
embed.append(evraw)
|
|
try:
|
|
msg, meta = pnutpy.api.create_message(room.pnut_chan, data={'text': text, 'raw': embed})
|
|
revent = Events(
|
|
event_id=event['event_id'],
|
|
room_id=event['room_id'],
|
|
pnut_msg_id=msg.id,
|
|
pnut_user_id=msg.user.id,
|
|
pnut_chan_id=room.pnut_chan,
|
|
deleted=False
|
|
)
|
|
db_session.add(revent)
|
|
db_session.commit()
|
|
|
|
if user is not None:
|
|
cctag = re.search('##$', text)
|
|
if cctag:
|
|
raw = []
|
|
cname = get_channel_settings(room.pnut_chan)['name']
|
|
text = text[:-2]
|
|
ftext = '\n\n[' + cname + "](https://patter.chat/room.html?channel=" + str(room.pnut_chan) + ")"
|
|
mtext = textwrap.wrap(text + ftext, 254)
|
|
if len(mtext) > 1:
|
|
longpost = {
|
|
'title': "",
|
|
'body': text,
|
|
'tstamp': time.time() * 1000
|
|
}
|
|
pretext = textwrap.wrap(text, 100)
|
|
text = pretext[0]
|
|
text += "... - https://longpo.st/p/{object_id} - #longpost"
|
|
raw.append({'type':"nl.chimpnut.blog.post", 'value': longpost})
|
|
|
|
text += ftext
|
|
r, meta = pnutpy.api.create_post(data={'text': text, 'raw': raw})
|
|
|
|
except pnutpy.errors.PnutAuthAPIException:
|
|
logger.exception('-unable to post to pnut channel-')
|
|
return
|
|
|
|
except Exception:
|
|
logger.exception('-something bad happened here-')
|
|
return
|
|
|
|
def msg_from_event(event):
|
|
text = None
|
|
raw = {}
|
|
if event['content']['msgtype'] == 'm.text' or event['content']['msgtype'] == 'm.notice':
|
|
text = event['content']['body']
|
|
|
|
elif event['content']['msgtype'] == 'm.emote':
|
|
text = "* " + event['content']['body']
|
|
|
|
elif event['content']['msgtype'] == 'm.image':
|
|
text = event['content']['body'] + "\n"
|
|
text += app.config['MATRIX_URL'] + '/_matrix/media/r0/download/' + event['content']['url'][6:]
|
|
raw = raw_from_event(event)
|
|
|
|
elif event['content']['msgtype'] == 'm.video':
|
|
text = event['content']['body'] + "\n"
|
|
text += app.config['MATRIX_URL'] + '/_matrix/media/r0/download/' + event['content']['url'][6:]
|
|
|
|
elif event['content']['msgtype'] == 'm.audio':
|
|
text = event['content']['body'] + "\n"
|
|
text += app.config['MATRIX_URL'] + '/_matrix/media/r0/download/' + event['content']['url'][6:]
|
|
|
|
elif event['content']['msgtype'] == 'm.file':
|
|
text = event['content']['body'] + "\n"
|
|
text += app.config['MATRIX_URL'] + '/_matrix/media/r0/download/' + event['content']['url'][6:]
|
|
|
|
else:
|
|
logger.debug('-unknown msg type- ' + event['content']['msgtype'])
|
|
return
|
|
|
|
return text, raw
|
|
|
|
def crosspost_raw(event):
|
|
cross_profile = {'username': event['user_id']}
|
|
matrix_profile = get_profile(event['user_id'])
|
|
if "avatar_url" in matrix_profile:
|
|
cross_profile['avatar_image'] = app.config['MATRIX_URL'] + '/_matrix/media/r0/download/' + matrix_profile['avatar_url'][6:]
|
|
|
|
embed = {
|
|
'type': "io.pnut.core.crosspost",
|
|
'value': {
|
|
'canonical_url': "https://matrix.to/#/" + event['room_id'] + "/" + event['event_id'] + ":" + app.config['MATRIX_DOMAIN'],
|
|
'source': {
|
|
'name': "matrix",
|
|
'url': "https://matrix.org"
|
|
},
|
|
'user': cross_profile
|
|
}
|
|
}
|
|
return embed
|
|
|
|
def raw_from_event(event):
|
|
url = app.config['MATRIX_URL'] + '/_matrix/media/r0/download/' + event['content']['url'][6:]
|
|
|
|
if event['content']['msgtype'] == 'm.image':
|
|
value = {'type': "photo", 'version': "1.0"}
|
|
value['url'] = url
|
|
value['title'] = event['content']['body']
|
|
if 'info' in event['content']:
|
|
if 'w' in event['content']['info']:
|
|
value['width'] = event['content']['info']['w']
|
|
else:
|
|
value['width'] = 200
|
|
if 'h' in event['content']['info']:
|
|
value['height'] = event['content']['info']['h']
|
|
else:
|
|
value['height'] = 200
|
|
if 'thumbnail_info' in event['content']['info']:
|
|
if 'thumbnail_url' in event['content']['info']:
|
|
value['thumbnail_url'] = app.config['MATRIX_URL'] + '/_matrix/media/r0/download/' + event['content']['info']['thumbnail_url'][6:]
|
|
if 'w' in event['content']['info']['thumbnail_info']:
|
|
value['thumbnail_width'] = event['content']['info']['thumbnail_info']['w']
|
|
else:
|
|
value['thumbnail_width'] = 200
|
|
if 'h' in event['content']['info']['thumbnail_info']:
|
|
value['thumbnail_height'] = event['content']['info']['thumbnail_info']['h']
|
|
else:
|
|
value['thumbnail_height'] = 200
|
|
|
|
elif event['content']['msgtype'] == 'm.video':
|
|
# TODO: Need to sort out the oembed for this media type
|
|
value = {'type': "html5video", 'version': "1.0"}
|
|
source = {'url': url}
|
|
value['title'] = event['content']['body']
|
|
if 'info' in event['content']:
|
|
value['width'] = event['content']['info']['w']
|
|
value['height'] = event['content']['info']['h']
|
|
source['type'] = event['content']['info']['mimetype']
|
|
else:
|
|
return None
|
|
value['sources'] = [source]
|
|
|
|
elif event['content']['msgtype'] == 'm.audio':
|
|
# TODO: Need to sort out the oembed for this media type
|
|
value = {'type': "audio", 'version': "1.0"}
|
|
return None
|
|
else:
|
|
return None
|
|
|
|
return {'type': "io.pnut.core.oembed", 'value': value}
|
|
|
|
def delete_message(event, user):
|
|
|
|
# TODO: should there be moderator handled redactions?
|
|
|
|
if user is not None:
|
|
token = user.pnut_user_token
|
|
else:
|
|
token = app.config['MATRIX_PNUT_TOKEN']
|
|
pnutpy.api.add_authorization_token(token)
|
|
|
|
e = Events.query.filter(and_(Events.event_id == event['redacts'], Events.deleted == False)).one_or_none()
|
|
if e is None:
|
|
logger.debug("- can't find the event to remove -")
|
|
return
|
|
|
|
try:
|
|
r, meta = pnutpy.api.delete_message(e.pnut_chan_id, e.pnut_msg_id)
|
|
e.deleted = True
|
|
db_session.commit()
|
|
|
|
except pnutpy.errors.PnutPermissionDenied as e:
|
|
pass
|
|
|
|
def get_profile(userid):
|
|
url = app.config['MATRIX_HOST'] + "/_matrix/client/r0/profile/" + userid
|
|
r = requests.get(url)
|
|
if r.status_code == 200:
|
|
return json.loads(r.text)
|
|
return userid
|
|
|
|
def get_channel_settings(channel_id):
|
|
channel_settings = {}
|
|
try:
|
|
channel, meta = pnutpy.api.get_channel(channel_id, include_raw=1)
|
|
|
|
for item in channel.raw:
|
|
if item.type == 'io.pnut.core.chat-settings':
|
|
channel_settings = item.value
|
|
|
|
except Exception:
|
|
logger.exception('-unable to get channel settings-')
|
|
|
|
return channel_settings
|
|
|
|
def create_room(channel, user):
|
|
channel_settings = {}
|
|
for item in channel.raw:
|
|
if item.type == 'io.pnut.core.chat-settings':
|
|
channel_settings = item.value
|
|
|
|
# Matrix sdk doesn't include all details in a single call
|
|
room = {'room_alias_name': app.config['MATRIX_PNUT_PREFIX'] + channel.id}
|
|
logger.debug(user)
|
|
logger.debug(room)
|
|
room['invite'] = [user.matrix_id]
|
|
if 'name' in channel_settings:
|
|
room['name'] = channel_settings['name']
|
|
if 'description' in channel_settings:
|
|
room['topic'] = channel_settings['description']
|
|
if channel.acl.read.public:
|
|
room['preset'] = 'public_chat'
|
|
room['visibility'] = 'public'
|
|
elif channel.acl.read.any_user or channel.acl.read.you:
|
|
room['preset'] = 'private_chat'
|
|
room['visibility'] = 'private'
|
|
else:
|
|
abort(401)
|
|
|
|
url = app.config['MATRIX_HOST'] + '/_matrix/client/api/v1/createRoom?access_token='
|
|
url += app.config['MATRIX_AS_TOKEN']
|
|
headers = {"Content-Type":"application/json"}
|
|
r = requests.post(url, headers=headers, data=json.dumps(room))
|
|
|
|
if r.status_code == 200:
|
|
#pnutpy.api.subscribe_channel(channel.id)
|
|
rdata = r.json()
|
|
rr = Rooms(
|
|
room_id=rdata['room_id'],
|
|
pnut_chan=channel.id,
|
|
portal=True
|
|
)
|
|
db_session.add(rr)
|
|
db_session.commit()
|
|
logger.debug(r.status_code)
|
|
logger.debug(r)
|
|
|
|
def new_matrix_user(username):
|
|
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
|
token=app.config['MATRIX_AS_TOKEN'])
|
|
data = {'type': 'm.login.application_service','user': app.config['MATRIX_PNUT_PREFIX'] + username}
|
|
try:
|
|
matrix_api.register(content=data)
|
|
|
|
except Exception:
|
|
errmsg = "- new_matrix_user user already exists -"
|
|
logger.warning(errmsg)
|
|
|
|
def on_admin_event(event):
|
|
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
|
token=app.config['MATRIX_AS_TOKEN'])
|
|
logger.debug("- admin room event recieved -")
|
|
|
|
if event['type'] != 'm.room.message':
|
|
return jsonify({})
|
|
|
|
msg = event['content']['body'].split(' ')
|
|
|
|
try:
|
|
if msg[0] == 'help':
|
|
if len(msg) > 1:
|
|
matrix_api.send_message(event['room_id'], cmd_admin_help(msg[1]))
|
|
else:
|
|
matrix_api.send_message(event['room_id'], cmd_admin_help())
|
|
|
|
elif msg[0] == 'list':
|
|
matrix_api.send_message(event['room_id'], cmd_admin_list())
|
|
|
|
elif msg[0] == 'unlink':
|
|
if len(msg) > 1:
|
|
matrix_api.send_message(event['room_id'], cmd_admin_unlink(msg[1]))
|
|
else:
|
|
matrix_api.send_message(event['room_id'], cmd_admin_help('unlink'))
|
|
|
|
elif msg[0] == 'link':
|
|
if len(msg) > 2:
|
|
matrix_api.send_message(event['room_id'], cmd_admin_link(msg[1], msg[2]))
|
|
else:
|
|
matrix_api.send_message(event['room_id'], cmd_admin_help('link'))
|
|
|
|
except Exception:
|
|
errmsg = "- on_admin_event -"
|
|
logger.exception(errmsg)
|
|
|
|
return jsonify({})
|
|
|
|
def cmd_admin_help(cmd=None):
|
|
|
|
help_usage = "help [command]"
|
|
help_desc = "Show information about available commands."
|
|
list_usage = "list"
|
|
list_desc = "List the rooms currently linked with pnut.io."
|
|
unlink_usage = "unlink <room_id> | <pnut_channel_id>"
|
|
unlink_desc = "Unlink a room between Matrix and pnut.io."
|
|
link_usage = "link <room_id> <pnut_channel_id>"
|
|
link_desc = "Link a room between Matrix and pnut.io."
|
|
|
|
if cmd == 'help':
|
|
text = "usage: " + help_usage + "\n\n"
|
|
text += help_desc
|
|
if cmd == 'list':
|
|
text = "usage: " + list_usage + "\n\n"
|
|
text += list_desc
|
|
elif cmd == 'unlink':
|
|
text = "usage: " + unlink_usage + "\n\n"
|
|
text += unlink_desc
|
|
elif cmd == 'link':
|
|
text = "usage: " + link_usage + "\n\n"
|
|
text += link_desc
|
|
else:
|
|
text = "The following commands are available:\n\n"
|
|
text += help_usage + "\n"
|
|
text += list_usage + "\n"
|
|
text += unlink_usage + "\n"
|
|
text += link_usage + "\n"
|
|
|
|
return text
|
|
|
|
def cmd_admin_list():
|
|
text = ""
|
|
rooms = Rooms.query.all()
|
|
|
|
if len(rooms) > 0:
|
|
text = "ID\tMATRIX ID\tPNUT CHANNEL\n"
|
|
else:
|
|
text = " - no rooms are currently linked - \n"
|
|
|
|
for room in rooms:
|
|
text += str(room.id) + '\t'
|
|
text += room.room_id + '\t\t\t\t\t'
|
|
text += str(room.pnut_chan) + '\t'
|
|
if room.portal:
|
|
text += "(portal)"
|
|
text += '\n'
|
|
|
|
return text
|
|
|
|
def cmd_admin_link(room_id, pnut_chan_id):
|
|
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
|
token=app.config['MATRIX_AS_TOKEN'])
|
|
pnutpy.api.add_authorization_token(app.config['MATRIX_PNUT_TOKEN'])
|
|
|
|
mrcheck = Rooms.query.filter(Rooms.room_id == room_id).one_or_none()
|
|
pncheck = Rooms.query.filter(Rooms.pnut_chan == pnut_chan_id).one_or_none()
|
|
|
|
if mrcheck is not None or pncheck is not None:
|
|
return "- room may already be linked -"
|
|
|
|
try:
|
|
channel, meta = pnutpy.api.subscribe_channel(pnut_chan_id)
|
|
r = matrix_api.join_room(room_id)
|
|
|
|
rec = Rooms(
|
|
room_id=room_id,
|
|
pnut_chan=channel.id,
|
|
portal=False
|
|
)
|
|
db_session.add(rec)
|
|
db_session.commit()
|
|
|
|
except pnutpy.errors.PnutAuthAPIException:
|
|
errmsg = "- unable to subscribe to channel -"
|
|
logger.exception(errmsg)
|
|
return errmsg
|
|
|
|
except Exception:
|
|
errmsg = "- unable to link room for some reason -"
|
|
logger.exception(errmsg)
|
|
return errmsg
|
|
|
|
def cmd_admin_unlink(rid):
|
|
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
|
token=app.config['MATRIX_AS_TOKEN'])
|
|
pnutpy.api.add_authorization_token(app.config['MATRIX_PNUT_TOKEN'])
|
|
|
|
if rid.startswith('!'):
|
|
room = Rooms.query.filter(Rooms.room_id == rid).one_or_none()
|
|
else:
|
|
room = Rooms.query.filter(Rooms.pnut_chan == rid).one_or_none()
|
|
|
|
if hasattr(room, 'portal'):
|
|
if room.portal:
|
|
alias = "#" + app.config['MATRIX_PNUT_PREFIX']
|
|
alias += str(room.pnut_chan) + ":"
|
|
alias += app.config['MATRIX_DOMAIN']
|
|
matrix_api.remove_room_alias(alias)
|
|
|
|
# Kicking users needs at least moderator privs
|
|
members = matrix_api.get_room_members(room.room_id)
|
|
reason = "Portal room has been unlinked by administrator"
|
|
for m in members['chunk']:
|
|
if m['content']['membership'] == 'join' and m['sender'] != app.config['MATRIX_AS_ID']:
|
|
if room.portal:
|
|
matrix_api.kick_user(room.room_id, m['sender'], reason=reason)
|
|
else:
|
|
if m['sender'].startswith("@" + app.config['MATRIX_PNUT_PREFIX']):
|
|
matrix_api.kick_user(room.room_id, m['sender'], reason=reason)
|
|
|
|
try:
|
|
channel, meta = pnutpy.api.unsubscribe_channel(room.pnut_chan)
|
|
matrix_api.leave_room(room.room_id)
|
|
|
|
if room is not None:
|
|
db_session.delete(room)
|
|
db_session.commit()
|
|
return "- room has been unlinked -"
|
|
else:
|
|
return "- unable to locate room to unlink -"
|
|
|
|
except Exception:
|
|
errmsg = "- error while unlinking room -"
|
|
logger.exception(errmsg)
|
|
return errmsg
|
|
|
|
def on_direct_invite(event):
|
|
if event['state_key'] == app.config['MATRIX_AS_ID']:
|
|
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
|
token=app.config['MATRIX_AS_TOKEN'])
|
|
dm = ControlRooms(room_id=event['room_id'])
|
|
|
|
else:
|
|
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
|
identity=event['state_key'],
|
|
token=app.config['MATRIX_AS_TOKEN'])
|
|
bridge_user = event['state_key']
|
|
pnut_user = bridge_user.replace(app.config['MATRIX_PNUT_PREFIX'],'').split(':')[0]
|
|
|
|
user = Users.query.filter(Users.matrix_id == event['sender']).one_or_none()
|
|
if user is not None:
|
|
# TODO: need to handle if the user isn't registered
|
|
pnutpy.api.add_authorization_token(user.pnut_user_token)
|
|
channel, meta = pnutpy.api.existing_pm(ids=pnut_user)
|
|
new_matrix_user(pnut_user)
|
|
|
|
dm = DirectRooms(room_id=event['room_id'],
|
|
bridge_user=bridge_user, pnut_chan=channel.id)
|
|
|
|
try:
|
|
matrix_api.join_room(event['room_id'])
|
|
|
|
db_session.add(dm)
|
|
db_session.commit()
|
|
|
|
except Exception:
|
|
errmsg = "- on_direct_invite -"
|
|
logger.exception(errmsg)
|
|
|
|
return jsonify({})
|
|
|
|
def on_leave_event(event):
|
|
direct = DirectRooms.query.filter(DirectRooms.room_id == event['room_id']).one_or_none()
|
|
if direct is not None:
|
|
|
|
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
|
identity=direct.bridge_user,
|
|
token=app.config['MATRIX_AS_TOKEN'])
|
|
try:
|
|
matrix_api.leave_room(event['room_id'])
|
|
db_session.delete(direct)
|
|
db_session.commit()
|
|
|
|
except Exception:
|
|
errmsg = "- on_leave_event -"
|
|
logger.exception(errmsg)
|
|
|
|
control = ControlRooms.query.filter(ControlRooms.room_id == event['room_id']).one_or_none()
|
|
if control is not None:
|
|
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
|
token=app.config['MATRIX_AS_TOKEN'])
|
|
try:
|
|
matrix_api.leave_room(event['room_id'])
|
|
db_session.delete(control)
|
|
db_session.commit()
|
|
|
|
except Exception:
|
|
errmsg = "- on_leave_event -"
|
|
logger.exception(errmsg)
|
|
|
|
return jsonify({})
|
|
|
|
def on_direct_message(event, user, room):
|
|
if user is not None:
|
|
token = user.pnut_user_token
|
|
prefix = ""
|
|
else:
|
|
token = app.config['MATRIX_PNUT_TOKEN']
|
|
matrix_profile = get_profile(event['user_id'])
|
|
if "displayname" in matrix_profile:
|
|
prefix = "[" + matrix_profile['displayname'] + "] (" + event['user_id'] + ")\n"
|
|
else:
|
|
prefix = "(" + event['user_id'] + ")\n"
|
|
pnutpy.api.add_authorization_token(token)
|
|
|
|
embed = [crosspost_raw(event)]
|
|
evtext, evraw = msg_from_event(event)
|
|
text = prefix + evtext
|
|
if len(evraw) > 0:
|
|
embed.append(evraw)
|
|
try:
|
|
msg, meta = pnutpy.api.create_message(room.pnut_chan, data={'text': text, 'raw': embed})
|
|
revent = Events(
|
|
event_id=event['event_id'],
|
|
room_id=event['room_id'],
|
|
pnut_msg_id=msg.id,
|
|
pnut_user_id=msg.user.id,
|
|
pnut_chan_id=room.pnut_chan,
|
|
deleted=False
|
|
)
|
|
db_session.add(revent)
|
|
db_session.commit()
|
|
|
|
except pnutpy.errors.PnutAuthAPIException:
|
|
logger.exception('-unable to post to pnut channel-')
|
|
|
|
except Exception:
|
|
logger.exception('-something bad happened here-')
|
|
|
|
return jsonify({})
|
|
|
|
def on_control_message(event):
|
|
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
|
token=app.config['MATRIX_AS_TOKEN'])
|
|
logger.debug("- direct room event received -")
|
|
|
|
if event['type'] != 'm.room.message':
|
|
return jsonify({})
|
|
|
|
msg = event['content']['body'].split(' ')
|
|
|
|
try:
|
|
if msg[0] == '!help' or msg[0] == 'help':
|
|
if len(msg) > 1:
|
|
matrix_api.send_message(event['room_id'], cmd_user_help(msg[1]))
|
|
else:
|
|
matrix_api.send_message(event['room_id'], cmd_user_help())
|
|
|
|
elif msg[0] == '!auth':
|
|
matrix_api.send_message(event['room_id'], cmd_user_auth())
|
|
|
|
elif msg[0] == '!save':
|
|
if len(msg) > 1:
|
|
matrix_api.send_message(event['room_id'], cmd_user_save(event['sender'], msg[1]))
|
|
else:
|
|
matrix_api.send_message(event['room_id'], cmd_user_save())
|
|
|
|
elif msg[0] == '!drop':
|
|
matrix_api.send_message(event['room_id'], cmd_user_drop(event['sender']))
|
|
|
|
elif msg[0] == '!status':
|
|
matrix_api.send_message(event['room_id'], cmd_user_status(event['sender']))
|
|
|
|
elif msg[0] == '!join':
|
|
if len(msg) > 1:
|
|
matrix_api.send_message(event['room_id'], cmd_user_join(event['sender'], msg[1]))
|
|
else:
|
|
matrix_api.send_message(event['room_id'], cmd_user_join(event['sender']))
|
|
|
|
|
|
except Exception:
|
|
errmsg = "- on_direct_message -"
|
|
logger.exception(errmsg)
|
|
|
|
return jsonify({})
|
|
|
|
def cmd_user_help(cmd=None):
|
|
reply = "This is an admin room for controlling your connection to pnut.io\n"
|
|
reply += "The following commands are available.\n\n"
|
|
reply += "!auth\t\t\t- Authorize your account on pnut.io\n"
|
|
reply += "!save <token>\t- Save your pnut.io auth token\n"
|
|
reply += "!drop\t\t\t- Drop your pnut.io auth token\n"
|
|
reply += "!status\t\t\t- Status of your pnut.io auth token\n"
|
|
reply += "!join <channel #>\t- Join a private channel on pnut.io\n"
|
|
|
|
return reply
|
|
|
|
def cmd_user_auth():
|
|
reply = "Visit the following URL to authorize your account on pnut.io.\n\n"
|
|
reply += "https://pnut.io/oauth/authenticate"
|
|
reply += "?client_id=6SeCRCpCZkmZOKFLFGWbcdAeq2fX1M5t"
|
|
reply += "&redirect_uri=urn:ietf:wg:oauth:2.0:oob"
|
|
reply += "&scope=write_post,presence,messages&response_type=token\n\n"
|
|
reply += "You will be provided with a token that you store with the !save command.\n"
|
|
|
|
return reply
|
|
|
|
def cmd_user_save(sender=None, token=None):
|
|
if token is None:
|
|
reply = "You must provide a token with this command.\n"
|
|
reply += "!save <token>"
|
|
return reply
|
|
|
|
pnutpy.api.add_authorization_token(token)
|
|
try:
|
|
response, meta = pnutpy.api.get_user('me')
|
|
|
|
user = Users(
|
|
matrix_id=sender,
|
|
pnut_user_id=response.id,
|
|
pnut_user_token=token
|
|
)
|
|
db_session.add(user)
|
|
db_session.commit()
|
|
|
|
reply = "Success! You are now authorized as " + response['username']
|
|
|
|
except pnutpy.errors.PnutAuthAPIException as e:
|
|
reply = "Error! Unable to authorize your account."
|
|
|
|
except Exception as e:
|
|
logging.exception('!save')
|
|
reply = "Error! Problem saving your token."
|
|
|
|
return reply
|
|
|
|
def cmd_user_drop(sender=None):
|
|
try:
|
|
user = Users.query.filter(Users.matrix_id == sender).one_or_none()
|
|
if user is not None:
|
|
db_session.delete(user)
|
|
db_session.commit()
|
|
reply = "Success! Your auth token has been removed."
|
|
else:
|
|
reply = "You do not appear to be registered."
|
|
|
|
except Exception as e:
|
|
logging.exception('!drop')
|
|
reply = "Error! Problem removing your token."
|
|
|
|
return reply
|
|
|
|
def cmd_user_status(sender=None):
|
|
try:
|
|
user = Users.query.filter(Users.matrix_id == sender).one_or_none()
|
|
if user is None:
|
|
reply = "You are currently not authorized on pnut.io"
|
|
else:
|
|
pnutpy.api.add_authorization_token(user.pnut_user_token)
|
|
response, meta = pnutpy.api.get_user('me')
|
|
reply = "You are currently authorized as " + response.username
|
|
|
|
except pnutpy.errors.PnutAuthAPIException as e:
|
|
reply = "You are currently not authorized on pnut.io"
|
|
|
|
except Exception as e:
|
|
logging.exception('!status')
|
|
reply = "Error! There was a problem checking your account."
|
|
|
|
return reply
|
|
|
|
def cmd_user_join(sender=None, channel_id=None):
|
|
if channel_id is None:
|
|
reply = "You must provide a channel id number with this command.\n"
|
|
reply += "!join <channel #>"
|
|
return reply
|
|
|
|
try:
|
|
user = Users.query.filter(Users.matrix_id == sender).one_or_none()
|
|
if user is None:
|
|
reply = "You are currently not authorized on pnut.io"
|
|
else:
|
|
pnutpy.api.add_authorization_token(user.pnut_user_token)
|
|
channel, meta = pnutpy.api.get_channel(channel_id, include_raw=1)
|
|
room = Rooms.query.filter(Rooms.pnut_chan == channel_id).one_or_none()
|
|
if room is None:
|
|
create_room(channel, user)
|
|
else:
|
|
matrix_api = MatrixHttpApi(app.config['MATRIX_HOST'],
|
|
token=app.config['MATRIX_AS_TOKEN'])
|
|
matrix_api.invite_user(room.room_id, sender)
|
|
reply = "ok"
|
|
|
|
except pnutpy.errors.PnutAuthAPIException as e:
|
|
reply = "You are currently not authorized on pnut.io"
|
|
|
|
except pnutpy.errors.PnutPermissionDenied:
|
|
reply = "You are not authorized for this channel"
|
|
|
|
except Exception:
|
|
logging.exception('!join')
|
|
reply = "Error! There was a problem joining the channel."
|
|
|
|
return reply
|