1
Fork 0
mirror of https://github.com/wlinator/luminara.git synced 2024-10-02 18:23:12 +00:00

Refactor: Code cleanup and consistency

This commit is contained in:
wlinator 2024-07-17 07:47:26 -04:00
parent 6d85960fca
commit 67c423fc7e
59 changed files with 914 additions and 389 deletions

View file

@ -1,15 +1,15 @@
TOKEN=
INSTANCE=BETA
INSTANCE=
OWNER_IDS=
XP_GAIN_PER_MESSAGE=1
XP_GAIN_COOLDOWN=8
XP_GAIN_PER_MESSAGE=
XP_GAIN_COOLDOWN=
DBX_OAUTH2_REFRESH_TOKEN=
DBX_APP_KEY=
DBX_APP_SECRET=
MARIADB_USER=wlinator
MARIADB_USER=
MARIADB_PASSWORD=
MARIADB_ROOT_PASSWORD=
MARIADB_DATABASE=lumidb
MARIADB_DATABASE=

33
.pre-commit-config.yaml Normal file
View file

@ -0,0 +1,33 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-yaml
- id: check-json
- id: check-toml
- repo: https://github.com/asottile/add-trailing-comma
rev: v3.1.0
hooks:
- id: add-trailing-comma
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.2
hooks:
# Run the linter.
- id: ruff
args: [--fix]
# Run the formatter.
- id: ruff-format
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.4
hooks:
- id: gitleaks
- repo: https://github.com/hija/clean-dotenv
rev: v0.0.7
hooks:
- id: clean-dotenv
exclude: ".archive/"

View file

@ -46,7 +46,8 @@ class LumiBot(bridge.Bot):
@staticmethod
async def convert_to_user(
ctx: commands.Context | bridge.Context, user_id: int
ctx: commands.Context | bridge.Context,
user_id: int,
) -> Optional[discord.User]:
"""
Converts a user ID to a User object.
@ -73,7 +74,8 @@ class LumiBot(bridge.Bot):
@staticmethod
async def convert_to_emoji(
ctx: commands.Context | bridge.Context, emoji: str
ctx: commands.Context | bridge.Context,
emoji: str,
) -> Optional[discord.Emoji]:
"""
Converts a emoji to an Emoji object.
@ -98,7 +100,8 @@ class LumiBot(bridge.Bot):
@staticmethod
async def convert_to_text_channel(
ctx: commands.Context | bridge.Context, channel_id: int
ctx: commands.Context | bridge.Context,
channel_id: int,
) -> Optional[discord.TextChannel]:
"""
Converts a channel ID to a TextChannel object.
@ -127,7 +130,8 @@ class LumiBot(bridge.Bot):
@staticmethod
async def convert_to_member(
ctx: commands.Context, user_id: int
ctx: commands.Context,
user_id: int,
) -> Optional[discord.Member]:
"""
Converts a user ID to a Member object.
@ -155,7 +159,8 @@ class LumiBot(bridge.Bot):
@staticmethod
async def get_or_fetch_channel(
guild: discord.Guild, channel_id: int
guild: discord.Guild,
channel_id: int,
) -> Optional[discord.abc.GuildChannel]:
"""
Retrieves a channel from the guild's cache or fetches it from the API if not found.
@ -179,7 +184,8 @@ class LumiBot(bridge.Bot):
@staticmethod
async def get_or_fetch_member(
guild: discord.Guild, user_id: int
guild: discord.Guild,
user_id: int,
) -> Optional[discord.Member]:
"""
Retrieves a member from the guild's cache or fetches them from the API if not found.

View file

@ -34,7 +34,8 @@ async def on_command_error(ctx, error):
elif isinstance(error, commands.CommandOnCooldown):
author_text = CONST.STRINGS["error_command_cooldown_author"]
description = CONST.STRINGS["error_command_cooldown_description"].format(
int(error.retry_after // 60), int(error.retry_after % 60)
int(error.retry_after // 60),
int(error.retry_after % 60),
)
ephemeral = True
@ -62,13 +63,13 @@ async def on_command_error(ctx, error):
elif isinstance(error, LumiExceptions.LumiException):
author_text = CONST.STRINGS["error_lumi_exception_author"]
description = CONST.STRINGS["error_lumi_exception_description"].format(
str(error)
str(error),
)
elif isinstance(error, LumiExceptions.NotAllowedInChannel):
author_text = CONST.STRINGS["error_not_allowed_in_channel_author"]
description = CONST.STRINGS["error_not_allowed_in_channel_description"].format(
error.command_channel.mention
error.command_channel.mention,
)
ephemeral = True
@ -89,7 +90,7 @@ async def on_command_error(ctx, error):
async def on_error(event: str, *args, **kwargs) -> None:
logger.exception(
f"on_error INFO: errors.event.{event} | '*args': {args} | '**kwargs': {kwargs}"
f"on_error INFO: errors.event.{event} | '*args': {args} | '**kwargs': {kwargs}",
)
logger.exception(f"on_error EXCEPTION: {sys.exc_info()}")
traceback.print_exc()

View file

@ -25,9 +25,14 @@ class EventHandler(Cog):
embed = Greet.message(member, config.welcome_message)
try:
await member.guild.get_channel(config.welcome_channel_id).send(embed=embed, content=member.mention)
await member.guild.get_channel(config.welcome_channel_id).send(
embed=embed,
content=member.mention,
)
except Exception as e:
logger.warning(f"Greet message not sent in '{member.guild.name}'. Channel ID may be invalid. {e}")
logger.warning(
f"Greet message not sent in '{member.guild.name}'. Channel ID may be invalid. {e}",
)
@Cog.listener()
async def on_member_update(self, before, after):
@ -44,16 +49,25 @@ class EventHandler(Cog):
if not config.boost_channel_id:
return
embed = lib.embeds.boost.Boost.message(member, config.boost_message, config.boost_image_url)
embed = lib.embeds.boost.Boost.message(
member,
config.boost_message,
config.boost_image_url,
)
try:
await member.guild.get_channel(config.boost_channel_id).send(embed=embed, content=member.mention)
await member.guild.get_channel(config.boost_channel_id).send(
embed=embed,
content=member.mention,
)
except Exception as e:
logger.warning(f"Boost message not sent in '{member.guild.name}'. Channel ID may be invalid. {e}")
logger.warning(
f"Boost message not sent in '{member.guild.name}'. Channel ID may be invalid. {e}",
)
@Cog.listener()
async def on_command_completion(self, ctx) -> None:
log_msg = '%s executed .%s' % (ctx.author.name, ctx.command.qualified_name)
log_msg = "%s executed .%s" % (ctx.author.name, ctx.command.qualified_name)
if ctx.guild is not None:
logger.debug(f"{log_msg} | guild: {ctx.guild.name} ")
@ -62,7 +76,7 @@ class EventHandler(Cog):
@Cog.listener()
async def on_application_command_completion(self, ctx) -> None:
log_msg = '%s executed /%s' % (ctx.author.name, ctx.command.qualified_name)
log_msg = "%s executed /%s" % (ctx.author.name, ctx.command.qualified_name)
if ctx.guild is not None:
logger.debug(f"{log_msg} | guild: {ctx.guild.name} ")

View file

@ -38,9 +38,9 @@ class ReactionHandler:
if processed:
await self.reaction_service.increment_reaction_usage(
int(data["id"])
int(data["id"]),
)
async def try_respond(self, data) -> bool:
"""
Tries to respond to the message.
@ -53,7 +53,7 @@ class ReactionHandler:
except Exception:
pass
return False
async def try_react(self, data) -> bool:
"""
Tries to react to the message.
@ -82,7 +82,7 @@ class ReactionListener(Cog):
:param message: The message to process.
"""
if not message.author.bot and not BlacklistUserService.is_user_blacklisted(
message.author.id
message.author.id,
):
await ReactionHandler(self.client, message).run_checks()

View file

@ -32,7 +32,8 @@ class XPHandler:
self.author: discord.Member | discord.User = message.author
self.guild: discord.Guild | None = message.guild
self.xp_conf: XpService = XpService(
self.author.id, self.guild.id if self.guild else 0
self.author.id,
self.guild.id if self.guild else 0,
)
self.guild_conf: Optional[GuildConfig] = None
@ -80,7 +81,8 @@ class XPHandler:
if level_message:
level_channel: Optional[discord.TextChannel] = await self.get_level_channel(
self.message, _gd
self.message,
_gd,
)
if level_channel:
@ -103,7 +105,9 @@ class XPHandler:
if role := self.guild.get_role(role_id):
with contextlib.suppress(
discord.Forbidden, discord.NotFound, discord.HTTPException
discord.Forbidden,
discord.NotFound,
discord.HTTPException,
):
if isinstance(self.author, discord.Member):
await self.author.add_roles(role, reason=reason)
@ -112,12 +116,16 @@ class XPHandler:
if replace and isinstance(self.author, discord.Member):
if role := self.guild.get_role(previous or role_id):
with contextlib.suppress(
discord.Forbidden, discord.NotFound, discord.HTTPException
discord.Forbidden,
discord.NotFound,
discord.HTTPException,
):
await self.author.remove_roles(role, reason=reason)
async def get_level_channel(
self, message: discord.Message, guild_config: GuildConfig
self,
message: discord.Message,
guild_config: GuildConfig,
) -> Optional[discord.TextChannel]:
"""
Retrieves the level up notification channel for the guild.
@ -134,13 +142,16 @@ class XPHandler:
with contextlib.suppress(discord.HTTPException):
return await self.client.convert_to_text_channel(
context, guild_config.level_channel_id
context,
guild_config.level_channel_id,
)
return None
@staticmethod
async def get_level_message(
guild_config: GuildConfig, level_config: XpService, author: discord.Member
guild_config: GuildConfig,
level_config: XpService,
author: discord.Member,
) -> Optional[str]:
"""
Retrieves the level up message for the user.
@ -161,11 +172,14 @@ class XPHandler:
case 2:
if not guild_config.level_message:
level_message = XPHandler.level_message_generic(
level_config.level, author
level_config.level,
author,
)
else:
level_message = formatter.template(
guild_config.level_message, author.name, level_config.level
guild_config.level_message,
author.name,
level_config.level,
)
case _:
raise ValueError("Invalid level message type")

View file

@ -29,7 +29,8 @@ def allowed_in_channel():
if command_channel_id:
command_channel = await ctx.bot.get_or_fetch_channel(
ctx.guild, command_channel_id
ctx.guild,
command_channel_id,
)
if ctx.channel.id != command_channel_id and command_channel:

View file

@ -6,7 +6,8 @@ from lib import formatter
def clean_error_embed(ctx):
embed = discord.Embed(
color=discord.Color.red(), description=f"**{ctx.author.name}** "
color=discord.Color.red(),
description=f"**{ctx.author.name}** ",
)
return embed
@ -73,7 +74,8 @@ class GenericErrors:
else:
embed.description += f"you can only do that command in {channel.mention}."
embed.set_footer(
text="This message will delete itself after 5s", icon_url=CONST.EXCLAIM_ICON
text="This message will delete itself after 5s",
icon_url=CONST.EXCLAIM_ICON,
)
return embed
@ -103,7 +105,8 @@ class EconErrors:
f"you already have a game of {ctx.command.name} running."
)
embed.set_footer(
text="Please finish this game first", icon_url=CONST.EXCLAIM_ICON
text="Please finish this game first",
icon_url=CONST.EXCLAIM_ICON,
)
return embed
@ -134,7 +137,8 @@ class HelpErrors:
embed = clean_error_embed(ctx)
embed.description += error
embed.set_footer(
text=f"See '{formatter.get_prefix(ctx)}help'", icon_url=CONST.EXCLAIM_ICON
text=f"See '{formatter.get_prefix(ctx)}help'",
icon_url=CONST.EXCLAIM_ICON,
)
return embed

View file

@ -12,7 +12,8 @@ streak_icon = resources["icons"]["streak"]
def clean_info_embed(ctx):
embed = discord.Embed(
color=discord.Color.blurple(), description=f"**{ctx.author.name}** "
color=discord.Color.blurple(),
description=f"**{ctx.author.name}** ",
)
return embed
@ -24,7 +25,8 @@ class MiscInfo:
embed = clean_info_embed(ctx)
embed.description += "I'm online!"
embed.set_footer(
text=f"Latency: {round(1000 * client.latency)}ms", icon_url=exclaim_icon
text=f"Latency: {round(1000 * client.latency)}ms",
icon_url=exclaim_icon,
)
return embed
@ -34,7 +36,8 @@ class MiscInfo:
embed = clean_info_embed(ctx)
embed.description += f"I've been online since <t:{unix_time}:R>"
embed.set_footer(
text=f"Latency: {round(1000 * client.latency)}ms", icon_url=exclaim_icon
text=f"Latency: {round(1000 * client.latency)}ms",
icon_url=exclaim_icon,
)
return embed

View file

@ -11,10 +11,16 @@ cross_icon = resources["icons"]["cross"]
exclaim_icon = resources["icons"]["exclaim"]
logo = resources["logo"]["transparent"]
def create_embed(title: str, description: str, color: int, icon_url: str) -> discord.Embed:
def create_embed(
title: str,
description: str,
color: int,
icon_url: str,
) -> discord.Embed:
embed = discord.Embed(
color=color,
description=description
description=description,
)
embed.set_author(name=title, icon_url=icon_url)
embed.set_footer(text="Reaction Service", icon_url=logo)
@ -22,24 +28,36 @@ def create_embed(title: str, description: str, color: int, icon_url: str) -> dis
return embed
def create_creation_embed(trigger_text: str, response: Optional[str], emoji_id: Optional[int], is_emoji: bool, is_full_match: bool) -> discord.Embed:
def create_creation_embed(
trigger_text: str,
response: Optional[str],
emoji_id: Optional[int],
is_emoji: bool,
is_full_match: bool,
) -> discord.Embed:
trigger_text = formatter.shorten(trigger_text, 50)
if response:
response = formatter.shorten(response, 50)
description = (
f"**Trigger Text:** `{trigger_text}`\n"
f"**Reaction Type:** {'Emoji' if is_emoji else 'Text'}\n"
f"{f'**Emoji ID:** `{str(emoji_id)}`' if is_emoji else f'**Response:** `{response}`'}\n"
f"**Full Match:** `{is_full_match}`"
f"**Full Match:** `{is_full_match}`"
)
return create_embed("Custom Reaction Created", description, 0xFF8C00, check_icon)
def create_failure_embed(trigger_text: str, is_emoji: bool, limit_reached: bool = False, trigger_already_exists: bool = False) -> discord.Embed:
def create_failure_embed(
trigger_text: str,
is_emoji: bool,
limit_reached: bool = False,
trigger_already_exists: bool = False,
) -> discord.Embed:
trigger_text = formatter.shorten(trigger_text, 50)
description = f"**Trigger Text:** `{trigger_text}`\n"
if limit_reached:
description += "Failed to add custom reaction. You have reached the limit of 100 custom reactions for this server."
elif trigger_already_exists:
@ -47,23 +65,30 @@ def create_failure_embed(trigger_text: str, is_emoji: bool, limit_reached: bool
else:
description += "Failed to add custom reaction."
return create_embed("Custom Reaction Creation Failed", description, 0xFF4500, cross_icon)
return create_embed(
"Custom Reaction Creation Failed",
description,
0xFF4500,
cross_icon,
)
def create_deletion_embed(trigger_text: str, is_emoji: bool) -> discord.Embed:
trigger_text = formatter.shorten(trigger_text, 50)
description = f"**Trigger Text:** `{trigger_text}`\n"
description += "Custom reaction has been successfully deleted."
return create_embed("Custom Reaction Deleted", description, 0xFF8C00, check_icon)
def create_not_found_embed(reaction_id: int) -> discord.Embed:
description = f"**Reaction ID:** `{reaction_id}`\n"
description += "No custom reaction found with the provided ID."
return create_embed("Custom Reaction Not Found", description, 0xFF4500, cross_icon)
def create_no_triggers_embed() -> discord.Embed:
description = (
"There are no custom reactions set up yet.\n\n"
@ -75,7 +100,10 @@ def create_no_triggers_embed() -> discord.Embed:
"**Text Reaction:**\n"
"A text reaction will respond with a specific text message when the trigger text is detected."
)
return create_embed("No Custom Reactions Found", description, 0xFF8C00, exclaim_icon)
return create_embed(
"No Custom Reactions Found",
description,
0xFF8C00,
exclaim_icon,
)

View file

@ -16,7 +16,9 @@ class BlackJackButtons(View):
await self.message.edit(view=None)
@discord.ui.button(
label="hit", style=discord.ButtonStyle.gray, emoji="<:hit:1119262723285467156> "
label="hit",
style=discord.ButtonStyle.gray,
emoji="<:hit:1119262723285467156> ",
)
async def hit_button_callback(self, button, interaction):
self.clickedHit = True
@ -36,7 +38,8 @@ class BlackJackButtons(View):
async def interaction_check(self, interaction) -> bool:
if interaction.user != self.ctx.author:
await interaction.response.send_message(
"You can't use these buttons, they're someone else's!", ephemeral=True
"You can't use these buttons, they're someone else's!",
ephemeral=True,
)
return False
else:
@ -68,7 +71,8 @@ class ExchangeConfirmation(View):
async def interaction_check(self, interaction) -> bool:
if interaction.user != self.ctx.author:
await interaction.response.send_message(
"You can't use these buttons, they're someone else's!", ephemeral=True
"You can't use these buttons, they're someone else's!",
ephemeral=True,
)
return False
else:

View file

@ -51,7 +51,9 @@ class IntroductionFinishButtons(View):
@discord.ui.button(label="Post it!", style=discord.ButtonStyle.green)
async def short_button_callback(
self, button: discord.ui.Button, interaction: discord.Interaction
self,
button: discord.ui.Button,
interaction: discord.Interaction,
) -> None:
await interaction.response.edit_message(view=None)
self.clickedConfirm = True
@ -59,7 +61,9 @@ class IntroductionFinishButtons(View):
@discord.ui.button(label="Stop", style=discord.ButtonStyle.red)
async def extended_button_callback(
self, button: discord.ui.Button, interaction: discord.Interaction
self,
button: discord.ui.Button,
interaction: discord.Interaction,
) -> None:
await interaction.response.edit_message(view=None)
self.stop()
@ -67,7 +71,8 @@ class IntroductionFinishButtons(View):
async def interaction_check(self, interaction: discord.Interaction) -> bool:
if interaction.user != self.ctx.author:
await interaction.response.send_message(
"You can't use these buttons.", ephemeral=True
"You can't use these buttons.",
ephemeral=True,
)
return False
else:

View file

@ -23,12 +23,14 @@ class ReactionHandler:
"My reply is no.",
"My sources say no.",
"Outlook not so good.",
"Very doubtful."
"Very doubtful.",
]
async def handle_message(self, message):
content = message.content.lower()
if (content.startswith("Lumi ") or content.startswith("Lumi, ")) and content.endswith("?"):
if (
content.startswith("Lumi ") or content.startswith("Lumi, ")
) and content.endswith("?"):
response = random.choice(self.eightball)
await message.reply(content=response)

View file

@ -4,7 +4,7 @@ import pytz
def seconds_until(hours, minutes):
eastern_timezone = pytz.timezone('US/Eastern')
eastern_timezone = pytz.timezone("US/Eastern")
now = datetime.datetime.now(eastern_timezone)

View file

@ -18,7 +18,7 @@ class BotAdmin(commands.Cog, name="Bot Admin"):
name="award",
description="This command can only be performed by a bot administrator.",
help="Awards cash to a specific user. This command can only be performed by a bot administrator.",
guild_only=True
guild_only=True,
)
@commands.guild_only()
@commands.is_owner()
@ -29,7 +29,7 @@ class BotAdmin(commands.Cog, name="Bot Admin"):
name="sqlselect",
aliases=["sqls"],
description="This command can only be performed by a bot administrator.",
help="Perform a SELECT query in the database. This command can only be performed by a bot administrator."
help="Perform a SELECT query in the database. This command can only be performed by a bot administrator.",
)
@commands.is_owner()
async def select(self, ctx, *, query: str):
@ -39,15 +39,15 @@ class BotAdmin(commands.Cog, name="Bot Admin"):
name="sqlinject",
aliases=["sqli"],
description="This command can only be performed by a bot administrator.",
help="Change a value in the database. This command can only be performed by a bot administrator."
help="Change a value in the database. This command can only be performed by a bot administrator.",
)
@commands.is_owner()
async def inject(self, ctx, *, query: str):
return await sql.inject_cmd(ctx, query)
@commands.command(
name="blacklist",
help="Add or remove a user from the blacklist. This command can only be performed by a bot administrator."
help="Add or remove a user from the blacklist. This command can only be performed by a bot administrator.",
)
@commands.is_owner()
async def blacklist(self, ctx, user: discord.User, *, reason: Optional[str] = None):

View file

@ -11,7 +11,7 @@ async def cmd(ctx, user: discord.User, amount: int):
embed = discord.Embed(
color=discord.Color.green(),
description=f"Awarded **${Currency.format(amount)}** to {user.name}."
description=f"Awarded **${Currency.format(amount)}** to {user.name}.",
)
await ctx.respond(embed=embed)

View file

@ -9,7 +9,9 @@ hammer_icon = resources["icons"]["hammer"]
async def blacklist_user(
ctx, user: discord.User, reason: Optional[str] = None
ctx,
user: discord.User,
reason: Optional[str] = None,
) -> None:
"""
Blacklists a user with an optional reason.
@ -26,6 +28,9 @@ async def blacklist_user(
color=discord.Color.red(),
)
embed.set_author(name="User Blacklisted", icon_url=hammer_icon)
embed.set_footer(text="There is no process to reinstate a blacklisted user. Appeals are not considered.", icon_url=exclaim_icon)
await ctx.send(embed=embed)
embed.set_footer(
text="There is no process to reinstate a blacklisted user. Appeals are not considered.",
icon_url=exclaim_icon,
)
await ctx.send(embed=embed)

View file

@ -12,7 +12,10 @@ async def select_cmd(ctx, query: str):
except sqlite3.Error as error:
results = error
return await ctx.respond(content=f"```SELECT {query}```\n```{results}```", ephemeral=True)
return await ctx.respond(
content=f"```SELECT {query}```\n```{results}```",
ephemeral=True,
)
async def inject_cmd(ctx, query: str):
@ -20,4 +23,7 @@ async def inject_cmd(ctx, query: str):
database.execute_query(query)
await ctx.respond(content=f"That worked!\n```{query}```", ephemeral=True)
except sqlite3.Error as error:
await ctx.respond(content=f"Query:\n```{query}```\nError message:\n```{error}```", ephemeral=True)
await ctx.respond(
content=f"Query:\n```{query}```\nError message:\n```{error}```",
ephemeral=True,
)

View file

@ -12,6 +12,7 @@ from modules.birthdays import birthday
from services.birthday_service import Birthday
from services.config_service import GuildConfig
class Birthdays(commands.Cog):
def __init__(self, client):
self.client = client
@ -20,11 +21,20 @@ class Birthdays(commands.Cog):
"""
birthday module - slash command only
"""
birthday = SlashCommandGroup(name="birthday", description="Birthday commands.", guild_only=True)
birthday = SlashCommandGroup(
name="birthday",
description="Birthday commands.",
guild_only=True,
)
@birthday.command(name="set", description="Set your birthday in this server.")
@checks.birthdays_enabled()
async def set_birthday(self, ctx, month: discord.Option(choices=CONST.BIRTHDAY_MONTHS), day: int):
async def set_birthday(
self,
ctx,
month: discord.Option(choices=CONST.BIRTHDAY_MONTHS),
day: int,
):
index = CONST.BIRTHDAY_MONTHS.index(month) + 1
await birthday.add(ctx, month, index, day)
@ -33,16 +43,20 @@ class Birthdays(commands.Cog):
async def delete_birthday(self, ctx):
await birthday.delete(ctx)
@birthday.command(name="upcoming", description="Shows the upcoming birthdays in this server.")
@birthday.command(
name="upcoming",
description="Shows the upcoming birthdays in this server.",
)
@checks.birthdays_enabled()
async def upcoming_birthdays(self, ctx):
await birthday.upcoming(ctx)
@tasks.loop(hours=23, minutes=55)
async def daily_birthday_check(self):
wait_time = time.seconds_until(7, 0)
logger.debug(f"Waiting until 7 AM Eastern for the daily birthday check: {round(wait_time)}s left.")
logger.debug(
f"Waiting until 7 AM Eastern for the daily birthday check: {round(wait_time)}s left.",
)
await asyncio.sleep(wait_time)
embed = discord.Embed(color=discord.Color.embed_background())
@ -55,17 +69,26 @@ class Birthdays(commands.Cog):
guild_config = GuildConfig(guild.id)
if not guild_config.birthday_channel_id:
logger.debug(f"Birthday announcements in guild with ID {guild.id} skipped: no birthday channel.")
logger.debug(
f"Birthday announcements in guild with ID {guild.id} skipped: no birthday channel.",
)
return
message = random.choice(CONST.BIRTHDAY_MESSAGES)
embed.description = message.format(member.name)
channel = await self.client.get_or_fetch_channel(guild, guild_config.birthday_channel_id)
channel = await self.client.get_or_fetch_channel(
guild,
guild_config.birthday_channel_id,
)
await channel.send(embed=embed, content=member.mention)
logger.debug(f"Birthday announcement Success! user/guild/chan ID: {member.id}/{guild.id}/{channel.id}")
logger.debug(
f"Birthday announcement Success! user/guild/chan ID: {member.id}/{guild.id}/{channel.id}",
)
except Exception as e:
logger.warning(f"Birthday announcement skipped processing user/guild {user_id}/{guild_id} | {e}")
logger.warning(
f"Birthday announcement skipped processing user/guild {user_id}/{guild_id} | {e}",
)
# wait one second to avoid rate limits
await asyncio.sleep(1)

View file

@ -17,7 +17,7 @@ async def add(ctx, month, month_index, day):
raise commands.BadArgument("the date you entered is invalid.")
date_str = f"{leap_year}-{month_index:02d}-{day:02d}"
date_obj = datetime.datetime.strptime(date_str, '%Y-%m-%d')
date_obj = datetime.datetime.strptime(date_str, "%Y-%m-%d")
birthday = Birthday(ctx.author.id, ctx.guild.id)
birthday.set(date_obj)
@ -38,7 +38,7 @@ async def upcoming(ctx):
icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
embed = discord.Embed(
color=discord.Color.embed_background()
color=discord.Color.embed_background(),
)
embed.set_author(name="Upcoming Birthdays!", icon_url=icon)
embed.set_thumbnail(url="https://i.imgur.com/79XfsbS.png")
@ -61,7 +61,7 @@ async def upcoming(ctx):
embed.add_field(
name=f"{name}",
value=f"🎂 {formatted_birthday}",
inline=False
inline=False,
)
found_birthdays += 1

View file

@ -1,5 +1,3 @@
import discord
from discord.commands import SlashCommandGroup
from discord.ext import commands, bridge
@ -24,8 +22,8 @@ class Config(commands.Cog):
aliases=["config"],
description="Show your server configuration.",
help="Shows information about how Lumi is configured in your server. "
"[Read the guide](https://wiki.wlinator.org/serverconfig).",
guild_only=True
"[Read the guide](https://wiki.wlinator.org/serverconfig).",
guild_only=True,
)
@commands.guild_only()
@commands.has_permissions(manage_channels=True)
@ -37,7 +35,7 @@ class Config(commands.Cog):
aliases=["sp"],
description="Set Lumi's prefix.",
help="Set the prefix for Lumi in this server. The maximum length of a prefix is 25.",
guild_only=True
guild_only=True,
)
@commands.guild_only()
@commands.has_permissions(manage_channels=True)
@ -49,7 +47,7 @@ class Config(commands.Cog):
aliases=["xpr"],
description="Show your server's XP rewards list.",
help="Read [the guide](https://wiki.wlinator.org/xprewards) before editing.",
guild_only="True"
guild_only="True",
)
@commands.guild_only()
@commands.has_permissions(manage_roles=True)
@ -61,11 +59,17 @@ class Config(commands.Cog):
aliases=["axpr"],
description="Add a Lumi XP reward.",
help="Add a Lumi XP reward. Read [the guide](https://wiki.wlinator.org/xprewards) before editing.",
guild_only="True"
guild_only="True",
)
@commands.guild_only()
@commands.has_permissions(manage_roles=True)
async def xp_reward_command_add(self, ctx, level: int, role: discord.Role, persistent: bool = False):
async def xp_reward_command_add(
self,
ctx,
level: int,
role: discord.Role,
persistent: bool = False,
):
await xp_reward.add_reward(ctx, level, role.id, persistent)
@bridge.bridge_command(
@ -73,7 +77,7 @@ class Config(commands.Cog):
aliases=["rxpr"],
description="Remove a Lumi XP reward.",
help="Remove a Lumi XP reward. Read [the guide](https://wiki.wlinator.org/xprewards) before editing.",
guild_only="True"
guild_only="True",
)
@commands.guild_only()
@commands.has_permissions(manage_roles=True)
@ -83,8 +87,12 @@ class Config(commands.Cog):
"""
The guild config code is a mess.
"""
config = SlashCommandGroup("config", "server config commands.", guild_only=True,
default_member_permissions=discord.Permissions(manage_channels=True))
config = SlashCommandGroup(
"config",
"server config commands.",
guild_only=True,
default_member_permissions=discord.Permissions(manage_channels=True),
)
birthday_config = config.create_subgroup(name="birthdays")
command_config = config.create_subgroup(name="commands")
intro_config = config.create_subgroup(name="intros")
@ -94,7 +102,7 @@ class Config(commands.Cog):
@birthday_config.command(
name="channel",
description="Set the birthday announcements channel."
description="Set the birthday announcements channel.",
)
async def config_birthdays_channel(self, ctx, *, channel: discord.TextChannel):
guild_config = GuildConfig(ctx.guild.id)
@ -103,9 +111,11 @@ class Config(commands.Cog):
embed = discord.Embed(
color=discord.Color.orange(),
description=f"✅ | Birthday announcements will be sent in {channel.mention}."
description=f"✅ | Birthday announcements will be sent in {channel.mention}.",
)
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
embed.set_author(name="Server Configuration", icon_url=guild_icon)
return await ctx.respond(embed=embed)
@ -120,7 +130,9 @@ class Config(commands.Cog):
embed = discord.Embed(
color=discord.Color.orange(),
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
embed.set_author(name="Server Configuration", icon_url=guild_icon)
if not guild_config.birthday_channel_id:
@ -135,7 +147,7 @@ class Config(commands.Cog):
@command_config.command(
name="channel",
description="Configure where members can use Lumi commands."
description="Configure where members can use Lumi commands.",
)
async def config_commands_channel(self, ctx, *, channel: discord.TextChannel):
guild_config = GuildConfig(ctx.guild.id)
@ -144,17 +156,21 @@ class Config(commands.Cog):
embed = discord.Embed(
color=discord.Color.orange(),
description=f"✅ | Commands can now only be used in {channel.mention}."
description=f"✅ | Commands can now only be used in {channel.mention}.",
)
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
embed.set_author(name="Server Configuration", icon_url=guild_icon)
embed.set_footer(text="Note: mod & config commands are still available everywhere.")
embed.set_footer(
text="Note: mod & config commands are still available everywhere.",
)
return await ctx.respond(embed=embed)
@command_config.command(
name="everywhere",
description="Allow members to do commands in all channels."
description="Allow members to do commands in all channels.",
)
async def config_commands_everywhere(self, ctx):
guild_config = GuildConfig(ctx.guild.id)
@ -163,9 +179,11 @@ class Config(commands.Cog):
embed = discord.Embed(
color=discord.Color.orange(),
description="✅ | Server members can now use Lumi commands in all channels. "
description="✅ | Server members can now use Lumi commands in all channels. ",
)
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
embed.set_author(name="Server Configuration", icon_url=guild_icon)
return await ctx.respond(embed=embed)
@ -213,7 +231,7 @@ class Config(commands.Cog):
@welcome_config.command(
name="channel",
description="Set the greeting announcements channel."
description="Set the greeting announcements channel.",
)
async def config_welcome_channel(self, ctx, *, channel: discord.TextChannel):
guild_config = GuildConfig(ctx.guild.id)
@ -222,16 +240,18 @@ class Config(commands.Cog):
embed = discord.Embed(
color=discord.Color.orange(),
description=f"✅ | New members will receive a welcome message in {channel.mention}."
description=f"✅ | New members will receive a welcome message in {channel.mention}.",
)
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
embed.set_author(name="Server Configuration", icon_url=guild_icon)
return await ctx.respond(embed=embed)
@welcome_config.command(
name="disable",
description="Disable greetings in this server."
description="Disable greetings in this server.",
)
async def config_welcome_disable(self, ctx):
guild_config = GuildConfig(ctx.guild.id)
@ -239,7 +259,9 @@ class Config(commands.Cog):
embed = discord.Embed(
color=discord.Color.orange(),
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
embed.set_author(name="Server Configuration", icon_url=guild_icon)
if not guild_config.welcome_channel_id:
@ -255,20 +277,31 @@ class Config(commands.Cog):
@welcome_config.command(
name="template",
description="Make a custom greeting template."
description="Make a custom greeting template.",
)
async def config_welcome_template(self, ctx, *, text: discord.Option(str, max_length=2000)):
async def config_welcome_template(
self,
ctx,
*,
text: discord.Option(str, max_length=2000),
):
guild_config = GuildConfig(ctx.guild.id)
guild_config.welcome_message = text
guild_config.push()
embed = discord.Embed(
color=discord.Color.orange(),
description="✅ | The greeting template was successfully updated."
description="✅ | The greeting template was successfully updated.",
)
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
embed.add_field(name="Template", value=text, inline=False)
embed.add_field(name="Example", value="An example will be sent in a separate message.", inline=False)
embed.add_field(
name="Example",
value="An example will be sent in a separate message.",
inline=False,
)
embed.set_author(name="Server Configuration", icon_url=guild_icon)
await ctx.respond(embed=embed)
@ -277,7 +310,7 @@ class Config(commands.Cog):
@boost_config.command(
name="channel",
description="Set the boost announcements channel."
description="Set the boost announcements channel.",
)
async def config_boosts_channel(self, ctx, *, channel: discord.TextChannel):
guild_config = GuildConfig(ctx.guild.id)
@ -286,16 +319,18 @@ class Config(commands.Cog):
embed = discord.Embed(
color=discord.Color.orange(),
description=f"✅ | I will announce server boosts in {channel.mention}."
description=f"✅ | I will announce server boosts in {channel.mention}.",
)
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
embed.set_author(name="Server Configuration", icon_url=guild_icon)
return await ctx.respond(embed=embed)
@boost_config.command(
name="disable",
description="Disable boost announcements in this server."
description="Disable boost announcements in this server.",
)
async def config_boosts_disable(self, ctx):
guild_config = GuildConfig(ctx.guild.id)
@ -303,7 +338,9 @@ class Config(commands.Cog):
embed = discord.Embed(
color=discord.Color.orange(),
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
embed.set_author(name="Server Configuration", icon_url=guild_icon)
if not guild_config.boost_channel_id:
@ -319,20 +356,31 @@ class Config(commands.Cog):
@boost_config.command(
name="template",
description="Make a custom boost announcement template."
description="Make a custom boost announcement template.",
)
async def config_boosts_template(self, ctx, *, text: discord.Option(str, max_length=2000)):
async def config_boosts_template(
self,
ctx,
*,
text: discord.Option(str, max_length=2000),
):
guild_config = GuildConfig(ctx.guild.id)
guild_config.boost_message = text
guild_config.push()
embed = discord.Embed(
color=discord.Color.orange(),
description="✅ | The booster template was successfully updated."
description="✅ | The booster template was successfully updated.",
)
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
embed.add_field(name="Template", value=text, inline=False)
embed.add_field(name="Example", value="An example will be sent in a separate message.", inline=False)
embed.add_field(
name="Example",
value="An example will be sent in a separate message.",
inline=False,
)
embed.set_author(name="Server Configuration", icon_url=guild_icon)
await ctx.respond(embed=embed)
@ -341,7 +389,7 @@ class Config(commands.Cog):
@boost_config.command(
name="image",
description="Add a custom image that will used for booster announcements."
description="Add a custom image that will used for booster announcements.",
)
async def config_boosts_image(self, ctx, *, image_url: str):
guild_config = GuildConfig(ctx.guild.id)
@ -354,7 +402,9 @@ class Config(commands.Cog):
elif not image_url.endswith(".jpg") and not image_url.lower().endswith(".png"):
return await ctx.respond(embed=GenericErrors.bad_url(ctx))
elif not image_url.startswith("http://") and not image_url.startswith("https://"):
elif not image_url.startswith("http://") and not image_url.startswith(
"https://",
):
return await ctx.respond(embed=GenericErrors.bad_url(ctx, "invalid URL."))
else:
@ -363,11 +413,21 @@ class Config(commands.Cog):
embed = discord.Embed(
color=discord.Color.orange(),
description="✅ | The booster image was successfully updated."
description="✅ | The booster image was successfully updated.",
)
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
embed.add_field(
name="Image",
value=image_url if image_url else "Original Image",
inline=False,
)
embed.add_field(
name="Example",
value="An example will be sent in a separate message.",
inline=False,
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
embed.add_field(name="Image", value=image_url if image_url else "Original Image", inline=False)
embed.add_field(name="Example", value="An example will be sent in a separate message.", inline=False)
embed.set_author(name="Server Configuration", icon_url=guild_icon)
await ctx.respond(embed=embed)
@ -376,7 +436,7 @@ class Config(commands.Cog):
@level_config.command(
name="channel",
description="Set the level announcements channel."
description="Set the level announcements channel.",
)
async def config_level_channel(self, ctx, *, channel: discord.TextChannel):
guild_config = GuildConfig(ctx.guild.id)
@ -385,19 +445,23 @@ class Config(commands.Cog):
embed = discord.Embed(
color=discord.Color.orange(),
description=f"✅ | All level announcements will be sent in {channel.mention}."
description=f"✅ | All level announcements will be sent in {channel.mention}.",
)
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
embed.set_author(name="Server Configuration", icon_url=guild_icon)
if guild_config.level_message_type == 0:
embed.set_footer(text="Warning: this module is disabled, please do '/config levels enable'")
embed.set_footer(
text="Warning: this module is disabled, please do '/config levels enable'",
)
return await ctx.respond(embed=embed)
@level_config.command(
name="currentchannel",
description="Send level announcements in the member's current channel."
description="Send level announcements in the member's current channel.",
)
async def config_level_samechannel(self, ctx):
guild_config = GuildConfig(ctx.guild.id)
@ -406,19 +470,23 @@ class Config(commands.Cog):
embed = discord.Embed(
color=discord.Color.orange(),
description="✅ | Members will receive level announcements in their current channel."
description="✅ | Members will receive level announcements in their current channel.",
)
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
embed.set_author(name="Server Configuration", icon_url=guild_icon)
if guild_config.level_message_type == 0:
embed.set_footer(text="Warning: this module is disabled, please do '/config levels enable'")
embed.set_footer(
text="Warning: this module is disabled, please do '/config levels enable'",
)
return await ctx.respond(embed=embed)
@level_config.command(
name="disable",
description="Disable levels and the Lumi XP system."
description="Disable levels and the Lumi XP system.",
)
async def config_level_disable(self, ctx):
guild_config = GuildConfig(ctx.guild.id)
@ -427,16 +495,18 @@ class Config(commands.Cog):
embed = discord.Embed(
color=discord.Color.orange(),
description="✅ | The Lumi XP system was successfully disabled."
description="✅ | The Lumi XP system was successfully disabled.",
)
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
embed.set_author(name="Server Configuration", icon_url=guild_icon)
return await ctx.respond(embed=embed)
@level_config.command(
name="enable",
description="Enable levels and the Lumi XP system."
description="Enable levels and the Lumi XP system.",
)
async def config_level_enable(self, ctx):
guild_config = GuildConfig(ctx.guild.id)
@ -444,7 +514,9 @@ class Config(commands.Cog):
embed = discord.Embed(
color=discord.Color.orange(),
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
embed.set_author(name="Server Configuration", icon_url=guild_icon)
if guild_config.level_message_type != 0:
@ -460,15 +532,22 @@ class Config(commands.Cog):
@level_config.command(
name="type",
description="Set the level announcements type."
description="Set the level announcements type.",
)
async def config_level_type(self, ctx, *, type: discord.Option(choices=["whimsical", "generic"])):
async def config_level_type(
self,
ctx,
*,
type: discord.Option(choices=["whimsical", "generic"]),
):
guild_config = GuildConfig(ctx.guild.id)
embed = discord.Embed(
color=discord.Color.orange(),
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
embed.set_author(name="Server Configuration", icon_url=guild_icon)
if type == "whimsical":
@ -477,8 +556,11 @@ class Config(commands.Cog):
guild_config.push()
embed.description = "✅ | Level announcements will be sarcastic comments."
embed.add_field(name="Example", value="📈 | **lucas** Lol it took you this long to reach **Level 15**.",
inline=False)
embed.add_field(
name="Example",
value="📈 | **lucas** Lol it took you this long to reach **Level 15**.",
inline=False,
)
return await ctx.respond(embed=embed)
else:
@ -487,14 +569,23 @@ class Config(commands.Cog):
guild_config.push()
embed.description = "✅ | Level announcements will be generic messages."
embed.add_field(name="Example", value="📈 | **lucas** you have reached **Level 15**.", inline=False)
embed.add_field(
name="Example",
value="📈 | **lucas** you have reached **Level 15**.",
inline=False,
)
return await ctx.respond(embed=embed)
@level_config.command(
name="template",
description="Make a custom leveling template."
description="Make a custom leveling template.",
)
async def config_level_template(self, ctx, *, text: discord.Option(str, max_length=2000)):
async def config_level_template(
self,
ctx,
*,
text: discord.Option(str, max_length=2000),
):
guild_config = GuildConfig(ctx.guild.id)
guild_config.level_message = text
guild_config.push()
@ -503,15 +594,19 @@ class Config(commands.Cog):
embed = discord.Embed(
color=discord.Color.orange(),
description="✅ | The level template was successfully updated."
description="✅ | The level template was successfully updated.",
)
guild_icon = (
ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
)
guild_icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
embed.add_field(name="Template", value=text, inline=False)
embed.add_field(name="Example", value=preview, inline=False)
embed.set_author(name="Server Configuration", icon_url=guild_icon)
if guild_config.level_message_type == 0:
embed.set_footer(text="Warning: this module is disabled, please do '/config levels enable'")
embed.set_footer(
text="Warning: this module is disabled, please do '/config levels enable'",
)
return await ctx.respond(embed=embed)

View file

@ -11,14 +11,17 @@ async def cmd(self, ctx):
embed = discord.Embed(
color=discord.Color.embed_background(),
description="Guide: https://wiki.wlinator.org/serverconfig"
description="Guide: https://wiki.wlinator.org/serverconfig",
)
icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
embed.set_author(name=f"{ctx.guild.name} config", icon_url=icon)
# birthdays
if guild_config.birthday_channel_id:
channel = await self.client.get_or_fetch_channel(ctx.guild, guild_config.birthday_channel_id)
channel = await self.client.get_or_fetch_channel(
ctx.guild,
guild_config.birthday_channel_id,
)
if channel:
birthday_config = f"✅ | in {channel.mention}."
@ -32,7 +35,10 @@ async def cmd(self, ctx):
# commands
if guild_config.command_channel_id:
channel = await self.client.get_or_fetch_channel(ctx.guild, guild_config.command_channel_id)
channel = await self.client.get_or_fetch_channel(
ctx.guild,
guild_config.command_channel_id,
)
if channel:
commands_config = f"✅ | commands only allowed in {channel.mention}."
@ -45,13 +51,18 @@ async def cmd(self, ctx):
# greetings
if guild_config.welcome_channel_id:
channel = await self.client.get_or_fetch_channel(ctx.guild, guild_config.welcome_channel_id)
channel = await self.client.get_or_fetch_channel(
ctx.guild,
guild_config.welcome_channel_id,
)
if channel:
greeting_config = f"✅ | in {channel.mention}"
if guild_config.welcome_message:
greeting_config += f" with template:\n```{guild_config.welcome_message}```"
greeting_config += (
f" with template:\n```{guild_config.welcome_message}```"
)
else:
greeting_config += " without custom template."
@ -64,7 +75,10 @@ async def cmd(self, ctx):
# boosts
if guild_config.boost_channel_id:
channel = await self.client.get_or_fetch_channel(ctx.guild, guild_config.boost_channel_id)
channel = await self.client.get_or_fetch_channel(
ctx.guild,
guild_config.boost_channel_id,
)
if channel:
boost_config = f"✅ | in {channel.mention}"
@ -73,7 +87,9 @@ async def cmd(self, ctx):
if guild_config.boost_image_url:
boost_config += f" with custom image and template:\n```{guild_config.boost_message}```"
else:
boost_config += f" with custom template:\n```{guild_config.boost_message}```"
boost_config += (
f" with custom template:\n```{guild_config.boost_message}```"
)
else:
if guild_config.boost_image_url:
boost_config += " with custom image, but no template."
@ -98,7 +114,10 @@ async def cmd(self, ctx):
level_config = "✅ | generic announcements"
if guild_config.level_channel_id and guild_config.level_message_type != 0:
channel = await self.client.get_or_fetch_channel(ctx.guild, guild_config.level_channel_id)
channel = await self.client.get_or_fetch_channel(
ctx.guild,
guild_config.level_channel_id,
)
if channel:
level_config += f" in {channel.mention}"

View file

@ -7,7 +7,9 @@ async def set_cmd(ctx, prefix):
if len(prefix) > 25:
return await ctx.respond(embed=MiscErrors.prefix_too_long(ctx))
guild_config = GuildConfig(ctx.guild.id) # generate a guild_config for if it didn't already exist
guild_config = GuildConfig(
ctx.guild.id,
) # generate a guild_config for if it didn't already exist
GuildConfig.set_prefix(guild_config.guild_id, prefix)
await ctx.respond(embed=MiscInfo.set_prefix(ctx, prefix))

View file

@ -11,7 +11,7 @@ async def show(ctx):
embed = discord.Embed(
color=discord.Color.embed_background(),
description="Read [the guide](https://wiki.wlinator.org/xprewards) before editing.\n"
description="Read [the guide](https://wiki.wlinator.org/xprewards) before editing.\n",
)
icon = ctx.guild.icon if ctx.guild.icon else art["logo"]["opaque"]
@ -21,7 +21,9 @@ async def show(ctx):
role_id, persistent = level_reward.rewards.get(level)
role = ctx.guild.get_role(role_id)
embed.description += f"\n**Level {level}** -> {role.mention if role else 'Role not found'}"
embed.description += (
f"\n**Level {level}** -> {role.mention if role else 'Role not found'}"
)
if bool(persistent):
embed.description += " (persistent)"

View file

@ -48,7 +48,9 @@ class Economy(commands.Cog):
return await daily.cmd(ctx)
@commands.slash_command(
name="give", description="Give a server member some cash.", guild_only=True
name="give",
description="Give a server member some cash.",
guild_only=True,
)
@commands.guild_only()
@checks.allowed_in_channel()

View file

@ -11,7 +11,7 @@ async def cmd(ctx):
embed = discord.Embed(
color=discord.Color.embed_background(),
description=f"**Cash**: ${balance}"
description=f"**Cash**: ${balance}",
)
embed.set_author(name=f"{ctx.author.name}'s wallet", icon_url=ctx.author.avatar.url)

View file

@ -14,7 +14,7 @@ from services.currency_service import Currency
from services.stats_service import BlackJackStats
resources = JsonCache.read_json("resources")
est = pytz.timezone('US/Eastern')
est = pytz.timezone("US/Eastern")
active_blackjack_games = {}
@ -48,7 +48,6 @@ async def cmd(ctx, bet: int):
active_blackjack_games[ctx.author.id] = True
try:
player_hand = []
dealer_hand = []
deck = get_new_deck()
@ -69,11 +68,18 @@ async def cmd(ctx, bet: int):
while status == 0:
if not playing_embed:
await ctx.respond(embed=blackjack_show(ctx, Currency.format_human(bet), player_hand,
dealer_hand, player_hand_value,
dealer_hand_value),
view=view,
content=ctx.author.mention)
await ctx.respond(
embed=blackjack_show(
ctx,
Currency.format_human(bet),
player_hand,
dealer_hand,
player_hand_value,
dealer_hand_value,
),
view=view,
content=ctx.author.mention,
)
playing_embed = True
@ -110,9 +116,14 @@ async def cmd(ctx, bet: int):
# refresh
view = interaction.BlackJackButtons(ctx)
embed = blackjack_show(ctx, Currency.format_human(bet), player_hand,
dealer_hand, player_hand_value,
dealer_hand_value)
embed = blackjack_show(
ctx,
Currency.format_human(bet),
player_hand,
dealer_hand,
player_hand_value,
dealer_hand_value,
)
await ctx.edit(embed=embed, view=view, content=ctx.author.mention)
@ -122,8 +133,14 @@ async def cmd(ctx, bet: int):
payout = bet * multiplier if not status == 5 else bet * 2
is_won = False if status == 1 or status == 4 else True
embed = blackjack_finished(ctx, Currency.format_human(bet), player_hand_value,
dealer_hand_value, Currency.format_human(payout), status)
embed = blackjack_finished(
ctx,
Currency.format_human(bet),
player_hand_value,
dealer_hand_value,
Currency.format_human(payout),
status,
)
if playing_embed:
await ctx.edit(embed=embed, view=None, content=ctx.author.mention)
@ -143,12 +160,15 @@ async def cmd(ctx, bet: int):
bet=bet,
payout=0,
hand_player=player_hand,
hand_dealer=dealer_hand
hand_dealer=dealer_hand,
)
stats.push()
elif status == 6:
await ctx.send(embed=EconErrors.out_of_time(ctx), content=ctx.author.mention)
await ctx.send(
embed=EconErrors.out_of_time(ctx),
content=ctx.author.mention,
)
ctx_currency.take_balance(bet)
ctx_currency.push()
@ -163,7 +183,7 @@ async def cmd(ctx, bet: int):
bet=bet,
payout=payout,
hand_player=player_hand,
hand_dealer=dealer_hand
hand_dealer=dealer_hand,
)
stats.push()
@ -176,30 +196,45 @@ async def cmd(ctx, bet: int):
del active_blackjack_games[ctx.author.id]
def blackjack_show(ctx, bet, player_hand, dealer_hand, player_hand_value, dealer_hand_value):
def blackjack_show(
ctx,
bet,
player_hand,
dealer_hand,
player_hand_value,
dealer_hand_value,
):
current_time = datetime.now(est).strftime("%I:%M %p")
thumbnail_url = None
embed = discord.Embed(
title="BlackJack",
color=discord.Color.dark_orange()
color=discord.Color.dark_orange(),
)
embed.description = f"**You**\n" \
f"Score: {player_hand_value}\n" \
f"*Hand: {' + '.join(player_hand)}*\n\n"
embed.description = (
f"**You**\n"
f"Score: {player_hand_value}\n"
f"*Hand: {' + '.join(player_hand)}*\n\n"
)
if len(dealer_hand) < 2:
embed.description += f"**Dealer**\n" \
f"Score: {dealer_hand_value}\n" \
f"*Hand: {dealer_hand[0]} + ??*"
embed.description += (
f"**Dealer**\n"
f"Score: {dealer_hand_value}\n"
f"*Hand: {dealer_hand[0]} + ??*"
)
else:
embed.description += f"**Dealer | Score: {dealer_hand_value}**\n" \
f"*Hand: {' + '.join(dealer_hand)}*"
embed.description += (
f"**Dealer | Score: {dealer_hand_value}**\n"
f"*Hand: {' + '.join(dealer_hand)}*"
)
embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar.url)
embed.set_footer(text=f"Bet ${bet} • deck shuffled • Today at {current_time}",
icon_url="https://i.imgur.com/96jPPXO.png")
embed.set_footer(
text=f"Bet ${bet} • deck shuffled • Today at {current_time}",
icon_url="https://i.imgur.com/96jPPXO.png",
)
if thumbnail_url:
embed.set_thumbnail(url=thumbnail_url)
@ -212,13 +247,16 @@ def blackjack_finished(ctx, bet, player_hand_value, dealer_hand_value, payout, s
thumbnail_url = None
embed = discord.Embed(
title="BlackJack"
title="BlackJack",
)
embed.description = (
f"You | Score: {player_hand_value}\n" f"Dealer | Score: {dealer_hand_value}"
)
embed.description = f"You | Score: {player_hand_value}\n" \
f"Dealer | Score: {dealer_hand_value}"
embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar.url)
embed.set_footer(text=f"Game finished • Today at {current_time}",
icon_url="https://i.imgur.com/96jPPXO.png")
embed.set_footer(
text=f"Game finished • Today at {current_time}",
icon_url="https://i.imgur.com/96jPPXO.png",
)
if status == 1:
name = "Busted.."
@ -258,9 +296,11 @@ def blackjack_finished(ctx, bet, player_hand_value, dealer_hand_value, payout, s
if thumbnail_url:
embed.set_thumbnail(url=thumbnail_url)
embed.add_field(name=name,
value=value,
inline=False)
embed.add_field(
name=name,
value=value,
inline=False,
)
embed.colour = color
return embed
@ -295,10 +335,10 @@ def calculate_hand_value(hand):
if rank.isdigit():
value += int(rank)
elif rank in ['J', 'Q', 'K']:
elif rank in ["J", "Q", "K"]:
value += 10
elif rank == 'A':
elif rank == "A":
value += 11
has_ace = True
aces_count += 1

View file

@ -17,7 +17,7 @@ async def cmd(ctx) -> None:
ctx,
author_text=CONST.STRINGS["daily_already_claimed_author"],
description=CONST.STRINGS["daily_already_claimed_description"].format(
unix_time
unix_time,
),
footer_text=CONST.STRINGS["daily_already_claimed_footer"],
)
@ -33,7 +33,7 @@ async def cmd(ctx) -> None:
ctx,
author_text=CONST.STRINGS["daily_success_claim_author"],
description=CONST.STRINGS["daily_success_claim_description"].format(
Currency.format(ctx_daily.amount)
Currency.format(ctx_daily.amount),
),
footer_text=CONST.STRINGS["daily_streak_footer"].format(ctx_daily.streak)
if ctx_daily.streak > 1

View file

@ -29,7 +29,7 @@ async def cmd(ctx, user, amount):
embed = discord.Embed(
color=discord.Color.green(),
description=f"**{ctx.author.name}** gave **${Currency.format(amount)}** to {user.name}."
description=f"**{ctx.author.name}** gave **${Currency.format(amount)}** to {user.name}.",
)
await ctx.respond(embed=embed)

View file

@ -11,13 +11,12 @@ async def cmd(self, ctx):
embed = discord.Embed(
color=discord.Color.embed_background(),
description=description
description=description,
)
embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar.url)
for item, quantity in inventory_dict.items():
if item.type == "badge":
if not embed.description:
embed.description = "**Badges:** "
@ -26,8 +25,10 @@ async def cmd(self, ctx):
else:
emote = self.client.get_emoji(item.emote_id)
embed.add_field(name=f"{emote} {item.display_name.capitalize()}",
value=f"*— amount: `{quantity}`*",
inline=False)
embed.add_field(
name=f"{emote} {item.display_name.capitalize()}",
value=f"*— amount: `{quantity}`*",
inline=False,
)
await ctx.respond(embed=embed)

View file

@ -12,7 +12,7 @@ from services.currency_service import Currency
from services.stats_service import SlotsStats
resources = JsonCache.read_json("resources")
est = pytz.timezone('US/Eastern')
est = pytz.timezone("US/Eastern")
async def cmd(self, ctx, bet):
@ -46,16 +46,26 @@ async def cmd(self, ctx, bet):
emojis = get_emotes(self.client)
# start with default "spinning" embed
await ctx.respond(embed=slots_spinning(ctx, 3, Currency.format_human(bet), results, emojis))
await ctx.respond(
embed=slots_spinning(ctx, 3, Currency.format_human(bet), results, emojis),
)
await asyncio.sleep(1)
for i in range(2, 0, -1):
await ctx.edit(embed=slots_spinning(ctx, i, Currency.format_human(bet), results, emojis))
await ctx.edit(
embed=slots_spinning(ctx, i, Currency.format_human(bet), results, emojis),
)
await asyncio.sleep(1)
# output final result
finished_output = slots_finished(ctx, type, Currency.format_human(bet),
Currency.format_human(payout), results, emojis)
finished_output = slots_finished(
ctx,
type,
Currency.format_human(bet),
Currency.format_human(payout),
results,
emojis,
)
await ctx.edit(embed=finished_output)
@ -71,7 +81,7 @@ async def cmd(self, ctx, bet):
bet=bet,
payout=payout,
spin_type=type,
icons=results
icons=results,
)
ctx_currency.push()
@ -104,7 +114,6 @@ def calculate_slots_results(bet, results):
# 3 of a kind
elif len(counts) == 1:
if results[0] == 5:
type = "three_diamonds"
multiplier = rewards[type]
@ -139,21 +148,25 @@ def slots_spinning(ctx, spinning_icons_amount, bet, results, emojis):
one = first_slots_emote
two = second_slots_emote
description = f"🎰{emojis['S_Wide']}{emojis['L_Wide']}{emojis['O_Wide']}{emojis['T_Wide']}{emojis['S_Wide']}🎰\n" \
f"{emojis['CBorderTLeft']}{emojis['HBorderT']}{emojis['HBorderT']}{emojis['HBorderT']}" \
f"{emojis['HBorderT']}{emojis['HBorderT']}{emojis['CBorderTRight']}\n" \
f"{emojis['VBorder']}{one}{emojis['VBorder']}{two}{emojis['VBorder']}" \
f"{three}{emojis['VBorder']}\n" \
f"{emojis['CBorderBLeft']}{emojis['HBorderB']}{emojis['HBorderB']}{emojis['HBorderB']}" \
f"{emojis['HBorderB']}{emojis['HBorderB']}{emojis['CBorderBRight']}\n" \
f"{emojis['Blank']}{emojis['Blank']}❓❓❓{emojis['Blank']}{emojis['Blank']}{emojis['Blank']}"
description = (
f"🎰{emojis['S_Wide']}{emojis['L_Wide']}{emojis['O_Wide']}{emojis['T_Wide']}{emojis['S_Wide']}🎰\n"
f"{emojis['CBorderTLeft']}{emojis['HBorderT']}{emojis['HBorderT']}{emojis['HBorderT']}"
f"{emojis['HBorderT']}{emojis['HBorderT']}{emojis['CBorderTRight']}\n"
f"{emojis['VBorder']}{one}{emojis['VBorder']}{two}{emojis['VBorder']}"
f"{three}{emojis['VBorder']}\n"
f"{emojis['CBorderBLeft']}{emojis['HBorderB']}{emojis['HBorderB']}{emojis['HBorderB']}"
f"{emojis['HBorderB']}{emojis['HBorderB']}{emojis['CBorderBRight']}\n"
f"{emojis['Blank']}{emojis['Blank']}❓❓❓{emojis['Blank']}{emojis['Blank']}{emojis['Blank']}"
)
embed = discord.Embed(
description=description
description=description,
)
embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar.url)
embed.set_footer(text=f"Bet ${bet} • jackpot = x15 • {current_time}",
icon_url="https://i.imgur.com/wFsgSnr.png")
embed.set_footer(
text=f"Bet ${bet} • jackpot = x15 • {current_time}",
icon_url="https://i.imgur.com/wFsgSnr.png",
)
return embed
@ -190,27 +203,33 @@ def slots_finished(ctx, payout_type, bet, payout, results, emojis):
is_lost = False
color = discord.Color.green()
description = f"🎰{emojis['S_Wide']}{emojis['L_Wide']}{emojis['O_Wide']}{emojis['T_Wide']}{emojis['S_Wide']}🎰\n" \
f"{emojis['CBorderTLeft']}{emojis['HBorderT']}{emojis['HBorderT']}{emojis['HBorderT']}" \
f"{emojis['HBorderT']}{emojis['HBorderT']}{emojis['CBorderTRight']}\n" \
f"{emojis['VBorder']}{first_slots_emote}{emojis['VBorder']}{second_slots_emote}" \
f"{emojis['VBorder']}{third_slots_emote}{emojis['VBorder']}\n" \
f"{emojis['CBorderBLeft']}{emojis['HBorderB']}{emojis['HBorderB']}{emojis['HBorderB']}" \
f"{emojis['HBorderB']}{emojis['HBorderB']}{emojis['CBorderBRight']}"
description = (
f"🎰{emojis['S_Wide']}{emojis['L_Wide']}{emojis['O_Wide']}{emojis['T_Wide']}{emojis['S_Wide']}🎰\n"
f"{emojis['CBorderTLeft']}{emojis['HBorderT']}{emojis['HBorderT']}{emojis['HBorderT']}"
f"{emojis['HBorderT']}{emojis['HBorderT']}{emojis['CBorderTRight']}\n"
f"{emojis['VBorder']}{first_slots_emote}{emojis['VBorder']}{second_slots_emote}"
f"{emojis['VBorder']}{third_slots_emote}{emojis['VBorder']}\n"
f"{emojis['CBorderBLeft']}{emojis['HBorderB']}{emojis['HBorderB']}{emojis['HBorderB']}"
f"{emojis['HBorderB']}{emojis['HBorderB']}{emojis['CBorderBRight']}"
)
if is_lost:
description += f"\n{emojis['Blank']}{emojis['LCentered']}{emojis['OCentered']}{emojis['SCentered']}" \
f"{emojis['ECentered']}{emojis['lost']}{emojis['Blank']}"
description += (
f"\n{emojis['Blank']}{emojis['LCentered']}{emojis['OCentered']}{emojis['SCentered']}"
f"{emojis['ECentered']}{emojis['lost']}{emojis['Blank']}"
)
else:
description += f"\n{emojis['Blank']}🎉{emojis['WSmall']}{emojis['ISmall']}{emojis['NSmall']}🎉{emojis['Blank']}"
embed = discord.Embed(
color=color,
description=description
description=description,
)
embed.add_field(name=field_name, value=field_value, inline=False)
embed.set_author(name=ctx.author.name, icon_url=ctx.author.avatar.url)
embed.set_footer(text=f"Game finished • {current_time}",
icon_url="https://i.imgur.com/wFsgSnr.png")
embed.set_footer(
text=f"Game finished • {current_time}",
icon_url="https://i.imgur.com/wFsgSnr.png",
)
return embed

