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