Prepare for option to disable replying to everything

Automatically accept room invites on sync
Leave rooms if everyone else leaves
README update
!gptbot dice command
Minor fixes
This commit is contained in:
Kumi 2023-05-01 15:49:26 +00:00
parent e369b63baf
commit 5b500d34b5
Signed by: kumi
GPG key ID: ECBCC9082395383F
12 changed files with 122 additions and 24 deletions

View file

@ -235,11 +235,11 @@ class GPTBot:
await COMMANDS.get(command, COMMANDS[None])(room, event, self)
def room_uses_classification(self, room: MatrixRoom | int) -> bool:
def room_uses_classification(self, room: MatrixRoom | str) -> bool:
"""Check if a room uses classification.
Args:
room (MatrixRoom): The room to check.
room (MatrixRoom | str): The room to check.
Returns:
bool: Whether the room uses classification.
@ -276,9 +276,18 @@ class GPTBot:
invites = self.matrix_client.invited_rooms
for invite in invites.keys():
self.logger.log(f"Accepting invite to room {invite}")
await self.matrix_client.join(invite)
async def send_image(self, room: MatrixRoom, image: bytes, message: Optional[str] = None):
"""Send an image to a room.
Args:
room (MatrixRoom): The room to send the image to.
image (bytes): The image to send.
message (str, optional): The message to send with the image. Defaults to None.
"""
self.logger.log(
f"Sending image of size {len(image)} bytes to room {room.room_id}")
@ -325,6 +334,14 @@ class GPTBot:
self.logger.log("Sent image")
async def send_message(self, room: MatrixRoom, message: str, notice: bool = False):
"""Send a message to a room.
Args:
room (MatrixRoom): The room to send the message to.
message (str): The message to send.
notice (bool): Whether to send the message as a notice. Defaults to False.
"""
markdowner = markdown2.Markdown(extras=["fenced-code-blocks"])
formatted_body = markdowner.convert(message)
@ -371,12 +388,12 @@ class GPTBot:
return await self.matrix_client._send(RoomSendResponse, method, path, data, (room.room_id,))
def log_api_usage(self, message: Event | str, room: MatrixRoom | int, api: str, tokens: int):
def log_api_usage(self, message: Event | str, room: MatrixRoom | str, api: str, tokens: int):
"""Log API usage to the database.
Args:
message (Event): The event that triggered the API usage.
room (MatrixRoom | int): The room the event was sent in.
room (MatrixRoom | str): The room the event was sent in.
api (str): The API that was used.
tokens (int): The number of tokens used.
"""
@ -447,7 +464,7 @@ class GPTBot:
self.matrix_client.encrypted_rooms = self.matrix_client.store.load_encrypted_rooms()
# Run initial sync
# Run initial sync (now includes joining rooms)
sync = await self.matrix_client.sync(timeout=30000)
if isinstance(sync, SyncResponse):
await self.response_callback(sync)
@ -461,11 +478,6 @@ class GPTBot:
self.matrix_client.add_response_callback(
self.response_callback, Response)
# Accept pending invites
self.logger.log("Accepting pending invites...")
await self.accept_pending_invites()
# Start syncing events
self.logger.log("Starting sync loop...")
try:
@ -474,15 +486,48 @@ class GPTBot:
self.logger.log("Syncing one last time...")
await self.matrix_client.sync(timeout=30000)
async def process_query(self, room: MatrixRoom, event: RoomMessageText, allow_classify: bool = True):
def respond_to_room_messages(self, room: MatrixRoom | str) -> bool:
"""Check whether the bot should respond to messages sent in a room.
Args:
room (MatrixRoom | str): The room to check.
Returns:
bool: Whether the bot should respond to messages sent in the room.
"""
if isinstance(room, MatrixRoom):
room = room.room_id
with self.database.cursor() as cursor:
cursor.execute(
"SELECT value FROM room_settings WHERE room_id = ? AND setting = ?", (room, "respond_to_messages"))
result = cursor.fetchone()
return True if not result else bool(int(result[0]))
async def process_query(self, room: MatrixRoom, event: RoomMessageText, from_chat_command: bool = False):
"""Process a query message. Generates a response and sends it to the room.
Args:
room (MatrixRoom): The room the message was sent in.
event (RoomMessageText): The event that triggered the query.
from_chat_command (bool, optional): Whether the query was sent via the `!gptbot chat` command. Defaults to False.
"""
if not (from_chat_command or self.respond_to_room_messages(room) or self.matrix_client.user_id in event.body):
return
await self.matrix_client.room_typing(room.room_id, True)
await self.matrix_client.room_read_markers(room.room_id, event.event_id)
if allow_classify and self.room_uses_classification(room):
classification, tokens = self.classification_api.classify_message(event.body, room.room_id)
if (not from_chat_command) and self.room_uses_classification(room):
classification, tokens = self.classification_api.classify_message(
event.body, room.room_id)
self.log_api_usage(event, room, f"{self.classification_api.api_code}-{self.classification_api.classification_api}", tokens)
self.log_api_usage(
event, room, f"{self.classification_api.api_code}-{self.classification_api.classification_api}", tokens)
if not classification["type"] == "chat":
event.body = f"!gptbot {classification['type']} {classification['prompt']}"
@ -522,7 +567,8 @@ class GPTBot:
return
if response:
self.log_api_usage(event, room, f"{self.chat_api.api_code}-{self.chat_api.chat_api}", tokens_used)
self.log_api_usage(
event, room, f"{self.chat_api.api_code}-{self.chat_api.chat_api}", tokens_used)
self.logger.log(f"Sending response to room {room.room_id}...")
@ -538,10 +584,19 @@ class GPTBot:
await self.matrix_client.room_typing(room.room_id, False)
def get_system_message(self, room: MatrixRoom | int) -> str:
def get_system_message(self, room: MatrixRoom | str) -> str:
"""Get the system message for a room.
Args:
room (MatrixRoom | str): The room to get the system message for.
Returns:
str: The system message.
"""
default = self.default_system_message
if isinstance(room, int):
if isinstance(room, str):
room_id = room
else:
room_id = room.room_id