View file

@ -21,7 +21,7 @@ async def cmd(self, ctx, game):
stats["amount_of_games"],
total_bet,
stats["winning_amount"],
total_payout
total_payout,
)
elif game == "Slots":
@ -31,12 +31,18 @@ async def cmd(self, ctx, game):
total_bet = Currency.format_human(stats["total_bet"])
total_payout = Currency.format_human(stats["total_payout"])
output = strings["stats_slots"].format(stats["amount_of_games"], total_bet, total_payout)
output = strings["stats_slots"].format(
stats["amount_of_games"],
total_bet,
total_payout,
)
output += "\n\n"
pair_emote = self.client.get_emoji(resources["slots"]["emotes"]["slots_0_id"])
three_emote = self.client.get_emoji(resources["slots"]["emotes"]["slots_4_id"])
diamonds_emote = self.client.get_emoji(resources["slots"]["emotes"]["slots_5_id"])
diamonds_emote = self.client.get_emoji(
resources["slots"]["emotes"]["slots_5_id"],
)
seven_emote = self.client.get_emoji(resources["slots"]["emotes"]["slots_6_id"])
output += f"{pair_emote} | **{stats['games_won_pair']}** pairs.\n"

View file

@ -131,7 +131,8 @@ class LeaderboardCommandView(discord.ui.View):
embed.set_author(name=CONST.STRINGS["xp_lb_author"], icon_url=icon)
for rank, (user_id, xp, level, xp_needed_for_next_level) in enumerate(
xp_lb[:5], start=1
xp_lb[:5],
start=1,
):
try:
member = await self.ctx.guild.fetch_member(user_id)
@ -141,7 +142,9 @@ class LeaderboardCommandView(discord.ui.View):
embed.add_field(
name=CONST.STRINGS["xp_lb_field_name"].format(rank, member.name),
value=CONST.STRINGS["xp_lb_field_value"].format(
level, xp, xp_needed_for_next_level
level,
xp,
xp_needed_for_next_level,
),
inline=False,
)
@ -165,7 +168,7 @@ class LeaderboardCommandView(discord.ui.View):
embed.add_field(
name=f"#{rank} - {name}",
value=CONST.STRINGS["xp_lb_currency_field_value"].format(
Currency.format(balance)
Currency.format(balance),
),
inline=False,
)
@ -191,7 +194,8 @@ class LeaderboardCommandView(discord.ui.View):
embed.add_field(
name=f"#{rank} - {name}",
value=CONST.STRINGS["xp_lb_dailies_field_value"].format(
streak, claimed_at
streak,
claimed_at,
),
inline=False,
)

