From 4f2aa85356a439cd6536bd98e7b8ac21834080a0 Mon Sep 17 00:00:00 2001 From: anima Date: Sat, 16 Mar 2024 11:50:01 +0100 Subject: [PATCH] update basic bot, add cog functionality --- bot/__init__.py | 2 +- bot/bot.py | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ bot/client.py | 17 -------- main.py | 46 ++------------------- 4 files changed, 109 insertions(+), 60 deletions(-) create mode 100644 bot/bot.py delete mode 100644 bot/client.py diff --git a/bot/__init__.py b/bot/__init__.py index d83d82a..1ec713d 100644 --- a/bot/__init__.py +++ b/bot/__init__.py @@ -1 +1 @@ -from .client import client \ No newline at end of file +from .bot import DiscordBot \ No newline at end of file diff --git a/bot/bot.py b/bot/bot.py new file mode 100644 index 0000000..6f50342 --- /dev/null +++ b/bot/bot.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +from discord.ext import commands, tasks +from discord import Intents + +import glob +import logging +import json + +class DiscordBot(commands.Bot): + __AUTHOR__ = 'anima' + __VERSION__ = '1.0.0' + + def __init__(self, *, intents: Intents = Intents().all(), **options: tasks.Any) -> None: + # lod config file + with open("bot.conf", 'r') as f: + self.botConfig = json.load(f) + + # setup logging + self.log = logging.getLogger(__name__) + self._set_loglevel() + logHandler = logging.FileHandler('DiscordBot.log') + logHandler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s - %(name)s : %(message)s')) + self.log.addHandler(logHandler) + + # init bot with prefix, intents and commands + super().__init__(command_prefix=self.botConfig['prefix'], intents=intents, **options) + self.add_commands() + + def _set_loglevel(self, level: str = 'info'): + """ + allow to change log level external of class + """ + match level: + case 'info': + self.log.setLevel(logging.INFO) + case 'warn': + self.log.setLevel(logging.WARNING) + case 'error': + self.log.setLevel(logging.ERROR) + case 'crit': + self.log.setLevel(logging.CRITICAL) + case 'debug': + self.log.setLevel(logging.DEBUG) + + def run(self, token: str = None, *, reconnect: bool = True) -> None: + # load token from config file if none given + if token == None: + token = self.botConfig['token'] + return super().run(token, reconnect=reconnect) + + async def on_ready(self) -> None: + self.log.info(f'Bot starts as {self.user}') + print(f'Bot starts as {self.user}') + + # initate cogs to extend functions + await self.load_cogs() + + self.fetch_guilds() + for guild in self.guilds: + self.log.info(f'Connect to Server {guild.name} (id: {guild.id})') + print(f'Connect to Server {guild.name} (id: {guild.id})') + + # start background loops + self.mainloop.start() + + async def load_cogs(self, reload: bool = False) -> None: + # fetch all cog.py files and add these to bot + for cogFile in glob.glob('**/cogs/**/cog.py', recursive=True): + cogPath = cogFile[:-3].replace('/', '.') + if reload: + self.log.info(f'Try reload cog from: {cogPath}') + await self.reload_extension(cogPath) + else: + self.log.info(f'Try load cog from: {cogPath}') + await self.load_extension(cogPath) + self.log.debug(f'Cogs loaded: {self.extensions}') + for cog in self.extensions.keys(): + print(f'Cog loaded: {cog}') + + def add_commands(self): + @self.command(name="relmods", pass_context=True) + @commands.has_role('@admin') + async def reload_cogs(ctx) -> None: + """ + allow on the fily reload of cogs + """ + await self.load_cogs(reload=True) + cogs = '' + for cog in self.extensions.keys(): + cogs += '✅ ' + cog.split('.')[-1] + '\n' + await ctx.send(f'Reload Modules ... \n{cogs}') + + @tasks.loop(minutes=1) + async def mainloop(self): + await self.wait_until_ready() + self.log.debug('Run mainloop') + +def main(): + bot = DiscordBot() + bot._set_loglevel('debug') + bot.run() + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/bot/client.py b/bot/client.py deleted file mode 100644 index ff51b7c..0000000 --- a/bot/client.py +++ /dev/null @@ -1,17 +0,0 @@ -import discord -from discord import app_commands - -guild_id = '283156308465811456' - -class client(discord.Client): - def __init__(self, intents=discord.Intents.default()): - super().__init__(intents=intents) - self.synced = False #we use this so the bot doesn't sync commands more than once - self.tree = tree = app_commands.CommandTree(self) - - async def on_ready(self): - await self.wait_until_ready() - if not self.synced: #check if slash commands have been synced - await self.tree.sync(guild = discord.Object(id=guild_id)) #guild specific: leave blank if global (global registration can take 1-24 hours) - self.synced = True - print(f"We have logged in as {self.user}.") \ No newline at end of file diff --git a/main.py b/main.py index 908aae7..4b46097 100644 --- a/main.py +++ b/main.py @@ -1,43 +1,5 @@ -""" - version 2.0.0 - author 4nima, oce - description template for discord bot - requirements - - discord - - flask (for keep_alive) -""" +#!/usr/bin/env python3 +from bot import DiscordBot -## Imports -import json -import logging as log -import discord -from bot import client -#from lib.keep_alive import keep_alive - -intents = discord.Intents.default() -intents.message_content = True - -guild_id = '283156308465811456' - -bot = client(intents) - -## Vars -### Load and set Bot Config -with open("bot.conf", 'r') as f: - BOTDATA = json.load(f) - -### Set logging options -log.basicConfig( - filename="bot.log", - level=log.DEBUG, - format='%(asctime)s - %(process)d-%(levelname)s: %(message)s', - datefmt='%Y-%m-%d %H:%M:%S' -) - -# run -# bot defination -@bot.tree.command(guild = discord.Object(id=guild_id), name = 'tester', description='testing') #guild specific slash command -async def slash2(interaction: discord.Interaction): - await interaction.response.send_message(f"I am working! I was made with Discord.py!", ephemeral = True) - -bot.run(BOTDATA['token']) \ No newline at end of file +bot = DiscordBot() +bot.run() \ No newline at end of file