Base bot: Support m.thread

This commit is contained in:
gammafn 2022-07-31 10:12:32 -05:00
parent b34f3eb636
commit a1004ff950
2 changed files with 44 additions and 25 deletions

View File

@ -774,7 +774,7 @@ class MatrixModule(BotModule):
# Echo what they said back # Echo what they said back
self.logger.debug(f"room: {room.name} sender: {event.sender} wants an echo") self.logger.debug(f"room: {room.name} sender: {event.sender} wants an echo")
await bot.send_text(room, ' '.join(args)) await bot.send_text(room, ' '.join(args), event=None)
def help(self): def help(self):
return 'Echoes back what user has said' return 'Echoes back what user has said'
@ -805,33 +805,36 @@ class Bot:
:return bool: Success upon sending the message :return bool: Success upon sending the message
""" """
async def send_text(self, room, body, msgtype="m.notice", bot_ignore=False): async def send_text(self, room, body, event=None, msgtype="m.notice", bot_ignore=False):
""" """
:param room: A MatrixRoom the text should be send to :param room: A MatrixRoom the text should be send to
:param body: Textual content of the message :param body: Textual content of the message
:param event: The event to reply to
:param msgtype: The message type for the room https://matrix.org/docs/spec/client_server/latest#m-room-message-msgtypes :param msgtype: The message type for the room https://matrix.org/docs/spec/client_server/latest#m-room-message-msgtypes
:param bot_ignore: Flag to mark the message to be ignored by the bot :param bot_ignore: Flag to mark the message to be ignored by the bot
:return: :return:
""" """
async def send_html(self, room, html, plaintext, msgtype="m.notice", bot_ignore=False): async def send_html(self, room, html, plaintext, event=None, msgtype="m.notice", bot_ignore=False):
""" """
:param room: A MatrixRoom the html should be send to :param room: A MatrixRoom the html should be send to
:param html: Html content of the message :param html: Html content of the message
:param plaintext: Plaintext content of the message :param plaintext: Plaintext content of the message
:param event: The event to reply to
:param msgtype: The message type for the room https://matrix.org/docs/spec/client_server/latest#m-room-message-msgtypes :param msgtype: The message type for the room https://matrix.org/docs/spec/client_server/latest#m-room-message-msgtypes
:param bot_ignore: Flag to mark the message to be ignored by the bot :param bot_ignore: Flag to mark the message to be ignored by the bot
:return: :return:
""" """
async def send_image(self, room, url, body, mimetype=None, width=None, height=None, size=None): async def send_image(self, room, url, body, event=None, mimetype=None, width=None, height=None, size=None):
""" """
:param room: A MatrixRoom the image should be send to :param room: A MatrixRoom the image should be send to
:param url: A MXC-Uri https://matrix.org/docs/spec/client_server/r0.6.0#mxc-uri :param url: A MXC-Uri https://matrix.org/docs/spec/client_server/r0.6.0#mxc-uri
:param body: A textual representation of the image :param body: A textual representation of the image
:param event: The event to reply to
:param mimetype: The mimetype of the image :param mimetype: The mimetype of the image
:param width: Width in pixel of the image :param width: Width in pixel of the image
:param height: Height in pixel of the image :param height: Height in pixel of the image
@ -849,17 +852,18 @@ class Bot:
""" """
async def upload_and_send_image(self, room, url, text=None, blob=False, blob_content_type="image/png"): async def upload_and_send_image(self, room, url, event=None, text=None, blob=False, blob_content_type="image/png"):
""" """
:param room: A MatrixRoom the image should be send to after uploading :param room: A MatrixRoom the image should be send to after uploading
:param url: Url of binary content of the image to upload :param url: Url of binary content of the image to upload
:param event: The event to reply to
:param text: A textual representation of the image :param text: A textual representation of the image
:param blob: Flag to indicate if the second param is an url or a binary content :param blob: Flag to indicate if the second param is an url or a binary content
:param blob_content_type: Content type of the image in case of binary content :param blob_content_type: Content type of the image in case of binary content
:return: :return:
""" """
async def send_location(self, room, body, latitude, longitude, bot_ignore=False): async def send_location(self, room, body, latitude, longitude, event=None bot_ignore=False):
""" """
:param room: A MatrixRoom the html should be send to :param room: A MatrixRoom the html should be send to
@ -867,6 +871,7 @@ class Bot:
:param body: Plaintext content of the message :param body: Plaintext content of the message
:param latitude: Latitude in WGS84 coordinates (float) :param latitude: Latitude in WGS84 coordinates (float)
:param longitude: Longitude in WGS84 coordinates (float) :param longitude: Longitude in WGS84 coordinates (float)
:param event: The event to reply to
:param bot_ignore: Flag to mark the message to be ignored by the bot :param bot_ignore: Flag to mark the message to be ignored by the bot
:return: :return:
""" """

52
bot.py
View File

@ -92,7 +92,7 @@ class Bot:
return self.uri_cache.get(cache_key) 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, url, event=None, text=None, blob=False, blob_content_type="image/png", no_cache=False):
""" """
:param room: A MatrixRoom the image should be send to after uploading :param room: A MatrixRoom the image should be send to after uploading
@ -111,15 +111,30 @@ class Bot:
if res: if res:
try: try:
matrix_uri, mimetype, w, h, size = res 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, matrix_uri, text, event, mimetype, w, h, size)
except ValueError: # broken cache? except ValueError: # broken cache?
self.logger.warning(f"Image cache for {url} could not be unpacked, attempting to re-upload...") self.logger.warning(f"Image cache for {url} could not be unpacked, attempting to re-upload...")
try: try:
matrix_uri, mimetype, w, h, size = await self.upload_image(url, blob=blob, no_cache=no_cache) matrix_uri, mimetype, w, h, size = await self.upload_image(url, event=event, blob=blob, no_cache=no_cache)
except (UploadFailed, ValueError): 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, f"Sorry. Something went wrong fetching {url} and uploading the image to matrix server :(", event=event)
return await self.send_image(room, matrix_uri, text, mimetype, w, h, size) return await self.send_image(room, matrix_uri, text, event, 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:
# m.thread support
relates_to = pre_event.source['content']['m.relates_to']
if relates_to['rel_type'] == 'm.thread':
msg['m.relates_to'] = relates_to
msg['m.relates_to']['m.in_reply_to'] = {'event_id': pre_event.event_id}
except (AttributeError, KeyError):
self.logger.warning(f'No pre-event passed. This module may not be set up to support m.thread.')
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. # Helper function to upload a image from URL to homeserver. Use send_image() to actually send it to room.
# Throws exception if upload fails # Throws exception if upload fails
@ -169,7 +184,6 @@ class Bot:
res = [response.content_uri, content_type, i.size[0], i.size[1], image_length] res = [response.content_uri, content_type, i.size[0], i.size[1], image_length]
if cache_key: if cache_key:
self.uri_cache[cache_key] = res self.uri_cache[cache_key] = res
self.save_settings() # save cache
return res return res
else: else:
response: UploadError response: UploadError
@ -177,7 +191,7 @@ class Bot:
raise UploadFailed raise UploadFailed
async def send_text(self, room, body, msgtype="m.notice", bot_ignore=False): async def send_text(self, room, body, event=None, msgtype="m.notice", bot_ignore=False):
""" """
:param room: A MatrixRoom the text should be send to :param room: A MatrixRoom the text should be send to
@ -194,9 +208,9 @@ class Bot:
if bot_ignore: if bot_ignore:
msg["org.vranki.hemppa.ignore"] = "true" 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, html, plaintext, event=None, msgtype="m.notice", bot_ignore=False):
""" """
:param room: A MatrixRoom the html should be send to :param room: A MatrixRoom the html should be send to
@ -215,9 +229,9 @@ class Bot:
} }
if bot_ignore: if bot_ignore:
msg["org.vranki.hemppa.ignore"] = "true" 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, asset='m.pin'): async def send_location(self, room, body, latitude, longitude, event=None, bot_ignore=False, asset='m.pin'):
""" """
:param room: A MatrixRoom the html should be send to :param room: A MatrixRoom the html should be send to
@ -235,9 +249,9 @@ class Bot:
"msgtype": "m.location", "msgtype": "m.location",
"org.matrix.msc3488.asset": { "type": asset } "org.matrix.msc3488.asset": { "type": asset }
} }
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, url, body, event=None, mimetype=None, width=None, height=None, size=None):
""" """
:param room: A MatrixRoom the image should be send to :param room: A MatrixRoom the image should be send to
@ -268,7 +282,7 @@ class Bot:
if size: if size:
msg["info"]["size"] = 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): async def set_room_avatar(self, room, uri, mimetype=None, width=None, height=None, size=None):
""" """
@ -315,7 +329,7 @@ class Bot:
return False return False
# Send message to the room # Send message to the room
await self.send_text(msg_room, message) await self.send_text(msg_room, None, message)
return True return True
async def find_or_create_private_msg(self, mxid, roomname): async def find_or_create_private_msg(self, mxid, roomname):
@ -423,7 +437,7 @@ class Bot:
if self.owners_only and not self.is_owner(event): if self.owners_only and not self.is_owner(event):
self.logger.info(f"Ignoring {event.sender}, because they're not an owner") 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, "Sorry, only bot owner can run commands.", event=event)
return return
# HACK to ignore messages for some time after joining. # HACK to ignore messages for some time after joining.
@ -446,11 +460,11 @@ class Bot:
try: try:
await moduleobject.matrix_message(self, room, event) await moduleobject.matrix_message(self, room, event)
except CommandRequiresAdmin: 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, f'Sorry, you need admin power level in this room to run that command.', event=event)
except CommandRequiresOwner: except CommandRequiresOwner:
await self.send_text(room, f'Sorry, only bot owner can run that command.') await self.send_text(room, f'Sorry, only bot owner can run that command.', event=event)
except Exception: 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, f'Module {command} experienced difficulty: {sys.exc_info()[0]} - see log for details', event=event)
self.logger.exception(f'unhandled exception in !{command}') self.logger.exception(f'unhandled exception in !{command}')
else: else:
self.logger.error(f"Unknown command: {command}") self.logger.error(f"Unknown command: {command}")