feat: improve bot functionality and logging setup

Enhanced the bot's functionality with several improvements:
- Added YAML dependency for configuration loading
- Introduced detailed logging for better monitoring
- Implemented invite event handling and operator check
- Updated error handling and user feedback messages
- Ensured the bot ignores its own messages

Increased the version to 0.1.1 to reflect these changes.
This commit is contained in:
Kumi 2024-08-18 18:53:59 +02:00
parent 6447362d04
commit 14e8d44999
Signed by: kumi
GPG key ID: ECBCC9082395383F
3 changed files with 80 additions and 21 deletions

View file

@ -7,7 +7,7 @@ allow-direct-references = true
[project]
name = "matrix-supportbot"
version = "0.1.0"
version = "0.1.1"
authors = [{ name = "Private.coffee Team", email = "support@private.coffee" }]
@ -26,6 +26,7 @@ classifiers = [
dependencies = [
"matrix-nio>=0.24.0",
"pyyaml"
]
[project.urls]

View file

@ -6,33 +6,48 @@ from nio import (
RoomMessageMedia,
LoginResponse,
RoomGetStateEventResponse,
RoomMemberEvent,
InviteMemberEvent,
JoinedMembersResponse,
RoomPutStateResponse,
)
import logging
class SupportBot:
def __init__(self, config):
self.client = AsyncClient(config["homeserver"])
self.client = AsyncClient(config["homeserver"], config["username"])
self.username = config["username"]
self.password = config["password"]
self.operator_room_id = config["operator_room_id"]
async def login(self):
response = await self.client.login(self.username, self.password)
response = await self.client.login(self.password)
if isinstance(response, LoginResponse):
print("Logged in successfully")
logging.info("Logged in successfully")
else:
print("Failed to log in")
logging.fatal("Failed to log in")
async def is_operator(self, user_id):
logging.info(f"Checking if {user_id} is an operator")
response = await self.client.joined_members(self.operator_room_id)
if isinstance(response, RoomMemberEvent):
return user_id in response.members
if not isinstance(response, JoinedMembersResponse):
logging.error(f"Failed to get members in operator room: {response}")
return False
for member in response.members:
if member.user_id == user_id:
return True
return False
async def start(self):
await self.client.sync(timeout=30000)
self.client.add_event_callback(self.message_callback, RoomMessageText)
self.client.add_event_callback(self.message_callback, RoomMessageMedia)
self.client.add_event_callback(self.invite_callback, InviteMemberEvent)
await self.client.sync_forever(timeout=30000)
async def message_callback(self, room: MatrixRoom, event):
@ -44,9 +59,24 @@ class SupportBot:
else:
await self.relay_message(room, sender, event)
async def invite_callback(self, room: MatrixRoom, event: InviteMemberEvent):
logging.info(f"Received invite event: {event}")
if event.membership == "invite" and event.state_key == self.client.user_id:
await self.client.join(room.room_id)
await self.client.room_send(
room.room_id,
"m.room.message",
{
"msgtype": "m.text",
"body": "Hello! To open a support ticket, please type `!supportbot openticket`.",
},
)
async def handle_command(self, room, sender, command):
if command == "!supportbot openticket":
await self.open_ticket(room, sender)
return
elif await self.is_operator(sender):
if command.startswith("!supportbot invite"):
await self.invite_operator(room, sender, command)
@ -54,13 +84,14 @@ class SupportBot:
await self.close_ticket(room, sender, command)
elif command == "!supportbot list":
await self.list_tickets(room)
else:
return
await self.client.room_send(
room.room_id,
"m.room.message",
{
"msgtype": "m.text",
"body": "You are not authorized to use this command.",
"body": "Sorry, I do not know this command, or you are not authorized to use it.",
},
)
@ -91,13 +122,19 @@ class SupportBot:
"status": "open",
}
await self.client.room_put_state(
state_event_response = await self.client.room_put_state(
room_id=self.operator_room_id,
event_type="m.room.custom.ticket",
state_key=state_event_key,
content=state_event_content,
)
if not isinstance(state_event_response, RoomPutStateResponse):
logging.error(
f"Failed to update state in operator room: {state_event_response}"
)
return
# Inform the operator room
await self.client.room_send(
self.operator_room_id,
@ -118,6 +155,15 @@ class SupportBot:
},
)
await self.client.room_send(
room.room_id,
"m.room.message",
{
"msgtype": "m.text",
"body": f"Ticket #{ticket_id} has been created. Please check your DMs.",
},
)
async def invite_operator(self, room, sender, command):
ticket_id = command.split()[2]
state_event_key = f"ticket_{ticket_id}"
@ -164,7 +210,10 @@ class SupportBot:
await self.client.room_send(
customer_room_id,
"m.room.message",
{"msgtype": "m.text", "body": f"Ticket #{ticket_id} has been closed."},
{
"msgtype": "m.text",
"body": f"Ticket #{ticket_id} has been closed. If you need further assistance, please open a new ticket.",
},
)
await self.client.room_send(
operator_room_id,
@ -212,6 +261,10 @@ class SupportBot:
return None
async def relay_message(self, room, sender, event):
# Ignore any messages from the bot itself
if sender == self.client.user_id:
return
ticket_id = await self.get_ticket_id_from_room(room.room_id)
if ticket_id:
state_event_key = f"ticket_{ticket_id}"
@ -229,5 +282,5 @@ class SupportBot:
# Relay the entire event content to the target room
await self.client.room_send(
target_room_id, event.type, event.source["content"]
target_room_id, "m.room.message", event.source["content"]
)

View file

@ -1,8 +1,9 @@
import yaml
import logging
import asyncio
from .bot import SupportBot
from .classes.bot import SupportBot
def load_config(config_file):
with open(config_file, 'r') as file:
@ -11,6 +12,10 @@ def load_config(config_file):
def main():
config = load_config("config.yaml")
bot = SupportBot(config)
logging.basicConfig(level=logging.DEBUG)
logging.getLogger("nio").setLevel(logging.WARNING)
asyncio.get_event_loop().run_until_complete(bot.login())
asyncio.get_event_loop().run_until_complete(bot.start())