Started implementing Universal User Identification (UUI)
- added database table "users" - table allows for assigning individual users a UUID for universal processing - will support account linking in the future - fixed a bug with reporting Discord commands in logskami_dev
parent
8074fbbef4
commit
aed3d24e33
10
bots.py
10
bots.py
|
@ -14,7 +14,7 @@ from bot_discord import DiscordBot
|
||||||
from bot_twitch import TwitchBot
|
from bot_twitch import TwitchBot
|
||||||
|
|
||||||
from modules.db import init_db_connection, run_db_operation
|
from modules.db import init_db_connection, run_db_operation
|
||||||
from modules.db import ensure_quotes_table
|
from modules.db import ensure_quotes_table, ensure_users_table
|
||||||
|
|
||||||
# Load environment variables
|
# Load environment variables
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
@ -56,7 +56,7 @@ def log(message, level="INFO", exec_info=False):
|
||||||
See 'config.json' for disabling/enabling logging levels
|
See 'config.json' for disabling/enabling logging levels
|
||||||
"""
|
"""
|
||||||
from modules import utility
|
from modules import utility
|
||||||
log_levels = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", "FATAL"]
|
log_levels = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", "FATAL"}
|
||||||
|
|
||||||
if level not in log_levels:
|
if level not in log_levels:
|
||||||
level = "INFO" # Default to INFO if an invalid level is provided
|
level = "INFO" # Default to INFO if an invalid level is provided
|
||||||
|
@ -99,7 +99,6 @@ async def main():
|
||||||
|
|
||||||
# Log initial start
|
# Log initial start
|
||||||
log("--------------- BOT STARTUP ---------------")
|
log("--------------- BOT STARTUP ---------------")
|
||||||
|
|
||||||
# Before creating your DiscordBot/TwitchBot, initialize DB
|
# Before creating your DiscordBot/TwitchBot, initialize DB
|
||||||
db_conn = init_db_connection(config_data, log)
|
db_conn = init_db_connection(config_data, log)
|
||||||
if not db_conn:
|
if not db_conn:
|
||||||
|
@ -110,6 +109,7 @@ async def main():
|
||||||
# auto-create the quotes table if it doesn't exist
|
# auto-create the quotes table if it doesn't exist
|
||||||
try:
|
try:
|
||||||
ensure_quotes_table(db_conn, log)
|
ensure_quotes_table(db_conn, log)
|
||||||
|
ensure_users_table(db_conn, log)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log(f"Critical: unable to ensure quotes table: {e}", "FATAL")
|
log(f"Critical: unable to ensure quotes table: {e}", "FATAL")
|
||||||
|
|
||||||
|
@ -132,6 +132,10 @@ async def main():
|
||||||
discord_task = asyncio.create_task(discord_bot.run(os.getenv("DISCORD_BOT_TOKEN")))
|
discord_task = asyncio.create_task(discord_bot.run(os.getenv("DISCORD_BOT_TOKEN")))
|
||||||
twitch_task = asyncio.create_task(twitch_bot.run())
|
twitch_task = asyncio.create_task(twitch_bot.run())
|
||||||
|
|
||||||
|
from modules.utility import dev_func
|
||||||
|
dev_func_result = dev_func(db_conn, log)
|
||||||
|
log(f"dev_func output: {dev_func_result}")
|
||||||
|
|
||||||
await asyncio.gather(discord_task, twitch_task)
|
await asyncio.gather(discord_task, twitch_task)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -91,6 +91,7 @@ def setup(bot, db_conn=None, log=None):
|
||||||
######################
|
######################
|
||||||
# Debug: Print that commands are being registered
|
# Debug: Print that commands are being registered
|
||||||
try:
|
try:
|
||||||
bot.log(f"Registering commands for Discord: {list(bot.commands.keys())}", "DEBUG")
|
command_names = [cmd.name for cmd in bot.commands] # Extract command names
|
||||||
|
bot.log(f"Registering commands for Discord: {command_names}", "DEBUG")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
bot.log(f"An error occured while printing registered commands for Discord: {e}", "WARNING")
|
bot.log(f"An error occured while printing registered commands for Discord: {e}", "WARNING")
|
162
modules/db.py
162
modules/db.py
|
@ -212,3 +212,165 @@ def ensure_quotes_table(db_conn, log_func):
|
||||||
raise RuntimeError(error_msg)
|
raise RuntimeError(error_msg)
|
||||||
|
|
||||||
log_func("Successfully created table 'quotes'.")
|
log_func("Successfully created table 'quotes'.")
|
||||||
|
|
||||||
|
#######################
|
||||||
|
# Ensure 'users' table
|
||||||
|
#######################
|
||||||
|
|
||||||
|
def ensure_users_table(db_conn, log_func):
|
||||||
|
"""
|
||||||
|
Checks if 'users' table exists. If not, creates it.
|
||||||
|
|
||||||
|
The 'users' table tracks user linkage across platforms:
|
||||||
|
- UUID: (PK) The universal ID for the user
|
||||||
|
- discord_user_id, discord_username, discord_user_display_name
|
||||||
|
- twitch_user_id, twitch_username, twitch_user_display_name
|
||||||
|
- datetime_linked (DATE/TIME of row creation)
|
||||||
|
- user_is_banned (BOOLEAN)
|
||||||
|
- user_is_bot (BOOLEAN)
|
||||||
|
|
||||||
|
This helps unify data for a single 'person' across Discord & Twitch.
|
||||||
|
"""
|
||||||
|
is_sqlite = "sqlite3" in str(type(db_conn)).lower()
|
||||||
|
|
||||||
|
# 1) Check existence
|
||||||
|
if is_sqlite:
|
||||||
|
check_sql = """
|
||||||
|
SELECT name
|
||||||
|
FROM sqlite_master
|
||||||
|
WHERE type='table'
|
||||||
|
AND name='users'
|
||||||
|
"""
|
||||||
|
else:
|
||||||
|
check_sql = """
|
||||||
|
SELECT table_name
|
||||||
|
FROM information_schema.tables
|
||||||
|
WHERE table_name = 'users'
|
||||||
|
AND table_schema = DATABASE()
|
||||||
|
"""
|
||||||
|
|
||||||
|
rows = run_db_operation(db_conn, "read", check_sql, log_func=log_func)
|
||||||
|
if rows and rows[0] and rows[0][0]:
|
||||||
|
log_func("Table 'users' already exists, skipping creation.", "DEBUG")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 2) Table does NOT exist => create it
|
||||||
|
log_func("Table 'users' does not exist; creating now...")
|
||||||
|
|
||||||
|
if is_sqlite:
|
||||||
|
create_table_sql = """
|
||||||
|
CREATE TABLE users (
|
||||||
|
UUID TEXT PRIMARY KEY,
|
||||||
|
discord_user_id TEXT,
|
||||||
|
discord_username TEXT,
|
||||||
|
discord_user_display_name TEXT,
|
||||||
|
twitch_user_id TEXT,
|
||||||
|
twitch_username TEXT,
|
||||||
|
twitch_user_display_name TEXT,
|
||||||
|
datetime_linked TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
user_is_banned BOOLEAN DEFAULT 0,
|
||||||
|
user_is_bot BOOLEAN DEFAULT 0
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
else:
|
||||||
|
create_table_sql = """
|
||||||
|
CREATE TABLE users (
|
||||||
|
UUID VARCHAR(36) PRIMARY KEY,
|
||||||
|
discord_user_id VARCHAR(100),
|
||||||
|
discord_username VARCHAR(100),
|
||||||
|
discord_user_display_name VARCHAR(100),
|
||||||
|
twitch_user_id VARCHAR(100),
|
||||||
|
twitch_username VARCHAR(100),
|
||||||
|
twitch_user_display_name VARCHAR(100),
|
||||||
|
datetime_linked DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
user_is_banned BOOLEAN DEFAULT FALSE,
|
||||||
|
user_is_bot BOOLEAN DEFAULT FALSE
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
|
||||||
|
result = run_db_operation(db_conn, "write", create_table_sql, log_func=log_func)
|
||||||
|
if result is None:
|
||||||
|
error_msg = "Failed to create 'users' table!"
|
||||||
|
log_func(error_msg, "ERROR")
|
||||||
|
raise RuntimeError(error_msg)
|
||||||
|
|
||||||
|
log_func("Successfully created table 'users'.")
|
||||||
|
|
||||||
|
|
||||||
|
########################
|
||||||
|
# Lookup user function
|
||||||
|
########################
|
||||||
|
|
||||||
|
def lookup_user(db_conn, log_func, identifier, identifier_type="discord_user_id"):
|
||||||
|
"""
|
||||||
|
Looks up a user in the 'users' table based on the given identifier_type:
|
||||||
|
- "uuid"
|
||||||
|
- "discord_user_id"
|
||||||
|
- "discord_username"
|
||||||
|
- "twitch_user_id"
|
||||||
|
- "twitch_username"
|
||||||
|
You can add more if needed.
|
||||||
|
|
||||||
|
Returns a dictionary with all columns:
|
||||||
|
{
|
||||||
|
"UUID": str,
|
||||||
|
"discord_user_id": str or None,
|
||||||
|
"discord_username": str or None,
|
||||||
|
"discord_user_display_name": str or None,
|
||||||
|
"twitch_user_id": str or None,
|
||||||
|
"twitch_username": str or None,
|
||||||
|
"twitch_user_display_name": str or None,
|
||||||
|
"datetime_linked": str (or datetime in MariaDB),
|
||||||
|
"user_is_banned": bool or int,
|
||||||
|
"user_is_bot": bool or int
|
||||||
|
}
|
||||||
|
|
||||||
|
If not found, returns None.
|
||||||
|
"""
|
||||||
|
|
||||||
|
valid_cols = ["uuid", "discord_user_id", "discord_username",
|
||||||
|
"twitch_user_id", "twitch_username"]
|
||||||
|
|
||||||
|
if identifier_type.lower() not in valid_cols:
|
||||||
|
if log_func:
|
||||||
|
log_func(f"lookup_user error: invalid identifier_type={identifier_type}", "WARNING")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Build the query
|
||||||
|
query = f"""
|
||||||
|
SELECT
|
||||||
|
UUID,
|
||||||
|
discord_user_id,
|
||||||
|
discord_username,
|
||||||
|
discord_user_display_name,
|
||||||
|
twitch_user_id,
|
||||||
|
twitch_username,
|
||||||
|
twitch_user_display_name,
|
||||||
|
datetime_linked,
|
||||||
|
user_is_banned,
|
||||||
|
user_is_bot
|
||||||
|
FROM users
|
||||||
|
WHERE {identifier_type} = ?
|
||||||
|
LIMIT 1
|
||||||
|
"""
|
||||||
|
|
||||||
|
rows = run_db_operation(db_conn, "read", query, params=(identifier,), log_func=log_func)
|
||||||
|
if not rows:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# We have at least one row
|
||||||
|
row = rows[0] # single row
|
||||||
|
# Build a dictionary
|
||||||
|
user_data = {
|
||||||
|
"UUID": row[0],
|
||||||
|
"discord_user_id": row[1],
|
||||||
|
"discord_username": row[2],
|
||||||
|
"discord_user_display_name": row[3],
|
||||||
|
"twitch_user_id": row[4],
|
||||||
|
"twitch_username": row[5],
|
||||||
|
"twitch_user_display_name": row[6],
|
||||||
|
"datetime_linked": row[7],
|
||||||
|
"user_is_banned": row[8],
|
||||||
|
"user_is_bot": row[9],
|
||||||
|
}
|
||||||
|
return user_data
|
|
@ -420,4 +420,17 @@ async def send_message(ctx, text):
|
||||||
"""
|
"""
|
||||||
Minimal helper to send a message to either Discord or Twitch.
|
Minimal helper to send a message to either Discord or Twitch.
|
||||||
"""
|
"""
|
||||||
await ctx.send(text)
|
await ctx.send(text)
|
||||||
|
|
||||||
|
###############################################
|
||||||
|
# Development Test Function (called upon start)
|
||||||
|
###############################################
|
||||||
|
def dev_func(db_conn, log):
|
||||||
|
from modules.db import lookup_user
|
||||||
|
id = "203190147582394369"
|
||||||
|
id_type = "discord_user_id"
|
||||||
|
uui_info = lookup_user(db_conn, log, identifier=id, identifier_type=id_type)
|
||||||
|
if uui_info:
|
||||||
|
return list(uui_info.values())
|
||||||
|
else:
|
||||||
|
return f"User with identifier '{id}' ({id_type}) not found"
|
Loading…
Reference in New Issue