SAE-A2-TruthInquiry/truthinquiry/ext/discord_bot.py

83 lines
2.7 KiB
Python

import os
import threading
import asyncio
import discord
import truthinquiry.app as app
class DiscordBot:
"""
Wrapper around a discord bot, providing utility methods to interact with it
:attr Client bot: the underlying discord bot from discord.py
:attr TextChannel __channel__: the channel used by the bot to send messages
:attr event_loop: event_loop used by discord.py. Used to schedule tasks from another thread
"""
def __init__(self):
self.bot = discord.Client(intents=discord.Intents.default())
self.__channel__ = None
@self.bot.event
async def on_ready():
print('Discord bot connected !')
self.event_loop = asyncio.get_event_loop()
self.__setup__channel()
self.update_games_presence()
def __setup__channel(self) -> None:
"""
Setup the channel that the bot will send message in
"""
if len(self.bot.guilds) == 1:
self.__channel__ = discord.utils.get(self.bot.guilds[0].channels, name="bot")
else:
print("Could not find channel #bot")
def try_start(self):
"""
Start the discord bot if the environment provides a token, else do nothing
"""
token = os.getenv("DISCORD_BOT_TOKEN")
if token:
thr = threading.Thread(target=self.bot.run, args=(token,))
thr.start()
return thr
else:
print("Token not present ! Discord bot not starting")
return None
def API(func):
"""
Decorator used to wrap APIs methods, to handle thread context change, and ready check
"""
def decorator(self, *args, **kwargs):
if self.bot and self.bot.is_ready():
self.event_loop.create_task(func(self, *args, **kwargs))
else:
print(f"Discord bot not ready, not processing function {func.__name__}()")
return decorator
@API
async def update_games_presence(self) -> None:
"""
Update the bot's status using the app's current context
"""
games_n = len(app.APP.games_list)
activity_name = f"Handling {games_n} game{'' if games_n==1 else 's'} !"
activity = discord.Activity(name=activity_name, type=discord.ActivityType.watching)
await self.bot.change_presence(activity=activity)
@API
async def send_message(self, text):
"""
Send a message to the channel used by the bot
"""
if self.__channel__:
await self.__channel__.send(text)
else:
print("channel not defined, not sending discord message")
discord_bot = DiscordBot()