Compare commits

..

13 commits

Author SHA1 Message Date
Funky Waddle 722a154fc1 Update matrix button classes to use current get_guild_role method 2025-06-29 02:24:54 -05:00
Funky Waddle 47896ecac7 Update main. Add db and guild attributes. Add reload_cog command 2025-06-29 02:24:00 -05:00
Funky Waddle 745c3a9202 Update View lib. Fix typo in if conditional 2025-06-29 02:23:04 -05:00
Funky Waddle 1cb19f8d9b Update Twitch lib. Remove debug output 2025-06-29 02:22:19 -05:00
Funky Waddle a2c4d14540 Update Roles lib with more helper methods, and typing parameters 2025-06-29 02:21:46 -05:00
Funky Waddle e6cf2194b2 Update Framework and Language Button libs to use current methods 2025-06-29 02:20:52 -05:00
Funky Waddle e018090af6 Modify Guild lib. Remove async from get_guild method 2025-06-29 02:19:55 -05:00
Funky Waddle 1ce6b538ae bot updates 2025-06-29 02:19:18 -05:00
Funky Waddle 5a81e4b2a1 Add helper methods to Db lib 2025-06-29 02:18:58 -05:00
Funky Waddle 881c900e6d Modify debug output of twitch notifications cog 2025-06-29 02:18:23 -05:00
Funky Waddle d308ea0c06 Modify roles cog to use app commands for adding and removing language and framework roles 2025-06-29 02:17:32 -05:00
Funky Waddle 67c1e340c2 Start setting up poll command 2025-06-29 02:14:57 -05:00
Funky Waddle b6a7a80f3c Sort Role Buttons on output 2025-06-29 02:13:51 -05:00
16 changed files with 247 additions and 50 deletions

View file

@ -28,14 +28,14 @@ class FunkyBot(commands.Bot):
intents=bot_intents
)
self.log_handler = BotLog("bot.log")
self.db = Db(db_conn_str, True)
self.guild = None
self.db = Db(db_conn_str, False)
self.guild_id = guild_id
self.guild = None
self.dev_mode = dev_mode == "1"
async def on_ready(self):
print(f"Logged in as {self.user.name}")
self.guild = await Guilds().get_guild(self)
self.guild = Guilds().get_guild(self)
await self.tree.sync(guild=self.guild)
async def async_cleanup(self):
@ -61,7 +61,7 @@ class FunkyBot(commands.Bot):
if role is not None:
if role not in member.roles:
await member.add_roles(role)
await channel.send(f"Another Penguin has joined the Collective. Everyone say hi to {member.mention}!")
await channel.send(f"{role.mention} Another Penguin has joined the Collective. Say hi to {member.mention}!")
async def load_cogs(self):
for filename in os.listdir("./cogs"):
@ -69,6 +69,12 @@ class FunkyBot(commands.Bot):
await self.load_extension(f"cogs.{filename[:-3]}")
print(f"Loaded {filename[:-3]}")
@commands.command()
async def reload_cog(self, ctx, cog):
ctx.message.delete()
await self.reload_cog(ctx, f"cogs.{cog}")
print(f"Reloaded {cog}")
async def main():
bot = FunkyBot()

View file

@ -1,12 +1,23 @@
import discord
from discord.ext import commands
from discord import app_commands
from libs.Cog import Cog
class Poll(Cog):
@commands.command()
async def poll(self, ctx, *, question):
print("poll command called")
@Cog.listener()
async def on_ready(self):
pass
@app_commands.command(
name="create_poll",
description="Create a poll",
)
@app_commands.describe(question="The question for the poll")
@app_commands.describe(timing="How long (in minutes) to keep the poll open")
@app_commands.describe(choices="The comma-delimited list of choices")
async def create_poll(self, interaction: discord.Interaction, question: str, timing: int, choices: str):
interaction.response.send_message(f"The create_poll command is not set up yet. But thank you for trying", ephemeral=True)
print(f"POLL: {interaction.user.name} ran the poll command, asking for {question}")
async def setup(bot):
await bot.add_cog(Poll(bot))

View file

@ -1,32 +1,174 @@
from discord.ext import commands
import discord
from discord import app_commands
from embeds.MatrixRolesEmbed import MatrixRolesEmbed
from embeds.LanguageRolesEmbed import LanguageRolesEmbed
from embeds.FrameworkRolesEmbed import FrameworkRolesEmbed
from libs.Channels import Channels
from libs.Guilds import Guilds
from libs.Cog import Cog
from libs.Roles import Roles
from data.models.Language import Language
from data.models.Framework import Framework
from views.MatrixButtons import MatrixButtons
from views.LanguageButtons import LanguageButtons
from views.FrameworkButtons import FrameworkButtons
from __main__ import guild_id
guild = discord.Object(id=guild_id)
class RolesCog(Cog):
def __init__(self, bot):
super().__init__(bot)
self.bot = bot
self.roles = Roles()
self.channel = None
self.messages_deletable = True
self.role_messages = {}
@commands.Cog.listener()
@Cog.listener()
async def on_ready(self):
guild = await Guilds().get_guild(self.bot)
channel = await Channels().get_channel(guild, "add-roles")
self.channel = await Channels().get_channel(self.bot.guild, "add-roles")
if channel is not None:
await channel.purge()
await channel.send(embed=MatrixRolesEmbed(), view=MatrixButtons())
await channel.send(embed=LanguageRolesEmbed(), view=LanguageButtons(self.bot))
await channel.send(embed=FrameworkRolesEmbed(), view=FrameworkButtons(self.bot))
if self.channel is not None:
await self.channel.purge()
self.role_messages["matrix"] = await self.channel.send(embed=MatrixRolesEmbed(), view=MatrixButtons())
self.role_messages["languages"] = await self.channel.send(embed=LanguageRolesEmbed(), view=LanguageButtons(self.bot))
self.role_messages["frameworks"] = await self.channel.send(embed=FrameworkRolesEmbed(), view=FrameworkButtons(self.bot))
async def role_exists_in_db(self, model, role):
return self.bot.db.role_exists(model, role)
async def edit_message(self, message_id, view):
message = await self.channel.fetch_message(message_id)
await message.edit(view=view)
async def get_role(self, role):
return await self.roles.get_guild_role(self.bot.guild, role)
async def add_guild_role(self, role):
message = f"Role `{role}` already exists."
if await self.roles.add_guild_role(role):
message = f"Role `{role}` created on server"
return message
async def remove_guild_role(self, role):
message = f"Role `{role}` doesn't exist."
if await self.roles.remove_guild_role(role):
message = f"Role `{role}` removed from server"
return message
async def add_to_db(self, model):
self.bot.db.add_record(model)
return f"Role `{model.label}` added to {model.__tablename__}"
async def remove_from_db(self, model, label):
self.bot.db.session.query(model).filter(model.label == label).delete()
self.bot.db.session.commit()
return f"Role `{label}` removed from {model.__tablename__}"
@app_commands.command(
name="add_language",
description="Add a language to the available language roles"
)
@app_commands.describe(language="Case-sensitive string of the language you wish to add")
@app_commands.guilds(guild)
@app_commands.checks.has_any_role("Royal Penguins", "Emperor Penguin")
async def add_language(self, interaction: discord.Interaction, language: str):
messages = [await self.add_guild_role(language)]
if not await self.role_exists_in_db(Language, language):
messages.append(await self.add_to_db(Language(label=language)))
await self.edit_message(self.role_messages["languages"].id, view=LanguageButtons(self.bot))
else:
messages.append(f"Role `{language}` already exists")
await interaction.response.send_message(f"\n".join(messages), ephemeral=True, delete_after=5)
@add_language.error
async def add_language_error(self, interaction: discord.Interaction, error):
await interaction.response.send_message(f"An error occurred while adding the language.\n{error}", ephemeral=True)
@app_commands.command(
name="remove_language",
description="Remove a language and associated frameworks from the available language roles."
)
@app_commands.describe(language="Case-sensitive string of the language you wish to remove.")
@app_commands.guilds(guild)
@app_commands.checks.has_any_role("Royal Penguins", "Emperor Penguin")
async def remove_language(self, interaction: discord.Interaction, language: str):
messages = []
fetched_language = self.bot.db.session.query(Language).filter(Language.label == language).first()
for framework in fetched_language.frameworks:
messages.append(await self.remove_guild_role(framework.label))
messages.append(await self.remove_from_db(Framework, framework.label))
messages.append(await self.remove_guild_role(language))
if await self.role_exists_in_db(Language, language):
messages.append(await self.remove_from_db(Language, language))
else:
messages.append(f"Role `{language}` doesn't exist in the db")
await self.edit_message(self.role_messages["frameworks"].id, view=FrameworkButtons(self.bot))
await self.edit_message(self.role_messages["languages"].id, view=LanguageButtons(self.bot))
await interaction.response.send_message(f"\n".join(messages), ephemeral=True)
@remove_language.error
async def remove_language_error(self, interaction: discord.Interaction, error):
await interaction.response.send_message(f"An error occurred while removing the language.\n{error}", ephemeral=True)
@app_commands.command(
name="add_framework",
description="Add a framework to the available framework roles"
)
@app_commands.describe(framework="Case-sensitive string of the framework you wish to add")
@app_commands.describe(language="Case-sensitive string of the language of the framework. Must already exist.")
@app_commands.guilds(guild)
@app_commands.checks.has_any_role("Royal Penguins", "Emperor Penguin")
async def add_framework(self, interaction: discord.Interaction, framework: str, language: str):
messages = []
lng = self.bot.db.session.query(Language).filter(Language.label == language).first()
if not lng:
await interaction.response.send_message(f"Language `{language}` doesn't exist", ephemeral=True, delete_after=10)
else:
messages.append(await self.add_guild_role(framework))
if not await self.role_exists_in_db(Framework, framework):
messages.append(await self.add_to_db(Framework(label=framework, language=lng)))
await self.edit_message(self.role_messages["frameworks"].id, view=FrameworkButtons(self.bot))
else:
messages.append(f"Role `{framework}` already exists")
await interaction.response.send_message(f"\n".join(messages), ephemeral=True, delete_after=5)
@add_framework.error
async def add_framework_error(self, interaction: discord.Interaction, error):
await interaction.response.send_message(f"An error occurred while adding the framework.\n{error}", ephemeral=True)
@app_commands.command(
name="remove_framework",
description="Remove a framework from the available framework roles"
)
@app_commands.describe(framework="Case-sensitive string of the framework to remove.")
@app_commands.guilds(guild)
@app_commands.checks.has_any_role("Royal Penguins", "Emperor Penguin")
async def remove_framework(self, interaction: discord.Interaction, framework: str):
messages = [await self.remove_guild_role(framework)]
if await self.role_exists_in_db(Framework, framework):
messages.append(await self.remove_from_db(Framework, framework))
await self.edit_message(self.role_messages["frameworks"].id, view=FrameworkButtons(self.bot))
else:
messages.append(f"Role `{framework}` doesn't exist")
await interaction.response.send_message(f"\n".join(messages), ephemeral=True, delete_after=5)
@remove_framework.error
async def remove_framework_error(self, interaction: discord.Interaction, error):
await interaction.response.send_message(f"An error occurred while removing the framework.\n{error}", ephemeral=True)
async def setup(bot):
await bot.add_cog(RolesCog(bot))
await bot.add_cog(RolesCog(bot), guilds=[discord.Object(id=bot.guild_id)])

View file

@ -21,7 +21,7 @@ class TwitchNotificationsCog(Cog):
@commands.Cog.listener()
async def on_ready(self):
await self.load_config()
self.guild = await Guilds().get_guild(self.bot)
self.guild = Guilds().get_guild(self.bot)
self.twitch = Twitch(self.config[self.guild.name]["twitch"]["client_id"], self.config[self.guild.name]["twitch"]["client_secret"])
await self.set_access_token_and_expires_date()
await self.set_notification_channel_and_purge_channel()
@ -62,13 +62,12 @@ class TwitchNotificationsCog(Cog):
async def check_twitch_online_streamers(self):
print("Online streamers are checked")
channel = await Channels().get_channel(self.guild, self.config[self.guild.name]["twitch"]["channel_name"])
print(channel)
if not channel:
return
watchlist_notifications = self.bot.db.session.query(TwitchLiveNotification).all()
watchlist = [notify.username for notify in watchlist_notifications]
print("WATCHLIST", watchlist)
print("Twitch Watchlist: ", watchlist)
online_notifications, offline_notifications = await self.twitch.get_notifications(watchlist)
print("online_notifications", online_notifications)

Binary file not shown.

View file

@ -27,3 +27,14 @@ class Db:
for filename in os.listdir('./data/models'):
if filename.endswith('.py'):
importlib.import_module('.' + filename[:-3], 'data.models')
def role_exists(self, model: Model, role: str):
does_exist = False
language = self.session.query(model).filter(model.label == role).first()
if language is not None:
does_exist = True
return does_exist
def add_record(self, model):
self.session.add(model)
self.session.commit()

