From 5e927146588800f386dcba9724c9ba6baac11935 Mon Sep 17 00:00:00 2001 From: Ryuga Date: Fri, 10 Apr 2026 14:05:13 +0530 Subject: [PATCH 1/4] AoC refactor merge from snappy --- bot/api_client.py | 34 ++++++++-- bot/cogs/advent_of_code.py | 123 ++++++++++++++++++++++++++++--------- 2 files changed, 123 insertions(+), 34 deletions(-) diff --git a/bot/api_client.py b/bot/api_client.py index b50a798..082da89 100644 --- a/bot/api_client.py +++ b/bot/api_client.py @@ -1,5 +1,4 @@ import os -import asyncio import json import logging from datetime import datetime, timezone @@ -293,20 +292,43 @@ async def search(self, search_for: str) -> List[dict]: class AdventOfCodeAPI(BaseAPIClient): - AOC_REQUEST_HEADER = {"user-agent": "Tortoise Discord Community AoC event bot"} - COOKIES = {"session": os.getenv("AOC_COOKIE")} + AOC_REQUEST_HEADER = { + "User-Agent": "Tortoise Discord Community AoC bot (github: Tortoise-Community)" + } + AOC_API_URL = "https://adventofcode.com/{year}/leaderboard/private/view/{leaderboard_id}" - def __init__(self, leaderboard_id: str, year: int = 2020): + def __init__( + self, + leaderboard_id: str, + year: int, + session_cookie: Optional[str] = None, + ) -> None: + session_cookie = session_cookie or os.getenv("AOC_COOKIE") + + if not session_cookie: + raise ValueError( + "AdventOfCodeAPI: session cookie is missing. " + "Pass session_cookie=... or set AOC_COOKIE." + ) + + base_url = self.AOC_API_URL.format( + year=year, + leaderboard_id=leaderboard_id + ) + super().__init__( - self.AOC_API_URL.format(year=year, leaderboard_id=leaderboard_id), + base_api_url=base_url, headers=self.AOC_REQUEST_HEADER, - cookies=self.COOKIES + cookies={"session": session_cookie}, ) async def get_leaderboard(self): return await self.get(endpoint=".json") + async def close(self): + await self.session.close() + class StackAPI(BaseAPIClient): STACK_API_URL = "https://api.stackexchange.com" diff --git a/bot/cogs/advent_of_code.py b/bot/cogs/advent_of_code.py index 29ea16a..e985297 100644 --- a/bot/cogs/advent_of_code.py +++ b/bot/cogs/advent_of_code.py @@ -1,19 +1,23 @@ import datetime +import discord from discord.ext import commands, tasks - +from discord import app_commands from bot.api_client import AdventOfCodeAPI from bot.utils.misc import format_timedelta from bot.utils.embed_handler import info, failure class AdventOfCode(commands.Cog): - TORTOISE_LEADERBOARD_ID = "432225" - TORTOISE_LEADERBOARD_INVITE = "432225-ab5dcfc1" + TORTOISE_LEADERBOARD_ID = "4922988" + TORTOISE_LEADERBOARD_INVITE = "4922988-d5f6845a" - def __init__(self, bot): + def __init__(self, bot: commands.Bot): self.bot = bot - self.aoc_api = AdventOfCodeAPI(self.TORTOISE_LEADERBOARD_ID) + self.aoc_api = AdventOfCodeAPI( + leaderboard_id=self.TORTOISE_LEADERBOARD_ID, + year=2025, + ) self._leaderboard_cache = None self.update_leaderboard_cache.start() @@ -21,34 +25,66 @@ def __init__(self, bot): async def update_leaderboard_cache(self): self._leaderboard_cache = await self.aoc_api.get_leaderboard() - @commands.command() - async def invite(self, ctx): + @update_leaderboard_cache.before_loop + async def before_update_leaderboard_cache(self): + # Ensure bot is ready before the loop starts + await self.bot.wait_until_ready() + + + @app_commands.command( + name="aoc_invite", + description="Shows invite code to the Tortoise Advent of Code leaderboard." + ) + async def invite(self, interaction: discord.Interaction): """Shows invite to our Tortoise Advent of Code leaderboard.""" + guild = interaction.guild + member = guild.me if guild is not None else interaction.client.user + invite_embed = info( - f"Use this code to join Tortoise AoC leaderboard: **{self.TORTOISE_LEADERBOARD_INVITE}**\n\n" - "To join you can go to the AoC website: https://adventofcode.com/2020/leaderboard", + ( + f"Use this code to join Tortoise AoC leaderboard: " + f"**{self.TORTOISE_LEADERBOARD_INVITE}**\n\n" + "To join you can go to the AoC website: " + "https://adventofcode.com/2025/leaderboard/private" + ), title="Tortoise AoC", - member=ctx.guild.me + member=member ) - await ctx.send(embed=invite_embed) + await interaction.response.send_message(embed=invite_embed) - @commands.command(aliases=["lb"]) - async def leaderboard(self, ctx): + @app_commands.command( + name="aoc_leaderboard", + description="Show the Tortoise Advent of Code leaderboard (cached)." + ) + async def leaderboard(self, interaction: discord.Interaction): """ Shows Tortoise leaderboard. Leaderboard is updated each 30 minutes. """ + guild = interaction.guild + if guild is None: + await interaction.response.send_message( + embed=failure("This command can only be used in a server."), + ephemeral=True, + ) + return if self._leaderboard_cache is None: - return await ctx.send(embed=failure("Please try again in few seconds as cache is not yet loaded.")) + await interaction.response.send_message( + embed=failure("Please try again in a few seconds, cache is not yet loaded."), + ephemeral=True, + ) + return sorted_members = { k: v for k, v in sorted( - self._leaderboard_cache["members"].items(), key=lambda item: item[1]["local_score"], reverse=True + self._leaderboard_cache["members"].items(), + key=lambda item: item[1]["local_score"], + reverse=True ) } - leaderboard = ["```py"] + leaderboard_lines = ["```py"] num_of_members = 10 position_counter = 0 @@ -58,33 +94,64 @@ async def leaderboard(self, ctx): break stars_pretty = f"{'★' + str(member_data['stars']):4}" - leaderboard.append( - f"{position_counter}. {member_data['local_score']:4}p {stars_pretty} {member_data['name']}" + leaderboard_lines.append( + f"{position_counter}. {member_data['local_score']:4}p " + f"{stars_pretty} {member_data['name']}" ) - leaderboard.append("```") - leaderboard_text = "\n".join(leaderboard) + leaderboard_lines.append("```") + leaderboard_text = "\n".join(leaderboard_lines) + embed = info( - f"{leaderboard_text}\n\nThe leaderboard is refreshed each 30 minutes.", - member=ctx.guild.me, + f"{leaderboard_text}\n\nThe leaderboard is refreshed every 30 minutes.", + member=guild.me, title="Tortoise AoC leaderboard" ) - await ctx.send(embed=embed) + await interaction.response.send_message(embed=embed) - @commands.command() - async def aoc_countdown(self, ctx): + @app_commands.command( + name="aoc_countdown", + description="Time until the next Advent of Code challenge starts." + ) + async def aoc_countdown(self, interaction: discord.Interaction): """Time until next challenge starts.""" + guild = interaction.guild + if guild is None: + await interaction.response.send_message( + embed=failure("This command can only be used in a server."), + ephemeral=True, + ) + return + utc_minus_5 = datetime.timezone(offset=datetime.timedelta(hours=-5)) now = datetime.datetime.now(tz=utc_minus_5) + if now.month != 12: - return await ctx.send(embed=failure("AoC is over!")) + await interaction.response.send_message( + embed=failure("AoC is over!"), + ephemeral=True, + ) + return current_day = now.day - end_date = datetime.datetime(year=2020, month=12, day=current_day+1, tzinfo=utc_minus_5) + end_date = datetime.datetime( + year=2025, + month=12, + day=current_day + 1, + tzinfo=utc_minus_5 + ) difference = end_date - now ends_in = format_timedelta(difference) - await ctx.send(embed=info(f"Day {current_day} ends in {ends_in}", title="Countdown", member=ctx.guild.me)) + + embed = info( + f"Day {current_day} ends in {ends_in}", + title="Countdown", + member=guild.me, + ) + await interaction.response.send_message(embed=embed) + + async def setup(bot): From 2dfe52c0657a7a25741d6a2d6cafb484db3e81ef Mon Sep 17 00:00:00 2001 From: Ryuga Date: Fri, 10 Apr 2026 14:31:26 +0530 Subject: [PATCH 2/4] chore: Move fun commands to snappy --- bot/bot.py | 1 - bot/cogs/games.py | 148 ---------------------------------- bot/constants.py | 75 ----------------- bot/utils/embed_handler.py | 47 ----------- bot/utils/gambling_backend.py | 106 ------------------------ 5 files changed, 377 deletions(-) delete mode 100644 bot/cogs/games.py delete mode 100644 bot/utils/gambling_backend.py diff --git a/bot/bot.py b/bot/bot.py index 5d64394..4757921 100644 --- a/bot/bot.py +++ b/bot/bot.py @@ -32,7 +32,6 @@ class Bot(commands.Bot): banned_extensions = ( "advent_of_code", "documentation", - "games", "help", "piston", "reddit", diff --git a/bot/cogs/games.py b/bot/cogs/games.py deleted file mode 100644 index 2fd30c3..0000000 --- a/bot/cogs/games.py +++ /dev/null @@ -1,148 +0,0 @@ -import logging - -import discord -from discord.ext import commands - -from bot.utils.gambling_backend import Game, Player -from bot.utils.embed_handler import simple_embed, black_jack_embed -from bot.constants import hit_emoji_id, stay_emoji_id, double_emoji_id, blackjack_player_limit - - -logger = logging.getLogger(__name__) - - -class Games(commands.Cog): - - def __init__(self, bot): - self.bot = bot - self.live_games = {} - self.reactable_messages = {} - self.reaction_options = { - hit_emoji_id: self.hit, - stay_emoji_id: self.stay, - double_emoji_id: self.double - } - - async def evaluate_results(self, game): - participants = game.participants - dealer_card_value = game.dealer.calculate_card_value() - for participant in list(participants): - player = participants[participant] - me = self.bot.get_user(player.user_id) - if dealer_card_value > 21 or dealer_card_value < player.card_value: - await player.message.edit(embed=black_jack_embed(me, player, outcome="win", hidden=False)) - elif dealer_card_value > player.card_value: - await player.message.edit(embed=black_jack_embed(me, player, outcome="lose", hidden=False)) - else: - await player.message.edit(embed=black_jack_embed(me, player, outcome="tie", hidden=False)) - await self.remove(player) - del self.live_games[game.channel] - - async def dealers_play(self, game): - while True: - card_value = game.dealer.calculate_card_value() - if card_value < 17: - game.dealer.cards.append(game.deck.get_random_card()) - else: - break - await self.evaluate_results(game) - - async def check_blackjack(self, player): - player.card_value = player.calculate_card_value() - me = self.bot.get_user(player.user_id) - if player.card_value > 21: - embed = black_jack_embed(me, player, outcome="lose") - embed.title = "Busted!" - await player.message.edit(embed=embed) - await self.remove(player) - if player.card_value == 21: - embed = black_jack_embed(me, player, outcome="win") - embed.title = "Blackjack!" - await player.message.edit(embed=embed) - await self.remove(player) - - async def remove(self, player): - await player.message.clear_reactions() - player.game.participants.pop(player.user_id) - self.reactable_messages.pop(player.message.id) - del player - - async def hit(self, player): - player.game.deck.give_random_card(player, 1) - me = self.bot.get_user(player.user_id) - await player.message.edit(embed=black_jack_embed(me, player)) - await self.check_blackjack(player) - await self.check_active_session(player.game) - - async def check_active_session(self, player): - participants = player.game.participants - for person in participants: - if participants[person].stay: - me = self.bot.get_user(player.user_id) - embed = black_jack_embed(me, player) - embed.description = "**Status: **Waiting for other players..." - await player.message.edit(embed=embed) - return - await self.dealers_play(player.game) - - async def stay(self, player): - player.stay = True - await self.check_blackjack(player) - await player.message.clear_reactions() - await self.check_active_session(player) - - async def double(self, player): - player.bet_amount *= 2 - player.game.deck.give_random_card(player, 1) - await self.stay(player) - - async def init_blackjack(self, ctx, bet_amount): - if ctx.channel.id in self.live_games: - game = self.live_games[ctx.channel.id] - else: - game = Game(ctx.channel.id) - game.deck.give_random_card(game.dealer, 2) - self.live_games[ctx.channel.id] = game - - if not len(game.participants) == blackjack_player_limit: - if ctx.author.id not in game.participants: - player = Player(ctx.author.id, bet_amount, game) - game.participants[ctx.author.id] = player - game.deck.give_random_card(player, 2) - embed = black_jack_embed(ctx.author, player) - msg = await ctx.channel.send(embed=embed) - player.message = msg - self.reactable_messages[msg.id] = player - emotes = [hit_emoji_id, stay_emoji_id, double_emoji_id] - for emote in emotes: - reaction = self.bot.get_emoji(emote) - await msg.add_reaction(reaction) - await self.check_blackjack(player) - else: - await ctx.channel.send(embed=simple_embed( - "You've already joined the game. You can try joining another lobby.", "", discord.Color.red()) - ) - else: - await ctx.channel.send( - embed=simple_embed("The lobby if full. Try in another channel.", "", discord.Color.red()) - ) - - @commands.command(aliases=['bj']) - async def blackjack(self, ctx): - """Initializes single/multiplayer blackjack game""" - # TODO: Bet amount update on server currency implementation - await self.init_blackjack(ctx, bet_amount=10) - - @commands.Cog.listener() - async def on_raw_reaction_add(self, payload): - if payload.message_id in self.reactable_messages: - reaction = self.bot.get_emoji(payload.emoji.id) - user = self.bot.get_user(payload.user_id) - player = self.reactable_messages.get(payload.message_id) - if payload.user_id == player.user_id: - await player.message.remove_reaction(reaction, user) - await self.reaction_options[payload.emoji.id](player) # noqa - - -async def setup(bot): - await bot.add_cog(Games(bot)) diff --git a/bot/constants.py b/bot/constants.py index ac79ca8..07b9694 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -277,81 +277,6 @@ class SuggestionStatus(Enum): approved = "Approved" -blackjack_player_limit = 4 - -spade_emotes = { - "A": "<:sA:755697555505020928>", - "2": "<:s2:755697541823201292>", - "3": "<:s3:755697544671133796>", - "4": "<:s4:755697547921588274>", - "5": "<:s5:755697547590238281>", - "6": "<:s6:755697552837443624>", - "7": "<:s7:755697553198284890>", - "8": "<:s8:755697553579704430>", - "9": "<:s9:755697554456313867>", - "10": "<:s10:755697555303825498>", - "K": "<:sK:755697557212233738>", - "Q": "<:sQ:755697557249982497> ", - "J": "<:sJ:755697555475529790>" -} - -club_emotes = { - "A": "<:cA:755697680797138954>", - "2": "<:c2:755697675281760357>", - "3": "<:c3:755697676095455342>", - "4": "<:c4:755697675923488789>", - "5": "<:c5:755697676468617267>", - "6": "<:c6:755697676846104666>", - "7": "<:c7:755697677060014131>", - "8": "<:c8:755697676921864273>", - "9": "<:c9:755697680763846727>", - "10": "<:c10:755697680449273918>", - "K": "<:cK:755697681157980200>", - "Q": "<:cQ:755697681023762454>", - "J": "<:cJ:755697680881025064>" -} - -heart_emotes = { - - "A": "<:hA:755698059094261830>", - "2": "<:h2:755698046297440287>", - "3": "<:h3:755698046624596079>", - "4": "<:h4:755698046821466162>", - "5": "<:h5:755698046536515605>", - "6": "<:h6:755698046850957335>", - "7": "<:h7:755698050466578553>", - "8": "<:h8:755698056405450773>", - "9": "<:h9:755698058070589460>", - "10": "<:h10:755698058989404160>", - "K": "<:hK:755698058968432652>", - "Q": "<:hQ:755698059379474552>", - "J": "<:hJ:755698059056513065>" -} - -diamond_emotes = { - - "A": "<:dA:755714705158307952>", - "2": "<:d2:755709806848901181>", - "3": "<:d3:755709807486566413>", - "4": "<:d4:755709807708602368>", - "5": "<:d5:755709807985688617>", - "6": "<:d7:755709808555851808>", - "7": "<:d6:755709808681680917>", - "8": "<:d8:755709809189191680>", - "9": "<:d9:755709809323671583>", - "10": "<:d10:755709809399037963>", - "K": "<:dk:755714708279001208>", - "Q": "<:dQ:755714709742813216>", - "J": "<:dJ:755714707150733353>" -} - -card_emotes = { - "spade": spade_emotes, - "club": club_emotes, - "heart": heart_emotes, - "diamond": diamond_emotes -} - # These are allowed but will get deleted and bot will upload them to pastebin and provide the link to paste # The message will be deletable by the author by reacting to emoji (wrong code, token leak) extension_to_pastebin = ( diff --git a/bot/utils/embed_handler.py b/bot/utils/embed_handler.py index e0a7248..e84cb10 100644 --- a/bot/utils/embed_handler.py +++ b/bot/utils/embed_handler.py @@ -348,53 +348,6 @@ async def create_suggestion_msg(channel: TextChannel, author: User, suggestion: return suggestion_msg - -def black_jack_template(author: User, player, description: str, color: Color) -> Embed: - """ - Creates black jack embed template. - :param author: User discord user from which to get name and avatar - :param player: player object - :param description: embed description - :param color: discord.Color - :return: discord.Embed - """ - embed = authored(description, author=author) - embed.colour = color - embed.set_thumbnail( - url="https://www.vhv.rs/dpng/d/541-5416003_poker-club-icon-splash-diwali-coasters-black-background.png" - ) - card_string = player.get_emote_string(hidden=False) - embed.add_field(name="Your hand", value=f"{card_string}") - embed.set_footer( - text="BlackJack", - icon_url="https://i.pinimg.com/originals/c3/5f/63/c35f630a4efb237206ec94f8950dcad5.png" - ) - return embed - - -def black_jack_embed(user: User, player, outcome: str = None, hidden: bool = True) -> Embed: - """ - Creates embed based on set of constraints for blackjack - :param user: discord.User - :param player: player object for blackjack - :param outcome: blackjack game outcome - :param hidden: dealer card value - :return: discord.Embed - """ - embed = black_jack_template(user, player, "", Color.gold()) - embed.add_field(name="Dealer hand", value=player.game.dealer.get_emote_string(hidden=hidden)) - if outcome == "win": - embed.colour = Color.dark_green() - embed.description = "**Outcome:** You won!" - elif outcome == "lose": - embed.colour = Color.dark_red() - embed.description = "**Outcome:** You lost!" - elif outcome == "tie": - embed.colour = Color.dark_grey() - embed.description = "**Outcome:** It's a tie!" - return embed - - def project_embed(projects: dict, me): desc = f"▰▱▰▱▰▱▰▱▰▱▰▱▰▱▰▱▰▱\n\n**Active repositories: **{len(projects)-1}\n" embed = simple_embed(title="Tortoise Community", message=desc, diff --git a/bot/utils/gambling_backend.py b/bot/utils/gambling_backend.py deleted file mode 100644 index 532fb07..0000000 --- a/bot/utils/gambling_backend.py +++ /dev/null @@ -1,106 +0,0 @@ -import random -from typing import List - -import discord -from bot.constants import card_emotes, blank_card_emoji - - -class Player: - def __init__(self, user_id: int, bet_amount, game, message: discord.Message = None, is_dealer=False): - self.user_id = user_id - self.bet_amount = int(bet_amount) - self.card_value = 0 - self.cards = [] - self.game = game - self.message = message - self.stay = False - self.is_dealer = is_dealer - - def calculate_card_value(self) -> int: - value = 0 - a_count = 0 - for card in self.cards: - if card.name == "A": - a_count += 1 - elif card.name in ("K", "Q", "J"): - value += 10 - else: - value += int(card.name) - - if not self.is_dealer: - if a_count != 0: - for _ in range(a_count): - if value + 11 > 21: - value += 1 - else: - value += 11 - self.card_value = value - return value - else: - if a_count != 0: - for _ in range(a_count): - if value > 17: - value += 1 - else: - value += 11 - self.card_value = value - return value - - def get_emote_string(self, hidden=True) -> str: - if not hidden: - emote_string = "".join(card.emote for card in self.cards) - return f"{emote_string}\nvalue: {self.card_value}" - return f"{self.cards[0].emote} + {blank_card_emoji}\nvalue: ?" - - -class Game(): - def __init__(self, channel: discord.TextChannel): # noqa - self.channel = channel - self.participants = {} - self.deck = Deck() - self.dealer = Player(0000, 0, game=self, is_dealer=True) - - -class Card(object): - def __init__(self, suit: str, name: str): - self.suit = suit - self.name = name - self.emote = self._get_emoji() - - def __str__(self): - return f"{self.name} of {self.suit}" - - def _get_emoji(self): - return card_emotes[self.suit].get(self.name) - - -class Deck: - suits = ("club", "heart", "diamond", "spade") - cards_set = ("A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "K", "Q", "J") - - def __init__(self): - self.cards = self.build_deck() - self.shuffle() - - def build_deck(self) -> List[Card]: - return [Card(suit, card) for suit in self.suits for card in self.cards_set] - - def shuffle(self) -> None: - for _ in range(random.randint(1, 9)): - random.shuffle(self.cards) - - def get_random_cards(self, count: int) -> List[Card]: - cards_list = [] - for i in range(count): - random_card = random.choice(self.cards) - cards_list.append(random_card) - self.cards.remove(random_card) - return cards_list - - def get_random_card(self) -> Card: - return self.get_random_cards(1)[0] - - def give_random_card(self, player: Player, count: int) -> None: - for random_card in self.get_random_cards(count): - player.cards.append(random_card) - player.calculate_card_value() From e2175d6384af18d75bd0982988c55114a6ca78ab Mon Sep 17 00:00:00 2001 From: Ryuga Date: Wed, 15 Apr 2026 14:06:11 +0530 Subject: [PATCH 3/4] chore: Move fun commands to snappy --- bot/cogs/misc.py | 245 +------------------------------------ bot/utils/embed_handler.py | 58 +-------- 2 files changed, 4 insertions(+), 299 deletions(-) diff --git a/bot/cogs/misc.py b/bot/cogs/misc.py index 9bf950a..2fa07fd 100644 --- a/bot/cogs/misc.py +++ b/bot/cogs/misc.py @@ -1,8 +1,5 @@ import os import time -import asyncio -import random -from textwrap import wrap import psutil import discord @@ -10,9 +7,8 @@ from discord import app_commands from bot.utils.message_handler import RemovableMessage -from bot.utils.embed_handler import info, status_embed -from bot.utils.checks import check_if_it_is_tortoise_guild, tortoise_bot_developer_only -from bot.constants import embed_space, tortoise_paste_service_link +from bot.utils.embed_handler import info +from bot.constants import embed_space class Miscellaneous(commands.Cog): @@ -21,68 +17,12 @@ def __init__(self, bot): self.process = psutil.Process(os.getpid()) self.countdown_started = False - @app_commands.command(name="say", description="Make the bot say a message") - @app_commands.check(tortoise_bot_developer_only) - async def say(self, interaction: discord.Interaction, message: str): - clean = await commands.clean_content().convert(interaction, message) - await interaction.response.send_message(clean) - - @app_commands.command(name="slap") - async def slap(self, interaction: discord.Interaction, member: discord.Member): - if interaction.user == member: - embed = info(f"{member.mention} slapped him/her self LOL", interaction.guild.me, "Slap!") - img_url = "https://media.giphy.com/media/j1zuL4htGTFQY/giphy.gif" - else: - embed = info( - f"{member.mention} got slapped in the face by: {interaction.user.mention}!", - interaction.guild.me, - "Slap!", - ) - img_url = "https://66.media.tumblr.com/05212c10d8ccfc5ab190926912431344/tumblr_mt7zwazvyi1rqfhi2o1_400.gif" - - embed.set_image(url=img_url) - await interaction.response.send_message(embed=embed) - - @app_commands.command(name="shoot") - async def shoot(self, interaction: discord.Interaction, member: discord.Member): - embed = info( - f"{member.mention} shot by {interaction.user.mention} :gun: :boom:", - interaction.guild.me, - "Boom!", - ) - embed.set_image(url="https://i.gifer.com/XdhK.gif") - await interaction.response.send_message(embed=embed) - - @app_commands.command(name="throw") - async def throw(self, interaction: discord.Interaction): - await interaction.response.send_message("```(╯°□°)╯︵ ┻━┻```") - @app_commands.command(name="members") async def members(self, interaction: discord.Interaction): await interaction.response.send_message( embed=info(f"**Member count:** {interaction.guild.member_count}", interaction.guild.me, "") ) - @app_commands.command(name="status") - async def status(self, interaction: discord.Interaction, member: discord.Member | None = None): - if member is None: - member = interaction.user - embed = status_embed(interaction, member) - await interaction.response.send_message(embed=embed) - - @app_commands.command(name="pfp") - async def pfp(self, interaction: discord.Interaction, member: discord.Member | None = None): - if member is None: - message, member = "Your avatar", interaction.user - elif member == interaction.guild.me: - message = "My avatar" - else: - message = f"{member} avatar" - - embed = info(message, interaction.guild.me) - embed.set_image(url=member.display_avatar.replace(size=4096)) - await interaction.response.send_message(embed=embed) - @app_commands.command(name="ping") async def ping(self, interaction: discord.Interaction): start = time.perf_counter() @@ -168,31 +108,6 @@ def construct_load_bar_string(percent: int, suffix_message: str = None, size: in return constructed - @app_commands.command(name="countdown") - @app_commands.check(check_if_it_is_tortoise_guild) - async def countdown(self, interaction: discord.Interaction, start: int): - if self.countdown_started: - return await interaction.response.send_message( - embed=info("There is already an ongoing timer", interaction.guild.me, "") - ) - - self.countdown_started = True - await interaction.response.send_message(str(start)) - message = await interaction.original_response() - - while start: - minutes, seconds = divmod(start, 60) - content = f"{minutes:02d}:{seconds:02d}" - try: - await message.edit(content=content) - except discord.HTTPException: - break - start -= 1 - await asyncio.sleep(1) - - self.countdown_started = False - await message.delete() - @app_commands.command(name="add_to_issues") async def add_to_issues(self, interaction: discord.Interaction): msg = ( @@ -203,7 +118,7 @@ async def add_to_issues(self, interaction: discord.Interaction): "██║░░██║██████╔╝██████╔╝  ░░░██║░░░╚█████╔╝  ██║██████╔╝██████╔╝╚██████╔╝███████╗██████╔╝\n" "╚═╝░░╚═╝╚═════╝░╚═════╝░  ░░░╚═╝░░░░╚════╝░  ╚═╝╚═════╝░╚═════╝░░╚═════╝░╚══════╝╚═════╝░\n" ) - await interaction.response.send_message(f"```{msg}```") + await interaction.response.send_message(f"```fix\n{msg}```") @app_commands.command(name="ask") async def ask(self, interaction: discord.Interaction): @@ -240,12 +155,6 @@ async def markdown(self, interaction: discord.Interaction): message = await interaction.original_response() await RemovableMessage.create_instance(self.bot, message, interaction.user) - # @app_commands.command(name="paste") - async def paste(self, interaction: discord.Interaction): - await interaction.response.send_message( - embed=info(f":page_facing_up: {tortoise_paste_service_link}", interaction.guild.me, title="") - ) - @app_commands.command(name="zen") async def zen(self, interaction: discord.Interaction): zen = """ @@ -273,154 +182,6 @@ async def zen(self, interaction: discord.Interaction): embed=info(zen, interaction.guild.me, title="The Zen of Python, by Tim Peters") ) - @app_commands.command(name="antigravity") - async def antigravity(self, interaction: discord.Interaction): - await interaction.response.send_message( - embed=info("https://xkcd.com/353/", interaction.guild.me, title="") - ) - - @app_commands.command(name="coin") - async def coin(self, interaction: discord.Interaction, times: int = 1): - sample_space = ("Head", "Tail") - - if times == 1: - coin_toss = sample_space[random.randint(0, 1)] - await interaction.response.send_message( - embed=info(f":coin: | Coin Toss | **{coin_toss}**", interaction.guild.me, title="") - ) - elif times <= 25: - coin_toss = ", ".join(sample_space[random.randint(0, 1)] for _ in range(times)) - await interaction.response.send_message( - embed=info( - f":coin: | Coin tossed {times} times | **{coin_toss}**", - interaction.guild.me, - title="", - ) - ) - else: - await interaction.response.send_message( - embed=info( - "Oops! You can't toss that many times. Try a number less than 25", - interaction.guild.me, - title="", - ) - ) - - @app_commands.command(name="dice") - async def dice(self, interaction: discord.Interaction, times: int = 1): - if times == 1: - dice_roll = random.randint(1, 6) - await interaction.response.send_message( - embed=info(f"🎲 | Dice Roll | **{dice_roll}**", interaction.guild.me, title="") - ) - elif times <= 25: - dice_roll = ", ".join(str(random.randint(1, 6)) for _ in range(times)) - await interaction.response.send_message( - embed=info( - f"🎲 | Dice Rolled {times} times | **{dice_roll}**", - interaction.guild.me, - title="", - ) - ) - else: - await interaction.response.send_message( - embed=info( - "Oops! You can't roll that many times. Try a number less than 25", - interaction.guild.me, - title="", - ) - ) - - @app_commands.command(name="randint") - async def randint( - self, - interaction: discord.Interaction, - low: int = 1, - high: int = 100, - n: int = 1, - ): - if low > high: - low, high = high, low - - if (low < -1000000000 or high > 1000000000 or n > 100): - await interaction.response.send_message( - embed=info( - "Oops! That was a lot, try with smaller arguments", - interaction.guild.me, - title="", - ) - ) - elif n == 1: - output = random.randint(low, high) - await interaction.response.send_message( - embed=info( - f"🔢 | Random number between {low} & {high} | **{output}**", - interaction.guild.me, - title="", - ) - ) - else: - output = ", ".join(str(random.randint(low, high)) for _ in range(n)) - await interaction.response.send_message( - embed=info( - f"🔢 | {n} Random numbers between {low} & {high} | **{output}**", - interaction.guild.me, - title="", - ) - ) - - @app_commands.command(name="choice") - async def choice(self, interaction: discord.Interaction, args: str): - choices = args.split(",") - if len(choices): - choice = random.choice(choices) - await interaction.response.send_message( - embed=info( - f"🎰 | Random choice | **{choice.strip()}**", - interaction.guild.me, - title="", - ) - ) - - @app_commands.command(name="shuffle") - async def shuffle(self, interaction: discord.Interaction, args: str): - choices = [word.strip() for word in args.split(",")] - if len(choices): - random.shuffle(choices) - await interaction.response.send_message( - embed=info( - f"📃 | Random shuffle | **{', '.join(choices)}**", - interaction.guild.me, - title="", - ) - ) - - @app_commands.command(name="speak") - @app_commands.checks.has_permissions(manage_messages=True) - async def speak(self, interaction: discord.Interaction, text: str): - tortoise = r''' - \ - \ ,-"""-. - oo._/ \___/ \ - (____)_/___\__\_) - /_// \\_\ ''' - - lines = wrap(text, 40) - width = max(map(len, lines)) - bubble = [" " + "-" * width] - - if len(lines) == 1: - bubble.append("< " + lines[0] + " >") - else: - bubble.append("/ " + lines[0] + " " * (width - len(lines[0])) + " \\") - for line in lines[1:-1]: - bubble.append("| " + line + " " * (width - len(line)) + " |") - bubble.append("\\ " + lines[-1] + " " * (width - len(lines[-1])) + " /") - - bubble.append(" " + "-" * width) - output = "\n".join(bubble) + tortoise - await interaction.response.send_message(f"```{output}```") - @app_commands.command(name="run_help", description="Show how to run code with the execution bot") async def run_help(self, interaction: discord.Interaction): content = ( diff --git a/bot/utils/embed_handler.py b/bot/utils/embed_handler.py index e84cb10..8f2930d 100644 --- a/bot/utils/embed_handler.py +++ b/bot/utils/embed_handler.py @@ -8,9 +8,7 @@ from bot import constants from bot.utils.custom_types import FakeInteraction -from bot.utils.misc import ( - get_badges, get_join_pos, has_verified_role, format_activity, get_device_status, format_date, get_user_avatar -) +from bot.utils.misc import get_user_avatar def simple_embed(message: str, title: str, color: Union[Color, int] = constants.default_color) -> Embed: @@ -196,60 +194,6 @@ def thumbnail(message: str, member: Union[Member, User], title: str = None) -> E return embed -def status_embed(ctx, member: Member) -> Embed: - """ - Construct status embed for certain member. - Status will have info such as member device, online status, activity, roles etc. - :param ctx: context variable to get the member - :param member: member to get data from - :return: discord.Embed - """ - - color_dict = { - Status.online: Color.green(), - Status.offline: 0x000000, - Status.idle: Color.orange(), - Status.dnd: Color.red() - } - - embed = Embed(title=str(member), color=color_dict[member.status]) - embed.description = get_badges(member) - embed.set_thumbnail(url=member.display_avatar.url) - - bot = constants.tick_no - nick = member.nick - verified = constants.tick_no - join_pos = get_join_pos(ctx, member) - activities = "" - - if member.bot: - bot = constants.tick_yes - - if has_verified_role(ctx, member): - verified = constants.tick_yes - - if not nick: - nick = constants.tick_no - - for activity in member.activities: - - clean_activity = format_activity(activity) - activities += f"{clean_activity}\n" - - embed.add_field(name=f"{constants.pin_emoji} General info", - value=f"**Nick** : {nick}\n**Bot** : {bot}\n" - f"**Verified** : {verified}\n**Join position** : {join_pos}") - embed.add_field(name=f"{constants.user_emoji} Status", value=get_device_status(member), inline=False) - embed.add_field(name="📆 Dates", - value=f"**Join date** : {format_date(member.joined_at)}\n " - f"**Creation Date** : {format_date(member.created_at)}", - inline=False) - - if not activities == "": - embed.add_field(name='Activities', value=activities, inline=False) - return embed - - def infraction_embed( interaction: Union[discord.Interaction, FakeInteraction], infracted_member: Union[Member, User], From b53df3be072d6cda59705483758be2357769f835 Mon Sep 17 00:00:00 2001 From: Ryuga Date: Wed, 15 Apr 2026 14:06:22 +0530 Subject: [PATCH 4/4] chore: Add ping for mod mail --- bot/cogs/tortoise_dm.py | 9 ++++++++- bot/constants.py | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/bot/cogs/tortoise_dm.py b/bot/cogs/tortoise_dm.py index 55d9f93..fb2e2f2 100644 --- a/bot/cogs/tortoise_dm.py +++ b/bot/cogs/tortoise_dm.py @@ -216,6 +216,7 @@ def __init__(self, bot): self._tortoise_guild = None self._admin_role = None self._moderator_role = None + self._mod_mail_ping_role = None self.cool_down = CoolDown(seconds=120) self.bot.loop.create_task(self.cool_down.start()) @@ -297,6 +298,12 @@ def moderator_role(self): self._moderator_role = self.tortoise_guild.get_role(constants.moderator_role_id) return self._moderator_role + @property + def mod_mail_ping_role(self): + if self._mod_mail_ping_role is None: + self._mod_mail_ping_role = self.tortoise_guild.get_role(constants.mod_mail_ping_role_id) + return self._mod_mail_ping_role + @commands.Cog.listener() async def on_message(self, message): if message.author == self.bot.user: @@ -451,8 +458,8 @@ async def create_mod_mail(self, user: discord.User, source: str = "dm"): submission_embed = authored_sm(f"{user.name} {source_text}", author=user) view = ModMailAcceptView(self, user.id) - await self.staff_channel.send("@here", delete_after=30) msg = await self.staff_channel.send( + self.mod_mail_ping_role.mention, embed=submission_embed, view=view ) diff --git a/bot/constants.py b/bot/constants.py index 07b9694..2171c50 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -77,6 +77,8 @@ apprentice_role_id = 1472725760723648522 fellow_role_id = 1472793939630358731 +mod_mail_ping_role_id = 1493890424518086807 + # Dev constants # active_role_id = 1482855148077187102 # active_plus_role_id = 1482855121649012758