diff --git a/src/bot.py b/src/bot.py index bb0dadd..0b5c94d 100644 --- a/src/bot.py +++ b/src/bot.py @@ -369,6 +369,33 @@ async def message_callback(self, room: MatrixRoom, event: RoomMessageText) -> No image_url = ( f"data:{image_mimetype};base64,{b64_image}" ) + if ( + "rel_type" + in event_source["content"]["m.relates_to"] + ): + if ( + "m.thread" + == event_source["content"]["m.relates_to"][ + "rel_type" + ] + ): + thread_root_id = event_source["content"][ + "m.relates_to" + ]["event_id"] + asyncio.create_task( + self.gpt_vision_cmd( + room_id, + reply_to_event_id, + prompt, + image_url, + sender_id, + raw_user_message, + reply_in_thread=True, + thread_root_id=thread_root_id, + ) + ) + return + asyncio.create_task( self.gpt_vision_cmd( room_id, @@ -408,20 +435,63 @@ async def message_callback(self, room: MatrixRoom, event: RoomMessageText) -> No thread_root_id = event_source["content"]["m.relates_to"]["event_id"] # thread is created by @bot if thread_root_id in self.chatbot.conversation: - try: - asyncio.create_task( - self.thread_chat( - room_id, - reply_to_event_id, - sender_id=sender_id, - thread_root_id=thread_root_id, - prompt=content_body, + msgtype = event_source["content"]["msgtype"] + if "m.text" == msgtype: + # !pic command for thread chatting + p = self.pic_prog.search(content_body) + if p: + prompt = p.group(1) + try: + asyncio.create_task( + self.pic( + room_id, + prompt, + reply_to_event_id, + sender_id, + raw_user_message, + reply_in_thread=True, + thread_root_id=thread_root_id, + ) + ) + except Exception as e: + logger.error(e, exc_info=True) + + return + + # !help command for thread chatting + h = self.help_prog.search(content_body) + if h: + try: + asyncio.create_task( + self.help( + room_id, + reply_to_event_id, + sender_id, + raw_user_message, + reply_in_thread=True, + thread_root_id=thread_root_id, + ) + ) + except Exception as e: + logger.error(e, exc_info=True) + + return + + # normal chatting function + try: + asyncio.create_task( + self.thread_chat( + room_id, + reply_to_event_id, + sender_id=sender_id, + thread_root_id=thread_root_id, + prompt=content_body, + ) ) - ) - except Exception as e: - logger.error(e, exe_info=True) + except Exception as e: + logger.error(e, exe_info=True) - return + return # common command @@ -1508,6 +1578,8 @@ async def gpt_vision_cmd( image_url: str, sender_id: str, user_message: str, + reply_in_thread=False, + thread_root_id=None, ) -> None: try: # sending typing state, seconds to milliseconds @@ -1528,6 +1600,8 @@ async def gpt_vision_cmd( reply_to_event_id=reply_to_event_id, sender_id=sender_id, user_message=user_message, + reply_in_thread=reply_in_thread, + thread_root_id=thread_root_id, ) except Exception as e: logger.error(e, exc_info=True) @@ -1605,7 +1679,16 @@ async def new( ) # !pic command - async def pic(self, room_id, prompt, replay_to_event_id, sender_id, user_message): + async def pic( + self, + room_id, + prompt, + replay_to_event_id, + sender_id, + user_message, + reply_in_thread=False, + thread_root_id=None, + ): try: if self.image_generation_endpoint is not None: await self.client.room_typing(room_id, timeout=int(self.timeout) * 1000) @@ -1629,7 +1712,14 @@ async def pic(self, room_id, prompt, replay_to_event_id, sender_id, user_message ) # send image for image_path in image_path_list: - await send_room_image(self.client, room_id, image_path) + await send_room_image( + self.client, + room_id, + image_path, + replay_to_event_id, + reply_in_thread=reply_in_thread, + thread_root_id=thread_root_id, + ) await aiofiles.os.remove(image_path) await self.client.room_typing(room_id, typing_state=False) else: @@ -1653,7 +1743,15 @@ async def pic(self, room_id, prompt, replay_to_event_id, sender_id, user_message ) # !help command - async def help(self, room_id, reply_to_event_id, sender_id, user_message): + async def help( + self, + room_id, + reply_to_event_id, + sender_id, + user_message, + reply_in_thread=False, + thread_root_id=None, + ): help_info = ( "!gpt [prompt], generate a one time response without context conversation\n" + "!chat [prompt], chat with context conversation\n" @@ -1672,6 +1770,8 @@ async def help(self, room_id, reply_to_event_id, sender_id, user_message): sender_id=sender_id, user_message=user_message, reply_to_event_id=reply_to_event_id, + reply_in_thread=reply_in_thread, + thread_root_id=thread_root_id, ) # send general error message diff --git a/src/send_image.py b/src/send_image.py index 5529f2c..f8b3137 100644 --- a/src/send_image.py +++ b/src/send_image.py @@ -14,7 +14,14 @@ logger = getlogger() -async def send_room_image(client: AsyncClient, room_id: str, image: str): +async def send_room_image( + client: AsyncClient, + room_id: str, + image: str, + reply_to_event_id=None, + reply_in_thread=False, + thread_root_id=None, +): """ image: image path """ @@ -57,6 +64,25 @@ async def send_room_image(client: AsyncClient, room_id: str, image: str): "url": resp.content_uri, } + if reply_in_thread: + content = { + "body": os.path.basename(image), # descriptive title + "info": { + "size": file_stat.st_size, + "mimetype": mime_type, + "w": width, # width in pixel + "h": height, # height in pixel + }, + "msgtype": "m.image", + "url": resp.content_uri, + "m.relates_to": { + "rel_type": "m.thread", + "event_id": thread_root_id, + "m.in_reply_to": {"event_id": reply_to_event_id}, + "is_falling_back": True, + }, + } + try: await client.room_send(room_id, message_type="m.room.message", content=content) except Exception as e: