diff --git a/README.md b/README.md index b998427..84e27e1 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,44 @@ # MatrixGPT -_ChatGPT bot for Matrix._ - -Uses code from [anoadragon453/nio-template](https://github.com/anoadragon453/nio-template). +_Chatbots for Matrix._ ## Install -```bash -sudo apt install libolm-dev gcc python3-dev -pip install -r requirements.txt -``` +1. Install requirements: + ```bash + sudo apt install libolm-dev gcc python3-dev + pip install -r requirements.txt + ``` +2. Copy `config.sample.yaml` to `config.yaml` and fill it out with your bot's Matrix auth and your API key(s). -Copy `config.sample.yaml` to `config.yaml` and fill it out with your bot's auth and your OpenAI API key. - -[Pantalaimon](https://github.com/matrix-org/pantalaimon) is **required** for the bot to be able to talk in encrypted rooms. - -Then invite your bot and start a chat by prefixing your message with `!c`. The bot will create a thread (you don't need -to use `!c` in the thread). +[Pantalaimon](https://github.com/matrix-org/pantalaimon) is **required** for the bot to be able to talk in encrypted +rooms. I included a sample Systemd service. ## Use -Invite the bot to your room and query it with the command `!c` (this can be changed in the config.) +Invite your bot to a room. + +Start a chat by prefixing your message with your trigger (for example, `!c`). The bot will create a thread when it +replies to you and you don't need to use the trigger in the thread. Don't try to use two bots in the same thread. +You can DM a bot for a private chat. Don't use the trigger prefix in a DM. + The bot will move its read marker when a new message is sent in the room. The bot can give helpful reactions: - 🚫 means that the user is not allowed to chat with the bot. -- ❌ means the bot encountered an exception. The bot restarts when it encounters an exception which means it will not be able to respond for a short time after this reaction. +- ❌ means the bot encountered an exception. The bot restarts when it encounters an exception which means it will not be + able to respond for a short time after this reaction. - ❌ 🔐 means there was a decryption failure. +Use `!matrixgpt` to view the bot's help. The bot also responds to `!bots`. + ## Encryption -This bot supports encryption. I recommend using [Pantalaimon](https://github.com/matrix-org/pantalaimon/) to manage encryption keys as the built-in solution is a little janky and may be unreliable. +This bot supports encryption. I recommend using [Pantalaimon](https://github.com/matrix-org/pantalaimon/) to manage +encryption keys as the built-in solution is a little janky and may be unreliable. diff --git a/matrix_gpt/callbacks.py b/matrix_gpt/callbacks.py index 0997d5d..3d2e7b0 100644 --- a/matrix_gpt/callbacks.py +++ b/matrix_gpt/callbacks.py @@ -7,7 +7,7 @@ from nio import (AsyncClient, InviteMemberEvent, MatrixRoom, MegolmEvent, RoomMe from .chat_functions import check_authorized, is_thread, check_command_prefix from .config import global_config -from .handle_actions import do_reply_msg, do_reply_threaded_msg, do_join_channel +from .handle_actions import do_reply_msg, do_reply_threaded_msg, do_join_channel, sound_off from .matrix import MatrixClientHelper logger = logging.getLogger('MatrixGPT') @@ -22,7 +22,7 @@ class MatrixBotCallbacks: async def handle_message(self, room: MatrixRoom, requestor_event: RoomMessageText) -> None: """ - Callback for when a message event is received + Callback for when a message event is received. """ # Mark all messages as read. mark_read_task = asyncio.create_task(self.client.room_read_markers(room.room_id, requestor_event.event_id, requestor_event.event_id)) @@ -35,6 +35,10 @@ class MatrixBotCallbacks: return if requestor_event.sender == self.client.user_id: return + if msg == '!bots' or msg == '!matrixgpt': + logger.debug(f'Message from {requestor_event.sender} in {room.room_id} --> "{msg}"') + await sound_off(room, requestor_event, self.client_helper) + return command_activated, sent_command_prefix, command_info = check_command_prefix(msg) if not command_activated and is_thread(requestor_event): diff --git a/matrix_gpt/generate.py b/matrix_gpt/generate.py index 4fa9d74..12f8181 100644 --- a/matrix_gpt/generate.py +++ b/matrix_gpt/generate.py @@ -3,7 +3,7 @@ import logging import traceback from typing import Union -from nio import RoomSendResponse +from nio import RoomSendResponse, MatrixRoom, RoomMessageText from matrix_gpt import MatrixClientHelper from matrix_gpt.api_client_manager import api_client_helper @@ -34,8 +34,8 @@ def assemble_messages(messages: list, mode: str): async def generate_ai_response( client_helper: MatrixClientHelper, - room, - event, + room: MatrixRoom, + event: RoomMessageText, msg: Union[str, list], command_info: CommandInfo, thread_root_id: str = None, @@ -104,7 +104,8 @@ async def generate_ai_response( text_response, reply_to_event_id=event.event_id, thread=True, - thread_root_id=thread_root_id if thread_root_id else event.event_id + thread_root_id=thread_root_id if thread_root_id else event.event_id, + markdown_convert=True ) await client.room_typing(room.room_id, typing_state=False, timeout=1000) if not isinstance(resp, RoomSendResponse): diff --git a/matrix_gpt/handle_actions.py b/matrix_gpt/handle_actions.py index 809b2f4..3a6958b 100644 --- a/matrix_gpt/handle_actions.py +++ b/matrix_gpt/handle_actions.py @@ -101,3 +101,22 @@ async def do_join_channel(client_helper: MatrixClientHelper, room: MatrixRoom, e return else: logger.error(f'Unable to join room: {room.room_id}') + + +async def sound_off(room: MatrixRoom, event: RoomMessageText, client_helper: MatrixClientHelper): + text_response = """## MatrixGPT + + + +### Commands + + +`!matrixgpt` - show this help message\n\n""" + for command in global_config['command']: + text_response = text_response + f"`{command['trigger']}` - Model: {command['model']}. Temperature: {command['temperature']}. Max tokens: {command['max_tokens']}.\n\n" + return await client_helper.send_text_to_room( + room.room_id, + text_response, + reply_to_event_id=event.event_id, + markdown_convert=True + ) diff --git a/matrix_gpt/matrix.py b/matrix_gpt/matrix.py index fb3d930..eb46cfc 100644 --- a/matrix_gpt/matrix.py +++ b/matrix_gpt/matrix.py @@ -119,7 +119,7 @@ class MatrixClientHelper: return await self.client.room_send(room_id, "m.reaction", content, ignore_unverified_devices=True) async def send_text_to_room(self, room_id: str, message: str, notice: bool = False, - markdown_convert: bool = True, reply_to_event_id: Optional[str] = None, + markdown_convert: bool = False, reply_to_event_id: Optional[str] = None, thread: bool = False, thread_root_id: Optional[str] = None, extra_error: Optional[str] = None, extra_msg: Optional[str] = None) -> Union[RoomSendResponse, ErrorResponse]: """Send text to a matrix room. @@ -142,9 +142,7 @@ class MatrixClientHelper: A RoomSendResponse if the request was successful, else an ErrorResponse. """ - # Determine whether to ping room members or not msgtype = "m.notice" if notice else "m.text" - content = {"msgtype": msgtype, "format": "org.matrix.custom.html", "body": message} if markdown_convert: