feat: Add misandrist insults and big CS2 events matches fetch from HLTV.org

This commit is contained in:
2026-07-04 22:36:07 +02:00
parent 2cf7cc94bb
commit 80d34e9660
13 changed files with 692 additions and 716 deletions

2
.gitignore vendored
View File

@@ -161,3 +161,5 @@ cython_debug/
data/ data/
.vscode/ .vscode/
*.disabled

14
Dockerfile Normal file
View File

@@ -0,0 +1,14 @@
FROM python:3.13.14-alpine
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV TOKEN=""
ENV URL=""
ENTRYPOINT ["python", "main.py"]

375
cogs/cs.py Normal file
View File

@@ -0,0 +1,375 @@
import discord, datetime, os, re, json
from selenium import webdriver
from bs4 import BeautifulSoup
from discord.ext import commands, tasks, bridge
from setup.logger import LOGGER
from models.cs_models import *
BASE_URL = "https://www.hltv.org"
PAGE_FILE = os.path.join("data","hltv.html")
MATCHES_FILE = os.path.join("data","matches.json")
# Cache duration in seconds (6 hours)
DATA_CACHE_DURATION = 3600 * 6
# Match age threshold in seconds (2 days)
OLD_MATCH_DURATION = 3600 * 24 * 2
# Reaction emojis for match events
EMOJI_CREATE_EVENT = "" # Create guild event for the match
EMOJI_REMOVE_EVENT = "" # Remove guild event for the match
class CSCog(commands.Cog):
def __init__(self, bot:discord.Bot):
self.bot = bot
self.matches:list[Match] = []
self.message_match_map = {} # Maps message IDs to Match objects
self.get_matches.start()
def is_up_to_date(self) -> bool:
"""Check if PAGE_FILE and MATCHES_FILE are up to date."""
try:
page_modified = datetime.datetime.fromtimestamp(os.stat(PAGE_FILE).st_ctime)
matches_modified = datetime.datetime.fromtimestamp(os.stat(MATCHES_FILE).st_ctime)
now = datetime.datetime.now()
page_age = now.timestamp() - page_modified.timestamp()
matches_age = now.timestamp() - matches_modified.timestamp()
return page_age <= DATA_CACHE_DURATION and matches_age <= DATA_CACHE_DURATION
except FileNotFoundError:
return False
def get_page(self) -> None:
"""Get the page from https://www.hltv.org/matches/ using Firefox webdriver and save to PAGE_FILE."""
driver = webdriver.Firefox()
driver.get("https://www.hltv.org/matches/")
html = driver.page_source
driver.close()
with open(PAGE_FILE, 'w') as f:
f.write(html)
def save(self) -> None:
"""Save self.matches to MATCHES_FILE."""
matches_data = [match.to_dict() for match in self.matches]
with open(MATCHES_FILE, 'w') as f:
json.dump(matches_data, f, indent=2)
def remove_old_matches(self) -> None:
"""Remove matches that are older than OLD_MATCH_DURATION."""
now = datetime.datetime.now()
filtered_matches = []
for match in self.matches:
if match.date is None:
# Keep matches with no date
filtered_matches.append(match)
else:
match_age = now.timestamp() - match.date.timestamp()
if match_age <= OLD_MATCH_DURATION:
filtered_matches.append(match)
self.matches = filtered_matches
@tasks.loop(minutes=5)
async def get_matches(self):
if self.is_up_to_date():
# Load from MATCHES_FILE
try:
with open(MATCHES_FILE) as f:
matches_data = json.load(f)
self.matches = [Match.from_dict(match_data) for match_data in matches_data]
self.remove_old_matches()
except Exception as e:
LOGGER.error(f"Error loading matches from cache: {e}")
self.matches = []
else:
# Fetch new data
self.get_page()
# Read and parse the page
with open(PAGE_FILE) as f:
html = f.read()
page = BeautifulSoup(html)
all_matches = []
for section in page.find_all("div", {"class": "matches-chronologically"}):
all_matches.extend(section.find_all("div", {"class": "match-wrapper"}))
self.matches = []
for match_event in all_matches:
event = match_event.find("div", {"class": "match-event"})
event_name = event.attrs["data-event-headline"]
# Extract event logo
event_logo_container = event.find("div", {"class": "match-event-logo-container"})
event_logo_url = None
if event_logo_container:
logo_img = event_logo_container.find("img")
if logo_img and logo_img.attrs.get("src"):
event_logo_url = logo_img.attrs["src"]
event_obj = Event(int(event.attrs["data-event-id"]), event_name, event_logo_url)
href = str(match_event.find("a", {"class": "match-top"}).attrs["href"])
match_href = "https://hltv.org" + href
match_time_elt = match_event.find("div", {"class": "match-time"})
if match_time_elt:
match_timestamp = int(match_time_elt.attrs["data-unix"])
match_date = datetime.datetime.fromtimestamp(match_timestamp/1000)
else:
match_date = None
match_format = match_event.find("div", {"class": "match-meta"}).text
teams = match_event.find_all("div", {"class": "match-team"})
match_teams = []
for match_team in teams:
team = match_team.find("div", {"class": "match-teamname"})
team_obj = Team(team.text, match_team.find("img").attrs["src"])
match_teams.append(team_obj)
if len(match_teams) == 0:
continue
match_obj = Match(match_href, match_format, event_obj, match_teams, match_date)
self.matches.append(match_obj)
# Remove old matches and save to cache
self.remove_old_matches()
self.save()
if self.matches:
print(str(self.matches[0]))
def check_event(self, match: Match, event: discord.ScheduledEvent) -> bool:
"""
Check if a scheduled event matches the given match.
Returns True if both team names appear in the event name (case-insensitive).
"""
event_name_lower = event.name.lower()
team1_name_lower = match.teams[0].name.lower()
team2_name_lower = match.teams[1].name.lower()
# Check if both team names are in the event name
return team1_name_lower in event_name_lower and team2_name_lower in event_name_lower
async def create_guild_event(self, guild: discord.Guild, match: Match) -> discord.ScheduledEvent | None:
"""
Create a scheduled guild event for the match.
Returns the created event or None if it already exists or creation fails.
"""
try:
# Check if event already exists
for event in guild.scheduled_events:
if self.check_event(match, event):
return event
# Create event name
event_name = f"{match.teams[0].name} vs {match.teams[1].name}"
# Set start and end times - use UTC timezone to match the embed display
if match.date:
# Convert the timestamp to UTC-aware datetime
timestamp = int(match.date.timestamp())
start_time = datetime.datetime.fromtimestamp(timestamp, tz=datetime.timezone.utc)
end_time = start_time + datetime.timedelta(hours=2) # Assume 2 hour match
else:
start_time = datetime.datetime.now(tz=datetime.timezone.utc)
end_time = start_time + datetime.timedelta(hours=2)
# Create the event
event = await guild.create_scheduled_event(
name=event_name,
start_time=start_time,
end_time=end_time,
description=f"Event: {match.event.name}\nFormat: {match.format}\nWatch on HLTV: {match.url}",
location=""
)
LOGGER.info(f"Created guild event for {event_name} in {guild.name}")
return event
except Exception as e:
LOGGER.error(f"Error creating guild event in {guild.name}: {e}")
return None
async def remove_guild_event(self, guild: discord.Guild, match: Match) -> bool:
"""
Remove the scheduled guild event for the match.
Returns True if an event was removed, False otherwise.
"""
try:
for event in guild.scheduled_events:
if self.check_event(match, event):
await event.delete()
LOGGER.info(f"Removed guild event {event.name} from {guild.name}")
return True
return False
except Exception as e:
LOGGER.error(f"Error removing guild event from {guild.name}: {e}")
return False
def create_match_embed(self, match: Match) -> discord.Embed:
"""
Create a Discord embed message containing match information.
Args:
match: The Match object to display
Returns:
discord.Embed: A formatted embed with match details
"""
# Create embed with title as the matchup
title = f"{match.teams[0].name} vs {match.teams[1].name}"
embed = discord.Embed(
title=title,
description=f"**Event:** {match.event.name}",
url=match.url,
color=discord.Color.blue()
)
# Add match format
embed.add_field(
name="Format",
value=match.format,
inline=True
)
# Add match date if available
if match.date:
embed.add_field(
name="Date & Time",
value=f"<t:{int(match.date.timestamp())}:F>",
inline=True
)
# Add team information with logos
teams_info = f"[{match.teams[0].name}]({match.url})\n[{match.teams[1].name}]({match.url})"
embed.add_field(
name="Teams",
value=teams_info,
inline=False
)
# Set thumbnail to event logo if available, otherwise use first team logo
if match.event.logo_url:
embed.set_thumbnail(url=match.event.logo_url)
elif match.teams[0].logo_url:
embed.set_thumbnail(url=match.teams[0].logo_url)
# Set footer with HLTV link
embed.set_footer(
text="HLTV.org",
icon_url="https://www.hltv.org/img/static/TopLogo2x.png"
)
return embed
@bridge.bridge_command()
async def test_match_embed(self, ctx:bridge.BridgeExtContext):
"""Send the first match as an embed to the test channel."""
TEST_CHANNEL_ID = 1305147782071451718
# Check if there are matches available
if not self.matches:
await ctx.send("❌ No matches available. Try running the scraper first.")
return
# Get the first match
first_match = self.matches[0]
# Create the embed
embed = self.create_match_embed(first_match)
# Send to test channel
try:
channel = self.bot.get_channel(TEST_CHANNEL_ID)
if channel is None:
await ctx.send(f"❌ Could not find channel with ID {TEST_CHANNEL_ID}")
return
message = await channel.send(embed=embed)
# Add reactions
await message.add_reaction(EMOJI_CREATE_EVENT)
await message.add_reaction(EMOJI_REMOVE_EVENT)
# Track the message -> match mapping
self.message_match_map[message.id] = first_match
await ctx.reply(f"✅ Sent embed for match: {first_match.teams[0].name} vs {first_match.teams[1].name}")
except Exception as e:
LOGGER.error(f"Error sending embed: {e}")
await ctx.send(f"❌ Error sending embed: {e}")
@commands.Cog.listener()
async def on_raw_reaction_add(self, payload: discord.RawReactionActionEvent):
"""Handle reactions added to match embed messages."""
# Ignore reactions from the bot itself
if payload.user_id == self.bot.user.id:
return
# Check if this is a tracked message
if payload.message_id not in self.message_match_map:
return
match = self.message_match_map[payload.message_id]
emoji = payload.emoji.name
# Get the guild
guild = self.bot.get_guild(payload.guild_id)
if guild is None:
return
# Get the message for replies
message = await self.get_reaction_message(payload)
if not message:
return
try:
if emoji == EMOJI_CREATE_EVENT:
# Create event
event = await self.create_guild_event(guild, match)
if event:
await message.reply(f"✅ Created guild event: **{event.name}**")
elif emoji == EMOJI_REMOVE_EVENT:
# Remove event
removed = await self.remove_guild_event(guild, match)
if removed:
await message.reply(
f"✅ Removed guild event for {match.teams[0].name} vs {match.teams[1].name}"
)
else:
await message.reply(
f"⚠️ No guild event found for this match"
)
except Exception as e:
LOGGER.error(f"Error handling reaction {emoji}: {e}")
async def get_reaction_message(self, payload: discord.RawReactionActionEvent) -> discord.Message | None:
"""Get the message object from a raw reaction payload."""
try:
channel = self.bot.get_channel(payload.channel_id)
if channel:
return await channel.fetch_message(payload.message_id)
except Exception as e:
LOGGER.error(f"Error fetching message: {e}")
return None
async def check_events(self):
"""Check scheduled events for matching CS matches."""
for match in self.matches:
for guild in self.bot.guilds:
events = [event for event in guild.scheduled_events if self.check_event(match, event)]
# print(guild.name, match)
# @commands.Cog.listener()
# async def on_ready(self):
# self.check_events.start()

31
cogs/insult.py Normal file
View File

@@ -0,0 +1,31 @@
import discord, json, os, random, re
import collections
from discord.ext import commands
from setup.logger import LOGGER
class InsultsCog(commands.Cog):
def __init__(self, bot:discord.Bot):
self.bot = bot
with open(os.path.join("data", "insults.txt")) as f:
self.insults = [l.strip() for l in f.readlines()]
@commands.Cog.listener()
async def on_message(self, message:discord.Message):
if len(message.content.split()) > 3:
author = message.author
if any(author.id == u.id for u in self.men):
r = int(random.random()*100)
print(r)
if r < 5:
insult = random.choice(self.insults)
await message.reply(insult)
@commands.Cog.listener()
async def on_ready(self):
self.men:list[discord.Member] = []
for guild in self.bot.guilds:
for role in guild.roles:
if re.match(r'^([hH]im|[hH]e).*$',role.name):
self.men.extend(role.members)
self.men = list(set(self.men))

9
compose.yaml Normal file
View File

@@ -0,0 +1,9 @@
services:
botmafieux:
build:
context: .
dockerfile: ./Dockerfile
tags:
- nebulo9/botmafieux:v2
container_name: botmafieux
env_file: ./.env

237
main.py
View File

@@ -1,164 +1,129 @@
import os, discord, json, sys, random import os, discord, json, sys, random
import modules.setup.db as db
from argparse import ArgumentParser from argparse import ArgumentParser
from datetime import datetime from datetime import datetime
from asyncio import create_task from asyncio import create_task
from dotenv import load_dotenv from dotenv import load_dotenv
from discord import option from discord import option
from discord.ext import tasks, commands from discord.ext import tasks, commands
from modules.setup.bot import bot, reload_feature from setup.bot import bot, reload_feature
from modules.setup.logger import LOGGER from setup.logger import LOGGER
from modules.cogs.productivity import TASKS as PRODUCTIVITY_TASKS, send_reminder
PARSER = ArgumentParser(description='BotMafieux for Discord.') PARSER = ArgumentParser(description='BotMafieux for Discord.')
PARSER.add_argument('--guild_id',type=int,help='The guild ID.') PARSER.add_argument('--guild_id',type=int,help='The guild ID.')
PARSER.add_argument('--channel_id',type=int,help='The channel ID.') PARSER.add_argument('--channel_id',type=int,help='The channel ID.')
PARSER.add_argument('-r','--reload',action='store_true',help='Reload the bot.') PARSER.add_argument('-r','--reload',action='store_true',help='Reload the bot.')
EXEC_ARGS = PARSER.parse_args() EXEC_ARGS = PARSER.parse_args()
@tasks.loop(time=datetime.time(datetime.strptime('00:00','%H:%M'))) # @tasks.loop(time=datetime.time(datetime.strptime('00:00','%H:%M')))
async def birthday_anouncements_task(): # async def birthday_anouncements_task():
LOGGER.debug('birthday_anouncements_task started.') # LOGGER.debug('birthday_anouncements_task started.')
for guild in bot.guilds: # for guild in bot.guilds:
birthday_settings = db.select('birthday_settings',f'guild_id = {guild.id}') # birthday_settings = db.select('birthday_settings',f'guild_id = {guild.id}')
if birthday_settings: # if birthday_settings:
if birthday_settings['is_enabled']: # if birthday_settings['is_enabled']:
for user in guild.members: # for user in guild.members:
user_productivity_data = db.select('guild_user_productivity',f'user_id = {user.id} AND guild_id = {guild.id}') # user_productivity_data = db.select('guild_user_productivity',f'user_id = {user.id} AND guild_id = {guild.id}')
if user_productivity_data: # if user_productivity_data:
if user_productivity_data['is_enabled']: # if user_productivity_data['is_enabled']:
user = guild.get_member(user.id) # user = guild.get_member(user.id)
if user: # if user:
birthday = user_productivity_data['birthday'] # birthday = user_productivity_data['birthday']
today = datetime.today().strftime('%d/%m') # today = datetime.today().strftime('%d/%m')
if birthday == today: # if birthday == today:
channel = guild.get_channel(user_productivity_data['channel_id']) # channel = guild.get_channel(user_productivity_data['channel_id'])
if channel: # if channel:
message = user_productivity_data['birthday_message'] # message = user_productivity_data['birthday_message']
if message: # if message:
message = message.replace('{user}',user.mention) # message = message.replace('{user}',user.mention)
await channel.send(message) # await channel.send(message)
else: # else:
LOGGER.debug(f'Channel {user_productivity_data["channel_id"]} not found.') # LOGGER.debug(f'Channel {user_productivity_data["channel_id"]} not found.')
else: # else:
LOGGER.debug(f'No birthday today for user {user.name} ({user.id}).') # LOGGER.debug(f'No birthday today for user {user.name} ({user.id}).')
else: # else:
LOGGER.debug(f'User {user.name} ({user.id}) not found.') # LOGGER.debug(f'User {user.name} ({user.id}) not found.')
else: # else:
LOGGER.debug(f'User {user.name} ({user.id}) has birthday announcements disabled.') # LOGGER.debug(f'User {user.name} ({user.id}) has birthday announcements disabled.')
else: # else:
LOGGER.debug(f'No birthday data found for user {user.name} ({user.id}).') # LOGGER.debug(f'No birthday data found for user {user.name} ({user.id}).')
else: # else:
LOGGER.debug(f'No productivity settings found for guild {guild.name} ({guild.id}).') # LOGGER.debug(f'No productivity settings found for guild {guild.name} ({guild.id}).')
LOGGER.debug('birthday_anouncements_task ended.') # LOGGER.debug('birthday_anouncements_task ended.')
@bot.slash_command(name='help',description='Displays the help message.') # @bot.slash_command(name='help',description='Displays the help message.')
async def help(ctx:discord.ApplicationContext): # async def help(ctx:discord.ApplicationContext):
"""Displays this message.""" # """Displays this message."""
command_name = 'help' # command_name = 'help'
LOGGER.debug(f'{ctx.author.name} used /{command_name}.') # LOGGER.debug(f'{ctx.author.name} used /{command_name}.')
embed = discord.Embed(title='Help',description='List of commands and their descriptions.',color=discord.Color.from_rgb(171,0,219)) # embed = discord.Embed(title='Help',description='List of commands and their descriptions.',color=discord.Color.from_rgb(171,0,219))
embed.set_author(name=bot.user.name,icon_url=bot.user.avatar.url) # embed.set_author(name=bot.user.name,icon_url=bot.user.avatar.url)
for command in bot.commands: # for command in bot.commands:
embed.add_field(name=command.name,value=command.description,inline=False) # embed.add_field(name=command.name,value=command.description,inline=False)
await ctx.send_response(embed=embed,ephemeral=True) # await ctx.send_response(embed=embed,ephemeral=True)
@bot.slash_command(name='reload',description='Reloads the bot.') # @bot.slash_command(name='reload',description='Reloads the bot.')
@option(name='feature',description='The feature to reload.',required=True,choices=['birthday','productivity']) # @option(name='feature',description='The feature to reload.',required=True,choices=['birthday','productivity'])
@commands.is_owner() # @commands.is_owner()
async def reload(ctx:discord.ApplicationContext, feature:str): # async def reload(ctx:discord.ApplicationContext, feature:str):
"""Reloads the bot.""" # """Reloads the bot."""
command_name = 'reload' # command_name = 'reload'
guild = ctx.guild # guild = ctx.guild
channel = ctx.channel # channel = ctx.channel
LOGGER.debug(f'{ctx.author.name} used /{command_name} {feature}.') # LOGGER.debug(f'{ctx.author.name} used /{command_name} {feature}.')
try: # try:
reload_feature(feature) # reload_feature(feature)
await ctx.send_response(f'{feature} reloaded!',ephemeral=True) # await ctx.send_response(f'{feature} reloaded!',ephemeral=True)
except Exception as e: # except Exception as e:
LOGGER.error(f'Error reloading feature {feature}: {e}') # LOGGER.error(f'Error reloading feature {feature}: {e}')
await ctx.send_response(f'Error reloading feature {feature}: {e}',ephemeral=True) # await ctx.send_response(f'Error reloading feature {feature}: {e}',ephemeral=True)
@bot.event # @bot.event
async def on_member_join(member:discord.Member): # async def on_member_join(member:discord.Member):
if not member.bot: # if not member.bot:
user_id = member.id # user_id = member.id
user_name = member.name # user_name = member.name
user_mention = member.mention # user_mention = member.mention
db_user = db.select('global_user',f'WHERE user_id = {user_id}') # db_user = db.select('global_user',f'WHERE user_id = {user_id}')
if not db_user: # if not db_user:
db.insert('global_user',user_id=user_id,user_name=user_name,user_mention=user_mention) # db.insert('global_user',user_id=user_id,user_name=user_name,user_mention=user_mention)
@bot.event # @bot.event
async def on_raw_member_remove(payload:discord.RawMemberRemoveEvent): # async def on_raw_member_remove(payload:discord.RawMemberRemoveEvent):
user = payload.user # user = payload.user
if not user.bot: # if not user.bot:
user_id = user.id # user_id = user.id
guild_id = payload.guild_id # guild_id = payload.guild_id
db.delete('guild_user_birthday',f'user_id = {user_id} AND guild_id = {guild_id}') # db.delete('guild_user_birthday',f'user_id = {user_id} AND guild_id = {guild_id}')
db.delete('guild_user_productivity',f'user_id = {user_id} AND guild_id = {guild_id}') # db.delete('guild_user_productivity',f'user_id = {user_id} AND guild_id = {guild_id}')
@bot.event # @bot.event
async def on_guild_join(guild:discord.Guild): # async def on_guild_join(guild:discord.Guild):
guild_id = guild.id # guild_id = guild.id
guild_name = guild.name # guild_name = guild.name
LOGGER.info(f'Bot joined guild {guild_name} ({guild_id}).') # LOGGER.info(f'Bot joined guild {guild_name} ({guild_id}).')
db.insert('guild',guild_id=guild_id) # db.insert('guild',guild_id=guild_id)
db.insert('birthday_settings',guild_id=guild_id,is_enabled=False,channel_id=random.choice(guild.text_channels).id) # db.insert('birthday_settings',guild_id=guild_id,is_enabled=False,channel_id=random.choice(guild.text_channels).id)
db.insert('productivity_settings',guild_id=guild_id,is_enabled=False) # db.insert('productivity_settings',guild_id=guild_id,is_enabled=False)
@bot.event # @bot.event
async def on_guild_remove(guild:discord.Guild): # async def on_guild_remove(guild:discord.Guild):
guild_id = guild.id # guild_id = guild.id
guild_name = guild.name # guild_name = guild.name
LOGGER.info(f'Bot left guild {guild_name} ({guild_id}).') # LOGGER.info(f'Bot left guild {guild_name} ({guild_id}).')
db.delete('birthday_settings',f'guild_id = {guild_id}') # db.delete('birthday_settings',f'guild_id = {guild_id}')
db.delete('productivity_settings',f'guild_id = {guild_id}') # db.delete('productivity_settings',f'guild_id = {guild_id}')
db.delete('guild',f'guild_id = {guild_id}') # db.delete('guild',f'guild_id = {guild_id}')
@bot.event @bot.event
async def on_ready(): async def on_ready():
LOGGER.info(f'{bot.user.name} has connected to Discord!') if bot.user:
LOGGER.debug(f'Guilds: {bot.guilds}') LOGGER.info(f'{bot.user.name} has connected to Discord!')
if EXEC_ARGS.reload and EXEC_ARGS.guild_id and EXEC_ARGS.channel_id: LOGGER.debug(f'Guilds: {','.join([guild.name for guild in bot.guilds])}')
guild = bot.get_guild(EXEC_ARGS.guild_id)
channel = guild.get_channel_or_thread(EXEC_ARGS.channel_id)
await channel.send(f'{bot.user.name} Bot reloaded!',silent=True)
birthday_anouncements_task.start()
for guild in bot.guilds:
for user in guild.members:
user_id = user.id
db_user = db.select('global_user',f'user_id = {user_id}')
if not db_user:
db.insert('global_user',user_id=user_id,user_name=user.name,user_mention=user.mention)
productivity_settings = db.select('productivity_settings',f'guild_id = {guild.id}')
if productivity_settings:
if productivity_settings['is_enabled']:
for user in guild.members:
user_productivity_data = db.select('guild_user_productivity',f'user_id = {user.id} AND guild_id = {guild.id}')
if user_productivity_data:
if user_productivity_data['is_enabled']:
next_reminder = user_productivity_data['next_reminder']
if next_reminder > datetime.now().timestamp():
cooldown = next_reminder - datetime.now().timestamp()
channel = guild.get_channel(user_productivity_data['channel_id'])
reminder_message = user_productivity_data['reminder_message']
PRODUCTIVITY_TASKS[str(user.id)] = create_task(send_reminder(cooldown,user,channel,reminder_message))
else:
user_productivity_data['next_reminder'] = datetime.now().timestamp() + user_productivity_data['cooldown']
db.update('guild_user_productivity',f'user_id = {user.id} AND guild_id = {guild.id}',next_reminder=user_productivity_data['next_reminder'])
channel = guild.get_channel(user_productivity_data['channel_id'])
reminder_message = user_productivity_data['reminder_message']
PRODUCTIVITY_TASKS[str(user.id)] = create_task(send_reminder(user_productivity_data['cooldown'],user,channel,reminder_message))
else:
LOGGER.debug(f'No productivity settings found for guild {guild.name} ({guild.id}).')
if __name__ == '__main__': if __name__ == '__main__':
db.check_conn() load_dotenv()
result = db.select('token','token_name = \'discord\'','token_value') TOKEN = os.getenv("TOKEN")
if result: if TOKEN:
TOKEN = result['token_value']
bot.run(TOKEN) bot.run(TOKEN)
else: else:
LOGGER.error('No token found.') LOGGER.error('No token found.')

119
models/cs_models.py Normal file
View File

@@ -0,0 +1,119 @@
import datetime
class Event:
def __init__(self, event_id:int, name:str, logo_url:str = None) -> None:
self.__event_id = event_id
self.__name = name
self.__logo_url = logo_url
@property
def event_id(self):
return self.__event_id
@property
def name(self):
return self.__name
@property
def logo_url(self):
return self.__logo_url
def to_dict(self) -> dict:
return {
'event_id': self.event_id,
'name': self.name,
'logo_url': self.logo_url
}
@classmethod
def from_dict(cls, data: dict) -> 'Event':
return cls(data['event_id'], data['name'], data.get('logo_url'))
def __str__(self) -> str:
return f"<Event event_id={self.event_id} name='{self.name}'>"
class Team:
def __init__(self, name:str, logo_url:str) -> None:
self.__name = name
self.__logo_url = logo_url
@property
def name(self):
return self.__name
@property
def logo_url(self):
return self.__logo_url
def to_dict(self) -> dict:
return {
'name': self.name,
'logo_url': self.logo_url
}
@classmethod
def from_dict(cls, data: dict) -> 'Team':
return cls(data['name'], data['logo_url'])
def __str__(self) -> str:
return f"<Team logo_url='{self.logo_url}' name='{self.name}'>"
class Match:
def __init__(self, url:str, format:str, event:Event, teams:list[Team], date=None):
self.__date = date
self.__url = url
self.__format = format
self.__event = event
self.__teams = tuple(teams)
@property
def date(self):
return self.__date
@property
def url(self):
return self.__url
@property
def format(self):
return self.__format
@property
def event(self):
return self.__event
@property
def teams(self):
return self.__teams
@property
def date_iso(self):
return self.__date.isoformat()
@property
def teams_names(self):
return (self.__teams[0].name, self.__teams[1].name)
@property
def teams_logos(self):
return (self.__teams[0].logo_url, self.__teams[1].logo_url)
def to_dict(self) -> dict:
return {
'url': self.url,
'format': self.format,
'event': self.event.to_dict(),
'teams': [team.to_dict() for team in self.teams],
'date': self.date.isoformat() if self.date else None
}
@classmethod
def from_dict(cls, data: dict) -> 'Match':
event = Event.from_dict(data['event'])
teams = [Team.from_dict(team_data) for team_data in data['teams']]
date = datetime.datetime.fromisoformat(data['date']) if data['date'] else None
return cls(data['url'], data['format'], event, teams, date)
def __str__(self) -> str:
return f"<Match date='{self.date}' url='{self.url}' format='{self.url}' event={self.event} teams='{','.join(self.teams_names)}'>"

View File

@@ -1,116 +0,0 @@
import discord, re
from discord import option
from discord.ext import commands
from datetime import datetime
from ..setup.logger import LOGGER
from ..setup import db
class BirthdayCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
@commands.slash_command(description='Sets birthday date. Must be in DAY/MONTH format.')
@option(name='date',description='Birthday date.',required=True)
@option(name='announcements',description='Enable or disable birthday announcements.',required=True,type=bool)
@option(name='message',description='The message to send when it is the user\'s birthday.',required=False)
@option(name='for_user',description='The user to set the birthday date for.',required=False,type=discord.Member)
async def birthday_set(self,ctx:discord.ApplicationContext, date:str,announcements:bool,message:str,for_user:discord.Member):
"""Sets birthday date. Must be in DAY/MONTH format."""
command_name = 'birthday_set'
guild_id = ctx.guild.id
author = ctx.author
birthday_settings = db.select('birthday_settings',f'guild_id = {guild_id}')
if birthday_settings:
if birthday_settings['is_enabled']:
if for_user:
if author.guild_permissions.administrator:
if re.match(r'^\d{1,2}\/\d{1,2}$', date):
user_data = db.select('guild_user_birthday',f'guild_id = {guild_id} AND user_id = {for_user.id}')
if user_data:
# Update user data
if message:
LOGGER.debug(f'{author.name} used /{command_name} {date} {announcements} {message} for {for_user.name}.')
db.update('guild_user_birthday',f'guild_id = {guild_id} AND user_id = {for_user.id}',birthday=date,is_enabled=announcements,birthday_message=message)
else:
LOGGER.debug(f'{author.name} used /{command_name} {date} {announcements} for {for_user.name}.')
db.update('guild_user_birthday',f'guild_id = {guild_id} AND user_id = {for_user.id}',birthday=date,is_enabled=announcements)
await ctx.send_response(f'{for_user.name}\'s birthday has been set to {date}.',ephemeral=True)
else:
# Create user data
if message:
LOGGER.debug(f'{author.name} used /{command_name} {date} {announcements} {message} for {for_user.name}.')
db.insert('guild_user_birthday',guild_id=guild_id,user_id=for_user.id,birthday=date,is_enabled=announcements,birthday_message=message)
else:
LOGGER.debug(f'{author.name} used /{command_name} {date} {announcements} for {for_user.name}.')
db.insert('guild_user_birthday',guild_id=guild_id,user_id=for_user.id,birthday=date,is_enabled=announcements)
await ctx.send_response(f'{for_user.name}\'s birthday has been set to {date}.',ephemeral=True)
else:
LOGGER.debug(f'{author.name} tried to use /{command_name} {date} {announcements} {message} for {for_user.name} but the date format is invalid.')
await ctx.send_response('The date format must be DAY/MONTH.',ephemeral=True)
else:
LOGGER.debug(f'{author.name} tried to use /{command_name} {date} {announcements} {message} for {for_user.name} but is not an administrator.')
await ctx.send_response('You must be an administrator to run the command with "for_user".',ephemeral=True)
else:
if re.match(r'^\d{1,2}\/\d{1,2}$', date):
user_data = db.select('guild_user_birthday',f'guild_id = {guild_id} AND user_id = {author.id}')
if user_data:
# Update user data
if message:
LOGGER.debug(f'{author.name} used /{command_name} {date} {announcements} {message}.')
db.update('guild_user_birthday',f'guild_id = {guild_id} AND user_id = {author.id}',birthday=date,is_enabled=announcements,birthday_message=message)
else:
LOGGER.debug(f'{author.name} used /{command_name} {date} {announcements}.')
db.update('guild_user_birthday',f'guild_id = {guild_id} AND user_id = {author.id}',birthday=date,is_enabled=announcements)
await ctx.send_response(f'Your birthday has been set to {date}.',ephemeral=True)
else:
# Create user data
if message:
LOGGER.debug(f'{author.name} used /{command_name} {date} {announcements} {message}.')
db.insert('guild_user_birthday',guild_id=guild_id,user_id=author.id,birthday=date,is_enabled=announcements,birthday_message=message)
else:
LOGGER.debug(f'{author.name} used /{command_name} {date} {announcements}.')
db.insert('guild_user_birthday',guild_id=guild_id,user_id=author.id,birthday=date,is_enabled=announcements)
await ctx.send_response(f'Your birthday has been set to {date}.',ephemeral=True)
else:
LOGGER.debug(f'{author.name} tried to use /{command_name} {date} {announcements} {message} but the date format is invalid.')
await ctx.send_response('The date format must be DAY/MONTH.',ephemeral=True)
else:
LOGGER.debug(f'{author.name} tried to use /{command_name} {date} {announcements} {message} but the birthday feature is not enabled.')
await ctx.send_response('The birthday feature is not enabled.',ephemeral=True)
else:
LOGGER.debug(f'{author.name} tried to use /{command_name} {date} {announcements} {message} but the birthday feature is not enabled.')
await ctx.send_response('The birthday feature is not enabled.',ephemeral=True)
@commands.slash_command(description='Configurate birthday announcements.')
@option(name='channel',description='The channel to send birthday announcements to.',required=True,type=discord.TextChannel)
@option(name='enable',description='Enable or disable birthday announcements.',required=True,type=bool)
async def birthday_config(self,ctx:discord.ApplicationContext, channel:discord.TextChannel,enable:bool):
"""Configurate birthday announcements."""
command_name = 'birthday_config'
guild_id = ctx.guild.id
author = ctx.author
birthday_settings = db.select('birthday_settings',f'guild_id = {guild_id}')
if birthday_settings:
# Update guild birthday settings
if birthday_settings['is_enabled']:
if author.guild_permissions.administrator:
LOGGER.debug(f'{author.name} used /{command_name} {channel.name} {enable}.')
db.update('birthday_settings',f'guild_id = {guild_id}',channel_id=channel.id,is_enabled=enable)
await ctx.send_response(f'Birthday announcements have been {"enabled" if enable else "disabled"} in {channel.mention}.',ephemeral=True)
else:
LOGGER.debug(f'{author.name} tried to use /{command_name} {channel.name} {enable} but is not an administrator.')
await ctx.send_response('You must be an administrator to run this command.',ephemeral=True)
else:
LOGGER.debug(f'{author.name} tried to use /{command_name} {channel.name} {enable} but the birthday feature is not enabled.')
await ctx.send_response('The birthday feature is not enabled.',ephemeral=True)
else:
# Create guild birthday settings
if author.guild_permissions.administrator:
LOGGER.debug(f'{author.name} used /{command_name} {channel.name} {enable}.')
db.insert('birthday_settings',guild_id=guild_id,channel_id=channel.id,is_enabled=enable)
await ctx.send_response(f'Birthday announcements have been {"enabled" if enable else "disabled"} in {channel.mention}.',ephemeral=True)
else:
LOGGER.debug(f'{author.name} tried to use /{command_name} {channel.name} {enable} but is not an administrator.')
await ctx.send_response('You must be an administrator to run this command.',ephemeral=True)

