-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from Tibo-Ulens/config
Add per-server configuration
- Loading branch information
Showing
9 changed files
with
205 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
"""create_config_table | ||
Revision ID: ce233a03ec25 | ||
Revises: 6f04bc4e4691 | ||
Create Date: 2022-12-22 04:10:27.310547 | ||
""" | ||
from alembic import op | ||
import sqlalchemy as sa | ||
|
||
|
||
# revision identifiers, used by Alembic. | ||
revision = "ce233a03ec25" | ||
down_revision = "6f04bc4e4691" | ||
branch_labels = None | ||
depends_on = None | ||
|
||
|
||
def upgrade() -> None: | ||
op.create_table( | ||
"config", | ||
sa.Column("guild_id", sa.Text, primary_key=True), | ||
sa.Column("verified_role", sa.Text, nullable=True), | ||
sa.Column("verification_channel", sa.Text, nullable=True), | ||
sa.UniqueConstraint("guild_id", name="unique_guild_id"), | ||
sa.UniqueConstraint("verified_role", name="unique_verified_role"), | ||
sa.UniqueConstraint("verification_channel", name="unique_verification_channel"), | ||
) | ||
|
||
|
||
def downgrade() -> None: | ||
op.drop_table("config") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
from discord import app_commands, Interaction, Role, TextChannel | ||
from discord.ext import commands | ||
from discord.ext.commands import Cog, command, Context | ||
import logging | ||
|
||
from bot.bot import Bot | ||
from bot.models.config import Config as ConfigModel | ||
from bot import constants | ||
|
||
|
||
logger = logging.getLogger("bot") | ||
|
||
|
||
class Config(Cog): | ||
config_group = app_commands.Group(name="config", description="bot configuration") | ||
|
||
def __init__(self, bot: Bot) -> None: | ||
self.bot = bot | ||
|
||
@command(name="freud_sync") | ||
@commands.guild_only() | ||
@commands.is_owner() | ||
async def sync(self, ctx: Context): | ||
ctx.bot.tree.copy_global_to(guild=ctx.guild) | ||
synced = await ctx.bot.tree.sync(guild=ctx.guild) | ||
|
||
logger.info(f"synced {len(synced)} commands to {ctx.guild.name}") | ||
await ctx.reply(f"synced {len(synced)} commands to the current guild") | ||
|
||
@app_commands.guild_only() | ||
@config_group.command( | ||
name="verified_role", | ||
description="Set the role to be applied to members once they have been verified", | ||
) | ||
@app_commands.describe(role="The role to be applied") | ||
async def set_verified_role(self, ia: Interaction, role: Role): | ||
guild_config = await ConfigModel.get_or_create(ia.guild_id) | ||
|
||
guild_config.verified_role = str(role.id) | ||
await guild_config.save() | ||
|
||
logger.info(f"set verified role to {role.id} for guild {ia.guild_id}") | ||
await ia.response.send_message(f"set verified role to <@&{role.id}>") | ||
|
||
@app_commands.guild_only() | ||
@config_group.command( | ||
name="verification_channel", | ||
description="Set the channel in which the /verify command can be used", | ||
) | ||
@app_commands.describe(channel="The channel to select") | ||
async def set_verification_channel(self, ia: Interaction, channel: TextChannel): | ||
guild_config = await ConfigModel.get_or_create(ia.guild_id) | ||
|
||
guild_config.verification_channel = str(channel.id) | ||
await guild_config.save() | ||
|
||
logger.info(f"set verification channel to {channel.id} for guild {ia.guild_id}") | ||
await ia.response.send_message(f"set verification channel to <#{channel.id}>") | ||
|
||
|
||
async def setup(bot: Bot): | ||
await bot.add_cog(Config(bot)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,12 +11,13 @@ | |
from bot import constants | ||
from bot.bot import Bot | ||
from bot.models.profile import Profile | ||
from bot.models.config import Config | ||
|
||
|
||
EMAIL_REGEX = re.compile(r"^[^\s@]+@ugent\.be$") | ||
CODE_REGEX = re.compile(r"^['|<]?([a-z0-9]{32})[>|']?$") | ||
|
||
EMAIL_MESSAGE = "From: [email protected]\nTo: {to}\nSubject: Psychology Discord Verification Code\n\nYour verification code for the psychology discord server is '{code}'" | ||
EMAIL_MESSAGE = "From: {from_}\nTo: {to}\nSubject: Psychology Discord Verification Code\n\nYour verification code for the psychology discord server is '{code}'" | ||
|
||
|
||
logger = logging.getLogger("bot") | ||
|
@@ -29,7 +30,7 @@ def __init__(self, bot: Bot) -> None: | |
|
||
@staticmethod | ||
def send_confirmation_email(to: str, code: str): | ||
message = EMAIL_MESSAGE.format(to=to, code=code) | ||
message = EMAIL_MESSAGE.format(from_=constants.SMTP_USER, to=to, code=code) | ||
|
||
email_logger.info(f"sending email to {to}...") | ||
server = smtplib.SMTP("smtp.gmail.com", 587) | ||
|
@@ -141,29 +142,62 @@ async def verify_code(self, iactn: Interaction, code: str): | |
|
||
return | ||
|
||
profile.confirmation_code = None | ||
await profile.save() | ||
|
||
user = iactn.user | ||
|
||
logger.info(f"[{author_id}] {iactn.user.name} verified succesfully") | ||
await user.add_roles( | ||
discord.utils.get(user.guild.roles, name=constants.VERIFIED_ROLE) | ||
) | ||
|
||
config = await Config.get(iactn.guild_id) | ||
if config is None: | ||
logger.error(f"no config for guild {iactn.guild_id} exists yet") | ||
await iactn.response.send_message( | ||
"The bot has not been set up properly yet, please notify a server admin" | ||
) | ||
return | ||
|
||
verified_role = config.verified_role | ||
if verified_role is None: | ||
logger.error(f"no verified role for guild {iactn.guild_id} exists yet") | ||
await iactn.response.send_message( | ||
"The bot has not been set up properly yet, please notify a server admin" | ||
) | ||
return | ||
|
||
await user.add_roles(discord.utils.get(user.guild.roles, id=int(verified_role))) | ||
await iactn.response.send_message( | ||
"You have verified succesfully! Welcome to the psychology server" | ||
"You have verified succesfully! Welcome to the server" | ||
) | ||
|
||
profile.confirmation_code = None | ||
await profile.save() | ||
|
||
@app_commands.command( | ||
name="verify", description="Verify that you are a true UGentStudent" | ||
) | ||
@app_commands.describe(argument="Your UGent email or verification code") | ||
async def verify(self, iactn: Interaction, argument: str): | ||
author_id = iactn.user.id | ||
|
||
if str(iactn.channel_id) != constants.VERIFY_CHANNEL: | ||
config = await Config.get(iactn.guild_id) | ||
if config is None: | ||
logger.error(f"no config for guild {iactn.guild_id} exists yet") | ||
await iactn.response.send_message( | ||
"The bot has not been set up properly yet, please notify a server admin" | ||
) | ||
return | ||
|
||
verification_channel = config.verification_channel | ||
if verification_channel is None: | ||
logger.error( | ||
f"no verification channel for guild {iactn.guild_id} exists yet" | ||
) | ||
await iactn.response.send_message( | ||
"The bot has not been set up properly yet, please notify a server admin" | ||
) | ||
return | ||
|
||
if str(iactn.channel_id) != verification_channel: | ||
await iactn.response.send_message( | ||
f"This command can only be used in <#{constants.VERIFY_CHANNEL}>", | ||
f"This command can only be used in <#{verification_channel}>", | ||
ephemeral=True, | ||
) | ||
return | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import logging | ||
from typing import Optional | ||
from sqlalchemy import Column, Text | ||
from sqlalchemy.future import select | ||
from sqlalchemy.orm import Query | ||
|
||
from bot.models import Base, Model, session_factory | ||
|
||
|
||
logger = logging.getLogger("models") | ||
|
||
|
||
class Config(Base, Model): | ||
__tablename__ = "config" | ||
|
||
guild_id = Column(Text, primary_key=True) | ||
verified_role = Column(Text, unique=True, nullable=False) | ||
verification_channel = Column(Text, unique=True, nullable=False) | ||
|
||
def __repr__(self) -> str: | ||
return f"<{self.__class__.__name__}> guild_id: {self.guild_id} verified_role: {self.verified_role} verification_channel: {self.verification_channel}" | ||
|
||
@classmethod | ||
async def get(cls, id_: int) -> Optional["Config"]: | ||
"""Find a config given its guild ID""" | ||
|
||
async with session_factory() as session: | ||
result: Query = await session.execute( | ||
select(cls).where(cls.guild_id == str(id_)) | ||
) | ||
|
||
r = result.first() | ||
if r is None: | ||
return None | ||
else: | ||
return r[0] | ||
|
||
@classmethod | ||
async def get_or_create(cls, id_: int) -> "Config": | ||
"""Find a config given its guild ID, or create an empty config if it does not exist""" | ||
|
||
async with session_factory() as session: | ||
result: Query = await session.execute( | ||
select(cls).where(cls.guild_id == str(id_)) | ||
) | ||
|
||
r = result.first() | ||
if r is None: | ||
logger.info(f"created new config for guild {id_}") | ||
return await Config.create(guild_id=str(id_), verified_role=None) | ||
else: | ||
return r[0] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters