From 05074d33bef26e16a27f2bf787890fa5598176eb Mon Sep 17 00:00:00 2001 From: Will Date: Wed, 10 Apr 2024 15:57:28 -0700 Subject: [PATCH] Fix SQL lockups, get commands working, remove DB and availcommands and .env --- .env | 6 -- .gitignore | 3 +- README.md | 36 +++++++++++ env.dist | 6 ++ main.py | 165 +++++++++++++++++++++++++++++++++++------------ requirements.txt | 1 - 6 files changed, 169 insertions(+), 48 deletions(-) delete mode 100644 .env create mode 100644 README.md create mode 100644 env.dist diff --git a/.env b/.env deleted file mode 100644 index 6f199c7..0000000 --- a/.env +++ /dev/null @@ -1,6 +0,0 @@ -##EXAMPLE .ENV FILE - -BOT_TOKEN = "INPUT DISCORD API KEY HERE" -BOT_CHANNEL_ID = INPUT CHANNEL ID FOR COMMANDS -WELCOME_CHANNEL_ID = INPUT CHANNEL ID FOR YOUR WELCOME MESSAGE MAINTENANCE -GUILD_ID = INPUT GUILD ID FOR YOUR DISCORD SERVER diff --git a/.gitignore b/.gitignore index 324bc7d..a7affa5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.env /.venv member_data.db -discord.log \ No newline at end of file +discord.log +SparkBot.log \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..59e0f29 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# SparkBot + +A Discord bot for Spark Studio Salem + +## Prerequisites + +- Go to https://discord.com/developers/applications and make an application +- Go to the Bot page. +- Copy `env.dist` to `.env` and add the Bot Token to this file. +- Choose what channels you want the bot to operate in on your server and add their IDs to the file as well. + +## Installation + +Requires Python 3. + +- Run `pip install -r requirements.txt` + +## Running + +Run `python3 main.py` + +## Usage + +### Available Commands + +``` +/help : to view all commands +/nick : to view current nickname +/setnick : to change nickname +/reinit : to re-initialize a user in the database (i.e. if they joined when the bot wasn't listening) +``` + +## Database + +Initialization of the structure is automatically handled inside `main.py` and +creates the SQLITE3 file `member_data.db`. \ No newline at end of file diff --git a/env.dist b/env.dist new file mode 100644 index 0000000..59e1411 --- /dev/null +++ b/env.dist @@ -0,0 +1,6 @@ +##EXAMPLE .ENV FILE + +BOT_TOKEN = ""INPUT DISCORD API KEY HERE" +BOT_CHANNEL_ID = INPUT CHANNEL ID FOR COMMANDS +WELCOME_CHANNEL_ID = INPUT WELCOME CHANNEL ID HERE +GUILD_ID = INPUT GUILD ID HERE diff --git a/main.py b/main.py index 039b366..40e7423 100644 --- a/main.py +++ b/main.py @@ -7,7 +7,7 @@ import sqlite3 import logging from datetime import datetime, timezone from discord import ui, Interaction - +import random async def welcome_message(): # Find the #welcome channel @@ -37,26 +37,44 @@ async def welcome_message(): async def update_nickname(member, firstname, lastname): #update server nickname from DB nickname = f"{firstname} {lastname}" - c.execute("UPDATE members SET nickname = ?, firstname = ?, lastname = ? WHERE user_id = ?", (nickname, firstname, lastname, member.id)) - conn.commit() - print(f'Updated DB nickname for: {member}, {firstname}, {lastname}, {nickname}') + with sqlite3.connect('member_data.db') as conn: + c = conn.cursor() + c.execute("UPDATE members SET nickname = ?, firstname = ?, lastname = ? WHERE user_id = ?", (nickname, firstname, lastname, member.id)) + conn.commit() + print(f'Updated DB nickname for: {member}, {firstname}, {lastname}, {nickname}') await member.edit(nick=nickname) async def remove_user(user): #Remove user from DB and delete server nickname - c.execute("DELETE FROM members WHERE user_id = ?", (user.id,)) - conn.commit() + with sqlite3.connect('member_data.db') as conn: + c = conn.cursor() + c.execute("DELETE FROM members WHERE user_id = ?", (user.id,)) + conn.commit() if user: await user.edit(nick=None) #TODO demote user role async def update_onboard(member): #increase onboarding status by 1 - print(f'Updating onboarding for: {member.display_name}') - c.execute("SELECT onboarding_status FROM members WHERE user_id = ?", (member.id,)) - status = c.fetchone() - status += 1 - c.execute("UPDATE members SET onboarding_status = ? WHERE user_id = ?", - (status, member.id)) - conn.commit() + logging.info(f'Updating onboarding for: {member.display_name} {member.id}') + + # Connect to the database using a context manager + with sqlite3.connect('member_data.db') as conn: + c = conn.cursor() + + c.execute("SELECT onboarding_status FROM members WHERE user_id = ?", (member.id,)) + row = c.fetchone() + + if row is None: + logging.error("No matching user found in the database.") + return + + status = row[0] + status += 1 + + c.execute("UPDATE members SET onboarding_status = ? WHERE user_id = ?", + (status, member.id)) + + conn.commit() + logging.warning(f'Updated onboarding for {member.display_name}: {status}') async def add_member_to_role(member, role_name): print(f"Adding {role_name} role to {member.display_name}") @@ -77,25 +95,26 @@ logging.basicConfig(filename='SparkBot.log', level=logging.INFO) intents = discord.Intents.all() # Connect to SQLite database -conn = sqlite3.connect('member_data.db') -c = conn.cursor() +with sqlite3.connect('member_data.db') as conn: + c = conn.cursor() -# Create table with fields if it doesn't exist already -c.execute('''CREATE TABLE IF NOT EXISTS members ( - user_id INTEGER PRIMARY KEY, - username TEXT, nickname TEXT, - firstname TEXT, lastname TEXT, - join_datetime TEXT, - onboarding_status INTEGER, - last_change_datetime TEXT - )''') + # Create table with fields if it doesn't exist already + c.execute('''CREATE TABLE IF NOT EXISTS members ( + user_id INTEGER PRIMARY KEY, + username TEXT, nickname TEXT, + firstname TEXT, lastname TEXT, + join_datetime TEXT, + onboarding_status INTEGER, + last_change_datetime TEXT + )''') + + conn.commit() -conn.commit() class PersistentViewBot(commands.Bot): def __init__(self): intents = discord.Intents().all() - super().__init__(command_prefix=commands.when_mentioned_or("."), intents=intents) + super().__init__(command_prefix=commands.when_mentioned_or("/"), intents=intents) async def setup_hook(self) -> None: self.add_view(OnboardButtons()) #self.add_view(OnboardButtons()) #Add More views with more add_view commands. @@ -151,26 +170,92 @@ async def on_ready(): except Exception as e: print(e) + print("Members in the DB:") + + with sqlite3.connect('member_data.db') as conn: + c = conn.cursor() + + c.execute("SELECT user_id, username, nickname FROM members") + for row in c.fetchall(): + print(f' ID: {row[0]} User: {row[1]} Nick: {row[2]}') + + print("Ready.") + +@client.command(name='reinit') +async def cmd_reinit(ctx): + """Re-initialize a user in the database (i.e. if the bot wasn't listening when they joined)""" + await on_member_join(ctx.author) + await ctx.send("Reinitialized!") + +@client.command(name='nick') +async def cmd_nick(ctx): + """View current nickname""" + await ctx.send(f'You are {ctx.author.nick}') + +@client.command(name='setnick') +async def cmd_setnick(ctx, arg1, arg2): + """Change nickname (use two words separated by a space)""" + await update_nickname(ctx.author, arg1, arg2) + await ctx.send(f'You are now {ctx.author.nick}') + +@client.command(name='99') +async def cmd_nine_nine(ctx): + brooklyn_99_quotes = [ + 'I\'m the human form of the 💯 emoji.', + 'Bingpot!', + ( + 'Cool. Cool cool cool cool cool cool cool, ' + 'no doubt no doubt no doubt no doubt.' + ), + ] + + response = random.choice(brooklyn_99_quotes) + await ctx.send(response) + +# @client.event +# async def on_message(message): +# if message.author == client.user: +# return + +# brooklyn_99_quotes = [ +# 'I\'m the human form of the 💯 emoji.', +# 'Bingpot!', +# ( +# 'Cool. Cool cool cool cool cool cool cool, ' +# 'no doubt no doubt no doubt no doubt.' +# ), +# ] + +# if message.content == '/99': +# response = random.choice(brooklyn_99_quotes) +# await message.channel.send(response) @client.event async def on_member_join(member): await welcome_message() - # Check if member already exists in the database - c.execute("SELECT * FROM members WHERE user_id = ?", (member.id,)) - existing_member = c.fetchone() - if not existing_member: # If it's the member's first time joining - # Add new member to the database - c.execute( - "INSERT OR REPLACE INTO members (user_id, username, join_datetime, onboarding_status, last_change_datetime) VALUES (?, ?, ?, ?, ?)", - (member.id, member.name, member.joined_at.isoformat(), 0, datetime.now(timezone.utc).isoformat())) - conn.commit() + with sqlite3.connect('member_data.db') as conn: + c = conn.cursor() - # Update member nickname - await member.edit(nick=c.execute("SELECT nickname FROM members WHERE user_id = ?", (member.id,)).fetchone()[0]) + # Check if member already exists in the database + c.execute("SELECT * FROM members WHERE user_id = ?", (member.id,)) - # Log member join - logging.info(f'Member {member.name} joined the server.') + existing_member = c.fetchone() + + if not existing_member: # If it's the member's first time joining + # Add new member to the database + c.execute( + "INSERT OR REPLACE INTO members (user_id, username, join_datetime, onboarding_status, last_change_datetime) VALUES (?, ?, ?, ?, ?)", + (member.id, member.name, member.joined_at.isoformat(), 0, datetime.now(timezone.utc).isoformat())) + conn.commit() + + # Update member nickname + await member.edit(nick=c.execute("SELECT nickname FROM members WHERE user_id = ?", (member.id,)).fetchone()[0]) + + # Log member join + logging.warning(f'Member {member.name} joined the server.') + else: + logging.info(f'Member {member.name} rejoined the server.') @client.tree.command(name="remove", description="Remove user from database, and remove user's nickname") @app_commands.describe(member="The member you want to remove") @@ -181,7 +266,7 @@ async def remove(interaction: discord.Integration, member: discord.Member): @client.event async def on_member_remove(member): # Log member leave - logging.info(f'Member {member.name} left the server.') + logging.warning(f'Member {member.name} left the server.') # Load bot token and welcome channel id from .env file diff --git a/requirements.txt b/requirements.txt index f8ff32a..4e13b5a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,5 @@ discord==2.3.2 discord.py==2.3.2 frozenlist==1.4.1 idna==3.6 -logging==0.4.9.6 multidict==6.0.5 yarl==1.9.4