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

Merge pull request #29 from wlinator/patch-v2.8

Patch v2.8
This commit is contained in:
wlinator 2024-08-14 06:36:27 -04:00
commit 5c9f1973e9
18 changed files with 335 additions and 246 deletions

View file

@ -11,6 +11,7 @@ import services.config_service
import services.help_service
from lib.constants import CONST
from services.blacklist_service import BlacklistUserService
from lib.exceptions.LumiExceptions import Blacklisted
from db.database import run_migrations
# Remove the default logger configuration
@ -42,7 +43,9 @@ client = Client.LumiBot(
@client.check
async def blacklist_check(ctx):
return not BlacklistUserService.is_user_blacklisted(ctx.author.id)
if BlacklistUserService.is_user_blacklisted(ctx.author.id):
raise Blacklisted
return True
def load_modules():

View file

@ -1,5 +1,39 @@
{
"lumi_exception_generic": "An error occurred.",
"lumi_exception_blacklisted": "User is blacklisted",
"admin_award_description": "awarded **${0}** to {1}.",
"admin_award_title": "Awarded Currency",
"admin_blacklist_author": "User Blacklisted",
"admin_blacklist_description": "user `{0}` has been blacklisted from Luminara.",
"admin_blacklist_footer": "There is no process to reinstate a blacklisted user. Appeals are not considered.",
"admin_sql_inject_description": "```sql\n{0}\n```",
"admin_sql_inject_error_description": "```sql\n{0}\n```\n```\n{1}\n```",
"admin_sql_inject_error_title": "SQL Query Error",
"admin_sql_inject_title": "SQL Query Executed",
"admin_sql_select_description": "```sql\nSELECT {0}\n```\n```\n{1}\n```",
"admin_sql_select_error_description": "```sql\nSELECT {0}\n```\n```\n{1}\n```",
"admin_sql_select_error_title": "SQL Select Query Error",
"admin_sql_select_title": "SQL Select Query",
"admin_sync_description": "command tree synced successfully.",
"admin_sync_error_description": "An error occurred while syncing: {0}",
"admin_sync_error_title": "Sync Error",
"admin_sync_title": "Sync Successful",
"bet_limit": "❌ | **{0}** you cannot place any bets above **${1}**.",
"birthday_check_started": "Daily birthday check started.",
"birthday_check_skipped": "Birthday announcements in guild with ID {0} skipped: no birthday channel.",
"birthday_check_success": "Birthday announcement Success! user/guild/chan ID: {0}/{1}/{2}",
"birthday_check_error": "Birthday announcement skipped processing user/guild {0}/{1} | {2}",
"birthday_check_finished": "Daily birthday check finished. {0} birthdays processed. {1} birthdays failed.",
"birthday_add_invalid_date": "The date you entered is invalid.",
"birthday_add_success_author": "Birthday Set",
"birthday_add_success_description": "your birthday has been set to **{0} {1}**.",
"birthday_delete_success_author": "Birthday Deleted",
"birthday_delete_success_description": "your birthday has been deleted from this server.",
"birthday_upcoming_author": "Upcoming Birthdays!",
"birthday_upcoming_description_line": "🎂 {0} - {1}",
"birthday_upcoming_no_birthdays_author": "No Upcoming Birthdays",
"birthday_upcoming_no_birthdays": "there are no upcoming birthdays in this server.",
"birthday_leap_year": "February 29",
"case_case_field": "Case:",
"case_case_field_value": "`{0}`",
"case_duration_field": "Duration:",
@ -97,6 +131,7 @@
"error_bot_missing_permissions_description": "Lumi lacks the required permissions to run this command.",
"error_command_cooldown_author": "Command Cooldown",
"error_command_cooldown_description": "try again in **{0:02d}:{1:02d}**.",
"error_command_not_found": "No command called \"{0}\" found.",
"error_image_url_invalid": "invalid image URL.",
"error_invalid_duration": "Invalid duration: {0}",
"error_invalid_duration_author": "Invalid Duration",
@ -150,12 +185,19 @@
"mod_banned_user": "user with ID `{0}` has been banned.",
"mod_dm_not_sent": "Failed to notify them in DM",
"mod_dm_sent": "notified them in DM",
"mod_kick_dm": "**{0}** you have been kicked from `{1}`.\n\n**Reason:** `{2}`",
"mod_kicked_author": "User Kicked",
"mod_kicked_user": "user `{0}` has been kicked.",
"mod_no_reason": "No reason provided.",
"mod_not_banned": "user with ID `{0}` is not banned.",
"mod_not_banned_author": "User Not Banned",
"mod_not_timed_out": "user `{0}` is not timed out.",
"mod_not_timed_out_author": "User Not Timed Out",
"mod_reason": "Moderator: {0} | Reason: {1}",
"mod_softban_dm": "**{0}** you have been softbanned from `{1}`.\n\n**Reason:** `{2}`",
"mod_softban_unban_reason": "Softban by {0}",
"mod_softbanned_author": "User Softbanned",
"mod_softbanned_user": "user `{0}` has been softbanned.",
"mod_timed_out_author": "User Timed Out",
"mod_timed_out_user": "user `{0}` has been timed out.",
"mod_timeout_dm": "**{0}** you have been timed out in `{1}` for `{2}`.\n\n**Reason:** `{3}`",
@ -166,13 +208,6 @@
"mod_warn_dm": "**{0}** you have been warned in `{1}`.\n\n**Reason:** `{2}`",
"mod_warned_author": "User Warned",
"mod_warned_user": "user `{0}` has been warned.",
"mod_kicked_author": "User Kicked",
"mod_kicked_user": "user `{0}` has been kicked.",
"mod_kick_dm": "**{0}** you have been kicked from `{1}`.\n\n**Reason:** `{2}`",
"mod_softbanned_author": "User Softbanned",
"mod_softban_dm": "**{0}** you have been softbanned from `{1}`.\n\n**Reason:** `{2}`",
"mod_softbanned_user": "user `{0}` has been softbanned.",
"mod_softban_unban_reason": "Softban by {0}",
"ping_author": "I'm online!",
"ping_footer": "Latency: {0}ms",
"ping_pong": "Pong!",
@ -194,6 +229,5 @@
"xp_lb_field_value": "level: **{0}**\nxp: `{1}/{2}`",
"xp_level": "Level {0}",
"xp_progress": "Progress to next level",
"xp_server_rank": "Server Rank: #{0}",
"error_command_not_found": "No command called \"{0}\" found."
"xp_server_rank": "Server Rank: #{0}"
}

View file

@ -11,7 +11,7 @@ from lib.exceptions import LumiExceptions
async def on_command_error(ctx, error):
if isinstance(error, commands.CommandNotFound):
if isinstance(error, (commands.CommandNotFound, LumiExceptions.Blacklisted)):
return
author_text = None

View file

@ -85,9 +85,7 @@ class Constants:
# birthdays
BIRTHDAY_MESSAGES = JsonCache.read_json("birthday")["birthday_messages"]
BIRTHDAY_MONTHS = JsonCache.read_json("birthday")["months"]
CONST = Constants()
BIRTHDAY_GIF_URL = "https://media1.tenor.com/m/NXvU9jbBUGMAAAAC/fireworks.gif"
CONST = Constants()

View file

@ -23,6 +23,7 @@ class EmbedBuilder:
timestamp=None,
hide_author=False,
hide_author_icon=False,
hide_timestamp=False,
):
if not hide_author:
if not author_text:
@ -50,7 +51,8 @@ class EmbedBuilder:
url=author_url,
)
embed.set_footer(text=footer_text, icon_url=footer_icon_url)
embed.timestamp = timestamp or datetime.datetime.now()
if not hide_timestamp:
embed.timestamp = timestamp or datetime.datetime.now()
if image_url:
embed.set_image(url=image_url)
@ -74,6 +76,7 @@ class EmbedBuilder:
timestamp=None,
hide_author=False,
hide_author_icon=False,
hide_timestamp=False,
):
return EmbedBuilder.create_embed(
ctx,
@ -91,6 +94,7 @@ class EmbedBuilder:
timestamp=timestamp,
hide_author=hide_author,
hide_author_icon=hide_author_icon,
hide_timestamp=hide_timestamp,
)
@staticmethod
@ -108,6 +112,7 @@ class EmbedBuilder:
timestamp=None,
hide_author=False,
hide_author_icon=False,
hide_timestamp=False,
):
return EmbedBuilder.create_embed(
ctx,
@ -125,6 +130,7 @@ class EmbedBuilder:
timestamp=timestamp,
hide_author=hide_author,
hide_author_icon=hide_author_icon,
hide_timestamp=hide_timestamp,
)
@staticmethod
@ -142,6 +148,7 @@ class EmbedBuilder:
timestamp=None,
hide_author=False,
hide_author_icon=False,
hide_timestamp=False,
):
return EmbedBuilder.create_embed(
ctx,
@ -159,6 +166,7 @@ class EmbedBuilder:
timestamp=timestamp,
hide_author=hide_author,
hide_author_icon=hide_author_icon,
hide_timestamp=hide_timestamp,
)
@staticmethod
@ -176,6 +184,7 @@ class EmbedBuilder:
timestamp=None,
hide_author=False,
hide_author_icon=False,
hide_timestamp=False,
):
return EmbedBuilder.create_embed(
ctx,
@ -193,4 +202,5 @@ class EmbedBuilder:
timestamp=timestamp,
hide_author=hide_author,
hide_author_icon=hide_author_icon,
hide_timestamp=hide_timestamp,
)