View file

@ -12,8 +12,8 @@ class FrameworkButton(discord.ui.Button):
self.language_role = language_role
async def callback(self, interaction: discord.Interaction):
role = await Roles().get_role(interaction.guild, self.label)
language_role = await Roles().get_role(interaction.guild, self.language_role)
role = await Roles().get_guild_role(interaction.guild, self.label)
language_role = await Roles().get_guild_role(interaction.guild, self.language_role)
messages = []
if role in interaction.user.roles:
await Roles().remove_role(interaction.guild, self.label, interaction.user)

View file

@ -5,7 +5,7 @@ class Guilds:
def __init__(self):
pass
async def get_guild(self, bot: commands.Bot) -> discord.Guild:
def get_guild(self, bot: commands.Bot) -> discord.Guild:
guild = None
for g in bot.guilds:
if bot.dev_mode and g.name == 'OgmaBotDev':

View file

@ -1,33 +1,36 @@
import discord
import importlib
import os
from discord.ui import Button
from discord import Interaction, ButtonStyle
from libs.Roles import Roles
from data.models.Language import Language
class LanguageButton(discord.ui.Button):
class LanguageButton(Button):
def __init__(self, style=discord.ButtonStyle.blurple, label="Not Set"):
def __init__(self, style=ButtonStyle.blurple, label="Not Set"):
super().__init__()
self.style = style
self.label = label
self.disabled: bool = False
async def callback(self, interaction: discord.Interaction):
role = await Roles().get_role(interaction.guild, self.label)
async def callback(self, interaction: Interaction):
role = await Roles().get_guild_role(interaction.guild, self.label)
language = interaction.client.db.session.query(Language).filter(Language.label == self.label).first()
lang_frameworks = [fw.label for fw in language.frameworks]
framework_roles = await Roles().get_roles(interaction.guild, lang_frameworks)
frameworks_to_remove = list(set(framework_roles) & set(interaction.user.roles))
messages = []
if role in interaction.user.roles:
await Roles().remove_role(interaction.guild, self.label, interaction.user)
lang_frameworks = await self.bot.db.language_frameworks(interaction.client.db)
framework_roles = await Roles().get_roles(interaction.guild, lang_frameworks)
frameworks_to_remove = list(set(framework_roles) & set(interaction.user.roles))
for framework in frameworks_to_remove:
await Roles().remove_role(interaction.guild, framework.name, interaction.user)
await Roles().remove_role(framework, interaction.user)
messages.append(f"You are no longer associated with the {framework.name} framework group")
messages.append(f"You are no longer associated with the {self.label} language group")
else:
await Roles().add_role(interaction.guild, self.label, interaction.user)
messages.append(f"You are now associated with the {self.label} language group")
await interaction.response.send_message("\n".join(messages), delete_after=5, ephemeral=True)
async def get_language_frameworks(self, db):
language = db.session.query(Language).filter(Language.label == self.label).first()
lang_frameworks = [fw.label for fw in language.frameworks]
return lang_frameworks

View file

