-
Notifications
You must be signed in to change notification settings - Fork 2
Added a clash of code module #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
666c224
b085cfd
a48f76f
f79c721
624a942
e141ba0
54476dc
ff6e295
d127906
3df4875
b91c8ae
da1fd32
065b1db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,3 +3,4 @@ psycopg2-binary==2.9.3 | |
| python-dotenv==0.20.0 | ||
| PyYAML==6.0 | ||
| SQLAlchemy==1.4.37 | ||
| requests==2.30.0 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,7 @@ | |
| from discord import app_commands | ||
|
|
||
| from . import session_maker | ||
| from .services import clash_of_code_helper | ||
| from .tables import CustomCommand, select | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
|
|
@@ -27,6 +28,8 @@ def __init__(self, *, intents: discord.Intents): | |
| async def on_ready(self): | ||
| logger.info(f"Logged on as {self.user}") | ||
|
|
||
| clash_of_code_helper.resume_update_loops() | ||
|
|
||
| for com in self.tree.get_commands(): | ||
| logger.debug(f"Globals {com} name : {com.name}") | ||
|
|
||
|
|
@@ -56,13 +59,21 @@ async def command(interaction: discord.Interaction): | |
| if active: | ||
| await self.tree.sync(guild=guild) | ||
|
|
||
| async def on_member_join(self, member): | ||
| async def on_member_join(self, member: discord.Member): | ||
| logger.debug(f"Member joined : {member}!") | ||
| if not (await member.guild.fetch_member(self.user.id)).guild_permissions.manage_nicknames: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is already a permission error handler, either we verify everywhere, either we are only doing it in the handler, but we must be consistant
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tell this to all the errors that were occuring in the console |
||
| logger.debug(f"Bot doesn't have manage nicknames permission on {member.guild}") | ||
| return | ||
|
|
||
| if not re.match(regex_name, member.display_name): | ||
| await member.edit(nick=f"{random.choice(names).capitalize()}{random.choice(adjectives).capitalize()}") | ||
|
|
||
| async def on_member_update(self, before, after): | ||
| async def on_member_update(self, before: discord.Member, after: discord.Member): | ||
| logger.debug(f"Member update : {after}!") | ||
| if not (await after.guild.fetch_member(self.user.id)).guild_permissions.manage_nicknames: | ||
| logger.debug(f"Bot doesn't have manage nicknames permission on {after.guild}") | ||
| return | ||
|
|
||
| if not after.guild_permissions.manage_nicknames and not re.match(regex_name, after.display_name): | ||
| await after.edit(nick=f"{random.choice(names).capitalize()}{random.choice(adjectives).capitalize()}") | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,3 +2,4 @@ | |
| from . import presentation | ||
| from . import custom | ||
| from . import config | ||
| from . import clash_of_code | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| import logging | ||
| import re | ||
| from typing import Tuple | ||
|
|
||
| import discord | ||
| from discord import app_commands | ||
| from sqlalchemy import select | ||
|
|
||
| from .. import vesta_client, session_maker, lang_file | ||
| from ..exceptions import CommandRuntimeError | ||
| from ..services import clash_of_code_helper, State | ||
| from ..services.clash_of_code_helper import start_update_loop | ||
| from ..tables import ClashOfCodeGuildGame | ||
| from ..tables import Guild | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
| session = session_maker() | ||
|
|
||
| regex_clash_of_code_game = r"^(https://|)(www.|)codingame.com/clashofcode/clash/[^/]+(/|)$" | ||
|
|
||
|
|
||
| @vesta_client.tree.command(name="clash-of-code", description="Invites users with the \"Clash of Code\" role to play") | ||
| @app_commands.describe(link="The link to the Clash of Code game") | ||
| async def clash_of_code(interaction: discord.Interaction, link: str): | ||
| if not re.match(regex_clash_of_code_game, link): | ||
| await _send_error(interaction, "coc_invalid_link") | ||
| return | ||
|
|
||
| try: | ||
| (game, game_id) = _get_game(link) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why some _ in the start of the function? That's not a library, and that's not some not-to-call methods of an object
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will fix |
||
| guild_game = _get_guild_game(interaction) | ||
| (guild, role, channel) = _get_guild(interaction) | ||
| except CommandRuntimeError as e: | ||
| await _send_error(interaction, e.message) | ||
| return | ||
|
|
||
| view = discord.ui.View() | ||
| view.add_item(discord.ui.Button( | ||
| label=lang_file.get("coc_game_join", interaction.guild), | ||
| url=game.link, | ||
| emoji="🎮" | ||
| )) | ||
|
|
||
| embed = game.embed(interaction.guild) | ||
| embed.set_author(name=interaction.user.name, icon_url=interaction.user.avatar.url) | ||
|
|
||
| announcement_message = await channel.send( | ||
| content=f"{role.mention} {lang_file.get('coc_game_invite', interaction.guild)}", | ||
|
RedsTom marked this conversation as resolved.
|
||
| embed=embed, | ||
| view=view | ||
| ) | ||
|
|
||
| guild_game.last_clash_id = game_id | ||
| guild_game.announcement_message_id = announcement_message.id | ||
| session.commit() | ||
|
|
||
| await interaction.response.send_message( | ||
| lang_file.get("coc_successfully_invited", interaction.guild), | ||
| ephemeral=True | ||
| ) | ||
|
|
||
| start_update_loop(message=announcement_message, guild=interaction.guild) | ||
|
|
||
|
|
||
| def _get_game(link: str): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. don't put some _ in front of the function name, as said before
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will fix |
||
| game_id = clash_of_code_helper.game_id_from_link(link) | ||
| game = clash_of_code_helper.fetch(game_id) | ||
|
|
||
| if not game: | ||
| raise CommandRuntimeError("coc_invalid_link") | ||
| if game.state != State.PENDING: | ||
| raise CommandRuntimeError("coc_game_already_started") | ||
|
|
||
| return game, game_id | ||
|
|
||
|
|
||
| def _get_guild_game(interaction: discord.Interaction) -> ClashOfCodeGuildGame: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if you have only one clash of code game per guild, maybe just use a foreign key in the guild table to a clash of code game
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will fix |
||
| r = select(ClashOfCodeGuildGame).where(ClashOfCodeGuildGame.guild_id == interaction.guild.id) | ||
| guild_game: ClashOfCodeGuildGame = session.scalar(r) | ||
|
|
||
| if guild_game and not guild_game.can_start_new(): | ||
| raise CommandRuntimeError("coc_already_in_progress") | ||
|
|
||
| if not guild_game: | ||
| logger.debug(f"Creating new guild game for guild {interaction.guild_id}") | ||
| guild_game = ClashOfCodeGuildGame(guild_id=interaction.guild_id) | ||
| session.add(guild_game) | ||
|
|
||
| return guild_game | ||
|
|
||
|
|
||
| def _get_guild(interaction: discord.Interaction) -> Tuple[Guild, discord.Role, discord.TextChannel]: | ||
| r = select(Guild).where(Guild.id == interaction.guild_id) | ||
| guild: Guild = session.scalar(r) | ||
|
|
||
| if not guild: | ||
| logger.debug(f"Add guild {interaction.guild_id} to the database") | ||
| guild = Guild(id=interaction.guild_id, name=interaction.guild.name) | ||
| session.add(guild) | ||
|
|
||
| return guild, _get_role(guild, interaction), _get_channel(guild, interaction) | ||
|
|
||
|
|
||
| def _get_role(guild: Guild, interaction: discord.Interaction) -> discord.Role: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there really a need to separate the get_role and get_channel functions from the code?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not really, but always good to split as much as possible to fulfill single responsibility |
||
| if not guild.coc_role: | ||
| raise CommandRuntimeError("coc_role_not_set") | ||
|
|
||
| role = interaction.guild.get_role(guild.coc_role) | ||
| if not role: | ||
| raise CommandRuntimeError("coc_role_not_found") | ||
|
|
||
| return role | ||
|
|
||
|
|
||
| def _get_channel(guild: Guild, interaction: discord.Interaction) -> discord.TextChannel: | ||
| if not guild.coc_channel: | ||
| raise CommandRuntimeError("coc_channel_not_set") | ||
|
|
||
| channel = interaction.guild.get_channel(guild.coc_channel) | ||
| if not channel: | ||
| raise CommandRuntimeError("coc_channel_not_found") | ||
|
|
||
| return channel | ||
|
|
||
|
|
||
| async def _send_error(interaction: discord.Interaction, key: str): | ||
| msg = lang_file.get(key, interaction.guild) | ||
| await interaction.response.send_message(f"❌ {msg}", ephemeral=True) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why letting the lang file named as "lang" and renaming the lang as "lang file"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ask @adraug