replaced config file format with toml
All checks were successful
git.dreamfall.space/pnut-matrix/pipeline/head This commit looks good

This commit is contained in:
Morgan McMillian 2025-01-12 08:15:57 -08:00
parent af411677a7
commit 03e895e87b
4 changed files with 468 additions and 223 deletions

View file

@ -22,6 +22,7 @@ dependencies = [
"websockets", "websockets",
"asyncclick", "asyncclick",
"peewee", "peewee",
"tomlkit",
] ]
[project.urls] [project.urls]

View file

@ -1,5 +1,6 @@
import json import json
import yaml import yaml
import tomlkit
import requests import requests
import logging import logging
import logging.config import logging.config
@ -30,14 +31,14 @@ def forbidden(error):
async def query_alias(alias): async def query_alias(alias):
logging.debug("--- query alias ---") logging.debug("--- query alias ---")
alias_localpart = alias.split(":")[0][1:] alias_localpart = alias.split(":")[0][1:]
channel_id = int(alias_localpart.split('_')[1]) channel_id = int(alias_localpart.lstrip(app.config['matrix']['namespace']))
room = PnutChannels.select().where(PnutChannels.pnut_chan == room = PnutChannels.select().where(PnutChannels.pnut_chan ==
channel_id).first() channel_id).first()
if room is not None: if room is not None:
abort(404) abort(404)
token = app.config['MATRIX_PNUT_TOKEN'] token = app.config['pnut']['bot_token']
pnutpy.api.add_authorization_token(token) pnutpy.api.add_authorization_token(token)
try: try:
logging.debug("---- getting the channel ----") logging.debug("---- getting the channel ----")
@ -59,9 +60,11 @@ async def query_alias(alias):
else: else:
topic = None topic = None
matrix_api = ClientAPI(app.config['MATRIX_AS_ID'], as_id = (f"@{app.config['matrix']['sender_local']}:"
base_url=app.config['MATRIX_HOST'], f"{app.config['matrix']['domain']}")
token=app.config['MATRIX_AS_TOKEN']) matrix_api = ClientAPI(as_id,
base_url=app.config['matrix']['homeserver'],
token=app.config['matrix']['as_token'])
if channel.acl.read.public: if channel.acl.read.public:
visibility = RoomDirectoryVisibility.PUBLIC visibility = RoomDirectoryVisibility.PUBLIC
@ -98,7 +101,7 @@ async def query_alias(alias):
async def on_receive_events(transaction): async def on_receive_events(transaction):
access_token = request.args.get('access_token', '') access_token = request.args.get('access_token', '')
if access_token != app.config['MATRIX_HS_TOKEN']: if access_token != app.config['matrix']['hs_token']:
abort(403) abort(403)
events = request.get_json()["events"] events = request.get_json()["events"]
@ -107,11 +110,11 @@ async def on_receive_events(transaction):
logging.debug(event) logging.debug(event)
logging.debug('~~~~~~~~~~~~~~~') logging.debug('~~~~~~~~~~~~~~~')
if (app.config['MATRIX_ADMIN_ROOM'] and # if (app.config['MATRIX_ADMIN_ROOM'] and
app.config['MATRIX_ADMIN_ROOM'] == event['room_id']): # app.config['MATRIX_ADMIN_ROOM'] == event['room_id']):
logging.debug('>----on_admin_event----<') # logging.debug('>----on_admin_event----<')
await on_admin_event(event) # await on_admin_event(event)
return jsonify({}) # return jsonify({})
user = PnutUsers.select().where(PnutUsers.matrix_id == user = PnutUsers.select().where(PnutUsers.matrix_id ==
event['sender']).first() event['sender']).first()
@ -142,11 +145,13 @@ async def on_receive_events(transaction):
return jsonify({}) return jsonify({})
async def new_message(event, user): async def new_message(event, user):
as_id = (f"@{app.config['matrix']['sender_local']}:"
f"{app.config['matrix']['domain']}")
if event['sender'] == app.config['MATRIX_AS_ID']: if event['sender'] == as_id:
return return
if app.config['MATRIX_PNUT_PREFIX'] in event['sender']: if app.config['matrix']['namespace'] in event['sender']:
return return
if user.room_id == event['room_id']: if user.room_id == event['room_id']:
@ -158,9 +163,9 @@ async def new_message(event, user):
logging.debug(f'room: {room}') logging.debug(f'room: {room}')
if room is None: if room is None:
if event['room_id'] == app.config['MATRIX_GLOBAL_ROOM']: if event['room_id'] == app.config['pnut']['global_room']:
room = PnutChannels(pnut_chan=0, room = PnutChannels(pnut_chan=0,
room_id=app.config['MATRIX_GLOBAL_ROOM']) room_id=app.config['pnut']['global_room'])
else: else:
logging.debug('-room not mapped-') logging.debug('-room not mapped-')
@ -168,14 +173,14 @@ async def new_message(event, user):
if room.is_direct: if room.is_direct:
logging.debug('>----on_direct_message----<') logging.debug('>----on_direct_message----<')
return on_direct_message(event, user, room) return await on_direct_message(event, user, room)
if user is not None: if user is not None:
token = user.pnut_user_token token = user.pnut_user_token
prefix = "" prefix = ""
else: else:
token = app.config['MATRIX_PNUT_TOKEN'] token = app.config['pnut']['bot_token']
matrix_profile = get_profile(event['sender']) matrix_profile = get_profile(event['sender'])
if ('displayname' in matrix_profile): if ('displayname' in matrix_profile):
prefix = (f"[{matrix_profile['displayname']}]" prefix = (f"[{matrix_profile['displayname']}]"
@ -261,9 +266,9 @@ async def new_message(event, user):
if room.pnut_chan != 0: if room.pnut_chan != 0:
logging.exception('-unable to post to pnut channel-') logging.exception('-unable to post to pnut channel-')
else: else:
matrix_api = ClientAPI(app.config['MATRIX_AS_ID'], matrix_api = ClientAPI(as_id,
base_url=app.config['MATRIX_HOST'], base_url=app.config['matrix']['homeserver'],
token=app.config['MATRIX_AS_TOKEN']) token=app.config['matrix']['as_token'])
await matrix_api.redact(event['room_id'], event['event_id'], await matrix_api.redact(event['room_id'], event['event_id'],
reason='user not authenticated') reason='user not authenticated')
@ -314,7 +319,7 @@ async def msg_from_event(event, user):
elif event['content']['msgtype'] == 'm.file': elif event['content']['msgtype'] == 'm.file':
file_url = event['content']['url'][6:] file_url = event['content']['url'][6:]
file_name = event['content']['body'] file_name = event['content']['body']
dl_url = (f"{app.config['MATRIX_URL']}" dl_url = (f"{app.config['matrix']['homeserver']}"
f"/_matrix/client/v1/media/download/{file_url}" f"/_matrix/client/v1/media/download/{file_url}"
f"/{file_name}") f"/{file_name}")
text = (f"[{file_name}]" text = (f"[{file_name}]"
@ -330,20 +335,22 @@ def crosspost_raw(event):
cross_profile = {'username': event['sender']} cross_profile = {'username': event['sender']}
matrix_profile = get_profile(event['sender']) matrix_profile = get_profile(event['sender'])
if "avatar_url" in matrix_profile: if "avatar_url" in matrix_profile:
cross_profile['avatar_image'] = (f"{app.config['MATRIX_URL']}" cross_profile['avatar_image'] = (f"{app.config['matrix']['homeserver']}"
f"/_matrix/media/r0/download/" f"/_matrix/media/r0/download/"
f"{matrix_profile['avatar_url'][6:]}") f"{matrix_profile['avatar_url'][6:]}")
crosspost = {} crosspost = {}
crosspost['canonical_url'] = (f"https://matrix.to/#/{event['room_id']}" crosspost['canonical_url'] = (f"https://matrix.to/#/{event['room_id']}"
f"/{event['event_id']}" f"/{event['event_id']}"
f":{app.config['MATRIX_DOMAIN']}") f":{app.config['matrix']['domain']}")
crosspost['source'] = {'name': "matrix.", 'url': "https://matrix.org"} crosspost['source'] = {'name': "matrix.", 'url': "https://matrix.org"}
crosspost['user'] = cross_profile crosspost['user'] = cross_profile
return crosspost return crosspost
async def media_from_event(event, user): async def media_from_event(event, user):
as_id = (f"@{app.config['matrix']['sender_local']}:"
f"{app.config['matrix']['domain']}")
mxc_url = event['content']['url'] mxc_url = event['content']['url']
if event['content']['msgtype'] == 'm.image': if event['content']['msgtype'] == 'm.image':
kind = 'image' kind = 'image'
@ -359,9 +366,9 @@ async def media_from_event(event, user):
'name': file_name, 'kind': kind, 'name': file_name, 'kind': kind,
'mimetype': mime_type, 'is_public': True} 'mimetype': mime_type, 'is_public': True}
matrix_api = ClientAPI(app.config['MATRIX_AS_ID'], matrix_api = ClientAPI(as_id,
base_url=app.config['MATRIX_HOST'], base_url=app.config['matrix']['homeserver'],
token=app.config['MATRIX_AS_TOKEN']) token=app.config['matrix']['as_token'])
media_file = await matrix_api.download_media(mxc_url) media_file = await matrix_api.download_media(mxc_url)
pnutpy.api.add_authorization_token(user.pnut_user_token) pnutpy.api.add_authorization_token(user.pnut_user_token)
@ -391,7 +398,7 @@ async def media_from_event(event, user):
def oembed_from_event(event): def oembed_from_event(event):
media_url = event['content']['url'][6:] media_url = event['content']['url'][6:]
file_name = event['content']['body'] file_name = event['content']['body']
dl_url = (f"{app.config['MATRIX_URL']}" dl_url = (f"{app.config['matrix']['homeserver']}"
f"/_matrix/client/v1/media/download/{media_url}" f"/_matrix/client/v1/media/download/{media_url}"
f"/{file_name}") f"/{file_name}")
@ -439,7 +446,7 @@ def delete_message(event, user):
if user is not None: if user is not None:
token = user.pnut_user_token token = user.pnut_user_token
else: else:
token = app.config['MATRIX_PNUT_TOKEN'] token = app.config['pnut']['bot_token']
pnutpy.api.add_authorization_token(token) pnutpy.api.add_authorization_token(token)
e = Events.select().where((Events.event_id == event['redacts']) & e = Events.select().where((Events.event_id == event['redacts']) &
@ -457,7 +464,7 @@ def delete_message(event, user):
pass pass
def get_profile(userid): def get_profile(userid):
url = app.config['MATRIX_HOST'] + "/_matrix/client/r0/profile/" + userid url = app.config['matrix']['homeserver'] + "/_matrix/client/r0/profile/" + userid
r = requests.get(url) r = requests.get(url)
if r.status_code == 200: if r.status_code == 200:
return json.loads(r.text) return json.loads(r.text)
@ -480,7 +487,7 @@ def get_channel_settings(channel_id):
async def create_pnut_matrix_room(channel, user): async def create_pnut_matrix_room(channel, user):
name = None name = None
topic = None topic = None
alias_localpart = f"{app.config['MATRIX_PNUT_PREFIX']}{channel.id}" alias_localpart = f"{app.config['matrix']['namespace']}{channel.id}"
invitees = [user.matrix_id] invitees = [user.matrix_id]
if channel.acl.read.public: if channel.acl.read.public:
@ -501,9 +508,11 @@ async def create_pnut_matrix_room(channel, user):
if 'description' in setting: if 'description' in setting:
topic = setting['description']['text'] topic = setting['description']['text']
matrix_api = ClientAPI(app.config['MATRIX_AS_ID'], as_id = (f"@{app.config['matrix']['sender_local']}:"
base_url=app.config['MATRIX_HOST'], f"{app.config['matrix']['domain']}")
token=app.config['MATRIX_AS_TOKEN']) matrix_api = ClientAPI(as_id,
base_url=app.config['matrix']['homeserver'],
token=app.config['matrix']['as_token'])
room_id = await matrix_api.create_room(alias_localpart, room_id = await matrix_api.create_room(alias_localpart,
invitees=invitees, invitees=invitees,
@ -529,15 +538,15 @@ async def create_pnut_matrix_room(channel, user):
def new_matrix_user(username): def new_matrix_user(username):
endpoint = "/_matrix/client/v3/register" endpoint = "/_matrix/client/v3/register"
url = app.config['MATRIX_HOST'] + endpoint url = app.config['matrix']['homeserver'] + endpoint
params = {'kind': 'user'} params = {'kind': 'user'}
data = { data = {
'type': 'm.login.application_service', 'type': 'm.login.application_service',
'username': app.config['MATRIX_PNUT_PREFIX'] + username 'username': app.config['matrix']['namespace'] + username
} }
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": "Bearer " + app.config['MATRIX_AS_TOKEN'] "Authorization": "Bearer " + app.config['matrix']['as_token']
} }
r = requests.post(url, headers=headers, json=data, params=params) r = requests.post(url, headers=headers, json=data, params=params)
if r.status_code == 200: if r.status_code == 200:
@ -550,96 +559,99 @@ def new_matrix_user(username):
logging.debug(r.text) logging.debug(r.text)
return return
async def on_admin_event(event): # async def on_admin_event(event):
matrix_api = ClientAPI(app.config['MATRIX_AS_ID'], # matrix_api = ClientAPI(app.config['MATRIX_AS_ID'],
base_url=app.config['MATRIX_HOST'], # base_url=app.config['MATRIX_HOST'],
token=app.config['MATRIX_AS_TOKEN']) # token=app.config['MATRIX_AS_TOKEN'])
#
# logging.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:
# await matrix_api.send_message(event['room_id'],
# cmd_admin_help(msg[1]))
# else:
# await matrix_api.send_message(event['room_id'],
# cmd_admin_help())
#
# elif msg[0] == 'list':
# await matrix_api.send_message(event['room_id'], cmd_admin_list())
#
# except Exception:
# errmsg = "- on_admin_event -"
# logging.exception(errmsg)
logging.debug("- admin room event recieved -") # 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."
#
# 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 TextMessageEventContent(msgtype='m.text', body=text)
if event['type'] != 'm.room.message': # def cmd_admin_list():
return jsonify({}) # text = ""
# rooms = PnutChannels.select()
msg = event['content']['body'].split(' ') #
# if len(rooms) > 0:
try: # text = "ID\tMATRIX ID\tPNUT CHANNEL\n"
if msg[0] == 'help': # else:
if len(msg) > 1: # text = " - no rooms are currently linked - \n"
await matrix_api.send_message(event['room_id'], #
cmd_admin_help(msg[1])) # for room in rooms:
else: # text += str(room.id) + '\t'
await matrix_api.send_message(event['room_id'], # text += room.room_id + '\t\t\t\t\t'
cmd_admin_help()) # text += str(room.pnut_chan) + '\t'
# text += '\n'
elif msg[0] == 'list': #
await matrix_api.send_message(event['room_id'], cmd_admin_list()) # return TextMessageEventContent(msgtype='m.text', body=text)
except Exception:
errmsg = "- on_admin_event -"
logging.exception(errmsg)
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."
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 TextMessageEventContent(msgtype='m.text', body=text)
def cmd_admin_list():
text = ""
rooms = PnutChannels.select()
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'
text += '\n'
return TextMessageEventContent(msgtype='m.text', body=text)
async def on_direct_invite(event): async def on_direct_invite(event):
as_id = (f"@{app.config['matrix']['sender_local']}:"
f"{app.config['matrix']['domain']}")
# direct chat with the appservice user # direct chat with the appservice user
if event['state_key'] == app.config['MATRIX_AS_ID']: if event['state_key'] == as_id:
matrix_api = ClientAPI(app.config['MATRIX_AS_ID'], matrix_api = ClientAPI(as_id,
base_url=app.config['MATRIX_HOST'], base_url=app.config['matrix']['homeserver'],
token=app.config['MATRIX_AS_TOKEN']) token=app.config['matrix']['as_token'])
dm = PnutUsers(matrix_id=event['sender'], room_id=event['room_id']) dm = PnutUsers(matrix_id=event['sender'], room_id=event['room_id'])
# direct chat with another pnut user # direct chat with another pnut user
elif app.config['MATRIX_PNUT_PREFIX'] in event['state_key']: elif app.config['matrix']['namespace'] in event['state_key']:
matrix_api = ClientAPI(app.config['MATRIX_AS_ID'], matrix_api = ClientAPI(as_id,
base_url=app.config['MATRIX_HOST'], base_url=app.config['matrix']['homeserver'],
token=app.config['MATRIX_AS_TOKEN'], token=app.config['matrix']['as_token'],
as_user_id=event['state_key']) as_user_id=event['state_key'])
bridge_user = event['state_key'] bridge_user = event['state_key']
pnut_user = bridge_user.replace(app.config['MATRIX_PNUT_PREFIX'], pnut_user = bridge_user.replace(app.config['matrix']['namespace'],
'').split(':')[0] '').split(':')[0]
user = PnutUsers.select().where(PnutUsers.matrix_id == user = PnutUsers.select().where(PnutUsers.matrix_id ==
@ -685,10 +697,12 @@ async def on_leave_event(event):
user = PnutUsers.select().where(PnutUsers.room_id == user = PnutUsers.select().where(PnutUsers.room_id ==
event['room_id']).first() event['room_id']).first()
as_id = (f"@{app.config['matrix']['sender_local']}:"
f"{app.config['matrix']['domain']}")
if direct_room is not None: if direct_room is not None:
matrix_api = ClientAPI(app.config['MATRIX_AS_ID'], matrix_api = ClientAPI(as_id,
base_url=app.config['MATRIX_HOST'], base_url=app.config['matrix']['homeserver'],
token=app.config['MATRIX_AS_TOKEN'], token=app.config['matrix']['as_token'],
as_user_id=direct_room.direct_pnut_user.lower()) as_user_id=direct_room.direct_pnut_user.lower())
try: try:
@ -699,13 +713,13 @@ async def on_leave_event(event):
errmsg = "- on_leave_event -" errmsg = "- on_leave_event -"
logging.exception(errmsg) logging.exception(errmsg)
def on_direct_message(event, user, room): async def on_direct_message(event, user, room):
if user is not None: if user is not None:
token = user.pnut_user_token token = user.pnut_user_token
prefix = "" prefix = ""
else: else:
token = app.config['MATRIX_PNUT_TOKEN'] token = app.config['pnut']['bot_token']
matrix_profile = get_profile(event['sender']) matrix_profile = get_profile(event['sender'])
if "displayname" in matrix_profile: if "displayname" in matrix_profile:
prefix = (f"[{matrix_profile['displayname']}]" prefix = (f"[{matrix_profile['displayname']}]"
@ -716,7 +730,7 @@ def on_direct_message(event, user, room):
raw = {} raw = {}
raw['io.pnut.core.crosspost'] = [crosspost_raw(event)] raw['io.pnut.core.crosspost'] = [crosspost_raw(event)]
evtext, evraw = msg_from_event(event) evtext, evraw = await msg_from_event(event, user)
text = prefix + evtext text = prefix + evtext
try: try:
@ -739,9 +753,11 @@ def on_direct_message(event, user, room):
return jsonify({}) return jsonify({})
async def on_control_message(event, user): async def on_control_message(event, user):
matrix_api = ClientAPI(app.config['MATRIX_AS_ID'], as_id = (f"@{app.config['matrix']['sender_local']}:"
base_url=app.config['MATRIX_HOST'], f"{app.config['matrix']['domain']}")
token=app.config['MATRIX_AS_TOKEN']) matrix_api = ClientAPI(as_id,
base_url=app.config['matrix']['homeserver'],
token=app.config['matrix']['as_token'])
if event['type'] != 'm.room.message': if event['type'] != 'm.room.message':
return jsonify({}) return jsonify({})
@ -835,13 +851,14 @@ def cmd_user_save(user, token=None):
return TextMessageEventContent(msgtype='m.text', body=reply) return TextMessageEventContent(msgtype='m.text', body=reply)
async def cmd_user_drop(user): async def cmd_user_drop(user):
as_id = (f"@{app.config['matrix']['sender_local']}:"
f"{app.config['matrix']['domain']}")
direct_rooms = PnutChannels.select().where(PnutChannels.direct_mtrx_user == direct_rooms = PnutChannels.select().where(PnutChannels.direct_mtrx_user ==
user.matrix_id) user.matrix_id)
for dir_room in direct_rooms: for dir_room in direct_rooms:
matrix_api = ClientAPI(app.config['MATRIX_AS_ID'], matrix_api = ClientAPI(as_id,
base_url=app.config['MATRIX_HOST'], base_url=app.config['matrix']['homeserver'],
token=app.config['MATRIX_AS_TOKEN'], token=app.config['matrix']['as_token'],
as_user_id=dir_room.direct_pnut_user.lower()) as_user_id=dir_room.direct_pnut_user.lower())
await matrix_api.leave_room(dir_room.room_id) await matrix_api.leave_room(dir_room.room_id)
dir_room.delete_instance() dir_room.delete_instance()
@ -849,9 +866,9 @@ async def cmd_user_drop(user):
private_rooms = PnutPrivateChanMembers.select().where( private_rooms = PnutPrivateChanMembers.select().where(
PnutPrivateChanMembers.pnut_user_id == user.pnut_user_id) PnutPrivateChanMembers.pnut_user_id == user.pnut_user_id)
for priv_room in private_rooms: for priv_room in private_rooms:
matrix_api = ClientAPI(app.config['MATRIX_AS_ID'], matrix_api = ClientAPI(as_id,
base_url=app.config['MATRIX_HOST'], base_url=app.config['matrix']['homeserver'],
token=app.config['MATRIX_AS_TOKEN']) token=app.config['matrix']['as_token'])
await matrix_api.kick_user(priv_room.room_id, user.matrix_id, await matrix_api.kick_user(priv_room.room_id, user.matrix_id,
reason='user left from bridge') reason='user left from bridge')
priv_room.delete_instance() priv_room.delete_instance()
@ -882,6 +899,8 @@ def cmd_user_status(user):
return TextMessageEventContent(msgtype='m.text', body=reply) return TextMessageEventContent(msgtype='m.text', body=reply)
async def cmd_user_join(user, channel_id=None): async def cmd_user_join(user, channel_id=None):
as_id = (f"@{app.config['matrix']['sender_local']}:"
f"{app.config['matrix']['domain']}")
if channel_id is None: if channel_id is None:
reply = "You must provide a channel id number with this command.\n" reply = "You must provide a channel id number with this command.\n"
reply += "!join <channel #>" reply += "!join <channel #>"
@ -898,9 +917,9 @@ async def cmd_user_join(user, channel_id=None):
if room is None: if room is None:
await create_pnut_matrix_room(channel, user) await create_pnut_matrix_room(channel, user)
else: else:
matrix_api = ClientAPI(app.config['MATRIX_AS_ID'], matrix_api = ClientAPI(as_id,
base_url=app.config['MATRIX_HOST'], base_url=app.config['matrix']['homeserver'],
token=app.config['MATRIX_AS_TOKEN']) token=app.config['matrix']['as_token'])
await matrix_api.invite_user(room.room_id, user.matrix_id) await matrix_api.invite_user(room.room_id, user.matrix_id)
reply = "ok" reply = "ok"
@ -935,14 +954,20 @@ class MLogFilter(logging.Filter):
def main(): def main():
a_parser = argparse.ArgumentParser() a_parser = argparse.ArgumentParser()
a_parser.add_argument('-c', '--config', dest='configyaml', a_parser.add_argument('-c', '--config', dest='config',
default="config.yaml", help="configuration file") default="config.toml", help="configuration file")
a_parser.add_argument('-l', '--log_config', dest='log_config',
default='logging-config.yaml',
help='logging configuration')
args = a_parser.parse_args() args = a_parser.parse_args()
with open(args.configyaml, "rb") as config_file: with open(args.config, "rb") as config_file:
config = yaml.load(config_file, Loader=yaml.SafeLoader) config = tomlkit.load(config_file)
logging.config.dictConfig(config['logging']) with open(args.log_config, 'rb') as log_config_file:
log_config = yaml.load(log_config_file, Loader=yaml.SafeLoader)
logging.config.dictConfig(log_config)
redact_filter = MLogFilter() redact_filter = MLogFilter()
logging.getLogger("werkzeug").addFilter(redact_filter) logging.getLogger("werkzeug").addFilter(redact_filter)
logging.getLogger("urllib3.connectionpool").addFilter(redact_filter) logging.getLogger("urllib3.connectionpool").addFilter(redact_filter)
@ -950,10 +975,11 @@ def main():
app.config.update(config) app.config.update(config)
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
db.init(config['SERVICE_DB']) db.init(config['database'])
db_create_tables() db_create_tables()
app.run(host=config['LISTEN_HOST'], port=config['LISTEN_PORT']) host_addr, host_port = config['listen_addr'].split(':')
app.run(host=host_addr, port=host_port)
if __name__ == '__main__': if __name__ == '__main__':
main() main()

View file

@ -7,6 +7,9 @@ import requests
import pnutpy import pnutpy
import json import json
import yaml import yaml
import tomlkit
import secrets
import string
from mautrix.client import ClientAPI from mautrix.client import ClientAPI
from mautrix.types import TextMessageEventContent, Format, MessageType, EventType from mautrix.types import TextMessageEventContent, Format, MessageType, EventType
@ -17,25 +20,132 @@ PNUT_API="https://api.pnut.io/v1"
@click.group() @click.group()
@click.option('--debug', '-d', is_flag=True) @click.option('--debug', '-d', is_flag=True)
@click.option('--config', '-c', required=True) @click.option('--config', '-c')
@click.pass_context @click.pass_context
async def cmd(ctx, debug, config): async def cmd(ctx, debug, config):
if debug: if debug:
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
ctx.ensure_object(dict) ctx.ensure_object(dict)
with open(config, "rb") as config_file: if config is None:
ctx.obj['config'] = yaml.load(config_file, Loader=yaml.SafeLoader) ctx.obj['config'] = tomlkit.document()
else:
with open(config, "rb") as config_file:
ctx.obj['config'] = tomlkit.load(config_file)
click.echo(ctx.obj['config'])
@cmd.command()
@click.option('--listen_addr', prompt='Host address and port to listen on',
default='127.0.0.1:5000')
@click.option('--homeserver', prompt='Matrix homeserver URL',
default='http://127.0.0.1:8008')
@click.option('--domain', prompt='Matrix domain')
@click.option('--namespace', prompt='Namespace for bridge)',
default='_pnut_')
@click.option('--sender', prompt='Sender localpart for bridge',
default='_pnut_bot')
@click.option('--client_id', prompt='Pnut Client ID')
@click.option('--client_secret', prompt='Pnut Client Secret')
@click.option('--bot_user', prompt='Pnut bot username')
@click.option('--bot_token', prompt='Pnut bot user token')
@click.option('--app_token', prompt='Pnut app token')
@click.option('--app_key', prompt='Pnut app stream key')
@click.pass_context
def generate_config(ctx, listen_addr, homeserver, domain, namespace, sender,
client_id, client_secret, bot_user, bot_token, app_token,
app_key):
alpha = string.ascii_letters + string.digits
as_token = ''.join(secrets.choice(alpha) for i in range(33))
hs_token = ''.join(secrets.choice(alpha) for i in range(33))
config = tomlkit.document()
config.add("listen_addr", listen_addr)
config.add("database", "store.db")
matrix = tomlkit.table()
matrix.add("homeserver", homeserver)
matrix.add("domain", domain)
matrix.add("as_token", as_token)
matrix.add("hs_token", hs_token)
matrix.add("namespace", namespace)
matrix.add("sender_local", sender)
config.add("matrix", matrix)
pnut = tomlkit.table()
pnut.add("client_id", client_id)
pnut.add("client_secret", client_secret)
pnut.add("bot_user", bot_user)
pnut.add("bot_token", bot_token)
pnut.add("app_token", app_token)
pnut.add("app_key", app_key)
pnut.add("global_stream", False)
pnut.add("global_room", "<ROOM_ID>")
pnut.add("global_humans_only", True)
config.add("pnut", pnut)
click.echo()
write_conf = click.confirm("Write pnut-matrix configuration to config.toml?")
if write_conf:
with open('config.toml', 'w') as write_file:
tomlkit.dump(config, write_file)
else:
click.echo()
click.echo('##################################################' +
'####################')
click.echo('# The following is the generated config.toml file.' +
' with pnut-matrix. #')
click.echo('##################################################' +
'####################')
click.echo(tomlkit.dumps(config))
click.echo('##################################################' +
'####################')
appsrv = {}
appsrv['id'] = 'pnut'
appsrv['url'] = f'http://127.0.0.1:5000'
appsrv['as_token'] = as_token
appsrv['hs_token'] = hs_token
appsrv['sender_localpart'] = sender
appsrv['namespaces'] = {
'users': [{'exclusive': True, 'regex': f'@{namespace}.*'}],
'rooms': [],
'aliases': [{'exclusive': True, 'regex': f'#{namespace}.*'}]}
write_asconf = click.confirm("Write matrix configuration to appservice.yaml?")
if write_asconf:
with open('appservice.yaml', 'w') as write_file:
yaml.dump(appsrv, write_file)
else:
click.echo()
click.echo('############################################################' +
'###########################')
click.echo('# The following is the generated appservice.yaml file to use' +
' with your matrix server. #')
click.echo('############################################################' +
'###########################')
click.echo(yaml.dump(appsrv))
click.echo('############################################################' +
'###########################')
@cmd.command() @cmd.command()
@click.pass_context @click.pass_context
async def get_streams(ctx): async def get_streams(ctx):
config = ctx.obj['config'] config = ctx.obj['config']
logging.debug(config) if 'pnut' not in config:
click.echo("Pnut configuration missing!")
exit(1)
if 'app_token' not in config['pnut']:
click.echo("Pnut app token missing from configuration!")
exit(1)
endpoint = "/streams" endpoint = "/streams"
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": "Bearer " + config['PNUT_APPTOKEN'] "Authorization": "Bearer " + config['pnut']['app_token']
} }
url = f"{PNUT_API}{endpoint}" url = f"{PNUT_API}{endpoint}"
@ -53,11 +163,18 @@ async def get_streams(ctx):
@click.pass_context @click.pass_context
def rm_stream(ctx, key): def rm_stream(ctx, key):
config = ctx.obj['config'] config = ctx.obj['config']
logging.debug(config) if 'pnut' not in config:
click.echo("Pnut configuration missing!")
exit(1)
if 'app_token' not in config['pnut']:
click.echo("Pnut app token missing from configuration!")
exit(1)
endpoint = f"/streams/{key}" endpoint = f"/streams/{key}"
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": "Bearer " + config['PNUT_APPTOKEN'] "Authorization": "Bearer " + config['pnut']['app_token']
} }
url = f"{PNUT_API}{endpoint}" url = f"{PNUT_API}{endpoint}"
@ -74,11 +191,18 @@ def rm_stream(ctx, key):
@click.pass_context @click.pass_context
def rm_streams(ctx): def rm_streams(ctx):
config = ctx.obj['config'] config = ctx.obj['config']
logging.debug(config) if 'pnut' not in config:
click.echo("Pnut configuration missing!")
exit(1)
if 'app_token' not in config['pnut']:
click.echo("Pnut app token missing from configuration!")
exit(1)
endpoint = f"/streams" endpoint = f"/streams"
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": "Bearer " + config['PNUT_APPTOKEN'] "Authorization": "Bearer " + config['pnut']['app_token']
} }
url = f"{PNUT_API}{endpoint}" url = f"{PNUT_API}{endpoint}"
@ -97,10 +221,18 @@ def rm_streams(ctx):
@click.pass_context @click.pass_context
def new_stream(ctx, key, object_types): def new_stream(ctx, key, object_types):
config = ctx.obj['config'] config = ctx.obj['config']
if 'pnut' not in config:
click.echo("Pnut configuration missing!")
exit(1)
if 'app_token' not in config['pnut']:
click.echo("Pnut app token missing from configuration!")
exit(1)
endpoint = f"/streams" endpoint = f"/streams"
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": "Bearer " + config['PNUT_APPTOKEN'] "Authorization": "Bearer " + config['pnut']['app_token']
} }
otypes = [x.strip() for x in object_types.split(',')] otypes = [x.strip() for x in object_types.split(',')]
data = { data = {
@ -124,10 +256,18 @@ def new_stream(ctx, key, object_types):
@click.pass_context @click.pass_context
def update_stream(ctx, key, object_types): def update_stream(ctx, key, object_types):
config = ctx.obj['config'] config = ctx.obj['config']
if 'pnut' not in config:
click.echo("Pnut configuration missing!")
exit(1)
if 'app_token' not in config['pnut']:
click.echo("Pnut app token missing from configuration!")
exit(1)
endpoint = f"/streams/{key}" endpoint = f"/streams/{key}"
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": "Bearer " + config['PNUT_APPTOKEN'] "Authorization": "Bearer " + config['pnut']['app_token']
} }
otypes = [x.strip() for x in object_types.split(',')] otypes = [x.strip() for x in object_types.split(',')]
data = { data = {
@ -171,18 +311,29 @@ def pnut_app_token(ctx):
# click.echo(r.text) # click.echo(r.text)
@cmd.command() @cmd.command()
@click.argument('username')
@click.pass_context @click.pass_context
def new_matrix_asuser(ctx, username): def create_matrix_asuser(ctx):
config = ctx.obj['config'] config = ctx.obj['config']
if 'matrix' not in config:
click.echo("Matrix configuration missing!")
exit(1)
if 'as_token' not in config['matrix']:
click.echo("matrix appservice token missing from configuration!")
exit(1)
if 'sender_local' not in config['matrix']:
click.echo("matrix local sender missing from configuration!")
exit(1)
endpoint = "/_matrix/client/v3/register" endpoint = "/_matrix/client/v3/register"
url = config['MATRIX_HOST'] + endpoint url = config['matrix']['homeserver'] + endpoint
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": "Bearer " + config['MATRIX_AS_TOKEN'] "Authorization": "Bearer " + config['matrix']['as_token']
} }
params = {'kind': 'user'} params = {'kind': 'user'}
data = {'type': 'm.login.application_service','username': username} data = {'type': 'm.login.application_service','username': config['matrix']['sender_local']}
r = requests.post(url, headers=headers, json=data, params=params) r = requests.post(url, headers=headers, json=data, params=params)
if r.status_code == 200: if r.status_code == 200:
@ -195,23 +346,36 @@ def new_matrix_asuser(ctx, username):
@cmd.command() @cmd.command()
@click.argument('invitee') @click.argument('invitee')
@click.pass_context @click.pass_context
def new_pnut_admin_room(ctx, invitee): def create_pnut_admin_room(ctx, invitee):
config = ctx.obj['config'] config = ctx.obj['config']
if 'matrix' not in config:
click.echo("Matrix configuration missing!")
exit(1)
if 'as_token' not in config['matrix']:
click.echo("matrix appservice token missing from configuration!")
exit(1)
if 'namespace' not in config['matrix']:
click.echo("matrix namespace missing from configuration!")
exit(1)
as_id = f"@{config['matrix']['sender_local']}:{config['matrix']['domain']}"
endpoint = "/_matrix/client/v3/createRoom" endpoint = "/_matrix/client/v3/createRoom"
url = config['MATRIX_HOST'] + endpoint url = config['matrix']['homeserver'] + endpoint
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": "Bearer " + config['MATRIX_AS_TOKEN'] "Authorization": "Bearer " + config['matrix']['as_token']
} }
data = { data = {
'visibility': "private", 'visibility': "private",
'is_direct': False, 'is_direct': False,
'name': "Pnut Bridge Admin Room", 'name': "Pnut Bridge Admin Room",
'room_alias_name': f"{config['MATRIX_PNUT_PREFIX']}admin", 'room_alias_name': f"{config['matrix']['namespace']}admin",
'invite': [invitee], 'invite': [invitee],
'power_level_content_override': { 'power_level_content_override': {
'users': { 'users': {
f"{config['MATRIX_AS_ID']}": 100, f"{as_id}": 100,
f"{invitee}": 100 f"{invitee}": 100
} }
} }
@ -228,13 +392,25 @@ def new_pnut_admin_room(ctx, invitee):
@cmd.command() @cmd.command()
@click.pass_context @click.pass_context
def new_pnut_global_room(ctx): def create_pnut_global_room(ctx):
config = ctx.obj['config'] config = ctx.obj['config']
if 'matrix' not in config:
click.echo("Matrix configuration missing!")
exit(1)
if 'as_token' not in config['matrix']:
click.echo("matrix appservice token missing from configuration!")
exit(1)
if 'namespace' not in config['matrix']:
click.echo("matrix namespace missing from configuration!")
exit(1)
endpoint = "/_matrix/client/v3/createRoom" endpoint = "/_matrix/client/v3/createRoom"
url = config['MATRIX_HOST'] + endpoint url = config['matrix']['homeserver'] + endpoint
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": "Bearer " + config['MATRIX_AS_TOKEN'] "Authorization": "Bearer " + config['matrix']['as_token']
} }
# data = { # data = {
# 'visibility': "public", # 'visibility': "public",
@ -248,7 +424,7 @@ def new_pnut_global_room(ctx):
data = { data = {
'visibility': "public", 'visibility': "public",
'name': "Pnut Global Stream", 'name': "Pnut Global Stream",
'room_alias_name': f"{config['MATRIX_PNUT_PREFIX']}global" 'room_alias_name': f"{config['matrix']['namespace']}global"
} }
logging.debug(data) logging.debug(data)
r = requests.post(url, headers=headers, json=data) r = requests.post(url, headers=headers, json=data)
@ -267,15 +443,24 @@ def new_pnut_global_room(ctx):
@click.pass_context @click.pass_context
def elevate_matrix_user(ctx, room_id, matrix_id, power_level): def elevate_matrix_user(ctx, room_id, matrix_id, power_level):
config = ctx.obj['config'] config = ctx.obj['config']
if 'matrix' not in config:
click.echo("Matrix configuration missing!")
exit(1)
if 'as_token' not in config['matrix']:
click.echo("matrix appservice token missing from configuration!")
exit(1)
as_id = f"@{config['matrix']['sender_local']}:{config['matrix']['domain']}"
endpoint = f"/_matrix/client/v3/rooms/{room_id}/state/" endpoint = f"/_matrix/client/v3/rooms/{room_id}/state/"
url = config['MATRIX_HOST'] + endpoint + "m.room.power_levels" url = config['MATRIX_HOST'] + endpoint + "m.room.power_levels"
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": "Bearer " + config['MATRIX_AS_TOKEN'] "Authorization": "Bearer " + config['matrix']['as_token']
} }
data = { data = {
'users': { 'users': {
f"{config['MATRIX_AS_ID']}": 100, f"{as_id}": 100,
f"{matrix_id}": int(power_level) f"{matrix_id}": int(power_level)
} }
} }
@ -293,9 +478,18 @@ def elevate_matrix_user(ctx, room_id, matrix_id, power_level):
@click.pass_context @click.pass_context
async def list_joined_rooms(ctx): async def list_joined_rooms(ctx):
config = ctx.obj['config'] config = ctx.obj['config']
matrix_api = ClientAPI(config['MATRIX_AS_ID'], if 'matrix' not in config:
base_url=config['MATRIX_HOST'], click.echo("Matrix configuration missing!")
token=config['MATRIX_AS_TOKEN']) exit(1)
if 'as_token' not in config['matrix']:
click.echo("matrix appservice token missing from configuration!")
exit(1)
as_id = f"@{config['matrix']['sender_local']}:{config['matrix']['domain']}"
matrix_api = ClientAPI(as_id,
base_url=config['matrix']['homeserver'],
token=config['matrix']['as_token'])
room_list = await matrix_api.get_joined_rooms() room_list = await matrix_api.get_joined_rooms()
for room_id in room_list: for room_id in room_list:
click.echo(room_id) click.echo(room_id)
@ -323,9 +517,18 @@ async def list_joined_rooms(ctx):
@click.pass_context @click.pass_context
async def part_room(ctx, room_id): async def part_room(ctx, room_id):
config = ctx.obj['config'] config = ctx.obj['config']
matrix_api = ClientAPI(config['MATRIX_AS_ID'], if 'matrix' not in config:
base_url=config['MATRIX_HOST'], click.echo("Matrix configuration missing!")
token=config['MATRIX_AS_TOKEN']) exit(1)
if 'as_token' not in config['matrix']:
click.echo("matrix appservice token missing from configuration!")
exit(1)
as_id = f"@{config['matrix']['sender_local']}:{config['matrix']['domain']}"
matrix_api = ClientAPI(as_id,
base_url=config['matrix']['homeserver'],
token=config['matrix']['as_token'])
# TODO: need to clear alias # TODO: need to clear alias
await matrix_api.leave_room(room_id) await matrix_api.leave_room(room_id)