@ -1,20 +1,24 @@
from typing import override, List
import discord
class Roles:
async def get_role(self, guild, role):
async def get_guild_role(self, guild: discord.Guild, role: str) -> discord.Role:
fetched_role = None
for r in guild.roles:
if r.name == role:
fetched_role = r
return fetched_role
async def get_roles(self, guild, roles):
async def get_roles(self, guild: discord.Guild, roles: List[str]) -> List[discord.Role]:
found_roles = []
for r in guild.roles:
if r.name in roles:
found_roles.append(r)
return found_roles
async def add_role(self, guild, role, user):
fetched_role = await self.get_role(guild, role)
async def add_role(self, guild: discord.Guild, role: str, user: discord.Member|discord.User) -> bool:
fetched_role = await self.get_guild_role(guild, role)
role_added = False
if fetched_role is not None:
if fetched_role not in user.roles:
@ -22,11 +26,34 @@ class Roles:
role_added = True
return role_added
async def remove_role(self, guild, role, user):
fetched_role = await self.get_role(guild, role)
async def remove_role(self, guild: discord.Guild, role: str, user: discord.Member|discord.User) -> bool:
fetched_role = await self.get_guild_role(guild, role)
role_removed = False
if fetched_role is not None:
if fetched_role in user.roles:
await user.remove_roles(fetched_role)
role_removed = True
return role_removed
@override
async def remove_role(self, role: discord.Role, user: discord.Member|discord.User) -> bool:
role_removed = False
if role is not None and role in user.roles:
await user.remove_roles(role)
role_removed = True
return role_removed
async def add_guild_role(self, role):
role_added = False
if not await self.get_guild_role(role):
await self.bot.guild.create_role(name=role)
role_added = True
return role_added
async def remove_guild_role(self, guild, role):
role_removed = False
fetched_role = await self.get_guild_role(role)
if fetched_role:
await fetched_role.delete()
role_removed = True
return role_removed

View file

@ -17,7 +17,6 @@ class Twitch:
now = datetime.datetime.now()
future = now + timedelta(weeks=3)
unx_time = future.timestamp()
print("unix time:", unx_time)
return int(unx_time)
async def get_access_token(self):
@ -28,7 +27,6 @@ class Twitch:
}
response = requests.post("https://id.twitch.tv/oauth2/token", params=params)
print("response:", response.json())
self.access_token = response.json()["access_token"]
self.access_token_expires = await self.get_unix_time()
return self.access_token, self.access_token_expires

View file

@ -12,7 +12,7 @@ class View(discord.ui.View):
self.load_buttons()
def get_buttons(self):
if self.button_dir is None:
if self.button_dir is not None:
for filename in os.listdir(self.button_dir):
if filename.endswith(".py"):
class_name = filename[:-3]

View file

@ -11,7 +11,7 @@ class FrameworkButtons(View):
super().__init__()
def get_buttons(self):
frameworks = self.bot.db.session.query(Framework).all()
frameworks = self.bot.db.session.query(Framework).order_by(Framework.label).all()
for framework in frameworks:
btn = FrameworkButton(label=framework.label, language_role=framework.language.label)

View file

@ -11,7 +11,7 @@ class LanguageButtons(View):
super().__init__()
def get_buttons(self):
languages = self.bot.db.session.query(Language).all()
languages = self.bot.db.session.query(Language).order_by(Language.label).all()
for language in languages:
btn = LanguageButton(label=language.label)

View file

@ -13,7 +13,7 @@ class MatrixPenguinsButton(discord.ui.Button):
async def callback(self, interaction: discord.Interaction):
member = interaction.user
content = []
matrix_role = await Roles().get_role(interaction.guild, "Matrix Penguins")
matrix_role = await Roles().get_guild_role(interaction.guild, "Matrix Penguins")
if matrix_role in member.roles:
if await Roles().remove_role(interaction.guild, "Matrix Penguins", member):

View file

@ -13,7 +13,7 @@ class MatrixRefuserPenguinsButton(discord.ui.Button):
async def callback(self, interaction: discord.Interaction):
member = interaction.user
content = []
matrix_refuser_role = await Roles().get_role(interaction.guild, "Matrix-Refuser Penguins")
matrix_refuser_role = await Roles().get_guild_role(interaction.guild, "Matrix-Refuser Penguins")
if matrix_refuser_role in member.roles:
if await Roles().remove_role(interaction.guild, "Matrix-Refuser Penguins", member):