View File

@@ -1,150 +0,0 @@
import discord
from datetime import datetime
from discord.ext import commands, tasks
from discord import option
from typing import Union, Optional
from asyncio import sleep, create_task
from ..setup.logger import LOGGER
from ..setup import db
TASKS = {}
async def send_reminder(cooldown:int,author:discord.Member,channel:discord.TextChannel,reminder_message:str):
while True:
await sleep(cooldown)
LOGGER.debug(f'Sending productivity reminder to {author.name} in {channel.name} of {channel.guild.name}.')
message = reminder_message.replace('{user}',author.mention)
await channel.send(message)
class ProductivityCog(commands.Cog):
def __init__(self,bot:discord.Bot) -> None:
self.bot = bot
@commands.slash_command(description='Configurate productivity reminder.')
@option(name='channel',description='The channel to send productivity reminders in.',required=True)
@option(name='enable',description='Enable or disable productivity reminders.',required=True)
@option(name='days',description='Number of days to wait before sending a reminder if the user has not been active in the channel.',required=True,type=int)
@option(name='custom_message',description='Custom message to send with the reminder.',required=False,type=str)
@option(name='for_user',description='The user to configurate productivity reminders for.',required=False,type=discord.Member)
async def productivity_set(self,ctx:discord.ApplicationContext, channel:Union[discord.TextChannel,discord.Thread], enable:bool, days:int, custom_message=None, for_user:discord.Member=None):
"""Configurate productivity reminder."""
command_name = 'productivity_set'
guild_id = ctx.guild.id
author = ctx.author
productivity_settings = db.select('productivity_settings',f'guild_id = {guild_id}')
if productivity_settings:
if productivity_settings['is_enabled']:
if for_user:
if author.guild_permissions.administrator:
user_data = db.select('guild_user_productivity',f'guild_id = {guild_id} AND user_id = {for_user.id}')
cooldown = days*60*60*24
next_reminder = int(datetime.now().timestamp() + cooldown)
if user_data:
# Update user data
if custom_message:
db.update('guild_user_productivity',f'guild_id = {guild_id} AND user_id = {for_user.id}',channel_id=channel.id,cooldown=cooldown,next_reminder=next_reminder,reminder_message=custom_message,is_enabled=enable)
LOGGER.debug(f'{author.name} used /{command_name} {channel.mention} {enable} {days} {custom_message} for {for_user.name}.')
await ctx.send_response(f'Productivity reminders for {for_user.name} have been set to {channel.mention}.',ephemeral=True)
else:
db.update('guild_user_productivity',f'guild_id = {guild_id} AND user_id = {for_user.id}',channel_id=channel.id,cooldown=cooldown,next_reminder=next_reminder,is_enabled=enable)
LOGGER.debug(f'{author.name} used /{command_name} {channel.mention} {enable} {days} for {for_user.name}.')
await ctx.send_response(f'Productivity reminders for {for_user.name} have been set to {channel.mention}.',ephemeral=True)
else:
# Create user data
if custom_message:
db.insert('guild_user_productivity',guild_id=guild_id,user_id=for_user.id,channel_id=channel.id,cooldown=cooldown,next_reminder=next_reminder,reminder_message=custom_message,is_enabled=enable)
LOGGER.debug(f'{author.name} used /{command_name} {channel.mention} {enable} {days} {custom_message} for {for_user.name}.')
await ctx.send_response(f'Productivity reminders for {for_user.name} have been set to {channel.mention}.',ephemeral=True)
else:
db.insert('guild_user_productivity',guild_id=guild_id,user_id=for_user.id,channel_id=channel.id,cooldown=cooldown,next_reminder=next_reminder,is_enabled=enable)
LOGGER.debug(f'{author.name} used /{command_name} {channel.mention} {enable} {days} for {for_user.name}.')
await ctx.send_response(f'Productivity reminders for {for_user.name} have been set to {channel.mention}.',ephemeral=True)
user_data = db.select('guild_user_productivity',f'guild_id = {guild_id} AND user_id = {for_user.id}')
if str(for_user.id) in TASKS.keys():
TASKS[str(for_user.id)].cancel()
TASKS[str(for_user.id)] = create_task(send_reminder(cooldown,for_user,channel,user_data['reminder_message']))
else:
LOGGER.debug(f'{author.name} tried to use /{command_name} {channel.mention} {enable} {days} {custom_message} for {for_user.name} but is not an administrator.')
await ctx.send_response('You must be an administrator to run the command with "for_user".',ephemeral=True)
else:
user_data = db.select('guild_user_productivity',f'guild_id = {guild_id} AND user_id = {author.id}')
cooldown = days*60*60*24
next_reminder = int(datetime.now().timestamp() + cooldown)
if user_data:
# Update user data
if custom_message:
db.update('guild_user_productivity',f'guild_id = {guild_id} AND user_id = {author.id}',channel_id=channel.id,cooldown=cooldown,next_reminder=next_reminder,reminder_message=custom_message,is_enabled=enable)
LOGGER.debug(f'{author.name} used /{command_name} {channel.mention} {enable} {days} {custom_message}.')
await ctx.send_response(f'Productivity reminders for {author.name} have been set to {channel.mention}.',ephemeral=True)
else:
db.update('guild_user_productivity',f'guild_id = {guild_id} AND user_id = {author.id}',channel_id=channel.id,cooldown=cooldown,next_reminder=next_reminder,is_enabled=enable)
LOGGER.debug(f'{author.name} used /{command_name} {channel.mention} {enable} {days}.')
await ctx.send_response(f'Productivity reminders for {author.name} have been set to {channel.mention}.',ephemeral=True)
else:
# Create user data
if custom_message:
db.insert('guild_user_productivity',guild_id=guild_id,user_id=author.id,channel_id=channel.id,cooldown=cooldown,next_reminder=next_reminder,reminder_message=custom_message,is_enabled=enable)
LOGGER.debug(f'{author.name} used /{command_name} {channel.mention} {enable} {days} {custom_message}.')
await ctx.send_response(f'Productivity reminders for {author.name} have been set to {channel.mention}.',ephemeral=True)
else:
db.insert('guild_user_productivity',guild_id=guild_id,user_id=author.id,channel_id=channel.id,cooldown=cooldown,next_reminder=next_reminder,is_enabled=enable)
LOGGER.debug(f'{author.name} used /{command_name} {channel.mention} {enable} {days}.')
await ctx.send_response(f'Productivity reminders for {author.name} have been set to {channel.mention}.',ephemeral=True)
user_data = db.select('guild_user_productivity',f'guild_id = {guild_id} AND user_id = {author.id}')
if str(author.id) in TASKS.keys():
TASKS[str(author.id)].cancel()
TASKS[str(author.id)] = create_task(send_reminder(cooldown,author,channel,user_data['reminder_message']))
else:
LOGGER.debug(f'{author.name} tried to use /{command_name} {channel.mention} {enable} {days} {custom_message} but the productivity feature is not enabled.')
await ctx.send_response('The productivity feature is not enabled.',ephemeral=True)
else:
LOGGER.debug(f'{author.name} tried to use /{command_name} {channel.mention} {enable} {days} {custom_message} but no productivity settings were found.')
await ctx.send_response('No productivity settings were found.',ephemeral=True)
@commands.slash_command(description='Enable or disable productivity reminders on the server.')
@option(name='enable',description='Enable or disable productivity reminders.',required=True)
async def productivity_config(self,ctx:discord.ApplicationContext, enable:bool):
"""Enable or disable productivity reminders on the server."""
command_name = 'productivity_config'
guild_id = ctx.guild.id
author = ctx.author
productivity_settings = db.select('productivity_settings',f'guild_id = {guild_id}')
if productivity_settings:
# Update guild productivity settings
if author.guild_permissions.administrator:
LOGGER.debug(f'{author.name} used /{command_name} {enable}.')
db.update('productivity_settings',f'guild_id = {guild_id}',is_enabled=enable)
await ctx.send_response(f'Productivity reminders have been {"enabled" if enable else "disabled"}.',ephemeral=True)
else:
LOGGER.debug(f'{author.name} tried to use /{command_name} {enable} but is not an administrator.')
await ctx.send_response('You must be an administrator to run this command.',ephemeral=True)
else:
# Create guild productivity settings
if author.guild_permissions.administrator:
LOGGER.debug(f'{author.name} used /{command_name} {enable}.')
db.insert('productivity_settings',guild_id=guild_id,is_enabled=enable)
await ctx.send_response(f'Productivity reminders have been {"enabled" if enable else "disabled"}.',ephemeral=True)
else:
LOGGER.debug(f'{author.name} tried to use /{command_name} {enable} but is not an administrator.')
await ctx.send_response('You must be an administrator to run this command.',ephemeral=True)
@commands.Cog.listener()
async def on_message(self,message:discord.Message):
author = message.author
if author.id != self.bot.user.id: # Ignore messages from the bot
guild_id = message.guild.id
productivity_settings = db.select('productivity_settings',f'guild_id = {guild_id}')
if productivity_settings:
if productivity_settings['is_enabled']:
user_productivity_data = db.select('guild_user_productivity',f'user_id = {author.id} AND guild_id = {guild_id}')
if user_productivity_data:
if user_productivity_data['is_enabled']:
next_reminder = int(datetime.now().timestamp() + user_productivity_data['cooldown'])
db.update('guild_user_productivity',f'user_id = {author.id} AND guild_id = {guild_id}',next_reminder=next_reminder)
if str(author.id) in TASKS.keys():
TASKS[str(author.id)].cancel()
TASKS[str(author.id)] = create_task(send_reminder(user_productivity_data['cooldown'],author,message.channel,user_productivity_data['reminder_message']))