View file

@ -2,6 +2,7 @@ import time
import logging import logging
import logging.config import logging.config
import yaml import yaml
import tomlkit
import json import json
import pnutpy import pnutpy
import requests import requests
@ -25,7 +26,6 @@ from pnut_matrix.models import *
logger = logging.getLogger() logger = logging.getLogger()
config = None config = None
matrix_url = None
class MLogFilter(logging.Filter): class MLogFilter(logging.Filter):
@ -52,16 +52,18 @@ async def new_pnut_message(msg, meta):
logger.debug("text: " + msg.content.text) logger.debug("text: " + msg.content.text)
# ignore messages posted by the bridge # ignore messages posted by the bridge
if msg.user.username == config['MATRIX_PNUT_USER']: if msg.user.username == config['pnut']['bot_user']:
return return
if msg.source.id == config['PNUTCLIENT_ID']: if msg.source.id == config['pnut']['client_id']:
return return
as_id = (f"@{config['matrix']['sender_local']}:"
f"{config['matrix']['domain']}")
matrix_id = matrix_id_from_pnut(msg.user.username) matrix_id = matrix_id_from_pnut(msg.user.username)
matrix_api = ClientAPI(config['MATRIX_AS_ID'], matrix_api = ClientAPI(as_id,
base_url=config['MATRIX_HOST'], base_url=config['matrix']['homeserver'],
token=config['MATRIX_AS_TOKEN'], token=config['matrix']['as_token'],
as_user_id=matrix_id.lower()) as_user_id=matrix_id.lower())
channel_id = int(msg.channel_id) channel_id = int(msg.channel_id)
@ -118,9 +120,9 @@ async def new_pnut_message(msg, meta):
logger.debug('-set_avatar-') logger.debug('-set_avatar-')
if room.is_private: if room.is_private:
matrix_api_as = ClientAPI(config['MATRIX_AS_ID'], matrix_api_as = ClientAPI(as_id,
base_url=config['MATRIX_HOST'], base_url=config['matrix']['homeserver'],
token=config['MATRIX_AS_TOKEN']) token=config['matrix']['as_token'])
await matrix_api_as.invite_user(room.room_id, matrix_id.lower()) await matrix_api_as.invite_user(room.room_id, matrix_id.lower())
await matrix_api.join_room(room.room_id) await matrix_api.join_room(room.room_id)
@ -145,10 +147,10 @@ async def new_pnut_message(msg, meta):
async def new_pnut_post(post, meta): async def new_pnut_post(post, meta):
if not config['PNUT_GLOBAL']: if not config['pnut']['global_stream']:
return return
if (config['PNUT_GLOBAL_HUMAN_ONLY'] and if (config['pnut']['global_humans_only'] and
post.user.type in ['feed', 'bot']): post.user.type in ['feed', 'bot']):
logging.debug('-skipping non human post-') logging.debug('-skipping non human post-')
return return
@ -160,10 +162,12 @@ async def new_pnut_post(post, meta):
text += f"<{post.user.username}> reposted >> " text += f"<{post.user.username}> reposted >> "
post = post.repost_of post = post.repost_of
as_id = (f"@{config['matrix']['sender_local']}:"
f"{config['matrix']['domain']}")
matrix_id = matrix_id_from_pnut(post.user.username) matrix_id = matrix_id_from_pnut(post.user.username)
matrix_api = ClientAPI(config['MATRIX_AS_ID'], matrix_api = ClientAPI(as_id,
base_url=config['MATRIX_HOST'], base_url=config['matrix']['homeserver'],
token=config['MATRIX_AS_TOKEN'], token=config['matrix']['as_token'],
as_user_id=matrix_id.lower()) as_user_id=matrix_id.lower())
try: try:
profile = await matrix_api.get_profile(matrix_id.lower()) profile = await matrix_api.get_profile(matrix_id.lower())
@ -184,7 +188,7 @@ async def new_pnut_post(post, meta):
await set_matrix_avatar(post.user) await set_matrix_avatar(post.user)
logger.debug('-set_avatar-') logger.debug('-set_avatar-')
room_id = config['MATRIX_GLOBAL_ROOM'] room_id = config['pnut']['global_room']
await matrix_api.join_room(room_id) await matrix_api.join_room(room_id)
postlink = f"https://posts.pnut.io/{post.id}" postlink = f"https://posts.pnut.io/{post.id}"
plaintext = f"{post.content.text}\n{postlink}" plaintext = f"{post.content.text}\n{postlink}"
@ -209,9 +213,11 @@ async def new_pnut_post(post, meta):
async def new_media(room_id, msg): async def new_media(room_id, msg):
matrix_id = matrix_id_from_pnut(msg.user.username) matrix_id = matrix_id_from_pnut(msg.user.username)
matrix_api = ClientAPI(config['MATRIX_AS_ID'], as_id = (f"@{config['matrix']['sender_local']}:"
base_url=config['MATRIX_HOST'], f"{config['matrix']['domain']}")
token=config['MATRIX_AS_TOKEN'], matrix_api = ClientAPI(as_id,
base_url=config['matrix']['homeserver'],
token=config['matrix']['as_token'],
as_user_id=matrix_id.lower()) as_user_id=matrix_id.lower())
if 'io.pnut.core.oembed' in msg.raw: if 'io.pnut.core.oembed' in msg.raw:
@ -275,9 +281,11 @@ async def new_media(room_id, msg):
async def delete_message(msg): async def delete_message(msg):
matrix_id = matrix_id_from_pnut(msg.user.username) matrix_id = matrix_id_from_pnut(msg.user.username)
matrix_api = ClientAPI(config['MATRIX_AS_ID'], as_id = (f"@{config['matrix']['sender_local']}:"
base_url=config['MATRIX_HOST'], f"{config['matrix']['domain']}")
token=config['MATRIX_AS_TOKEN'], matrix_api = ClientAPI(as_id,
base_url=config['matrix']['homeserver'],
token=config['matrix']['as_token'],
as_user_id=matrix_id.lower()) as_user_id=matrix_id.lower())
events = Events.select().where((Events.pnut_id == msg.id) & events = Events.select().where((Events.pnut_id == msg.id) &
@ -288,8 +296,8 @@ async def delete_message(msg):
event.save() event.save()
def matrix_id_from_pnut(username): def matrix_id_from_pnut(username):
matrix_id = (f"@{config['MATRIX_PNUT_PREFIX']}{username}" matrix_id = (f"@{config['matrix']['namespace']}{username}"
f":{config['MATRIX_DOMAIN']}") f":{config['matrix']['domain']}")
return matrix_id return matrix_id
def matrix_display_from_pnut(user): def matrix_display_from_pnut(user):
@ -308,17 +316,21 @@ def matrix_display_from_pnut(user):
async def set_matrix_display(user): async def set_matrix_display(user):
matrix_id = matrix_id_from_pnut(user.username) matrix_id = matrix_id_from_pnut(user.username)
matrix_api = ClientAPI(config['MATRIX_AS_ID'], as_id = (f"@{config['matrix']['sender_local']}:"
base_url=config['MATRIX_HOST'], f"{config['matrix']['domain']}")
token=config['MATRIX_AS_TOKEN'], matrix_api = ClientAPI(as_id,
base_url=config['matrix']['homeserver'],
token=config['matrix']['as_token'],
as_user_id=matrix_id.lower()) as_user_id=matrix_id.lower())
await matrix_api.set_displayname(matrix_display_from_pnut(user)) await matrix_api.set_displayname(matrix_display_from_pnut(user))
async def set_matrix_avatar(user): async def set_matrix_avatar(user):
matrix_id = matrix_id_from_pnut(user.username) matrix_id = matrix_id_from_pnut(user.username)
matrix_api = ClientAPI(config['MATRIX_AS_ID'], as_id = (f"@{config['matrix']['sender_local']}:"
base_url=config['MATRIX_HOST'], f"{config['matrix']['domain']}")
token=config['MATRIX_AS_TOKEN'], matrix_api = ClientAPI(as_id,
base_url=config['matrix']['homeserver'],
token=config['matrix']['as_token'],
as_user_id=matrix_id.lower()) as_user_id=matrix_id.lower())
dl = requests.get(user.content.avatar_image.url, stream=True) dl = requests.get(user.content.avatar_image.url, stream=True)
@ -347,15 +359,15 @@ async def set_matrix_avatar(user):
def new_matrix_user(username): def new_matrix_user(username):
endpoint = "/_matrix/client/v3/register" endpoint = "/_matrix/client/v3/register"
url = config['MATRIX_HOST'] + endpoint url = config['matrix']['homeserver'] + endpoint
params = {'kind': 'user'} params = {'kind': 'user'}
data = { data = {
'type': 'm.login.application_service', 'type': 'm.login.application_service',
'username': config['MATRIX_PNUT_PREFIX'] + username 'username': config['matrix']['namespace'] + username
} }
headers = { headers = {
"Content-Type": "application/json", "Content-Type": "application/json",
"Authorization": "Bearer " + config['MATRIX_AS_TOKEN'] "Authorization": "Bearer " + config['matrix']['as_token']
} }
logger.debug(data) logger.debug(data)
r = requests.post(url, headers=headers, json=data, params=params) r = requests.post(url, headers=headers, json=data, params=params)
@ -371,9 +383,9 @@ def new_matrix_user(username):
def new_room(pnut_user, invitees, chan): def new_room(pnut_user, invitees, chan):
dr = None dr = None
url = matrix_url + '/createRoom' url = config['matrix']['homeserver'] + '/_matrix/client/v3/createRoom'
params = { params = {
"access_token": config['MATRIX_AS_TOKEN'], "access_token": config['matrix']['as_token'],
"user_id": pnut_user.lower() "user_id": pnut_user.lower()
} }
content = { content = {
@ -434,15 +446,15 @@ async def on_message(message):
await new_pnut_post(pnut_post, meta) await new_pnut_post(pnut_post, meta)
async def asmain(): async def asmain():
if config['MATRIX_ADMIN_ROOM']: # if config['MATRIX_ADMIN_ROOM']:
logger.debug("- sould join admin room -") # logger.debug("- sould join admin room -")
matrix_api_as = ClientAPI(config['MATRIX_AS_ID'], # matrix_api_as = ClientAPI(config['MATRIX_AS_ID'],
base_url=config['MATRIX_HOST'], # base_url=config['MATRIX_HOST'],
token=config['MATRIX_AS_TOKEN']) # token=config['MATRIX_AS_TOKEN'])
await matrix_api_as.join_room(config['MATRIX_ADMIN_ROOM']) # await matrix_api_as.join_room(config['MATRIX_ADMIN_ROOM'])
ws_url = 'wss://stream.pnut.io/v1/app?access_token=' ws_url = 'wss://stream.pnut.io/v1/app?access_token='
ws_url += config['PNUT_APPTOKEN'] + '&key=' + config['PNUT_APPKEY'] ws_url += config['pnut']['app_token'] + '&key=' + config['pnut']['app_key']
ws_url += '&include_raw=1' ws_url += '&include_raw=1'
async for websocket in connect(uri=ws_url): async for websocket in connect(uri=ws_url):
try: try:
@ -456,25 +468,28 @@ async def asmain():
def main(): def main():
global config global config
global matrix_url
a_parser = argparse.ArgumentParser() a_parser = argparse.ArgumentParser()
a_parser.add_argument('-c', '--config', dest='configyaml', a_parser.add_argument('-c', '--config', dest='config',
default="config.yaml", help="configuration file") default="config.toml", help="configuration file")
a_parser.add_argument('-l', '--log_config', dest='log_config',
default='logging-config.yaml',
help='logging configuration')
args = a_parser.parse_args() args = a_parser.parse_args()
with open(args.configyaml, "rb") as config_file: with open(args.config, 'rb') as config_file:
config = yaml.load(config_file, Loader=yaml.SafeLoader) config = tomlkit.load(config_file)
db.init(config['SERVICE_DB']) with open(args.log_config, 'rb') as log_config_file:
log_config = yaml.load(log_config_file, Loader=yaml.SafeLoader)
db.init(config['database'])
db_create_tables() db_create_tables()
logging.config.dictConfig(config['logging']) logging.config.dictConfig(log_config)
redact_filter = MLogFilter() redact_filter = MLogFilter()
logging.getLogger("werkzeug").addFilter(redact_filter) logging.getLogger("werkzeug").addFilter(redact_filter)
logging.getLogger("urllib3.connectionpool").addFilter(redact_filter) logging.getLogger("urllib3.connectionpool").addFilter(redact_filter)
matrix_url = config['MATRIX_HOST'] + '/_matrix/client/v3'
asyncio.run(asmain()) asyncio.run(asmain())
logger.info('!! shutdown initiated !!') logger.info('!! shutdown initiated !!')