From c78912783adf284f62b767b9084f5901921525ce Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 21 Jun 2024 15:01:11 -0400 Subject: [PATCH] FEAT: Added Privacy Policy/TOS REF: Changed Birthday and Currency Modules to use a single Database (cut down on some space) FEAT: Added a list of always enabled modules and modules that can be disabled. --- .gitignore | 2 +- config.py | 7 ++ main.py | 22 +++++-- modules/admin/policy_module.py | 109 +++++++++++++++++++++++++++++++ modules/data/db.py | 30 +++++++++ modules/money/currency_module.py | 39 ++++++++--- modules/user/birthday_module.py | 4 +- 7 files changed, 195 insertions(+), 18 deletions(-) create mode 100644 modules/admin/policy_module.py create mode 100644 modules/data/db.py diff --git a/.gitignore b/.gitignore index 36b9a8b..aec1887 100644 --- a/.gitignore +++ b/.gitignore @@ -160,4 +160,4 @@ cython_debug/ # option (not recommended) you can uncomment the following to ignore the entire idea folder. #.idea/ -/birthdays.db \ No newline at end of file +/selena.db \ No newline at end of file diff --git a/config.py b/config.py index e999ad3..430ca11 100644 --- a/config.py +++ b/config.py @@ -13,3 +13,10 @@ PLEX_TOKEN = os.getenv("PLEX_TOKEN") TWITCH_CLIENT_ID = os.getenv("TWITCH_CLIENT_ID") TWITCH_CLIENT_SECRET = os.getenv("TWITCH_CLIENT_SECRET") TWITCH_CHANNEL = os.getenv("TWITCH_CHANNEL") + +# List of enabled modules +ENABLED_MODULES = [ + "modules.media.spotify_module", + "modules.user.birthday_module", + "modules.money.currency_module", +] diff --git a/main.py b/main.py index be1d60f..013bebb 100644 --- a/main.py +++ b/main.py @@ -1,5 +1,9 @@ import discord import config +import logging + +# Set up logging +logging.basicConfig(level=logging.DEBUG) class Selena(discord.Client): @@ -11,21 +15,25 @@ class Selena(discord.Client): guild = discord.Object(id=config.DISCORD_GUILD_ID) await self.load_extensions() self.tree.copy_global_to(guild=guild) - await self.tree.sync(guild=guild) + await self.tree.sync() async def load_extension(self, name): module = __import__(name, fromlist=["setup"]) await module.setup(self) async def load_extensions(self): - extensions = [ + # Mandatory modules that cannot be disabled + mandatory_modules = [ "modules.admin.logger_module", - "modules.media.spotify_module", - "modules.user.birthday_module", - "modules.money.currency_module", - # Add other modules here + "modules.admin.policy_module", ] - for extension in extensions: + + # Load mandatory modules + for extension in mandatory_modules: + await self.load_extension(extension) + + # Load enabled modules from configuration + for extension in config.ENABLED_MODULES: await self.load_extension(extension) async def on_ready(self): diff --git a/modules/admin/policy_module.py b/modules/admin/policy_module.py new file mode 100644 index 0000000..99c0360 --- /dev/null +++ b/modules/admin/policy_module.py @@ -0,0 +1,109 @@ +import discord +from discord import app_commands +import logging + +logger = logging.getLogger(__name__) + + +class PolicyModule: + def __init__(self, bot): + self.bot = bot + self.add_commands() + + def add_commands(self): + @app_commands.command( + name="privacy_policy", description="View the Privacy Policy" + ) + async def privacy_policy(interaction: discord.Interaction): + await interaction.response.defer() + try: + privacy_policy_text = """ + **Privacy Policy** + + 1. **Data Collection** + - We collect your Discord user ID and messages sent to the bot. + - No sensitive personal data is collected. + + 2. **Data Usage** + - Your data is used to provide and improve the bot's functionality. + - We do not share your data with third parties. + + 3. **Data Storage** + - Data is stored securely on our servers. + - Data is retained for as long as necessary to provide our services. + + 4. **Your Rights** + - You have the right to access, modify, and delete your data. + - To exercise these rights, contact the bot admin. + + 5. **Changes to Privacy Policy** + - We may update this policy from time to time. + - You will be notified of any significant changes. + + 6. **Contact** + - For any questions about this policy, contact the bot admin. + """ + await interaction.followup.send( + embed=discord.Embed( + title="Privacy Policy", + description=privacy_policy_text, + color=discord.Color.blue() + ) + ) + logger.info(f"User {interaction.user.id} viewed the privacy policy") + except Exception as e: + await interaction.followup.send(f"An error occurred: {e}") + logger.error(f"Error in privacy_policy command: {e}") + + @app_commands.command( + name="terms_of_service", description="View the Terms of Service" + ) + async def terms_of_service(interaction: discord.Interaction): + await interaction.response.defer() + try: + tos_text = """ + **Terms of Service** + + 1. **Acceptance of Terms** + - By using this bot, you agree to these terms. + - If you do not agree, do not use the bot. + + 2. **Use of the Bot** + - You must follow all applicable laws and regulations. + - Do not use the bot for any illegal or unauthorized purpose. + + 3. **Changes to Terms** + - We may update these terms from time to time. + - You will be notified of any significant changes. + + 4. **Termination** + - We reserve the right to terminate or restrict your access to the bot at any time, without notice or liability. + + 5. **Disclaimer of Warranties** + - The bot is provided "as is" without warranties of any kind. + - We do not guarantee that the bot will be error-free or uninterrupted. + + 6. **Limitation of Liability** + - We shall not be liable for any damages arising from your use of the bot. + + 7. **Contact** + - For any questions about these terms, contact the bot admin. + """ + await interaction.followup.send( + embed=discord.Embed( + title="Terms of Service", + description=tos_text, + color=discord.Color.blue() + ) + ) + logger.info(f"User {interaction.user.id} viewed the terms of service") + except Exception as e: + await interaction.followup.send(f"An error occurred: {e}") + logger.error(f"Error in terms_of_service command: {e}") + + self.bot.tree.add_command(privacy_policy) + self.bot.tree.add_command(terms_of_service) + + +async def setup(bot): + PolicyModule(bot) diff --git a/modules/data/db.py b/modules/data/db.py new file mode 100644 index 0000000..7210e9c --- /dev/null +++ b/modules/data/db.py @@ -0,0 +1,30 @@ +import sqlite3 + + +def initialize_db(): + conn = sqlite3.connect('selena.db') + cursor = conn.cursor() + + # Birthdays table + cursor.execute(''' + CREATE TABLE IF NOT EXISTS birthdays ( + user_id TEXT PRIMARY KEY, + birthday TEXT + ) + ''') + + # Currency table + cursor.execute(''' + CREATE TABLE IF NOT EXISTS currency ( + user_id TEXT PRIMARY KEY, + balance INTEGER, + last_earned TIMESTAMP + ) + ''') + + conn.commit() + conn.close() + + +def get_connection(): + return sqlite3.connect('selena.db') diff --git a/modules/money/currency_module.py b/modules/money/currency_module.py index 5099ab8..8721d2a 100644 --- a/modules/money/currency_module.py +++ b/modules/money/currency_module.py @@ -1,15 +1,19 @@ +# Flake8:noqa: E501 import discord from discord import app_commands import sqlite3 import logging import random -from .currency_db import initialize_db, get_connection +from datetime import datetime, timedelta +from modules.data.db import initialize_db, get_connection logger = logging.getLogger(__name__) initialize_db() class CurrencyModule: + COOLDOWN_PERIOD = timedelta(minutes=5) # Set the cooldown period here + def __init__(self, bot): self.bot = bot self.add_commands() @@ -21,15 +25,34 @@ class CurrencyModule: async def earn_currency(interaction: discord.Interaction): await interaction.response.defer() try: - amount = random.choices([random.randint(1, 10), 100], [0.99, 0.01])[0] conn = get_connection() cursor = conn.cursor() - cursor.execute('INSERT OR IGNORE INTO currency (user_id, balance) VALUES (?, ?)', - (str(interaction.user.id), 0)) - cursor.execute('UPDATE currency SET balance = balance + ? WHERE user_id = ?', - (amount, str(interaction.user.id))) + user_id = str(interaction.user.id) + + # Check cooldown + cursor.execute('SELECT last_earned FROM currency WHERE user_id = ?', (user_id,)) + result = cursor.fetchone() + if result and result[0]: + last_earned = datetime.fromisoformat(result[0]) + if datetime.now() - last_earned < CurrencyModule.COOLDOWN_PERIOD: + await interaction.followup.send( + embed=discord.Embed( + title="Earn Currency", + description="You are on cooldown. Please try again later.", + color=discord.Color.red() + ) + ) + conn.close() + logger.info(f"User {user_id} attempted to earn currency but is on cooldown.") + return + + amount = random.choices([random.randint(1, 10), 100], [0.99, 0.01])[0] + cursor.execute('INSERT OR IGNORE INTO currency (user_id, balance, last_earned) VALUES (?, ?, ?)', + (user_id, 0, datetime.now().isoformat())) + cursor.execute('UPDATE currency SET balance = balance + ?, last_earned = ? WHERE user_id = ?', + (amount, datetime.now().isoformat(), user_id)) conn.commit() - cursor.execute('SELECT balance FROM currency WHERE user_id = ?', (str(interaction.user.id),)) + cursor.execute('SELECT balance FROM currency WHERE user_id = ?', (user_id,)) new_balance = cursor.fetchone()[0] conn.close() await interaction.followup.send( @@ -39,7 +62,7 @@ class CurrencyModule: color=discord.Color.green() ) ) - logger.info(f"User {interaction.user.id} earned {amount} currency. New balance: {new_balance}") + logger.info(f"User {user_id} earned {amount} currency. New balance: {new_balance}") except Exception as e: await interaction.followup.send(f"An error occurred: {e}") logger.error(f"Error in earn_currency command: {e}") diff --git a/modules/user/birthday_module.py b/modules/user/birthday_module.py index 1afd6f0..f1e1d14 100644 --- a/modules/user/birthday_module.py +++ b/modules/user/birthday_module.py @@ -1,9 +1,9 @@ -# Flake8: noqa +# Flake8:noqa: E501 import discord from discord import app_commands import sqlite3 import logging -from .birthday_db import initialize_db, get_connection +from modules.data.db import initialize_db, get_connection logger = logging.getLogger(__name__) initialize_db()