mirror of https://github.com/stijndcl/didier
Snipe
parent
87caeec47b
commit
72415aeed0
|
@ -16,9 +16,10 @@ from didier.exceptions import expect
|
|||
from didier.menus.bookmarks import BookmarkSource
|
||||
from didier.menus.common import Menu
|
||||
from didier.utils.discord import colours
|
||||
from didier.utils.discord.assets import get_author_avatar
|
||||
from didier.utils.discord.assets import get_author_avatar, get_user_avatar
|
||||
from didier.utils.discord.constants import Limits
|
||||
from didier.utils.types.datetime import str_to_date
|
||||
from didier.utils.types.string import leading
|
||||
from didier.utils.types.string import abbreviate, leading
|
||||
from didier.views.modals import CreateBookmark
|
||||
|
||||
|
||||
|
@ -204,9 +205,7 @@ class Discord(commands.Cog):
|
|||
user = user or ctx.author
|
||||
|
||||
embed = discord.Embed(colour=colours.github_white(), title="GitHub Links")
|
||||
embed.set_author(
|
||||
name=user.display_name, icon_url=user.avatar.url if user.avatar is not None else user.default_avatar.url
|
||||
)
|
||||
embed.set_author(name=user.display_name, icon_url=get_user_avatar(user))
|
||||
|
||||
embed.set_footer(text="Links can be added using didier github add <link>.")
|
||||
|
||||
|
@ -323,6 +322,39 @@ class Discord(commands.Cog):
|
|||
await message.add_reaction("📌")
|
||||
return await interaction.response.send_message("📌", ephemeral=True)
|
||||
|
||||
@commands.hybrid_command(name="snipe")
|
||||
async def snipe(self, ctx: commands.Context):
|
||||
"""Publicly shame people when they edit or delete one of their messages.
|
||||
|
||||
Note that uncached messages will not be sniped.
|
||||
"""
|
||||
if ctx.guild is None:
|
||||
return await ctx.reply("Snipe only works in servers.", mention_author=False, ephemeral=True)
|
||||
|
||||
sniped_data = self.client.sniped.get(ctx.channel.id, None)
|
||||
if sniped_data is None:
|
||||
return await ctx.reply(
|
||||
"There's no one to make fun of in this channel.", mention_author=False, ephemeral=True
|
||||
)
|
||||
|
||||
embed = discord.Embed(colour=discord.Colour.blue())
|
||||
|
||||
embed.set_author(name=sniped_data[0].author.display_name, icon_url=get_user_avatar(sniped_data[0].author))
|
||||
|
||||
if sniped_data[1] is not None:
|
||||
embed.title = "Edit Snipe"
|
||||
embed.add_field(
|
||||
name="Before", value=abbreviate(sniped_data[0].content, Limits.EMBED_FIELD_VALUE_LENGTH), inline=False
|
||||
)
|
||||
embed.add_field(
|
||||
name="After", value=abbreviate(sniped_data[1].content, Limits.EMBED_FIELD_VALUE_LENGTH), inline=False
|
||||
)
|
||||
else:
|
||||
embed.title = "Delete Snipe"
|
||||
embed.add_field(name="Message", value=sniped_data[0].content)
|
||||
|
||||
return await ctx.reply(embed=embed, mention_author=False)
|
||||
|
||||
|
||||
async def setup(client: Didier):
|
||||
"""Load the cog"""
|
||||
|
|
|
@ -3,7 +3,7 @@ import os
|
|||
import pathlib
|
||||
import re
|
||||
from functools import cached_property
|
||||
from typing import Union
|
||||
from typing import Union, Optional
|
||||
|
||||
import discord
|
||||
from aiohttp import ClientSession
|
||||
|
@ -20,6 +20,7 @@ from didier.data.embeds.schedules import Schedule, parse_schedule
|
|||
from didier.exceptions import HTTPException, NoMatch
|
||||
from didier.utils.discord.prefix import get_prefix
|
||||
from didier.utils.easter_eggs import detect_easter_egg
|
||||
from didier.utils.discord.snipe import should_snipe
|
||||
from didier.utils.types.datetime import tz_aware_now
|
||||
|
||||
__all__ = ["Didier"]
|
||||
|
@ -36,6 +37,7 @@ class Didier(commands.Bot):
|
|||
initial_extensions: tuple[str, ...] = ()
|
||||
http_session: ClientSession
|
||||
schedules: dict[settings.ScheduleType, Schedule] = {}
|
||||
sniped: dict[int, tuple[discord.Message, Optional[discord.Message]]] = {}
|
||||
wordle_words: set[str] = set()
|
||||
|
||||
def __init__(self):
|
||||
|
@ -335,6 +337,20 @@ class Didier(commands.Bot):
|
|||
if easter_egg is not None:
|
||||
await message.reply(easter_egg, mention_author=False)
|
||||
|
||||
async def on_message_delete(self, message: discord.Message):
|
||||
"""Event triggered when a message is deleted"""
|
||||
if not should_snipe(message):
|
||||
return
|
||||
|
||||
self.sniped[message.channel.id] = (message, None,)
|
||||
|
||||
async def on_message_edit(self, before: discord.Message, after: discord.Message):
|
||||
"""Event triggered when a message is edited"""
|
||||
if not should_snipe(before):
|
||||
return
|
||||
|
||||
self.sniped[before.channel.id] = (before, after,)
|
||||
|
||||
async def on_ready(self):
|
||||
"""Event triggered when the bot is ready"""
|
||||
print(settings.DISCORD_READY_MESSAGE)
|
||||
|
|
|
@ -3,10 +3,18 @@ from typing import Union
|
|||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
__all__ = ["get_author_avatar"]
|
||||
__all__ = ["get_author_avatar", "get_user_avatar"]
|
||||
|
||||
|
||||
def get_user_avatar(user: Union[discord.User, discord.Member]) -> discord.Asset:
|
||||
"""Get a user's avatar asset"""
|
||||
if isinstance(user, discord.Member):
|
||||
return user.display_avatar or user.default_avatar
|
||||
|
||||
return user.avatar or user.default_avatar
|
||||
|
||||
|
||||
def get_author_avatar(ctx: Union[commands.Context, discord.Interaction]) -> discord.Asset:
|
||||
"""Get a user's avatar asset"""
|
||||
"""Get the avatar asset of a command author"""
|
||||
author = ctx.author if isinstance(ctx, commands.Context) else ctx.user
|
||||
return author.avatar or author.default_avatar
|
||||
return get_user_avatar(author)
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import discord
|
||||
|
||||
from didier.utils.regexes import STEAM_CODE
|
||||
|
||||
|
||||
__all__ = ["should_snipe"]
|
||||
|
||||
|
||||
def should_snipe(message: discord.Message) -> bool:
|
||||
"""Check if a message should be sniped or not"""
|
||||
# Don't snipe DM's
|
||||
if message.guild is None:
|
||||
return False
|
||||
|
||||
# Don't snipe bots
|
||||
if message.author.bot:
|
||||
return False
|
||||
|
||||
return not STEAM_CODE.is_in(message.content)
|
|
@ -0,0 +1,19 @@
|
|||
from typing import Union
|
||||
from dataclasses import dataclass
|
||||
import re
|
||||
|
||||
__all__ = ["STEAM_CODE"]
|
||||
|
||||
|
||||
@dataclass
|
||||
class Regex:
|
||||
"""Dataclass for a type of pattern"""
|
||||
pattern: str
|
||||
flags: Union[int, re.RegexFlag] = 0
|
||||
|
||||
def is_in(self, text: str) -> bool:
|
||||
"""Check if a match for a pattern can be found within a string"""
|
||||
return re.search(self.pattern, text, self.flags) is not None
|
||||
|
||||
|
||||
STEAM_CODE = Regex(pattern="[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}", flags=re.IGNORECASE)
|
Loading…
Reference in New Issue