View file

@ -1,4 +1,5 @@
from discord.ext import commands
from lib.constants import CONST
class BirthdaysDisabled(commands.CheckFailure):
@ -14,6 +15,16 @@ class LumiException(commands.CommandError):
A generic exception to raise for quick error handling.
"""
def __init__(self, message="An error occurred."):
def __init__(self, message=CONST.STRINGS["lumi_exception_generic"]):
self.message = message
super().__init__(message)
class Blacklisted(commands.CommandError):
"""
Raised when a user is blacklisted.
"""
def __init__(self, message=CONST.STRINGS["lumi_exception_blacklisted"]):
self.message = message
super().__init__(message)

View file

@ -1,5 +1,6 @@
import discord
from lib.constants import CONST
from lib.embed_builder import EmbedBuilder
from services.currency_service import Currency
@ -9,9 +10,13 @@ async def cmd(ctx, user: discord.User, amount: int):
curr.add_balance(amount)
curr.push()
embed = discord.Embed(
color=discord.Color.green(),
description=f"Awarded **${Currency.format(amount)}** to {user.name}.",
embed = EmbedBuilder.create_success_embed(
ctx,
author_text=CONST.STRINGS["admin_award_title"],
description=CONST.STRINGS["admin_award_description"].format(
Currency.format(amount),
user.name,
),
)
await ctx.respond(embed=embed)

View file

@ -1,13 +1,9 @@
from typing import Optional
import discord
from config.parser import JsonCache
from lib.constants import CONST
from services.blacklist_service import BlacklistUserService
resources = JsonCache.read_json("art")
exclaim_icon = resources["icons"]["exclaim"]
hammer_icon = resources["icons"]["hammer"]
from lib.embed_builder import EmbedBuilder
async def blacklist_user(
@ -15,24 +11,15 @@ async def blacklist_user(
user: discord.User,
reason: Optional[str] = None,
) -> None:
"""
Blacklists a user with an optional reason.
Args:
user_id (int): The ID of the user to blacklist.
reason (str, optional): The reason for blacklisting the user. Defaults to "No reason was given".
"""
blacklist_service = BlacklistUserService(user.id)
blacklist_service.add_to_blacklist(reason)
embed = discord.Embed(
description=f"User `{user.name}` has been blacklisted from Luminara.",
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,
embed = EmbedBuilder.create_success_embed(
ctx,
author_text=CONST.STRINGS["admin_blacklist_author"],
description=CONST.STRINGS["admin_blacklist_description"].format(user.name),
footer_text=CONST.STRINGS["admin_blacklist_footer"],
hide_timestamp=True,
)
await ctx.send(embed=embed)

View file

@ -1,4 +1,7 @@
import sqlite3
import mysql.connector
from lib.constants import CONST
from lib.embed_builder import EmbedBuilder
from lib.formatter import shorten
from db import database
@ -9,21 +12,49 @@ async def select_cmd(ctx, query: str):
try:
results = database.select_query(f"SELECT {query}")
except sqlite3.Error as error:
results = error
embed = EmbedBuilder.create_success_embed(
ctx,
author_text=CONST.STRINGS["admin_sql_select_title"],
description=CONST.STRINGS["admin_sql_select_description"].format(
shorten(query, 200),
shorten(str(results), 200),
),
show_name=False,
)
except mysql.connector.Error as error:
embed = EmbedBuilder.create_error_embed(
ctx,
author_text=CONST.STRINGS["admin_sql_select_error_title"],
description=CONST.STRINGS["admin_sql_select_error_description"].format(
shorten(query, 200),
shorten(str(error), 200),
),
show_name=False,
)
return await ctx.respond(
content=f"```SELECT {query}```\n```{results}```",
ephemeral=True,
)
return await ctx.respond(embed=embed, ephemeral=True)
async def inject_cmd(ctx, query: str):
try:
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,
embed = EmbedBuilder.create_success_embed(
ctx,
author_text=CONST.STRINGS["admin_sql_inject_title"],
description=CONST.STRINGS["admin_sql_inject_description"].format(
shorten(query, 200),
),
show_name=False,
)
except mysql.connector.Error as error:
embed = EmbedBuilder.create_error_embed(
ctx,
author_text=CONST.STRINGS["admin_sql_inject_error_title"],
description=CONST.STRINGS["admin_sql_inject_error_description"].format(
shorten(query, 200),
shorten(str(error), 200),
),
show_name=False,
)
await ctx.respond(embed=embed, ephemeral=True)

View file

@ -1,6 +1,7 @@
import discord
from lib.embed_builder import EmbedBuilder
from lib.exceptions.LumiExceptions import LumiException
from lib.constants import CONST
async def sync_commands(client, ctx):
@ -8,9 +9,11 @@ async def sync_commands(client, ctx):
await client.sync_commands()
embed = EmbedBuilder.create_success_embed(
ctx,
author_text="Sync Successful",
description="command tree synced successfully.",
author_text=CONST.STRINGS["admin_sync_title"],
description=CONST.STRINGS["admin_sync_description"],
)
await ctx.send(embed=embed)
except discord.HTTPException as e:
raise LumiException(f"An error occurred while syncing: {e}") from e
raise LumiException(
CONST.STRINGS["admin_sync_error_description"].format(e),
) from e

View file

@ -1,16 +1,12 @@
import asyncio
import random
import datetime
import pytz
import discord
from discord.commands import SlashCommandGroup
from discord.ext import commands, tasks
from loguru import logger
from lib import checks, time
from lib import checks
from lib.constants import CONST
from modules.birthdays import birthday
from services.birthday_service import Birthday
from services.config_service import GuildConfig
from modules.birthdays import birthday, daily_check
class Birthdays(commands.Cog):
@ -18,9 +14,6 @@ class Birthdays(commands.Cog):
self.client = client
self.daily_birthday_check.start()
"""
birthday module - slash command only
"""
birthday = SlashCommandGroup(
name="birthday",
description="Birthday commands.",
@ -43,47 +36,9 @@ class Birthdays(commands.Cog):
async def upcoming_birthdays(self, ctx):
await birthday.upcoming(ctx)
@tasks.loop(hours=23, minutes=55)
@tasks.loop(time=datetime.time(hour=12, minute=0, tzinfo=pytz.UTC)) # 12 PM UTC
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.",
)
await asyncio.sleep(wait_time)
embed = discord.Embed(color=discord.Color.embed_background())
embed.set_image(url="https://media1.tenor.com/m/NXvU9jbBUGMAAAAC/fireworks.gif")
for user_id, guild_id in Birthday.get_birthdays_today():
try:
guild = await self.client.fetch_guild(guild_id)
member = await guild.fetch_member(user_id)
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.",
)
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,
)
await channel.send(embed=embed, content=member.mention)
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}",
)
# wait one second to avoid rate limits
await asyncio.sleep(1)
await daily_check.daily_birthday_check(self.client)
def setup(client):

View file

@ -4,7 +4,8 @@ import datetime
import discord
from discord.ext import commands
from lib.embeds.info import BdayInfo
from lib.embed_builder import EmbedBuilder
from lib.constants import CONST
from services.birthday_service import Birthday
@ -13,59 +14,75 @@ async def add(ctx, month, month_index, day):
leap_year = 2020
max_days = calendar.monthrange(leap_year, month_index)[1]
if not (1 <= day <= max_days):
raise commands.BadArgument("the date you entered is invalid.")
if not 1 <= day <= max_days:
raise commands.BadArgument(CONST.STRINGS["birthday_add_invalid_date"])
date_str = f"{leap_year}-{month_index:02d}-{day:02d}"
date_obj = datetime.datetime.strptime(date_str, "%Y-%m-%d")
date_obj = datetime.datetime(leap_year, month_index, day)
birthday = Birthday(ctx.author.id, ctx.guild.id)
birthday.set(date_obj)
await ctx.respond(embed=BdayInfo.set_month(ctx, month, day))
embed = EmbedBuilder.create_success_embed(
ctx,
author_text=CONST.STRINGS["birthday_add_success_author"],
description=CONST.STRINGS["birthday_add_success_description"].format(
month,
day,
),
show_name=True,
)
await ctx.respond(embed=embed)
async def delete(ctx):
"""Delete a user's birthday in a specific server."""
birthday = Birthday(ctx.author.id, ctx.guild.id)
birthday.delete()
await ctx.respond(embed=BdayInfo.delete(ctx))
Birthday(ctx.author.id, ctx.guild.id).delete()
embed = EmbedBuilder.create_success_embed(
ctx,
author_text=CONST.STRINGS["birthday_delete_success_author"],
description=CONST.STRINGS["birthday_delete_success_description"],
show_name=True,
)
await ctx.respond(embed=embed)
async def upcoming(ctx):
"""Get the upcoming birthdays for a specific server."""
upcoming_birthdays = Birthday.get_upcoming_birthdays(ctx.guild.id)
icon = ctx.guild.icon if ctx.guild.icon else "https://i.imgur.com/79XfsbS.png"
embed = discord.Embed(
color=discord.Color.embed_background(),
if not upcoming_birthdays:
embed = EmbedBuilder.create_warning_embed(
ctx,
author_text=CONST.STRINGS["birthday_upcoming_no_birthdays_author"],
description=CONST.STRINGS["birthday_upcoming_no_birthdays"],
show_name=True,
)
await ctx.respond(embed=embed)
return
embed = EmbedBuilder.create_success_embed(
ctx,
author_text=CONST.STRINGS["birthday_upcoming_author"],
description="",
show_name=False,
)
embed.set_author(name="Upcoming Birthdays!", icon_url=icon)
embed.set_thumbnail(url="https://i.imgur.com/79XfsbS.png")
embed.set_thumbnail(url=CONST.LUMI_LOGO_TRANSPARENT)
found_birthdays = 0
for user_id, birthday in upcoming_birthdays:
birthday_lines = []
for user_id, birthday in upcoming_birthdays[:10]:
try:
member = await ctx.guild.fetch_member(user_id)
name = member.name
except discord.HTTPException:
continue # skip user if not in guild
try:
birthday_date = datetime.datetime.strptime(birthday, "%m-%d")
formatted_birthday = birthday_date.strftime("%B %-d")
except ValueError:
# leap year error
formatted_birthday = "February 29"
embed.add_field(
name=f"{name}",
value=f"🎂 {formatted_birthday}",
inline=False,
)
found_birthdays += 1
if found_birthdays >= 5:
break
birthday_lines.append(
CONST.STRINGS["birthday_upcoming_description_line"].format(
member.name,
formatted_birthday,
),
)
except (discord.HTTPException, ValueError):
continue
embed.description = "\n".join(birthday_lines)
await ctx.respond(embed=embed)

View file

@ -0,0 +1,63 @@
import asyncio
import random
from loguru import logger
from lib.constants import CONST
from services.birthday_service import Birthday
from services.config_service import GuildConfig
from lib.embed_builder import EmbedBuilder
async def daily_birthday_check(client):
logger.info(CONST.STRINGS["birthday_check_started"])
birthdays_today = Birthday.get_birthdays_today()
processed_birthdays = 0
failed_birthdays = 0
if birthdays_today:
for user_id, guild_id in birthdays_today:
try:
guild = await client.fetch_guild(guild_id)
member = await guild.fetch_member(user_id)
guild_config = GuildConfig(guild.id)
if not guild_config.birthday_channel_id:
logger.debug(
CONST.STRINGS["birthday_check_skipped"].format(guild.id),
)
continue
message = random.choice(CONST.BIRTHDAY_MESSAGES)
embed = EmbedBuilder.create_success_embed(
None,
author_text="Happy Birthday!",
description=message.format(member.name),
show_name=False,
)
embed.set_image(url=CONST.BIRTHDAY_GIF_URL)
channel = await guild.fetch_channel(guild_config.birthday_channel_id)
await channel.send(embed=embed, content=member.mention)
logger.debug(
CONST.STRINGS["birthday_check_success"].format(
member.id,
guild.id,
channel.id,
),
)
processed_birthdays += 1
except Exception as e:
logger.warning(
CONST.STRINGS["birthday_check_error"].format(user_id, guild_id, e),
)
failed_birthdays += 1
# wait one second to avoid rate limits
await asyncio.sleep(1)
logger.info(
CONST.STRINGS["birthday_check_finished"].format(
processed_birthdays,
failed_birthdays,
),
)

View file

@ -1,8 +1,8 @@
import discord
from discord.ext.commands import guild_only
from discord.ext import bridge, commands
from discord.ext.commands import guild_only
from modules.moderation import ban, cases, warn, timeout, kick, softban
from modules.moderation import ban, cases, kick, softban, timeout, warn
class Moderation(commands.Cog):
@ -28,25 +28,6 @@ class Moderation(commands.Cog):
):
await ban.ban_user(self, ctx, target, reason)
@bridge.bridge_command(
name="unban",
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.",
contexts={discord.InteractionContextType.guild},
)
@bridge.has_permissions(ban_members=True)
@commands.bot_has_permissions(ban_members=True)
@guild_only()
async def unban_command(
self,
ctx,
target: discord.User,
*,
reason: str | None = None,
):
await ban.unban_user(ctx, target, reason)
@bridge.bridge_command(
name="case",
aliases=["c"],
@ -56,11 +37,7 @@ class Moderation(commands.Cog):
)
@bridge.has_permissions(view_audit_log=True)
@guild_only()
async def case_command(
self,
ctx,
case_number: int,
):
async def case_command(self, ctx, case_number: int):
await cases.view_case_by_number(ctx, ctx.guild.id, case_number)
@bridge.bridge_command(
@ -75,22 +52,6 @@ class Moderation(commands.Cog):
async def cases_command(self, ctx):
await cases.view_all_cases_in_guild(ctx, ctx.guild.id)
@bridge.bridge_command(
name="modcases",
aliases=["moderatorcases", "mc"],
description="View all cases by a specific moderator.",
help="Lists all moderation cases handled by a specific moderator in the current server.",
contexts={discord.InteractionContextType.guild},
)
@bridge.has_permissions(view_audit_log=True)
@guild_only()
async def moderator_cases_command(
self,
ctx,
moderator: discord.User,
):
await cases.view_all_cases_by_mod(ctx, ctx.guild.id, moderator)
@bridge.bridge_command(
name="editcase",
aliases=["uc", "ec"],
@ -100,32 +61,58 @@ class Moderation(commands.Cog):
)
@bridge.has_permissions(view_audit_log=True)
@guild_only()
async def edit_case_command(
self,
ctx,
case_number: int,
*,
new_reason: str,
):
async def edit_case_command(self, ctx, case_number: int, *, new_reason: str):
await cases.edit_case_reason(ctx, ctx.guild.id, case_number, new_reason)
@bridge.bridge_command(
name="warn",
aliases=["w"],
description="Warn a user.",
help="Warns a user in the server.",
name="kick",
aliases=["k"],
description="Kick a user from the server.",
help="Kicks a user from the server.",
contexts={discord.InteractionContextType.guild},
)
@bridge.has_permissions(kick_members=True)
@commands.bot_has_permissions(kick_members=True)
@guild_only()
async def warn_command(
async def kick_command(
self,
ctx,
target: discord.Member,
*,
reason: str | None = None,
):
await warn.warn_user(ctx, target, reason)
await kick.kick_user(self, ctx, target, reason)
@bridge.bridge_command(
name="modcases",
aliases=["moderatorcases", "mc"],
description="View all cases by a specific moderator.",
help="Lists all moderation cases handled by a specific moderator in the current server.",
contexts={discord.InteractionContextType.guild},
)
@bridge.has_permissions(view_audit_log=True)
@guild_only()
async def moderator_cases_command(self, ctx, moderator: discord.Member):
await cases.view_all_cases_by_mod(ctx, ctx.guild.id, moderator)
@bridge.bridge_command(
name="softban",
aliases=["sb"],
description="Softban a user from the server.",
help="Softbans a user from the server (ban and immediately unban to delete messages).",
contexts={discord.InteractionContextType.guild},
)
@bridge.has_permissions(ban_members=True)
@commands.bot_has_permissions(ban_members=True)
@guild_only()
async def softban_command(
self,
ctx,
target: discord.Member,
*,
reason: str | None = None,
):
await softban.softban_user(ctx, target, reason)
@bridge.bridge_command(
name="timeout",
@ -147,6 +134,25 @@ class Moderation(commands.Cog):
):
await timeout.timeout_user(self, ctx, target, duration, reason)
@bridge.bridge_command(
name="unban",
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.",
contexts={discord.InteractionContextType.guild},
)
@bridge.has_permissions(ban_members=True)
@commands.bot_has_permissions(ban_members=True)
@guild_only()
async def unban_command(
self,
ctx,
target: discord.User,
*,
reason: str | None = None,
):
await ban.unban_user(ctx, target, reason)
@bridge.bridge_command(
name="untimeout",
aliases=["removetimeout", "rto", "uto"],
@ -167,42 +173,22 @@ class Moderation(commands.Cog):
await timeout.untimeout_user(ctx, target, reason)
@bridge.bridge_command(
name="kick",
aliases=["k"],
description="Kick a user from the server.",
help="Kicks a user from the server.",
name="warn",
aliases=["w"],
description="Warn a user.",
help="Warns a user in the server.",
contexts={discord.InteractionContextType.guild},
)
@bridge.has_permissions(kick_members=True)
@commands.bot_has_permissions(kick_members=True)
@guild_only()
async def kick_command(
async def warn_command(
self,
ctx,
target: discord.Member,
*,
reason: str | None = None,
):
await kick.kick_user(self, ctx, target, reason)
@bridge.bridge_command(
name="softban",
aliases=["sb"],
description="Softban a user from the server.",
help="Softbans a user from the server (ban and immediately unban to delete messages).",
contexts={discord.InteractionContextType.guild},
)
@bridge.has_permissions(ban_members=True)
@commands.bot_has_permissions(ban_members=True)
@guild_only()
async def softban_command(
self,
ctx,
target: discord.Member,
*,
reason: str | None = None,
):
await softban.softban_user(ctx, target, reason)
await warn.warn_user(ctx, target, reason)
def setup(client):

View file

@ -64,7 +64,7 @@ async def view_all_cases_in_guild(ctx, guild_id: int):
await paginator.respond(ctx)
async def view_all_cases_by_mod(ctx, guild_id: int, moderator: discord.User):
async def view_all_cases_by_mod(ctx, guild_id: int, moderator: discord.Member):
cases = case_service.fetch_cases_by_moderator(guild_id, moderator.id)
if not cases:

View file

@ -58,10 +58,10 @@ async def add_reaction(
is_emoji,
is_full_match,
)
await ctx.respond(embed=embed)
else:
embed = create_failure_embed(trigger_text, is_emoji)
await ctx.respond(embed=embed)
await ctx.respond(embed=embed)
async def check_reaction_limit(

View file

@ -38,9 +38,7 @@ class Birthday:
tz = pytz.timezone("US/Eastern")
today = datetime.datetime.now(tz).strftime("%m-%d")
birthdays = database.select_query(query, (today,))
return birthdays
return database.select_query(query, (today,))
@staticmethod
def get_upcoming_birthdays(guild_id):
@ -53,10 +51,4 @@ class Birthday:
data = database.select_query(query, (guild_id,))
upcoming = []
for row in data:
user_id = row[0]
birthday = row[1]
upcoming.append((user_id, birthday))
return upcoming
return [(row[0], row[1]) for row in data]

View file

@ -18,10 +18,10 @@ class LumiHelp(commands.HelpCommand):
}
def get_command_qualified_name(self, command):
return "`{}{}`".format(self.context.clean_prefix, command.qualified_name)
return f"`{self.context.clean_prefix}{command.qualified_name}`"
async def send_bot_help(self, mapping):
embed = EmbedBuilder.create_info_embed(
embed = EmbedBuilder.create_success_embed(
ctx=self.context,
author_text="Help Command",
show_name=False,
@ -53,17 +53,11 @@ class LumiHelp(commands.HelpCommand):
show_name=False,
)
usage_value = "`{}{} {}`".format(
self.context.clean_prefix,
command.qualified_name,
command.signature,
usage_value = (
f"`{self.context.clean_prefix}{command.qualified_name} {command.signature}`"
)
for alias in command.aliases:
usage_value += "\n`{}{} {}`".format(
self.context.clean_prefix,
alias,
command.signature,
)
usage_value += f"\n`{self.context.clean_prefix}{alias} {command.signature}`"
embed.add_field(name="Usage", value=usage_value, inline=False)
channel = self.get_destination()