diff --git a/.github/workflows/discord_sync.yml b/.github/workflows/discord_sync.yml new file mode 100644 index 0000000..6a92ee8 --- /dev/null +++ b/.github/workflows/discord_sync.yml @@ -0,0 +1,15 @@ +name: Discord Webhook + +on: [push] + +jobs: + git: + runs-on: ubuntu-latest + steps: + + - uses: actions/checkout@v2 + + - name: Run Discord Webhook + uses: johnnyhuy/actions-discord-git-webhook@main + with: + webhook_url: ${{ secrets.YOUR_DISCORD_WEBHOOK_URL }} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..73f5626 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Current File", + "type": "debugpy", + "request": "launch", + "program": "E://Development//Kenny Projects//Eona//main.py", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/birthday.py b/birthday.py new file mode 100644 index 0000000..324ec0a --- /dev/null +++ b/birthday.py @@ -0,0 +1,113 @@ +import discord +from discord.ext import tasks +import sqlite3 +from logger import logger +import datetime + + +class Birthday: + def __init__(self, client): + self.client = client + self.db_path = "data/EONA.db" + self.logger = logger + + self.ensure_db() + + def ensure_db(self): + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + cursor.execute("""CREATE TABLE IF NOT EXISTS birthdays ( + user_id INTEGER, + guild_id INTEGER, + user_name TEXT, + birthday TEXT, + PRIMARY KEY (user_id, guild_id) + );""") + conn.commit() + conn.close() + self.logger.info("Birthday table ensured") + + async def set_birthday(self, user_id, guild_id, birthday): + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + cursor.execute("""INSERT INTO birthdays (user_id, guild_id, birthday) + VALUES (?, ?, ?) ON CONFLICT(user_id, guild_id) + DO UPDATE SET birthday = excluded.birthday;""", + (user_id, guild_id, birthday)) + conn.commit() + conn.close() + self.logger.info(f"{user_id}! Your birthday was set to {birthday}") + + + async def get_birthday(self, user_id, guild_id): + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + cursor.execute("SELECT birthday FROM birthdays WHERE user_id = ? AND guild_id = ?", (user_id, guild_id)) + row = cursor.fetchone() + conn.close() + return row[0] if row else None + + @tasks.loop(hours=24) + async def check_birthdays(self): + today = datetime.date.today().strftime("%Y-%m-%d") + conn = sqlite3.connect(self.db_path) + cursor = conn.cursor() + cursor.execute("SELECT user_id, guild_id FROM birthdays where birthday = ?", (today,)) + rows = cursor.fetchall() + conn.close() + for user_id, guild_id in rows: + guild = self.client.get_guild(int(guild_id)) + if guild: + user = guild.get_member(int(user_id)) + if user: + channel = guild.system_channel or next((ch for ch in guild.text_channels if ch.permissions_for(guild.me).send_messages), None) + if channel: + await channel.send(f"Happy birthday {user.mention}! Happy birthday! 🎉🎂") + + async def setup_hook(self): + self.check_birthdays.start() + self.logger.info("Birthday module loaded") + + def setup(self, tree: discord.app_commands.CommandTree): + @tree.command(name="set_birthday", description="Set your birthday (YYYY-MM-DD)") + async def set_birthday_command(interaction: discord.Interaction): + await interaction.response.send_modal(BirthdayModal(self)) + + @tree.command(name="get_birthday", description="Check your birthday") + async def get_birthday_command(interaction: discord.Interaction): + user_id = str(interaction.user.id) + guild_id = str(interaction.guild.id) + birthday = await self.get_birthday(user_id, guild_id) + if birthday: + await interaction.response.send_message(f"Your birthday has been set to {birthday}", ephemeral=True) + else: + await interaction.response.send_message("Your birthday has not been set. Please use `/set_birthday` to set your birthday.", ephemeral=True) + + if not tree.get_command("set_birthday"): + tree.add_command(set_birthday_command) + if not tree.get_command("get_birthday"): + tree.add_command(get_birthday_command) + + +class BirthdayModal(discord.ui.Modal, title="Set your birthday"): + birthday = discord.ui.TextInput(label="Your birthday", placeholder="YYYY-MM-DD", style=discord.TextStyle.short, required=True) + + def __init__(self, birthday_module): + super().__init__() + self.birthday_module = birthday_module + + async def on_submit(self, interaction: discord.Interaction): + user_id = str(interaction.user.id) + guild_id = str(interaction.guild.id) + try: + birthday = self.birthday.value + await self.birthday_module.set_birthday(user_id, guild_id, birthday) + await interaction.response.send_message(f"Your birthday has been set to {birthday}", ephemeral=True) + except ValueError as e: + await interaction.response.send_message(str(e), ephemeral=True) + + +def setup(client): + birthday_module = Birthday(client) + client.add_cog(birthday_module) + client.birthday_module = birthday_module \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..aea2a01 --- /dev/null +++ b/config.py @@ -0,0 +1,11 @@ +from dotenv import load_dotenv +import os + +load_dotenv() + +config = { + "TOKEN": os.getenv("DISCORD_TOKEN"), + "modules": { + 'birthday': {'enabled': True}, + } +} diff --git a/logger.py b/logger.py new file mode 100644 index 0000000..3e3b890 --- /dev/null +++ b/logger.py @@ -0,0 +1,19 @@ +import logging +from logging.handlers import RotatingFileHandler + +logging.basicConfig( + level=logging.ERROR, + format="%(asctime)s - %(name)s - " "%(levelname)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", +) + +log_file = "logs/EONA.log" +handler = RotatingFileHandler(log_file, maxBytes=1e6, backupCount=5) +formatter = logging.Formatter( + "%(asctime)s:%(levelname)s:%(name)s: %(message)s") +handler.setFormatter(formatter) + + +logger = logging.getLogger("EONA") +logger.addHandler(handler) +logger.setLevel(logging.INFO) diff --git a/main.py b/main.py new file mode 100644 index 0000000..b57d5af --- /dev/null +++ b/main.py @@ -0,0 +1,45 @@ +from config import config +import discord +from logger import logger + +logger.info("Starting EONA...") + +TOKEN = config["TOKEN"] + +intents = discord.Intents.default() + + +class EONA(discord.Client): + def __init__(self): + super().__init__(intents=intents) + self.tree = discord.app_commands.CommandTree(self) + self.load_modules() + + async def setup_hook(self): + logger.info("Setting up...") + await self.tree.sync() + logger.info("Commands Synced") + + def load_modules(self): + try: + from config import config + except ImportError: + logger.error("config.py not found") + try: + if config["modules"]["birthday"]["enabled"]: + from birthday import Birthday + birthday = Birthday(self) + birthday.setup(self.tree) + logger.info("Birthday module loaded") + except KeyError: + logger.error("Birthday module not enabled") + + +client = EONA() + + +@client.event +async def on_ready(): + print(f"Logged in as {client.user} (ID: {client.user.id})") + +client.run(TOKEN) diff --git a/sample.env b/sample.env new file mode 100644 index 0000000..e9bcc29 --- /dev/null +++ b/sample.env @@ -0,0 +1,2 @@ +# Move or copy sample.env to .env +DISCORD_TOKEN = "PUT YOUR TOKEN HERE" \ No newline at end of file