View file

@ -98,7 +98,9 @@ class Misc(commands.Cog):
xkcd submodule - slash command only
"""
xkcd: SlashCommandGroup = SlashCommandGroup(
"xkcd", "A web comic of romance, sarcasm, math, and language.", guild_only=False
"xkcd",
"A web comic of romance, sarcasm, math, and language.",
guild_only=False,
)
@xkcd.command(name="latest", description="Get the latest xkcd comic.")

View file

@ -18,8 +18,10 @@ async def get_avatar(ctx: bridge.Context, member: Member) -> None:
The discord context object.
member : Member
The member to get the avatar of.
"""
guild_avatar: Optional[str] = member.guild_avatar.url if member.guild_avatar else None
"""
guild_avatar: Optional[str] = (
member.guild_avatar.url if member.guild_avatar else None
)
profile_avatar: Optional[str] = member.avatar.url if member.avatar else None
files: list[File] = [

View file

@ -16,14 +16,16 @@ async def cmd(self, ctx: bridge.Context, unix_timestamp: int) -> None:
memory_usage_in_mb: float = psutil.Process().memory_info().rss / (1024 * 1024)
total_rows: str = Currency.format(BlackJackStats.get_total_rows_count())
description: str = "".join([
CONST.STRINGS["info_uptime"].format(unix_timestamp),
CONST.STRINGS["info_latency"].format(round(1000 * self.client.latency)),
CONST.STRINGS["info_memory"].format(memory_usage_in_mb),
CONST.STRINGS["info_system"].format(platform.system(), os.name),
CONST.STRINGS["info_api_version"].format(discord.__version__),
CONST.STRINGS["info_database_records"].format(total_rows)
])
description: str = "".join(
[
CONST.STRINGS["info_uptime"].format(unix_timestamp),
CONST.STRINGS["info_latency"].format(round(1000 * self.client.latency)),
CONST.STRINGS["info_memory"].format(memory_usage_in_mb),
CONST.STRINGS["info_system"].format(platform.system(), os.name),
CONST.STRINGS["info_api_version"].format(discord.__version__),
CONST.STRINGS["info_database_records"].format(total_rows),
],
)
embed: discord.Embed = EmbedBuilder.create_success_embed(
ctx,

View file

@ -25,17 +25,18 @@ async def cmd(self, ctx: bridge.Context) -> None:
author_text=CONST.STRINGS["intro_no_guild_author"],
description=CONST.STRINGS["intro_no_guild"],
footer_text=CONST.STRINGS["intro_service_name"],
)
),
)
return
question_mapping: Dict[str, str] = CONST.KRC_QUESTION_MAPPING
channel: Optional[discord.abc.GuildChannel] = guild.get_channel(
CONST.KRC_INTRO_CHANNEL_ID
CONST.KRC_INTRO_CHANNEL_ID,
)
if not channel or isinstance(
channel, (discord.ForumChannel, discord.CategoryChannel)
channel,
(discord.ForumChannel, discord.CategoryChannel),
):
await ctx.respond(
embed=EmbedBuilder.create_error_embed(
@ -43,7 +44,7 @@ async def cmd(self, ctx: bridge.Context) -> None:
author_text=CONST.STRINGS["intro_no_channel_author"],
description=CONST.STRINGS["intro_no_channel"],
footer_text=CONST.STRINGS["intro_service_name"],
)
),
)
return
@ -68,7 +69,7 @@ async def cmd(self, ctx: bridge.Context) -> None:
author_text=CONST.STRINGS["intro_stopped_author"],
description=CONST.STRINGS["intro_stopped"],
footer_text=CONST.STRINGS["intro_service_name"],
)
),
)
return
@ -76,7 +77,8 @@ async def cmd(self, ctx: bridge.Context) -> None:
def check(message: discord.Message) -> bool:
return message.author == ctx.author and isinstance(
message.channel, discord.DMChannel
message.channel,
discord.DMChannel,
)
answer_mapping: Dict[str, str] = {}
@ -88,12 +90,14 @@ async def cmd(self, ctx: bridge.Context) -> None:
author_text=key,
description=question,
footer_text=CONST.STRINGS["intro_question_footer"],
)
),
)
try:
answer: discord.Message = await self.client.wait_for(
"message", check=check, timeout=120
"message",
check=check,
timeout=120,
)
answer_content: str = answer.content.replace("\n", " ")
@ -104,7 +108,7 @@ async def cmd(self, ctx: bridge.Context) -> None:
author_text=CONST.STRINGS["intro_too_long_author"],
description=CONST.STRINGS["intro_too_long"],
footer_text=CONST.STRINGS["intro_service_name"],
)
),
)
return
@ -117,7 +121,7 @@ async def cmd(self, ctx: bridge.Context) -> None:
author_text=CONST.STRINGS["intro_timeout_author"],
description=CONST.STRINGS["intro_timeout"],
footer_text=CONST.STRINGS["intro_service_name"],
)
),
)
return
@ -147,9 +151,9 @@ async def cmd(self, ctx: bridge.Context) -> None:
embed=EmbedBuilder.create_embed(
ctx,
description=CONST.STRINGS["intro_post_confirmation"].format(
channel.mention
channel.mention,
),
)
),
)
else:
await ctx.send(
@ -158,5 +162,5 @@ async def cmd(self, ctx: bridge.Context) -> None:
author_text=CONST.STRINGS["intro_stopped_author"],
description=CONST.STRINGS["intro_stopped"],
footer_text=CONST.STRINGS["intro_service_name"],
)
),
)

View file

@ -9,7 +9,8 @@ from lib.constants import CONST
async def cmd(ctx: bridge.BridgeContext) -> None:
await ctx.respond(
embed=EmbedBuilder.create_success_embed(
ctx, description=CONST.STRINGS["invite_description"]
ctx,
description=CONST.STRINGS["invite_description"],
),
view=InviteButton(),
)

View file

@ -3,22 +3,28 @@ from lib.embed_builder import EmbedBuilder
from lib.constants import CONST
from datetime import datetime
async def ping(self, ctx: bridge.BridgeContext) -> None:
embed = EmbedBuilder.create_success_embed(
ctx,
author_text=CONST.STRINGS["ping_author"],
description=CONST.STRINGS["ping_pong"],
footer_text=CONST.STRINGS["ping_footer"].format(round(1000 * self.client.latency))
footer_text=CONST.STRINGS["ping_footer"].format(
round(1000 * self.client.latency),
),
)
await ctx.respond(embed=embed)
async def uptime(self, ctx: bridge.BridgeContext, start_time: datetime) -> None:
unix_timestamp: int = int(round(self.start_time.timestamp()))
embed = EmbedBuilder.create_success_embed(
ctx,
author_text=CONST.STRINGS["ping_author"],
description=CONST.STRINGS["ping_uptime"].format(unix_timestamp),
footer_text=CONST.STRINGS["ping_footer"].format(round(1000 * self.client.latency))
footer_text=CONST.STRINGS["ping_footer"].format(
round(1000 * self.client.latency),
),
)
await ctx.respond(embed=embed)

View file

@ -7,7 +7,11 @@ from typing import Optional
_xkcd = Client()
async def print_comic(ctx: bridge.Context, latest: bool = False, number: Optional[int] = None) -> None:
async def print_comic(
ctx: bridge.Context,
latest: bool = False,
number: Optional[int] = None,
) -> None:
try:
if latest:
comic = _xkcd.get_latest_comic(raw_comic_image=True)
@ -20,11 +24,14 @@ async def print_comic(ctx: bridge.Context, latest: bool = False, number: Optiona
embed=EmbedBuilder.create_success_embed(
ctx,
author_text=CONST.STRINGS["xkcd_title"].format(comic.id, comic.title),
description=CONST.STRINGS["xkcd_description"].format(comic.explanation_url, comic.comic_url),
description=CONST.STRINGS["xkcd_description"].format(
comic.explanation_url,
comic.comic_url,
),
footer_text=CONST.STRINGS["xkcd_footer"],
image_url=comic.image_url,
show_name=False,
)
),
)
except HttpError:
@ -34,5 +41,5 @@ async def print_comic(ctx: bridge.Context, latest: bool = False, number: Optiona
author_text=CONST.STRINGS["xkcd_not_found_author"],
description=CONST.STRINGS["xkcd_not_found"],
footer_text=CONST.STRINGS["xkcd_footer"],
)
),
)

View file

@ -5,7 +5,6 @@ from modules.moderation import ban
class Moderation(commands.Cog):
def __init__(self, client):
self.client = client
@ -14,12 +13,18 @@ class Moderation(commands.Cog):
aliases=["b"],
description="Ban a user from the server.",
help="Bans a user from the server, you can use ID or mention them.",
guild_only=True
guild_only=True,
)
@bridge.has_permissions(ban_members=True)
@commands.bot_has_permissions(ban_members=True)
@commands.guild_only()
async def ban_command(self, ctx, target: discord.User, *, reason: str | None = None):
async def ban_command(
self,
ctx,
target: discord.User,
*,
reason: str | None = None,
):
await ban.ban_user(self, ctx, target, reason)
@bridge.bridge_command(
@ -27,12 +32,18 @@ class Moderation(commands.Cog):
aliases=["ub", "pardon"],
description="Unbans a user from the server.",
help="Unbans a user from the server, you can use ID or provide their username.",
guild_only=True
guild_only=True,
)
@bridge.has_permissions(ban_members=True)
@commands.bot_has_permissions(ban_members=True)
@commands.guild_only()
async def unban_command(self, ctx, target: discord.User, *, reason: str | None = None):
async def unban_command(
self,
ctx,
target: discord.User,
*,
reason: str | None = None,
):
await ban.unban_user(ctx, target, reason)

View file

@ -24,10 +24,12 @@ async def ban_user(cog, ctx, target: discord.User, reason):
ctx,
author_text=CONST.STRINGS["mod_banned_author"],
description=CONST.STRINGS["mod_ban_dm"].format(
target.name, ctx.guild.name, reason
target.name,
ctx.guild.name,
reason,
),
show_name=False,
)
),
)
dm_sent = True
@ -36,8 +38,9 @@ async def ban_user(cog, ctx, target: discord.User, reason):
await member.ban(
reason=CONST.STRINGS["mod_reason"].format(
ctx.author.name, formatter.shorten(reason, 200)
)
ctx.author.name,
formatter.shorten(reason, 200),
),
)
return await ctx.respond(
embed=EmbedBuilder.create_success_embed(
@ -47,7 +50,7 @@ async def ban_user(cog, ctx, target: discord.User, reason):
footer_text=CONST.STRINGS["mod_dm_sent"]
if dm_sent
else CONST.STRINGS["mod_dm_not_sent"],
)
),
)
# not a member in this guild, so ban right away
@ -55,7 +58,8 @@ async def ban_user(cog, ctx, target: discord.User, reason):
await ctx.guild.ban(
target,
reason=CONST.STRINGS["mod_reason"].format(
ctx.author.name, formatter.shorten(reason, 200)
ctx.author.name,
formatter.shorten(reason, 200),
),
)
return await ctx.respond(
@ -63,7 +67,7 @@ async def ban_user(cog, ctx, target: discord.User, reason):
ctx,
author_text=CONST.STRINGS["mod_banned_author"],
description=CONST.STRINGS["mod_banned_user"].format(target.id),
)
),
)
@ -75,7 +79,8 @@ async def unban_user(ctx, target: discord.User, reason):
await ctx.guild.unban(
target,
reason=CONST.STRINGS["mod_reason"].format(
ctx.author.name, formatter.shorten(reason, 200)
ctx.author.name,
formatter.shorten(reason, 200),
),
)
return await ctx.respond(
@ -83,7 +88,7 @@ async def unban_user(ctx, target: discord.User, reason):
ctx,
author_text=CONST.STRINGS["mod_unbanned_author"],
description=CONST.STRINGS["mod_unbanned"].format(target.id),
)
),
)
except (discord.NotFound, discord.HTTPException):
@ -92,5 +97,5 @@ async def unban_user(ctx, target: discord.User, reason):
ctx,
author_text=CONST.STRINGS["mod_not_banned_author"],
description=CONST.STRINGS["mod_not_banned"].format(target.id),
)
),
)

View file

@ -4,7 +4,11 @@ from discord.ext.commands import BadArgument
from lib.exceptions import LumiExceptions
def actionable(target: discord.Member, invoker: discord.Member, bot_user: discord.Member) -> None:
def actionable(
target: discord.Member,
invoker: discord.Member,
bot_user: discord.Member,
) -> None:
"""
Checks if the invoker and client have a higher role than the target user.

