diff --git a/__main__.py b/__main__.py index 1d5184d..d5afd0f 100644 --- a/__main__.py +++ b/__main__.py @@ -48,7 +48,7 @@ class FunkyBot(commands.Bot): the_channel = await Channels().get_channel(self.guild, channel) await the_channel.purge() if message is not None: - await the_channel.send(message) + # await the_channel.send(message) ... async def close(self): diff --git a/cogs/roles.py b/cogs/roles.py index 9a3589d..0fb459d 100644 --- a/cogs/roles.py +++ b/cogs/roles.py @@ -1,11 +1,13 @@ from discord.ext import 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 views.MatrixButtons import MatrixButtons from views.LanguageButtons import LanguageButtons +from views.FrameworkButtons import FrameworkButtons class RolesCog(Cog): @@ -22,7 +24,8 @@ class RolesCog(Cog): if channel is not None: await channel.purge() await channel.send(embed=MatrixRolesEmbed(), view=MatrixButtons()) - await channel.send(embed=LanguageRolesEmbed(), view=LanguageButtons()) + await channel.send(embed=LanguageRolesEmbed(), view=LanguageButtons(self.bot)) + await channel.send(embed=FrameworkRolesEmbed(), view=FrameworkButtons(self.bot)) async def setup(bot): diff --git a/cogs/twitch_notifications.py b/cogs/twitch_notifications.py index 57db973..a6f0aae 100644 --- a/cogs/twitch_notifications.py +++ b/cogs/twitch_notifications.py @@ -6,6 +6,7 @@ from libs.Channels import Channels from libs.Guilds import Guilds from embeds.TwitchGameNotificationEmbed import TwitchGameNotificationEmbed from discord.ext import commands, tasks +from data.models.TwitchLiveNotification import TwitchLiveNotification class TwitchNotificationsCog(Cog): @@ -65,7 +66,11 @@ class TwitchNotificationsCog(Cog): if not channel: return - online_notifications, offline_notifications = await self.twitch.get_notifications(self.config[self.guild.name]['twitch']['watchlist']) + watchlist_notifications = self.bot.db.session.query(TwitchLiveNotification).all() + watchlist = [notify.username for notify in watchlist_notifications] + print("WATCHLIST", watchlist) + + online_notifications, offline_notifications = await self.twitch.get_notifications(watchlist) print("online_notifications", online_notifications) print("offline_notifications", offline_notifications) diff --git a/config.json b/config.json index 9286fa5..3aad36f 100644 --- a/config.json +++ b/config.json @@ -12,15 +12,12 @@ }, "OgmaBotDev": { "twitch": { - "client_id": "[redacted]", - "client_secret": "[redacted]", - "access_token": "[redacted]", - "channel_id": "[redacted]", + "client_id": "p41kt8by26lf849jz63sdgri1plpqq", + "client_secret": "2wh0kjznetlf1nw19tkoedd73ilje7", + "access_token": "j1ourtl76gti896r5lllcfjikrsjap", + "channel_id": 1385668221582970981, "channel_name": "live-penguins", - "expire_date": "[redacted]", - "watchlist": [ - "funkywaddle" - ] + "expire_date": 1752548253 } } } \ No newline at end of file diff --git a/data/TwitchLiveNotification.py b/data/TwitchLiveNotification.py deleted file mode 100644 index e69de29..0000000 diff --git a/data/bot.db b/data/bot.db new file mode 100644 index 0000000..35dbe0e Binary files /dev/null and b/data/bot.db differ diff --git a/data/models/Framework.py b/data/models/Framework.py new file mode 100644 index 0000000..e540964 --- /dev/null +++ b/data/models/Framework.py @@ -0,0 +1,11 @@ +from sqlalchemy import String, ForeignKey +from sqlalchemy.orm import mapped_column, Mapped, relationship +from libs.Db import Db + + +class Framework(Db.Model): + __tablename__ = 'frameworks' + id:Mapped[int] = mapped_column(primary_key=True) + language_id: Mapped[int] = mapped_column(ForeignKey('languages.id'), nullable=False) + label:Mapped[str] = mapped_column(String(64), nullable=False) + language:Mapped["Language"] = relationship(back_populates='frameworks') \ No newline at end of file diff --git a/data/models/Language.py b/data/models/Language.py new file mode 100644 index 0000000..216f883 --- /dev/null +++ b/data/models/Language.py @@ -0,0 +1,11 @@ +from sqlalchemy import String +from sqlalchemy.orm import mapped_column, Mapped, relationship +from libs.Db import Db +from typing import List + + +class Language(Db.Model): + __tablename__ = 'languages' + id:Mapped[int] = mapped_column(primary_key=True) + label:Mapped[str] = mapped_column(String(64), nullable=False) + frameworks:Mapped[List["Framework"]] = relationship(back_populates='language') \ No newline at end of file diff --git a/data/models/TwitchLiveNotification.py b/data/models/TwitchLiveNotification.py new file mode 100644 index 0000000..10fd020 --- /dev/null +++ b/data/models/TwitchLiveNotification.py @@ -0,0 +1,11 @@ +from sqlalchemy import String +from sqlalchemy.orm import mapped_column, Mapped +from libs.Db import Db + + +class TwitchLiveNotification(Db.Model): + __tablename__ = 'twitch_live_notifications' + id:Mapped[int] = mapped_column(primary_key=True) + username:Mapped[str] = mapped_column(String(64), nullable=False) + + diff --git a/embeds/FrameworkRolesEmbed.py b/embeds/FrameworkRolesEmbed.py new file mode 100644 index 0000000..a1d0302 --- /dev/null +++ b/embeds/FrameworkRolesEmbed.py @@ -0,0 +1,20 @@ +import discord +from discord import Embed + + +class FrameworkRolesEmbed(Embed): + + def __init__(self): + super().__init__() + self.title = "Framework Roles" + self.description = ("Please select your choice of frameworks to be associated with." + "\nThese roles provide no functionality other than being alerted when it is tagged." + "\nThis is mainly for when someone shares information about the programming framework" + " or is in need of help and asking a question") + self.add_field( + name="Additional", + value="Adding a framework will also add the associated language." + "\nRemoving a framework will NOT remove the associated language", + inline=False) + self.add_field(name="Instructions", value="Click to add. Click again to remove.", inline=False) + self.color = discord.Color.purple() \ No newline at end of file diff --git a/embeds/MatrixRolesEmbed.py b/embeds/MatrixRolesEmbed.py index 495a262..6e33f6f 100644 --- a/embeds/MatrixRolesEmbed.py +++ b/embeds/MatrixRolesEmbed.py @@ -7,9 +7,8 @@ class MatrixRolesEmbed(Embed): def __init__(self): super().__init__() self.title = "Matrix Roles" - self.description = ("Please select your choice between these two roles. " - "\nAlso, clicking on one of them will remove the other, if you have it. " - "\n(Clicking Matrix Penguins, will remove Matrix-Refuser Penguins and add Matrix Penguins).") + self.description = "Please select your choice between these two roles." + self.add_field(name="Additional", value="Adding one of these, will remove the other", inline=False) self.add_field(name="Instructions", value="Click to add. Click again to remove.", inline=False) self.add_field(name="Matrix Penguins", value="By accepting this role, you will be alerted whenever someone tags the role in one of the Tech channels", inline=True) diff --git a/libs/Db.py b/libs/Db.py index 642bf61..e2797f8 100644 --- a/libs/Db.py +++ b/libs/Db.py @@ -1,26 +1,29 @@ from sqlalchemy import create_engine -from sqlalchemy.sql import sqltypes -from sqlalchemy.orm import declarative_base, sessionmaker -from enum import Enum +from sqlalchemy.orm import DeclarativeBase, Session +import os +import importlib + class Db: - class DataTypes(Enum): - Integer = sqltypes.Integer - Float = sqltypes.Float - Boolean = sqltypes.Boolean - Text = sqltypes.Text - Date = sqltypes.Date - Time = sqltypes.Time - DateTime = sqltypes.DateTime - String = sqltypes.String + class Model(DeclarativeBase): + __table_args__ = dict(extend_existing=True) + pass def __init__(self, db, echo=False): self.db = db self.echo_output = echo self.Engine = self.create_engine() - self.Model = declarative_base() - self.session = sessionmaker(bind=self.Engine) + self.session = Session(bind=self.Engine) + self.import_models() + self.create_tables() def create_engine(self): return create_engine(self.db, echo=self.echo_output) + def create_tables(self): + self.Model.metadata.create_all(bind=self.Engine) + + def import_models(self): + for filename in os.listdir('./data/models'): + if filename.endswith('.py'): + importlib.import_module('.' + filename[:-3], 'data.models') diff --git a/libs/FrameworkButton.py b/libs/FrameworkButton.py new file mode 100644 index 0000000..b7d829d --- /dev/null +++ b/libs/FrameworkButton.py @@ -0,0 +1,27 @@ +import discord +from libs.Roles import Roles + + +class FrameworkButton(discord.ui.Button): + + def __init__(self, style=discord.ButtonStyle.green, label="Not Set", language_role=None): + super().__init__() + self.style = style + self.label = label + self.disabled: bool = False + 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) + messages = [] + if role in interaction.user.roles: + await Roles().remove_role(interaction.guild, self.label, interaction.user) + messages.append(f"You are no longer associated with the {self.label} framework group.") + else: + if language_role is not None and language_role not in interaction.user.roles: + await Roles().add_role(interaction.guild, self.language_role, interaction.user) + messages.append(f"You are now associated with the {self.language_role} language group.") + await Roles().add_role(interaction.guild, self.label, interaction.user) + messages.append(f"You are now associated with the {self.label} framework group.") + await interaction.response.send_message("\n".join(messages), delete_after=5, ephemeral=True) \ No newline at end of file diff --git a/libs/LanguageButton.py b/libs/LanguageButton.py index a03ab3f..3392cc7 100644 --- a/libs/LanguageButton.py +++ b/libs/LanguageButton.py @@ -1,22 +1,33 @@ import discord +import importlib +import os from libs.Roles import Roles +from data.models.Language import Language class LanguageButton(discord.ui.Button): - def __init__(self): + def __init__(self, style=discord.ButtonStyle.blurple, label="Not Set"): super().__init__() - self.style = discord.ButtonStyle.blurple - self.label = "Not Set" + 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) - message = None + + 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) - message = f"You are no longer associated with the {self.label} language group" + for framework in frameworks_to_remove: + await Roles().remove_role(interaction.guild, framework.name, 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) - message = f"You are now associated with the {self.label} language group" - await interaction.response.send_message(message, delete_after=5, ephemeral=True) \ No newline at end of file + 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) diff --git a/libs/View.py b/libs/View.py new file mode 100644 index 0000000..dd96d51 --- /dev/null +++ b/libs/View.py @@ -0,0 +1,28 @@ +import discord +import importlib +import os + +class View(discord.ui.View): + + def __init__(self, button_dir=None): + super().__init__(timeout=None) + self.button_dir = button_dir + self.buttons = [] + self.get_buttons() + self.load_buttons() + + def get_buttons(self): + if self.button_dir is None: + for filename in os.listdir(self.button_dir): + if filename.endswith(".py"): + class_name = filename[:-3] + module = self.button_dir.replace("./", "", 1) + module = module.replace("/", ".") + btn_module = importlib.import_module(f"{module}.{class_name}") + btn_class = getattr(btn_module, class_name) + self.buttons.append(btn_class()) + return self.buttons + + def load_buttons(self): + for btn in self.buttons: + self.add_item(btn) \ No newline at end of file diff --git a/views/FrameworkButtons.py b/views/FrameworkButtons.py new file mode 100644 index 0000000..b39cc1f --- /dev/null +++ b/views/FrameworkButtons.py @@ -0,0 +1,19 @@ +import discord + +from libs.View import View +from data.models.Framework import Framework +from libs.FrameworkButton import FrameworkButton + +class FrameworkButtons(View): + + def __init__(self, bot): + self.bot = bot + super().__init__() + + def get_buttons(self): + frameworks = self.bot.db.session.query(Framework).all() + + for framework in frameworks: + btn = FrameworkButton(label=framework.label, language_role=framework.language.label) + self.buttons.append(btn) + return self.buttons \ No newline at end of file diff --git a/views/LanguageButtons.py b/views/LanguageButtons.py index 940be36..f4c3be4 100644 --- a/views/LanguageButtons.py +++ b/views/LanguageButtons.py @@ -1,18 +1,19 @@ import discord -from views.items.JSLanguageButton import JSLanguageButton -from views.items.PHPLanguageButton import PHPLanguageButton -from views.items.RubyLanguageButton import RubyLanguageButton -from views.items.CSLanguageButton import CSLanguageButton -from views.items.JavaLanguageButton import JavaLanguageButton -from views.items.PythonLanguageButton import PythonLanguageButton -class LanguageButtons(discord.ui.View): +from libs.View import View +from data.models.Language import Language +from libs.LanguageButton import LanguageButton - def __init__(self): - super().__init__(timeout=None) - self.add_item(PHPLanguageButton()) - self.add_item(JSLanguageButton()) - self.add_item(RubyLanguageButton()) - self.add_item(CSLanguageButton()) - self.add_item(JavaLanguageButton()) - self.add_item(PythonLanguageButton()) +class LanguageButtons(View): + + def __init__(self, bot): + self.bot = bot + super().__init__() + + def get_buttons(self): + languages = self.bot.db.session.query(Language).all() + + for language in languages: + btn = LanguageButton(label=language.label) + self.buttons.append(btn) + return self.buttons diff --git a/views/MatrixButtons.py b/views/MatrixButtons.py index cc14a7c..fa7ee54 100644 --- a/views/MatrixButtons.py +++ b/views/MatrixButtons.py @@ -1,51 +1,6 @@ -import discord -from libs.Roles import Roles -from views.items.MatrixPenguinsButton import MatrixPenguinsButton -from views.items.MatrixRefuserPenguinsButton import MatrixRefuserPenguinsButton +from libs.View import View -class MatrixButtons(discord.ui.View): +class MatrixButtons(View): def __init__(self): - super().__init__(timeout=None) - self.matrix_role = None - self.matrix_refuser_role = None - self.add_item(MatrixPenguinsButton()) - self.add_item(MatrixRefuserPenguinsButton()) - - # async def set_roles(self, guild): - # self.matrix_role = await Roles().get_role(guild, "Matrix Penguins") - # self.matrix_refuser_role = await Roles().get_role(guild, "Matrix-Refuser Penguins") - # - # @discord.ui.button(label="Matrix Penguins", style=discord.ButtonStyle.blurple) - # async def matrix_click(self, interaction: discord.Interaction, button): - # member = interaction.user - # content = [] - # await self.set_roles(interaction.guild) - # - # if self.matrix_role in member.roles: - # if await Roles().remove_role(interaction.guild, "Matrix Penguins", member): - # content.append("Matrix Penguins has been removed") - # else: - # if await Roles().remove_role(interaction.guild, "Matrix-Refuser Penguins", member): - # content.append("Matrix-Refuser Penguins has been removed") - # if await Roles().add_role(interaction.guild, "Matrix Penguins", member): - # content.append("Matrix Penguins has been added") - # - # await interaction.response.send_message(" and ".join(content), delete_after=5, ephemeral=True) - # - # @discord.ui.button(label="Matrix-Refuser Penguin", style=discord.ButtonStyle.red) - # async def refuser_click(self, interaction: discord.Interaction, button): - # member = interaction.user - # content = [] - # await self.set_roles(interaction.guild) - # - # if self.matrix_refuser_role in member.roles: - # if await Roles().remove_role(interaction.guild, "Matrix-Refuser Penguins", member): - # content.append("Matrix-Refuser Penguin has been removed") - # else: - # if await Roles().remove_role(interaction.guild, "Matrix Penguins", member): - # content.append("Matrix Penguins has been removed") - # if await Roles().add_role(interaction.guild, "Matrix-Refuser Penguins", member): - # content.append("Matrix-Refuser Penguins has been added") - # - # await interaction.response.send_message(" and ".join(content), delete_after=5, ephemeral=True) \ No newline at end of file + super().__init__("./views/items/matrix_buttons") \ No newline at end of file diff --git a/views/items/CSLanguageButton.py b/views/items/CSLanguageButton.py deleted file mode 100644 index cddea2a..0000000 --- a/views/items/CSLanguageButton.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from libs.LanguageButton import LanguageButton - - -class CSLanguageButton(LanguageButton): - - def __init__(self): - super().__init__() - self.style = discord.ButtonStyle.blurple - self.label = "C#" - self.disabled: bool = False diff --git a/views/items/JSLanguageButton.py b/views/items/JSLanguageButton.py deleted file mode 100644 index 7f18608..0000000 --- a/views/items/JSLanguageButton.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from libs.LanguageButton import LanguageButton - - -class JSLanguageButton(LanguageButton): - - def __init__(self): - super().__init__() - self.style = discord.ButtonStyle.blurple - self.label = "JavaScript" - self.disabled: bool = False diff --git a/views/items/JavaLanguageButton.py b/views/items/JavaLanguageButton.py deleted file mode 100644 index adb6c64..0000000 --- a/views/items/JavaLanguageButton.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from libs.LanguageButton import LanguageButton - - -class JavaLanguageButton(LanguageButton): - - def __init__(self): - super().__init__() - self.style = discord.ButtonStyle.blurple - self.label = "Java" - self.disabled: bool = False diff --git a/views/items/PHPLanguageButton.py b/views/items/PHPLanguageButton.py deleted file mode 100644 index 5ddcc78..0000000 --- a/views/items/PHPLanguageButton.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from libs.LanguageButton import LanguageButton - - -class PHPLanguageButton(LanguageButton): - - def __init__(self): - super().__init__() - self.style = discord.ButtonStyle.blurple - self.label = "PHP" - self.disabled: bool = False diff --git a/views/items/PythonLanguageButton.py b/views/items/PythonLanguageButton.py deleted file mode 100644 index d3a839a..0000000 --- a/views/items/PythonLanguageButton.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from libs.LanguageButton import LanguageButton - - -class PythonLanguageButton(LanguageButton): - - def __init__(self): - super().__init__() - self.style = discord.ButtonStyle.blurple - self.label = "Python" - self.disabled: bool = False diff --git a/views/items/RubyLanguageButton.py b/views/items/RubyLanguageButton.py deleted file mode 100644 index cbe7126..0000000 --- a/views/items/RubyLanguageButton.py +++ /dev/null @@ -1,11 +0,0 @@ -import discord -from libs.LanguageButton import LanguageButton - - -class RubyLanguageButton(LanguageButton): - - def __init__(self): - super().__init__() - self.style = discord.ButtonStyle.blurple - self.label = "Ruby" - self.disabled: bool = False diff --git a/views/items/MatrixPenguinsButton.py b/views/items/matrix_buttons/MatrixPenguinsButton.py similarity index 100% rename from views/items/MatrixPenguinsButton.py rename to views/items/matrix_buttons/MatrixPenguinsButton.py diff --git a/views/items/MatrixRefuserPenguinsButton.py b/views/items/matrix_buttons/MatrixRefuserPenguinsButton.py similarity index 100% rename from views/items/MatrixRefuserPenguinsButton.py rename to views/items/matrix_buttons/MatrixRefuserPenguinsButton.py