Skip to content

Commit

Permalink
Support io.element.thread:
Browse files Browse the repository at this point in the history
- Wrap bot.client.room_send() to modify outgoing messages based on
  pre_event
- Test for existence of m.relates_to
- Add event parameter to all module's uses of send_(text|html|...)

Fixes #192
  • Loading branch information
xPMo committed Feb 4, 2022
1 parent e3e353a commit ed5ddaa
Show file tree
Hide file tree
Showing 34 changed files with 233 additions and 219 deletions.
48 changes: 31 additions & 17 deletions bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def get_uri_cache(self, url, blob=False):
return self.uri_cache.get(cache_key)


async def upload_and_send_image(self, room, url, text=None, blob=False, blob_content_type="image/png", no_cache=False):
async def upload_and_send_image(self, room, event, url, text=None, blob=False, blob_content_type="image/png", no_cache=False):
"""
:param room: A MatrixRoom the image should be send to after uploading
Expand All @@ -111,15 +111,29 @@ async def upload_and_send_image(self, room, url, text=None, blob=False, blob_con
if res:
try:
matrix_uri, mimetype, w, h, size = res
return await self.send_image(room, matrix_uri, text, mimetype, w, h, size)
return await self.send_image(room, event, matrix_uri, text, mimetype, w, h, size)
except ValueError: # broken cache?
self.logger.warning(f"Image cache for {url} could not be unpacked, attempting to re-upload...")
try:
matrix_uri, mimetype, w, h, size = await self.upload_image(url, blob=blob, no_cache=no_cache)
except (UploadFailed, ValueError):
return await self.send_text(room, f"Sorry. Something went wrong fetching {url} and uploading the image to matrix server :(")
return await self.send_text(room, event, f"Sorry. Something went wrong fetching {url} and uploading the image to matrix server :(")

return await self.send_image(room, matrix_uri, text, mimetype, w, h, size)
return await self.send_image(room, event, matrix_uri, text, mimetype, w, h, size)

# Wrapper around matrix-nio's client.room_send
# Use src_event context to modify the msg
async def room_send(self, room_id, pre_event, msgtype, msg, **kwargs):
try:
# io.element.thread support
relates_to = pre_event.source['content']['m.relates_to']
if relates_to['rel_type'] == 'io.element.thread':
msg['m.relates_to'] = relates_to
msg['m.relates_to']['m.in_reply_to'] = {'event_id': pre_event.event_id}
except (AttributeError, KeyError):
pass

return await self.client.room_send(room_id, msgtype, msg, **kwargs)

# Helper function to upload a image from URL to homeserver. Use send_image() to actually send it to room.
# Throws exception if upload fails
Expand Down Expand Up @@ -176,7 +190,7 @@ async def upload_image(self, url_or_bytes, blob=False, blob_content_type="image/

raise UploadFailed

async def send_text(self, room, body, msgtype="m.notice", bot_ignore=False):
async def send_text(self, room, event, body, msgtype="m.notice", bot_ignore=False):
"""
:param room: A MatrixRoom the text should be send to
Expand All @@ -193,9 +207,9 @@ async def send_text(self, room, body, msgtype="m.notice", bot_ignore=False):
if bot_ignore:
msg["org.vranki.hemppa.ignore"] = "true"

return await self.client.room_send(room.room_id, 'm.room.message', msg)
return await self.room_send(room.room_id, event, 'm.room.message', msg)

async def send_html(self, room, html, plaintext, msgtype="m.notice", bot_ignore=False):
async def send_html(self, room, event, html, plaintext, msgtype="m.notice", bot_ignore=False):
"""
:param room: A MatrixRoom the html should be send to
Expand All @@ -214,9 +228,9 @@ async def send_html(self, room, html, plaintext, msgtype="m.notice", bot_ignore=
}
if bot_ignore:
msg["org.vranki.hemppa.ignore"] = "true"
await self.client.room_send(room.room_id, 'm.room.message', msg)
await self.room_send(room.room_id, event, 'm.room.message', msg)

async def send_location(self, room, body, latitude, longitude, bot_ignore=False):
async def send_location(self, room, event, body, latitude, longitude, bot_ignore=False):
"""
:param room: A MatrixRoom the html should be send to
Expand All @@ -232,9 +246,9 @@ async def send_location(self, room, body, latitude, longitude, bot_ignore=False)
"geo_uri": 'geo:' + str(latitude) + ',' + str(longitude),
"msgtype": "m.location",
}
await self.client.room_send(room.room_id, 'm.room.message', locationmsg)
await self.room_send(room.room_id, event, 'm.room.message', locationmsg)