View File

@@ -1,304 +0,0 @@
import os, psycopg2
from dotenv import load_dotenv
from typing import Optional, Callable, Any, Union
from .logger import LOGGER
load_dotenv()
DB_NAME = os.getenv('DB_NAME')
DB_USER = os.getenv('DB_USER')
DB_PASSWORD = os.getenv('DB_PASSWORD')
DB_HOST = os.getenv('DB_HOST')
def check_conn():
"""Tests the database connection."""
connection = psycopg2.connect(
database=DB_NAME,
user=DB_USER,
password=DB_PASSWORD,
host=DB_HOST,
port="5432"
)
if connection.status:
LOGGER.debug('Established connection to database.')
LOGGER.debug(f'Connection status: {connection.status}')
else:
LOGGER.error('Failed to connect to database.')
connection.close()
LOGGER.debug('Closed connection to database.')
def insert(table:str,**kwargs):
"""
Inserts the given data into the given table.
Global_User
user_id: int
user_mention: str
user_mention: str
Guild
guild_id: int
Birthday_Settings
guild_id: int
is_enabled: bool
channel_id: int
Productivity_Settings
guild_id: int
is_enabled: bool
Guild_User_Birthday
user_id: int
guild_id: int
birthday: int
birthday_message: str | None
is_enabled: bool
Guild_User_Productivity
user_id: int
guild_id: int
is_enabled: bool
channel_id: int
cooldown: int
next_reminder: int
reminder_message: str | None
"""
connection = psycopg2.connect(
database=DB_NAME,
user=DB_USER,
password=DB_PASSWORD,
host=DB_HOST,
port="5432",
)
cursor = connection.cursor()
query = f"INSERT INTO {table} ("
params = []
for key,value in kwargs.items():
query += f"{key},"
params.append(value)
query = query.rstrip(",") # Remove trailing comma
query += ") VALUES ("
for _ in range(len(params)):
query += "%s,"
query = query.rstrip(",") # Remove trailing comma
query += ")"
query = cursor.mogrify(query, params)
cursor.execute(query)
connection.commit()
cursor.close()
connection.close()
LOGGER.debug(f'Inserted data into {table}.')
def update(table:str,where:str,**kwargs):
"""
Updates the given data in the given table.
Global_User
user_id: int
user_mention: str
user_mention: str
Guild
guild_id: int
Birthday_Settings
guild_id: int
is_enabled: bool
channel_id: int
Productivity_Settings
guild_id: int
is_enabled: bool
Guild_User_Birthday
user_id: int
guild_id: int
birthday: int
birthday_message: str | None
is_enabled: bool
Guild_User_Productivity
user_id: int
guild_id: int
is_enabled: bool
channel_id: int
cooldown: int
next_reminder: int
reminder_message: str | None
"""
connection = psycopg2.connect(
database=DB_NAME,
user=DB_USER,
password=DB_PASSWORD,
host=DB_HOST,
port="5432",
)
cursor = connection.cursor()
query = f"UPDATE {table} SET "
params = []
for key,value in kwargs.items():
query += f"{key} = %s,"
params.append(value)
query = query.rstrip(",") # Remove trailing comma
query += f" WHERE {where}"
query = cursor.mogrify(query, params)
cursor.execute(query)
connection.commit()
cursor.close()
connection.close()
LOGGER.debug(f'Updated data in {table}.')
def delete(table:str,where:str):
"""
Deletes the given data from the given table.
Global_User
user_id: int
user_mention: str
user_mention: str
Guild
guild_id: int
Birthday_Settings
guild_id: int
is_enabled: bool
channel_id: int
Productivity_Settings
guild_id: int
is_enabled: bool
Guild_User_Birthday
user_id: int
guild_id: int
birthday: int
birthday_message: str | None
is_enabled: bool
Guild_User_Productivity
user_id: int
guild_id: int
is_enabled: bool
channel_id: int
cooldown: int
next_reminder: int
reminder_message: str | None
"""
connection = psycopg2.connect(
database=DB_NAME,
user=DB_USER,
password=DB_PASSWORD,
host=DB_HOST,
port="5432",
)
cursor = connection.cursor()
query = f"DELETE FROM {table} WHERE {where}"
cursor.execute(query)
connection.commit()
cursor.close()
connection.close()
LOGGER.debug(f'Deleted data from {table}.')
def select(table:str,where:str,*columns:str) -> dict[str,Any] | None:
"""
Selects the given data from the given table, and returns it as a dictionary.
Global_User
user_id: int
user_mention: str
user_mention: str
Guild
guild_id: int
Birthday_Settings
guild_id: int
is_enabled: bool
channel_id: int
Productivity_Settings
guild_id: int
is_enabled: bool
Guild_User_Birthday
user_id: int
guild_id: int
birthday: int
birthday_message: str | None
is_enabled: bool
Guild_User_Productivity
user_id: int
guild_id: int
is_enabled: bool
channel_id: int
cooldown: int
next_reminder: int
reminder_message: str | None
"""
connection = psycopg2.connect(
database=DB_NAME,
user=DB_USER,
password=DB_PASSWORD,
host=DB_HOST,
port="5432"
)
connection.readonly = True
cursor = connection.cursor()
query = f"SELECT "
if columns:
for column in columns:
query += f"{column},"
query = query.rstrip(",")
else:
query += "*"
query += f" FROM {table} WHERE {where}"
cursor.execute(query)
record = cursor.fetchone()
result = None
if record is None:
LOGGER.debug(f'No data found in {table} for {where}.')
else:
result = {}
if len(columns) > 0:
for i in range(len(columns)):
result[columns[i]] = record[i]
else:
if table.lower() == 'global_user':
result['user_id'] = record[0]
result['user_name'] = record[1]
result['user_mention'] = record[2]
elif table.lower() == 'guild':
result['guild_id'] = record[0]
elif table.lower() == 'birthday_settings':
result['guild_id'] = record[0]
result['is_enabled'] = record[1]
result['channel_id'] = record[2]
elif table.lower() == 'productivity_settings':
result['guild_id'] = record[0]
result['is_enabled'] = record[1]
elif table.lower() == 'guild_user_birthday':
result['user_id'] = record[0]
result['guild_id'] = record[1]
result['birthday'] = record[2]
result['is_enabled'] = record[3]
elif table.lower() == 'guild_user_productivity':
result['user_id'] = record[0]
result['guild_id'] = record[1]
result['is_enabled'] = record[2]
result['channel_id'] = record[3]
result['cooldown'] = record[4]
result['next_reminder'] = record[5]
result['reminder_message'] = record[6]
else:
LOGGER.error(f'Unknown table {table}.')
cursor.close()
connection.close()
return result

View File

@@ -1,3 +1,31 @@
python-dotenv aiohappyeyeballs==2.6.2
py-cord aiohttp==3.14.1
psycopg2-binary aiosignal==1.4.0
asarPy==1.0.1
attrs==26.1.0
beautifulsoup4==4.15.0
certifi==2026.5.20
charset-normalizer==3.4.7
frozenlist==1.8.0
h11==0.16.0
idna==3.18
lxml==6.1.1
multidict==6.7.1
outcome==1.3.0.post0
propcache==0.5.2
psycopg2-binary==2.9.12
py-cord==2.8.0
PySocks==1.7.1
python-dotenv==1.2.2
requests==2.34.2
selenium==4.45.0
sniffio==1.3.1
sortedcontainers==2.4.0
soupsieve==2.8.4
trio==0.33.0
trio-websocket==0.12.2
typing_extensions==4.15.0
urllib3==2.7.0
websocket-client==1.9.0
wsproto==1.3.2
yarl==1.24.2

View File

@@ -1,7 +1,10 @@
import discord import discord
from ..cogs.birthday import BirthdayCog from discord.ext import bridge
from ..cogs.productivity import ProductivityCog # from cogs.birthday import BirthdayCog
from .logger import LOGGER # from cogs.productivity import ProductivityCog
from cogs.cs import CSCog
from cogs.insult import InsultsCog
from setup.logger import LOGGER
bot_intents = discord.Intents.default() bot_intents = discord.Intents.default()
bot_intents.message_content = True bot_intents.message_content = True
@@ -10,9 +13,9 @@ bot_intents.presences = True
bot_intents.guilds = True bot_intents.guilds = True
bot_intents.reactions = True bot_intents.reactions = True
bot = discord.Bot(command_prefix='$', intents=bot_intents) bot = bridge.Bot(command_prefix='$', intents=bot_intents)
COGS = [BirthdayCog, ProductivityCog] COGS = [CSCog, InsultsCog]
for cog in COGS: for cog in COGS:
bot.add_cog(cog(bot)) bot.add_cog(cog(bot))