diff --git a/bot/bot.py b/bot/bot.py index b3e2e309..a8df4cf5 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -84,7 +84,8 @@ class OpenAssistantBot(BotBase): @self.tree.command() async def help(interaction: discord.Interaction): """Sends the user a list of all available commands""" - await interaction.response.send_message(f"help command by {interaction.user.name}") + await self.post_help(interaction.user) + await interaction.response.send_message(f"@{interaction.user.display_name}, I've sent you a PM.") @self.tree.command() async def work(interaction: discord.Interaction): @@ -95,6 +96,10 @@ class OpenAssistantBot(BotBase): q = task_handlers.Questionnaire() await interaction.response.send_modal(q) + async def post_help(self, user: discord.abc.User) -> discord.Message: + is_bot_owner = user.id == self.owner_id + return await self.post_template("help.msg", channel=user, is_bot_owner=is_bot_owner) + async def post_boot_message(self) -> discord.Message: return await self.post_template( "boot.msg", bot_name=BOT_NAME, version=__version__, git_hash=get_git_head_hash(), debug=self.debug @@ -104,7 +109,13 @@ class OpenAssistantBot(BotBase): return await self.post_template("welcome.msg") async def delete_all_old_bot_messages(self) -> None: - logger.info("Begin deleting old bot messages.") + logger.info("Deleting old threads...") + for thread in self.bot_channel.threads: + if thread.owner_id == self.client.user.id: + await thread.delete() + logger.info("Completed deleting old theards.") + + logger.info("Deleting old bot messages...") look_until = utcnow() - timedelta(days=365) async for msg in self.bot_channel.history(limit=None): msg: discord.Message @@ -119,7 +130,7 @@ class OpenAssistantBot(BotBase): return msg async def next_task(self): - task_type = protocol_schema.TaskRequestType.rate_summary + task_type = protocol_schema.TaskRequestType.random task = self.backend.fetch_task(task_type, user=None) await self.print_separtor("New Task") @@ -199,22 +210,47 @@ class OpenAssistantBot(BotBase): command_text: str = message.content command_text = command_text[1:] match command_text: - case "sync" | "sync.guild" | "sync.copy_global" | "sync.clear_guild" | "sync.clear_guild": + case "help" | "?": + await self.post_help(user=message.author) + case "sync" | "sync.guild" | "sync.copy_global" | "sync.clear_guild": if is_owner: await self._sync(command_text, message) case _: await message.reply(f"unknown command: {command_text}") + def recipient_filter(self, message: discord.Message) -> bool: + channel = message.channel + + if ( + message.channel.type == discord.ChannelType.private + or message.channel.type == discord.ChannelType.private_thread + ): + return True + + if ( + message.channel.type == discord.ChannelType.text + or message.channel.type == discord.ChannelType.public_thread + ): + while channel: + if self.bot_channel and channel.id == self.bot_channel.id: + return True + channel = channel.parent + + return False + async def handle_message(self, message: discord.Message): + if not self.recipient_filter(message): + return + user_id = message.author.id user_display_name = message.author.name + logger.debug( + f"{message.type} {message.channel.type} from ({user_display_name}) {user_id}: {message.content} ({type(message.content)})" + ) + command_prefix = "!" - if ( - message.channel.type == discord.ChannelType.private - and message.type == discord.MessageType.default - and message.content.startswith(command_prefix) - ): + if message.type == discord.MessageType.default and message.content.startswith(command_prefix): is_owner = self.owner_id and user_id == self.owner_id await self.handle_command(message, is_owner) @@ -228,10 +264,6 @@ class OpenAssistantBot(BotBase): if handler and not handler.handler.completed: handler.handler.on_reply(message) - logger.debug( - f"{message.type} {message.channel.type} from ({user_display_name}) {user_id}: {message.content} ({type(message.content)})" - ) - async def remove_completed_handlers(self): completed = [k for k, v in self.reply_handlers.items() if v.handler is None or v.handler.completed] if len(completed) == 0: diff --git a/bot/bot_base.py b/bot/bot_base.py index 52b98f2c..7ac2e2ac 100644 --- a/bot/bot_base.py +++ b/bot/bot_base.py @@ -36,14 +36,20 @@ class BotBase(ABC): if self.bot_channel is None: raise RuntimeError(f"bot channel '{self.bot_channel_name}' not found") - async def post(self, content: str, view: discord.ui.View = None) -> discord.Message: - self.ensure_bot_channel() - return await self.bot_channel.send(content=content, view=view) + async def post( + self, content: str, *, view: discord.ui.View = None, channel: discord.abc.Messageable = None + ) -> discord.Message: + if channel is None: + self.ensure_bot_channel() + channel = self.bot_channel + return await channel.send(content=content, view=view) - async def post_template(self, name: str, *, view: discord.ui.View = None, **kwargs: Any) -> discord.Message: + async def post_template( + self, name: str, *, view: discord.ui.View = None, channel: discord.abc.Messageable = None, **kwargs: Any + ) -> discord.Message: logger.debug(f"rendering {name}") text = self.templates.render(name, **kwargs) - return await self.post(text, view) + return await self.post(text, view=view, channel=channel) def register_reply_handler(self, msg_id: int, handler: ChannelHandlerBase): if msg_id in self.reply_handlers: diff --git a/bot/channel_handlers.py b/bot/channel_handlers.py index 74d88414..b92d2273 100644 --- a/bot/channel_handlers.py +++ b/bot/channel_handlers.py @@ -66,15 +66,15 @@ class AutoDestructThreadHandler(ChannelHandlerBase): return await super().read() except ChannelExpiredException: await self.cleanup() - raise async def cleanup(self): + logger.debug("AutoDestructThreadHandler.cleanup") if self.thread: - logger.debug(f"[expired] deleting thread: {self.thread.name}") + logger.debug(f"deleting thread: {self.thread.name}") await self.thread.delete() self.thread = None if self.first_message: - logger.debug(f"[expired] deleting first_message: {self.first_message.content}") + logger.debug(f"deleting first_message: {self.first_message.content}") await self.first_message.delete() self.first_message = None diff --git a/bot/task_handlers.py b/bot/task_handlers.py index a18f666d..dd81bbde 100644 --- a/bot/task_handlers.py +++ b/bot/task_handlers.py @@ -30,10 +30,14 @@ class ChannelTaskBase(AutoDestructThreadHandler): msg = await self.send_first_message() self.first_message = msg self.thread = await bot.bot_channel.create_thread(message=discord.Object(msg.id), name=self.thread_name) + await self.on_thread_created(self.thread) self.expiry_date = utcnow() + self.expires_after if self.expires_after else None bot.register_reply_handler(msg_id=msg.id, handler=self) return msg + async def on_thread_created(self, thread: discord.Thread) -> None: + pass + @abstractmethod async def send_first_message(self) -> discord.message: ... @@ -47,10 +51,11 @@ class SummarizeStoryHandler(ChannelTaskBase): return await self.bot.post_template("task_summarize_story.msg", task=self.task) async def handler_loop(self): - msg = await self.read() - print("received: ", msg, type(msg)) - logger.info("on_summarize_story_reply") - await msg.add_reaction("✅") + while True: + msg = await self.read() + print("received: ", msg, type(msg)) + logger.info("on_summarize_story_reply") + await msg.add_reaction("✅") class InitialPromptHandler(ChannelTaskBase): @@ -61,9 +66,10 @@ class InitialPromptHandler(ChannelTaskBase): return await self.bot.post_template("task_initial_prompt.msg", task=self.task) async def handler_loop(self): - msg = await self.read() - logger.info("on_initial_prompt_reply") - await msg.add_reaction("✅") + while True: + msg = await self.read() + logger.info("on_initial_prompt_reply") + await msg.add_reaction("✅") class UserReplyHandler(ChannelTaskBase): @@ -74,9 +80,10 @@ class UserReplyHandler(ChannelTaskBase): return await self.bot.post_template("task_user_reply.msg", task=self.task) async def handler_loop(self): - msg = await self.read() - logger.info("on_user_reply_reply") - await msg.add_reaction("✅") + while True: + msg = await self.read() + logger.info("on_user_reply_reply") + await msg.add_reaction("✅") class AssistantReplyHandler(ChannelTaskBase): @@ -87,9 +94,10 @@ class AssistantReplyHandler(ChannelTaskBase): return await self.bot.post_template("task_assistant_reply.msg", task=self.task) async def handler_loop(self): - msg = await self.read() - logger.info("on_assistant_reply_reply") - await msg.add_reaction("✅") + while True: + msg = await self.read() + logger.info("on_assistant_reply_reply") + await msg.add_reaction("✅") class RankInitialPromptsHandler(ChannelTaskBase): @@ -100,9 +108,10 @@ class RankInitialPromptsHandler(ChannelTaskBase): return await self.bot.post_template("task_rank_initial_prompts.msg", task=self.task) async def handler_loop(self): - msg = await self.read() - logger.info("on_rank_initial_prompts_reply") - await msg.add_reaction("✅") + while True: + msg = await self.read() + logger.info("on_rank_initial_prompts_reply") + await msg.add_reaction("✅") class RankConversationsHandler(ChannelTaskBase): @@ -113,9 +122,10 @@ class RankConversationsHandler(ChannelTaskBase): return await self.bot.post_template("task_rank_conversation_replies.msg", task=self.task) async def handler_loop(self): - msg = await self.read() - logger.info("on_rank_conversation_reply") - await msg.add_reaction("✅") + while True: + msg = await self.read() + logger.info("on_rank_conversation_reply") + await msg.add_reaction("✅") class RatingButton(discord.ui.Button): @@ -139,15 +149,21 @@ class RateSummaryHandler(ChannelTaskBase): task: protocol_schema.RateSummaryTask thread_name: str = "Rate" - async def send_first_message(self) -> discord.message: - async def rating_response_handler(score, interaction: discord.Interaction): - logger.info("rating_response_handler", score) - await interaction.response.send_message(f"got your feedback: {score}") + async def _rating_response_handler(self, score, interaction: discord.Interaction): + logger.info("rating_response_handler", score) + if self.thread: + await self.thread.send(f"{interaction.user.name} got your feedback: {score}") + await interaction.response.send_message(f"got your feedback: {score}") - view = generate_rating_view(self.task.scale.min, self.task.scale.max, rating_response_handler) - return await self.bot.post_template("task_rate_summary.msg", view=view, task=self.task) + async def send_first_message(self) -> discord.message: + return await self.bot.post("first message") + + async def on_thread_created(self, thread: discord.Thread) -> None: + view = generate_rating_view(self.task.scale.min, self.task.scale.max, self._rating_response_handler) + return await self.bot.post_template("task_rate_summary.msg", view=view, channel=thread, task=self.task) async def handler_loop(self): - msg = await self.read() - logger.info("on_rate_summary_reply") - await msg.add_reaction("✅") + while True: + msg = await self.read() + logger.info("on_rate_summary_reply") + await msg.add_reaction("✅") diff --git a/bot/templates/help.msg b/bot/templates/help.msg new file mode 100644 index 00000000..ca033c47 --- /dev/null +++ b/bot/templates/help.msg @@ -0,0 +1,15 @@ +**Open-Assistant Bot Help** + +Available slash-commands: + +`/work` Requests a new personalized human feedback task +`/help` Show this message + +{% if is_bot_owner %} +Commands for bot owners: + +`!sync` +`!sync.guild` +`!sync.copy_global` +`!sync.clear_guild` +{% endif %} \ No newline at end of file