async def send_image(self, room, url, body, mimetype=None, width=None, height=None, size=None):
async def send_image(self, room, event, url, body, mimetype=None, width=None, height=None, size=None):
"""
:param room: A MatrixRoom the image should be send to
Expand Down Expand Up @@ -265,7 +279,7 @@ async def send_image(self, room, url, body, mimetype=None, width=None, height=No
if size:
msg["info"]["size"] = size

return await self.client.room_send(room.room_id, 'm.room.message', msg)
return await self.room_send(room.room_id, event, 'm.room.message', msg)

async def set_room_avatar(self, room, uri, mimetype=None, width=None, height=None, size=None):
"""
Expand Down Expand Up @@ -312,7 +326,7 @@ async def send_msg(self, mxid, roomname, message):
return False

# Send message to the room
await self.send_text(msg_room, message)
await self.send_text(msg_room, None, message)
return True

async def find_or_create_private_msg(self, mxid, roomname):
Expand Down Expand Up @@ -420,7 +434,7 @@ async def message_cb(self, room, event):

if self.owners_only and not self.is_owner(event):
self.logger.info(f"Ignoring {event.sender}, because they're not an owner")
await self.send_text(room, "Sorry, only bot owner can run commands.")
await self.send_text(room, event, "Sorry, only bot owner can run commands.")
return

# HACK to ignore messages for some time after joining.
Expand All @@ -443,11 +457,11 @@ async def message_cb(self, room, event):
try:
await moduleobject.matrix_message(self, room, event)
except CommandRequiresAdmin:
await self.send_text(room, f'Sorry, you need admin power level in this room to run that command.')
await self.send_text(room, event, f'Sorry, you need admin power level in this room to run that command.')
except CommandRequiresOwner:
await self.send_text(room, f'Sorry, only bot owner can run that command.')
await self.send_text(room, event, f'Sorry, only bot owner can run that command.')
except Exception:
await self.send_text(room, f'Module {command} experienced difficulty: {sys.exc_info()[0]} - see log for details')
await self.send_text(room, event, f'Module {command} experienced difficulty: {sys.exc_info()[0]} - see log for details')
self.logger.exception(f'unhandled exception in !{command}')
else:
self.logger.error(f"Unknown command: {command}")
Expand Down
8 changes: 4 additions & 4 deletions modules/alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ async def matrix_message(self, bot, room, event):
bot.module_aliases.update({args[0]: args[1]})
self.aliases.update({args[0]: args[1]})
bot.save_settings()
await bot.send_text(room, f'Aliased !{args[0]} to !{args[1]}')
await bot.send_text(room, event, f'Aliased !{args[0]} to !{args[1]}')

elif len(args) == 2:
if args.pop(0) in ['rm', 'remove']:
Expand All @@ -43,7 +43,7 @@ async def matrix_message(self, bot, room, event):
old = bot.module_aliases.pop(args[0])
self.aliases.pop(args[0])
bot.save_settings()
await bot.send_text(room, f'Removed alias !{args[0]}')
await bot.send_text(room, event, f'Removed alias !{args[0]}')

elif len(args) == 1:
if args.pop(0) in ['ls', 'list']:
Expand All @@ -55,10 +55,10 @@ async def matrix_message(self, bot, room, event):
for k, v in inverted.items():
v = ', '.join(v)
msg.append(f'- {k} = {v}')
await bot.send_text(room, '\n'.join(msg))
await bot.send_text(room, event, '\n'.join(msg))

elif args.pop(0) == 'help':
await bot.send_text(room, self.long_help(bot=bot, event=event))
await bot.send_text(room, event, self.long_help(bot=bot, event=event))

def help(self):
return 'Manage command aliases'
Expand Down
40 changes: 20 additions & 20 deletions modules/apod.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ async def matrix_message(self, bot, room, event):
args = event.body.split()

if len(args) == 1:
await self.send_apod(bot, room, self.apod_api_url)
await self.send_apod(bot, room, event, self.apod_api_url)
elif len(args) == 2:
if args[1] == "stats":
await self.send_stats(bot, room)
Expand All @@ -60,44 +60,44 @@ async def matrix_message(self, bot, room, event):
elif args[1] == "help":
await self.command_help(bot, room)
elif args[1] == "avatar":
await self.send_apod(bot, room, self.apod_api_url, set_room_avatar=bot.is_admin(room, event))
await self.send_apod(bot, room, event, self.apod_api_url, set_room_avatar=bot.is_admin(room, event))
else:
date = args[1]
if re.match(self.APOD_DATE_PATTERN, date) is not None:
uri = self.apod_by_date_api_url + date
await self.send_apod(bot, room, uri)
await self.send_apod(bot, room, event, uri)
else:
await bot.send_text(room, "invalid date. accepted: YYYY-MM-DD")
await bot.send_text(room, event, "invalid date. accepted: YYYY-MM-DD")
elif len(args) == 3:
if args[1] == "apikey":
await self.update_api_key(bot, room, event, args[2])
elif args[1] == "avatar":
date = args[2]
if re.match(self.APOD_DATE_PATTERN, date) is not None:
uri = self.apod_by_date_api_url + date
await self.send_apod(bot, room, uri, set_room_avatar=bot.is_admin(room, event))
await self.send_apod(bot, room, event, uri, set_room_avatar=bot.is_admin(room, event))
else:
await bot.send_text(room, "invalid date. accepted: YYYY-MM-DD")
await bot.send_text(room, event, "invalid date. accepted: YYYY-MM-DD")