View file

@ -13,7 +13,10 @@ class Triggers(commands.Cog):
self.client = client
trigger = SlashCommandGroup(
"trigger", "Manage custom reactions.", guild_only=True, default_member_permissions=discord.Permissions(manage_guild=True)
"trigger",
"Manage custom reactions.",
guild_only=True,
default_member_permissions=discord.Permissions(manage_guild=True),
)
add = trigger.create_subgroup("add", "Add new custom reactions.")
@ -28,7 +31,7 @@ class Triggers(commands.Cog):
ctx,
trigger_text: str,
response: str,
is_full_match: bool
is_full_match: bool,
):
await add_reaction(ctx, trigger_text, response, None, False, is_full_match)
@ -43,10 +46,10 @@ class Triggers(commands.Cog):
ctx,
trigger_text: str,
emoji: discord.Emoji,
is_full_match: bool
is_full_match: bool,
):
await add_reaction(ctx, trigger_text, None, emoji.id, True, is_full_match)
@trigger.command(
name="delete",
description="Delete an existing custom reaction.",
@ -56,10 +59,10 @@ class Triggers(commands.Cog):
async def delete_reaction_command(
self,
ctx,
reaction_id: int
reaction_id: int,
):
await delete_reaction(ctx, reaction_id)
@trigger.command(
name="list",
description="List all custom reactions.",
@ -68,9 +71,10 @@ class Triggers(commands.Cog):
@commands.guild_only()
async def list_reactions_command(
self,
ctx
ctx,
):
await list_reactions(ctx)
def setup(client: LumiBot):
client.add_cog(Triggers(client))
client.add_cog(Triggers(client))

View file

@ -3,18 +3,38 @@ from typing import Optional
from services.reactions_service import CustomReactionsService
from lib.embeds.triggers import create_creation_embed, create_failure_embed
async def add_reaction(ctx: bridge.Context, trigger_text: str, response: Optional[str], emoji_id: Optional[int], is_emoji: bool, is_full_match: bool) -> None:
async def add_reaction(
ctx: bridge.Context,
trigger_text: str,
response: Optional[str],
emoji_id: Optional[int],
is_emoji: bool,
is_full_match: bool,
) -> None:
if ctx.guild is None:
return
reaction_service = CustomReactionsService()
guild_id: int = ctx.guild.id
creator_id: int = ctx.author.id
if not await check_reaction_limit(ctx, reaction_service, guild_id, trigger_text, is_emoji):
if not await check_reaction_limit(
ctx,
reaction_service,
guild_id,
trigger_text,
is_emoji,
):
return
if not await check_existing_trigger(ctx, reaction_service, guild_id, trigger_text, is_emoji):
if not await check_existing_trigger(
ctx,
reaction_service,
guild_id,
trigger_text,
is_emoji,
):
return
success: bool = await reaction_service.create_custom_reaction(
@ -25,27 +45,51 @@ async def add_reaction(ctx: bridge.Context, trigger_text: str, response: Optiona
emoji_id=emoji_id,
is_emoji=is_emoji,
is_full_match=is_full_match,
is_global=False, # only bot admins can create global custom reactions
is_global=False, # only bot admins can create global custom reactions
)
if success:
embed = create_creation_embed(trigger_text, response, emoji_id, is_emoji, is_full_match)
embed = create_creation_embed(
trigger_text,
response,
emoji_id,
is_emoji,
is_full_match,
)
await ctx.respond(embed=embed)
else:
embed = create_failure_embed(trigger_text, is_emoji)
await ctx.respond(embed=embed)
async def check_reaction_limit(ctx: bridge.Context, reaction_service: CustomReactionsService, guild_id: int, trigger_text: str, is_emoji: bool) -> bool:
async def check_reaction_limit(
ctx: bridge.Context,
reaction_service: CustomReactionsService,
guild_id: int,
trigger_text: str,
is_emoji: bool,
) -> bool:
if await reaction_service.count_custom_reactions(guild_id) >= 100:
embed = create_failure_embed(trigger_text, is_emoji, limit_reached=True)
await ctx.respond(embed=embed)
return False
return True
async def check_existing_trigger(ctx: bridge.Context, reaction_service: CustomReactionsService, guild_id: int, trigger_text: str, is_emoji: bool) -> bool:
async def check_existing_trigger(
ctx: bridge.Context,
reaction_service: CustomReactionsService,
guild_id: int,
trigger_text: str,
is_emoji: bool,
) -> bool:
existing_trigger = await reaction_service.find_trigger(guild_id, trigger_text)
if existing_trigger:
embed = create_failure_embed(trigger_text, is_emoji, trigger_already_exists=True)
embed = create_failure_embed(
trigger_text,
is_emoji,
trigger_already_exists=True,
)
await ctx.respond(embed=embed)
return False
return True
return True

View file

@ -1,11 +1,16 @@
from discord.ext import bridge
from services.reactions_service import CustomReactionsService
from lib.embeds.triggers import create_deletion_embed, create_failure_embed, create_not_found_embed
from lib.embeds.triggers import (
create_deletion_embed,
create_failure_embed,
create_not_found_embed,
)
async def delete_reaction(ctx: bridge.Context, reaction_id: int) -> None:
if ctx.guild is None:
return
reaction_service = CustomReactionsService()
guild_id: int = ctx.guild.id
@ -27,4 +32,4 @@ async def delete_reaction(ctx: bridge.Context, reaction_id: int) -> None:
await ctx.respond(embed=embed)
else:
embed = create_failure_embed(trigger_text, is_emoji)
await ctx.respond(embed=embed)
await ctx.respond(embed=embed)

View file

@ -10,10 +10,11 @@ resources = JsonCache.read_json("art")
check_icon = resources["icons"]["check"]
logo = resources["logo"]["transparent"]
async def list_reactions(ctx: bridge.Context) -> None:
if ctx.guild is None:
return
reaction_service = CustomReactionsService()
guild_id: int = ctx.guild.id
@ -37,7 +38,7 @@ async def list_reactions(ctx: bridge.Context) -> None:
embed = discord.Embed(
title=f"ID: {reaction['id']}",
description=description,
color=0xFF8C00
color=0xFF8C00,
)
embed.set_author(name="Custom Reactions", icon_url=check_icon)
embed.set_footer(text="Reaction Service", icon_url=logo)

6
poetry.lock generated
View file

@ -755,13 +755,13 @@ voice = ["PyNaCl (>=1.3.0,<1.6)"]
[[package]]
name = "pyright"
version = "1.1.371"
version = "1.1.372"
description = "Command line wrapper for pyright"
optional = false
python-versions = ">=3.7"
files = [
{file = "pyright-1.1.371-py3-none-any.whl", hash = "sha256:cce52e42ff73943243e7e5e24f2a59dee81b97d99f4e3cf97370b27e8a1858cd"},
{file = "pyright-1.1.371.tar.gz", hash = "sha256:777b508b92dda2db476214c400ce043aad8d8f3dd0e10d284c96e79f298308b5"},
{file = "pyright-1.1.372-py3-none-any.whl", hash = "sha256:25b15fb8967740f0949fd35b963777187f0a0404c0bd753cc966ec139f3eaa0b"},
{file = "pyright-1.1.372.tar.gz", hash = "sha256:a9f5e0daa955daaa17e3d1ef76d3623e75f8afd5e37b437d3ff84d5b38c15420"},
]
[package.dependencies]

View file

@ -38,4 +38,3 @@ class BlacklistUserService:
"""
result: List[Tuple[bool]] = database.select_query(query, (user_id,))
return any(active for (active,) in result)

View file

@ -31,10 +31,19 @@ class GuildConfig:
"""
try:
(birthday_channel_id, command_channel_id, intro_channel_id,
welcome_channel_id, welcome_message, boost_channel_id, boost_message, boost_image_url,
level_channel_id, level_message, level_message_type) = \
database.select_query(query, (self.guild_id,))[0]
(
birthday_channel_id,
command_channel_id,
intro_channel_id,
welcome_channel_id,
welcome_message,
boost_channel_id,
boost_message,
boost_image_url,
level_channel_id,
level_message,
level_message_type,
) = database.select_query(query, (self.guild_id,))[0]
self.birthday_channel_id = birthday_channel_id
self.command_channel_id = command_channel_id
@ -71,11 +80,23 @@ class GuildConfig:
WHERE guild_id = %s;
"""
database.execute_query(query, (self.birthday_channel_id, self.command_channel_id,
self.intro_channel_id, self.welcome_channel_id, self.welcome_message,
self.boost_channel_id, self.boost_message, self.boost_image_url,
self.level_channel_id, self.level_message,
self.level_message_type, self.guild_id))
database.execute_query(
query,
(
self.birthday_channel_id,
self.command_channel_id,
self.intro_channel_id,
self.welcome_channel_id,
self.welcome_message,
self.boost_channel_id,
self.boost_message,
self.boost_image_url,
self.level_channel_id,
self.level_message,
self.level_message_type,
self.guild_id,
),
)
@staticmethod
def get_prefix(guild_id):

View file

@ -74,14 +74,18 @@ class Currency:
@staticmethod
def format_human(num):
num = float('{:.3g}'.format(num))
num = float("{:.3g}".format(num))
magnitude = 0
while abs(num) >= 1000:
magnitude += 1
num /= 1000.0
return '{}{}'.format('{:f}'.format(num).rstrip('0').rstrip('.'),
['', 'K', 'M', 'B', 'T', 'Q', 'Qi', 'Sx', 'Sp', 'Oc', 'No', 'Dc'][magnitude])
return "{}{}".format(
"{:f}".format(num).rstrip("0").rstrip("."),
["", "K", "M", "B", "T", "Q", "Qi", "Sx", "Sp", "Oc", "No", "Dc"][
magnitude
],
)
# A Thousand = K
# Million = M

View file

@ -17,7 +17,10 @@ class Dailies:
self.tz = pytz.timezone("US/Eastern")
self.time_now: datetime = datetime.now(tz=self.tz)
self.reset_time: datetime = self.time_now.replace(
hour=7, minute=0, second=0, microsecond=0
hour=7,
minute=0,
second=0,
microsecond=0,
)
data: Tuple[Optional[str], int] = Dailies.get_data(user_id)

View file

@ -76,16 +76,18 @@ class LumiHelp(commands.HelpCommand):
channel = self.get_destination()
await channel.send(
embed=HelpErrors.error_message(
self.context, f'No command called "{group.qualified_name}" found.'
)
self.context,
f'No command called "{group.qualified_name}" found.',
),
)
async def send_cog_help(self, cog):
channel = self.get_destination()
await channel.send(
embed=HelpErrors.error_message(
self.context, f'No command called "{cog.qualified_name}" found.'
)
self.context,
f'No command called "{cog.qualified_name}" found.',
),
)
async def command_callback(self, ctx, *, command=None):
@ -112,7 +114,8 @@ class LumiHelp(commands.HelpCommand):
cmd = bot.all_commands.get(keys[0].removeprefix(self.context.prefix))
if cmd is None:
string = await maybe_coro(
self.command_not_found, self.remove_mentions(keys[0])
self.command_not_found,
self.remove_mentions(keys[0]),
)
return await self.send_error_message(string)
@ -121,13 +124,17 @@ class LumiHelp(commands.HelpCommand):
found = cmd.all_commands.get(key)
except AttributeError:
string = await maybe_coro(
self.subcommand_not_found, cmd, self.remove_mentions(key)
self.subcommand_not_found,
cmd,
self.remove_mentions(key),
)
return await self.send_error_message(string)
else:
if found is None:
string = await maybe_coro(
self.subcommand_not_found, cmd, self.remove_mentions(key)
self.subcommand_not_found,
cmd,
self.remove_mentions(key),
)
return await self.send_error_message(string)
cmd = found

View file

@ -23,10 +23,12 @@ class Inventory:
ON DUPLICATE KEY UPDATE quantity = quantity + %s;
"""
database.execute_query(query, (self.user_id, item.id, abs(quantity), abs(quantity)))
database.execute_query(
query,
(self.user_id, item.id, abs(quantity), abs(quantity)),
)
def take_item(self, item: item_service.Item, quantity=1):
query = """
INSERT INTO inventory (user_id, item_id, quantity)
VALUES (%s, %s, 0)
@ -36,7 +38,10 @@ class Inventory:
END;
"""
database.execute_query(query, (self.user_id, item.id, self.user_id, item.id, abs(quantity)))
database.execute_query(
query,
(self.user_id, item.id, self.user_id, item.id, abs(quantity)),
)
def get_inventory(self):
query = "SELECT item_id, quantity FROM inventory WHERE user_id = %s AND quantity > 0"

View file

@ -8,7 +8,9 @@ class CustomReactionsService:
pass
async def find_trigger(
self, guild_id: int, message_content: str
self,
guild_id: int,
message_content: str,
) -> Optional[Dict[str, Any]]:
message_content = message_content.lower()
query = """
@ -21,7 +23,8 @@ class CustomReactionsService:
LIMIT 1
"""
result = database.select_query(
query, (guild_id, message_content, message_content, guild_id)
query,
(guild_id, message_content, message_content, guild_id),
)
if result:
reaction = result[0] # Get the first result from the list
@ -91,7 +94,7 @@ class CustomReactionsService:
"created_at": reaction[10],
"updated_at": reaction[11],
"type": "emoji" if reaction[4] else "text",
}
},
)
return reactions

View file

@ -18,7 +18,14 @@ class BlackJackStats:
VALUES (%s, %s, %s, %s, %s, %s)
"""
values = (self.user_id, self.is_won, self.bet, self.payout, self.hand_player, self.hand_dealer)
values = (
self.user_id,
self.is_won,
self.bet,
self.payout,
self.hand_player,
self.hand_dealer,
)
database.execute_query(query, values)
@ -34,15 +41,20 @@ class BlackJackStats:
FROM blackjack
WHERE user_id = %s;
"""
(amount_of_games, total_bet,
total_payout, winning_amount, losing_amount) = database.select_query(query, (user_id,))[0]
(
amount_of_games,
total_bet,
total_payout,
winning_amount,
losing_amount,
) = database.select_query(query, (user_id,))[0]
return {
"amount_of_games": amount_of_games,
"total_bet": total_bet,
"total_payout": total_payout,
"winning_amount": winning_amount,
"losing_amount": losing_amount
"losing_amount": losing_amount,
}
@staticmethod
@ -77,7 +89,14 @@ class SlotsStats:
VALUES (%s, %s, %s, %s, %s, %s)
"""
values = (self.user_id, self.is_won, self.bet, self.payout, self.spin_type, self.icons)
values = (
self.user_id,
self.is_won,
self.bet,
self.payout,
self.spin_type,
self.icons,
)
database.execute_query(query, values)
@ -99,9 +118,15 @@ class SlotsStats:
WHERE user_id = %s
"""
(amount_of_games, total_bet,
total_payout, games_won_pair, games_won_three_of_a_kind,
games_won_three_diamonds, games_won_jackpot) = database.select_query(query, (user_id,))[0]
(
amount_of_games,
total_bet,
total_payout,
games_won_pair,
games_won_three_of_a_kind,
games_won_three_diamonds,
games_won_jackpot,
) = database.select_query(query, (user_id,))[0]
return {
"amount_of_games": amount_of_games,
@ -110,5 +135,5 @@ class SlotsStats:
"games_won_pair": games_won_pair,
"games_won_three_of_a_kind": games_won_three_of_a_kind,
"games_won_three_diamonds": games_won_three_diamonds,
"games_won_jackpot": games_won_jackpot
"games_won_jackpot": games_won_jackpot,
}

View file

@ -30,11 +30,11 @@ class Comic:
"""
def __init__(
self,
xkcd_dict: dict[str, Any],
raw_image: bytes | None = None,
comic_url: str | None = None,
explanation_url: str | None = None,
self,
xkcd_dict: dict[str, Any],
raw_image: bytes | None = None,
comic_url: str | None = None,
explanation_url: str | None = None,
) -> None:
self.id: int | None = xkcd_dict.get("num")
self.date: datetime.date | None = self._determine_date(xkcd_dict)
@ -59,7 +59,9 @@ class Comic:
"""
try:
return datetime.date(
int(xkcd_dict["year"]), int(xkcd_dict["month"]), int(xkcd_dict["day"])
int(xkcd_dict["year"]),
int(xkcd_dict["month"]),
int(xkcd_dict["day"]),
)
except (KeyError, ValueError):
@ -102,9 +104,9 @@ class Comic:
class Client:
def __init__(
self,
api_url: str = "https://xkcd.com",
explanation_wiki_url: str = "https://www.explainxkcd.com/wiki/index.php/",
self,
api_url: str = "https://xkcd.com",
explanation_wiki_url: str = "https://www.explainxkcd.com/wiki/index.php/",
) -> None:
self._api_url = api_url
self._explanation_wiki_url = explanation_wiki_url
@ -154,7 +156,11 @@ class Client:
comic_url: str = f"{self._api_url}/{response_dict['num']}/"
explanation_url: str = f"{self._explanation_wiki_url}{response_dict['num']}"
return Comic(response_dict, comic_url=comic_url, explanation_url=explanation_url)
return Comic(
response_dict,
comic_url=comic_url,
explanation_url=explanation_url,
)
def _fetch_comic(self, comic_id: int, raw_comic_image: bool) -> Comic:
"""
@ -252,14 +258,19 @@ class Client:
HttpError
If the request fails.
"""
comic_url = self.latest_comic_url() if comic_id <= 0 else self.comic_id_url(comic_id)
comic_url = (
self.latest_comic_url() if comic_id <= 0 else self.comic_id_url(comic_id)
)
try:
response = httpx.get(comic_url)
response.raise_for_status()
except httpx.HTTPStatusError as exc:
raise HttpError(exc.response.status_code, exc.response.reason_phrase) from exc
raise HttpError(
exc.response.status_code,
exc.response.reason_phrase,
) from exc
return response.text
@ -291,7 +302,10 @@ class Client:
response.raise_for_status()
except httpx.HTTPStatusError as exc:
raise HttpError(exc.response.status_code, exc.response.reason_phrase) from exc
raise HttpError(
exc.response.status_code,
exc.response.reason_phrase,
) from exc
return response.content

View file

@ -52,7 +52,8 @@ class XpService:
try:
user_xp, user_level, cooldown = database.select_query(
query, (self.user_id, self.guild_id)
query,
(self.user_id, self.guild_id),
)[0]
except (IndexError, TypeError):
user_xp, user_level, cooldown = 0, 0, None
@ -83,14 +84,16 @@ class XpService:
ORDER BY user_level DESC, user_xp DESC
"""
data: List[Tuple[int, int, int]] = database.select_query(
query, (self.guild_id,)
query,
(self.guild_id,),
)
leaderboard: List[Tuple[int, int, int, int]] = [
(row[0], row[1], row[2], rank) for rank, row in enumerate(data, start=1)
]
return next(
(entry[3] for entry in leaderboard if entry[0] == self.user_id), None
(entry[3] for entry in leaderboard if entry[0] == self.user_id),
None,
)
@staticmethod
@ -118,18 +121,20 @@ class XpService:
user_xp: int = row[1]
user_level: int = row[2]
needed_xp_for_next_level: int = XpService.xp_needed_for_next_level(
user_level
user_level,
)
leaderboard.append(
(row_user_id, user_xp, user_level, needed_xp_for_next_level)
(row_user_id, user_xp, user_level, needed_xp_for_next_level),
)
return leaderboard
@staticmethod
def generate_progress_bar(
current_value: int, target_value: int, bar_length: int = 10
current_value: int,
target_value: int,
bar_length: int = 10,
) -> str:
"""
Generates an XP progress bar based on the current level and XP.
@ -214,7 +219,8 @@ class XpRewardService:
ORDER BY level DESC
"""
data: List[Tuple[int, int, bool]] = database.select_query(
query, (self.guild_id,)
query,
(self.guild_id,),
)
return {level: (role_id, persistent) for level, role_id, persistent in data}
@ -239,7 +245,8 @@ class XpRewardService:
ON DUPLICATE KEY UPDATE role_id = %s, persistent = %s;
"""
database.execute_query(
query, (self.guild_id, level, role_id, persistent, role_id, persistent)
query,
(self.guild_id, level, role_id, persistent, role_id, persistent),
)
self.rewards[level] = (role_id, persistent)