Files
hmgrisen/bot/src/run.py
T
2026-11-03 01:52:13 +01:00

133 lines
4.4 KiB
Python

import asyncio
import os
import signal
from time import time
import aiohttp
import asyncpg
import discord
from discord.ext import commands
from dotenv import load_dotenv
from logger import BotLogger
# Load config file
load_dotenv()
class Bot(commands.Bot):
def __init__(self):
super().__init__(
command_prefix=commands.when_mentioned_or(os.environ["BOT_PREFIX"]),
case_insensitive=True,
intents=discord.Intents.all(),
allowed_mentions=discord.AllowedMentions(everyone=False),
)
self.logger = BotLogger().logger # Initialize logger
self.cog_files = set(os.listdir("./src/cogs"))
self.galtinn = {
"api_url": os.environ.get("GALTINN_API_URL", "http://127.0.0.1:8000"),
"client_id": os.environ.get("GALTINN_CLIENT_ID"),
"redirect_uri": os.environ.get("GALTINN_REDIRECT_URI"),
"auth_token": os.environ.get("GALTINN_AUTH_TOKEN"),
}
self.galtinn_roles = {
"member": int(os.environ.get("BOT_GALTINN_ROLE_MEMBER")),
"volunteer": int(os.environ.get("BOT_GALTINN_ROLE_VOLUNTEER")),
}
self.guild_id = int(os.environ.get("BOT_DEV_GUILD", 1162158668079444199))
self.emoji = {
"online": os.environ.get("BOT_EMOJI_ONLINE"),
"idle": os.environ.get("BOT_EMOJI_IDLE"),
"dnd": os.environ.get("BOT_EMOJI_DND"),
"offline": os.environ.get("BOT_EMOJI_OFFLINE"),
}
async def setup_hook(self):
self.http_session = aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=15))
# DB needs to be setup here because contructor is not async
# Not ideal but does the job
# Credentials are kept for cogs that need a dedicated connection outside the pool
self.db_credentials = {
"host": os.environ["DATABASE_HOST"],
"database": os.environ["DATABASE_NAME"],
"user": os.environ["DATABASE_USER"],
"password": os.environ["DATABASE_PASSWORD"],
}
self.db = await asyncpg.create_pool(**self.db_credentials)
# Docker and supervisor stop the bot with SIGTERM. Close gracefully so cogs are unloaded
try:
asyncio.get_running_loop().add_signal_handler(signal.SIGTERM, lambda: asyncio.create_task(self.close()))
except NotImplementedError:
pass # Not supported on Windows
# Load cogs
cogs = os.listdir("./src/cogs")
for file in cogs:
if file.endswith(".py"):
name = file[:-3]
await self.load_extension(f"cogs.{name}")
# Sync slash commands
if os.environ.get("BOT_CONFIG_MODE") == "prod":
await self.tree.sync()
else:
self.tree.copy_global_to(guild=discord.Object(id=self.guild_id))
await self.tree.sync(guild=discord.Object(id=self.guild_id))
async def __aexit__(self, *args):
"""
Close the HTTP session and database pool after the bot has shut down. This isn't done in close(),
because bot.run doesn't wait for a close() started by the SIGTERM handler to finish
"""
await super().__aexit__(*args)
if hasattr(self, "http_session"):
await self.http_session.close()
if hasattr(self, "db"):
await self.db.close()
# Create bot instance
bot = Bot()
@bot.event
async def on_ready():
if not hasattr(bot, "uptime"):
bot.uptime = time()
# Print bot info
print(f"Username: {bot.user.name}")
print(f"ID: {bot.user.id}")
print(f"Version: {discord.__version__}")
print("." * 50 + "\n")
# Set initial presence
# Presence status
status_types = {
"online": discord.Status.online,
"dnd": discord.Status.dnd,
"idle": discord.Status.idle,
"offline": discord.Status.offline,
}
status_type = status_types.get(os.environ.get("BOT_PRESENCE_STATUS", "online").lower(), discord.Status.online)
# Presence actitivity
activities = {"playing": 0, "listening": 2, "watching": 3}
activity_type = activities.get(os.environ.get("BOT_PRESENCE_ACTIVITY", "playing").lower(), 0)
await bot.change_presence(
activity=discord.Activity(type=activity_type, name=os.environ.get("BOT_PRESENCE_MESSAGE", "Yeet")),
status=status_type,
)
bot.run(os.environ["BOT_TOKEN"], reconnect=True, log_handler=None)