async def send_apod(self, bot, room, uri, set_room_avatar=False):
async def send_apod(self, bot, room, event, uri, set_room_avatar=False):
self.logger.debug(f"send request using uri {uri}")
response = requests.get(uri)

if response.status_code == 400:
self.logger.error("unable to request apod api. status: %d text: %s", response.status_code, response.text)
return await bot.send_text(room, response.json().get("msg"))
return await bot.send_text(room, event, response.json().get("msg"))

if response.status_code != 200:
self.logger.error("unable to request apod api. response: [status: %d text: %s]", response.status_code, response.text)
return await bot.send_text(room, "sorry. something went wrong accessing the api :(")
return await bot.send_text(room, event, "sorry. something went wrong accessing the api :(")

apod = Apod.create_from_json(response.json())
self.logger.debug(apod)

if apod.media_type != "image":
return await self.send_unknown_mediatype(room, bot, apod)
return await self.send_unknown_mediatype(room, event, bot, apod)

await bot.send_html(room, f"<b>{html.escape(apod.title)} ({html.escape(apod.date)})</b>", f"{apod.title} ({apod.date})")
await bot.send_html(room, event, f"<b>{html.escape(apod.title)} ({html.escape(apod.date)})</b>", f"{apod.title} ({apod.date})")
try:
matrix_uri = None
matrix_uri, mimetype, w, h, size = bot.get_uri_cache(apod.hdurl)
Expand All @@ -106,15 +106,15 @@ async def send_apod(self, bot, room, uri, set_room_avatar=False):
try:
matrix_uri, mimetype, w, h, size = await bot.upload_image(apod.hdurl)
except (UploadFailed, TypeError, ValueError):
await self.send_text(room, f"Something went wrong uploading {apod.hdurl}.")
await bot.send_image(room, matrix_uri, apod.hdurl, mimetype, w, h, size)
await bot.send_text(room, f"{apod.explanation}")
await self.send_text(room, event, f"Something went wrong uploading {apod.hdurl}.")
await bot.send_image(room, event, matrix_uri, apod.hdurl, mimetype, w, h, size)
await bot.send_text(room, event, f"{apod.explanation}")
if matrix_uri and set_room_avatar:
await bot.set_room_avatar(room, matrix_uri, mimetype, w, h, size)

async def send_unknown_mediatype(self, room, bot, apod):
async def send_unknown_mediatype(self, room, event, bot, apod):
self.logger.debug(f"unknown media_type: {apod.media_type}. sending raw information")
await bot.send_html(room,
await bot.send_html(room, event,
f"<p><strong>{html.escape(apod.title)} ({html.escape(apod.date)})</strong>"
f"<br>(Original URL: {apod.url})</p>"
f"\n<p>{apod.explanation}</p>",
Expand All @@ -140,12 +140,12 @@ def help(self):

async def send_stats(self, bot, room):
msg = f"collected {len(self.matrix_uri_cache)} upload matrix uri's"
await bot.send_text(room, msg)
await bot.send_text(room, event, msg)

async def clear_uri_cache(self, bot, room):
self.matrix_uri_cache.clear()
bot.save_settings()
await bot.send_text(room, "cleared uri cache")
await bot.send_text(room, event, "cleared uri cache")

async def command_help(self, bot, room):
msg = """commands:
Expand All @@ -156,12 +156,12 @@ async def command_help(self, bot, room):
- help - show command help
- avatar, avatar YYYY-MM-DD - Additionally set the room's avatar to the fetched image (Must be done as admin)
"""
await bot.send_text(room, msg)
await bot.send_text(room, event, msg)

async def update_api_key(self, bot, room, event, apikey):
bot.must_be_owner(event)
self.api_key = apikey
self.update_api_urls()
bot.save_settings()
await bot.send_text(room, 'Api key set')
await bot.send_text(room, event, 'Api key set')

Loading

0 comments on commit ed5ddaa

Please sign in to comment.