anniversary added, fixed discord timestamps, added watchlist cog

This commit is contained in:
Brandon 2022-06-21 17:11:15 -04:00
parent 4049db3c24
commit c40ae60d0e
8 changed files with 780 additions and 27 deletions

View file

@ -8,6 +8,7 @@ import discord
from .utils import * from .utils import *
from datetime import datetime from datetime import datetime
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from dateutil.tz import tzlocal
import time import time
import os import os
import asyncio import asyncio
@ -179,12 +180,12 @@ class ActivityLogger(commands.Cog):
since_created = (ctx.message.created_at - user.created_at).days since_created = (ctx.message.created_at - user.created_at).days
if joined_at is not None: if joined_at is not None:
since_joined = (ctx.message.created_at - joined_at).days since_joined = (ctx.message.created_at - joined_at).days
user_joined = f"<t:{int(joined_at.timestamp())}>" user_joined = f"<t:{int(joined_at.astimezone(tzlocal()).timestamp())}>"
else: else:
since_joined = "?" since_joined = "?"
user_joined = "Unknown" user_joined = "Unknown"
user_created = f"<t:{int(user.created_at.timestamp())}>" user_created = f"<t:{int(user.created_at.astimezone(tzlocal()).timestamp())}>"
member_number = sorted(guild.members, key=lambda m: m.joined_at or ctx.message.created_at).index(user) + 1 member_number = sorted(guild.members, key=lambda m: m.joined_at or ctx.message.created_at).index(user) + 1
created_on = "{}\n({} days ago)".format(user_created, since_created) created_on = "{}\n({} days ago)".format(user_created, since_created)

View file

@ -24,9 +24,11 @@ class Birthday(commands.Cog):
"channel": None, "channel": None,
"role": None, "role": None,
"dm_message": ":tada: Aurelia wishes you a very happy birthday! :tada:", "dm_message": ":tada: Aurelia wishes you a very happy birthday! :tada:",
"anni_role": None,
"anni_message": ":tada: Aurelia is excited to wish you for {years} year{s} in CoE! :tada:",
} }
default_member = {"birthday": None, "birthday_handeled": False} default_member = {"birthday": None, "birthday_handeled": False, "anniversary": False, "anni_handled": False}
self.config.register_guild(**default_guild) self.config.register_guild(**default_guild)
self.config.register_member(**default_member) self.config.register_member(**default_member)
@ -49,6 +51,13 @@ class Birthday(commands.Cog):
return date, age return date, age
@staticmethod
def get_years_in_guild(member: discord.Member):
joined = member.joined_at.date()
now = datetime.datetime.utcnow()
return now.year - joined.year
def cog_unload(self): def cog_unload(self):
self.bday_task.cancel() self.bday_task.cancel()
@ -69,6 +78,64 @@ class Birthday(commands.Cog):
continue continue
for member in guild.members: for member in guild.members:
await self.check_member_bday(member) await self.check_member_bday(member)
await self.check_member_anni(member)
async def check_member_anni(self, member: discord.Member):
today = datetime.datetime.utcnow().date()
anni = member.joined_at.date()
if not (await self.config.member(member).anniversary()):
return
anni = anni.replace(year=today.year)
handled = await self.config.member(member).anni_handled()
if anni == today:
if not handled:
# dm user
dm = await self.config.guild(member.guild).anni_message()
y = self.get_years_in_guild(member)
s = "s" if y > 1 else ""
dm = dm.format(years=y, s=s)
try:
await member.send(dm)
except:
pass
# send anni in channel
channel = await self.config.guild(member.guild).channel()
channel = self.bot.get_channel(channel)
if channel:
embed = discord.Embed(color=discord.Colour.gold())
embed.description = f"{member.mention} has been in {member.guild} for **{y} year{s}!**"
# embed.set_footer("Add your birthday using the `bday` command!")
try:
content = f"Congratulations {member.mention}!"
await channel.send(content=content, embed=embed, allowed_mentions=discord.AllowedMentions.all())
except:
pass
# add role, if available
role = await self.config.guild(member.guild).anni_role()
role = member.guild.get_role(role)
if role:
try:
await member.add_roles(role, reason="Birthday cog")
except:
pass
await self.config.member(member).anni_handled.set(True)
else:
if handled:
# remove anni role
role = await self.config.guild(member.guild).anni_role()
role = member.guild.get_role(role)
if role:
try:
await member.remove_roles(role, reason="Birthday cog")
except:
pass
# unhandled their anniversary, cya next year!
await self.config.member(member).anni_handled.set(False)
async def check_member_bday(self, member: discord.Member): async def check_member_bday(self, member: discord.Member):
today = datetime.datetime.utcnow().date() today = datetime.datetime.utcnow().date()
@ -135,6 +202,53 @@ class Birthday(commands.Cog):
# await self.check_bdays() # await self.check_bdays()
# await self.check_member_bday(member) # await self.check_member_bday(member)
@commands.group(name="anniset")
@commands.guild_only()
@checks.admin_or_permissions(administrator=True)
async def anniset(self, ctx):
"""
Manage anniversary settings
"""
pass
@anniset.command(name="dmmessage")
async def anniset_dmmessage(self, ctx, *, message: str = None):
"""Set message DMed to users when its their anniversary!
Leave empty to get/clear current message
In your message, you can use `{years}` which will be replaced with the number of years in the server,
With this, to make it grammatically correct use {s} which will be a `s` if years > 1
"""
if not message:
current = await self.config.guild(ctx.guild).anni_message()
await ctx.send(f"Current message is `{current}`\nDo you want to reset it to default?")
pred = MessagePredicate.yes_or_no(ctx)
try:
await self.bot.wait_for("message", check=pred, timeout=30)
except asyncio.TimeoutError:
await ctx.send("Took too long.")
return
if pred.result:
await self.config.guild(ctx.guild).anni_message.clear()
await ctx.send("DM message reset to default.")
else:
await ctx.send("Nothing changed.")
return
await self.config.guild(ctx.guild).anni_message.set(message)
await ctx.tick()
@anniset.command(name="role")
@checks.bot_has_permissions(manage_roles=True)
async def anniset_role(self, ctx, *, role: discord.Role = None):
"""Set role to give users on their anniversary"""
if not role:
await self.config.guild(ctx.guild).anni_role.clear()
else:
await self.config.guild(ctx.guild).anni_role.set(role.id)
await ctx.tick()
@commands.group(name="bdayset") @commands.group(name="bdayset")
@commands.guild_only() @commands.guild_only()
@checks.admin_or_permissions(administrator=True) @checks.admin_or_permissions(administrator=True)
@ -187,6 +301,60 @@ class Birthday(commands.Cog):
await ctx.tick() await ctx.tick()
@commands.group(name="anni")
@commands.guild_only()
async def anni(self, ctx):
"""Manage your server anniversary"""
pass
@anni.command(name="set")
async def anni_set(self, ctx, toggle: bool = None):
"""
Opt in to anniversary announcements
"""
current = await self.config.member(ctx.author).anniversary()
if toggle is None:
if current:
await ctx.send("You are currently opted in for anniversary messages.")
return
else:
await ctx.send("You are currently NOT opted in for anniversary messages.")
return
await self.config.member(ctx.author).anniversary.set(toggle)
await ctx.tick()
@anni.command(name="list")
async def anni_list(self, ctx):
"""List anniversaries in the server"""
members = []
anniversaries = []
for member in ctx.guild.members:
if not (await self.config.member(member).anniversary()):
continue
anni = member.joined_at.date()
members.append(member.display_name)
anniversaries.append(anni.strftime("%b %d, %Y"))
pages = []
raw = list(
pagify(
tabulate({"Member": members, "Anniversary": anniversaries}, tablefmt="github", headers="keys"),
page_length=1700,
delims=["\n"],
priority=True,
)
)
for i, page in enumerate(raw):
pages.append(box(f"{page}\n\n-----------------\nPage {i+1} of {len(raw)}"))
if not pages:
await ctx.send("No one has their anniversary set in your server!")
else:
await menu(ctx, pages, DEFAULT_CONTROLS)
@commands.group(name="bday") @commands.group(name="bday")
@commands.guild_only() @commands.guild_only()
async def bday(self, ctx): async def bday(self, ctx):

View file

@ -1,22 +1,25 @@
{ {
"author": [ "author": [
"PancakeSparkle", "PancakeSparkle",
"shroomdog26", "shroomdog26",
"brandons209" "brandons209"
], ],
"bot_version": [ "bot_version": [
3, 3,
4, 4,
0 0
], ],
"description": "Cog used for setting birthdays and announcing them and giving people roles on their birthday", "description": "Cog used for setting birthdays and announcing them and giving people roles on their birthday. Also can announce server anniversaries.",
"hidden": false, "hidden": false,
"install_msg": "Use [p]bday to bring up the help menu", "install_msg": "Use [p]bday to bring up the help menu",
"requirements": ["python-dateutil", "tabulate"], "requirements": [
"short": "Cog used for setting birthdays and announcing them and giving people roles on their birthday", "python-dateutil",
"tags": [ "tabulate"
"brandons209", ],
"pancakesparkle" "short": "Cog used for setting birthdays and announcing them and giving people roles on their birthday, also does the same for anniversaries in the server.",
], "tags": [
"end_user_data_statement": "This cog will store a user's birthday." "brandons209",
} "pancakesparkle"
],
"end_user_data_statement": "This cog will store a user's birthday."
}

7
watchlist/__init__.py Normal file
View file

@ -0,0 +1,7 @@
from .watchlist import Watchlist
__red_end_user_data_statement__ = "This doesn't store any user data."
def setup(bot):
bot.add_cog(Watchlist(bot))

12
watchlist/info.json Normal file
View file

@ -0,0 +1,12 @@
{
"author": [
"brandons209"
],
"install_msg": "Thank you for installing my cog!",
"name": "Watchlist",
"short": "Watch for user's joining your guild and get notified when they join/leave.",
"description": "Allows guild staff to better keep track of persons of interest in their guild.",
"tags": [
"watchlist"
]
}

559
watchlist/watchlist.py Normal file
View file

@ -0,0 +1,559 @@
import asyncio
import discord
import datetime
from tabulate import tabulate
from typing import Optional, Literal, Union
from redbot.core import Config, checks, commands
from redbot.core.utils.chat_formatting import *
from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
class WatchlistUser:
"""
Maintains watchlist user data and provides functions for modification
"""
def __init__(
self,
bot,
user_id: int,
watchlist_number: int,
reason: str,
added_by: int,
message: discord.Message = None,
amended_by: int = None,
amended_time: int = None,
):
"""
Create a new user on a watchlist
Args:
bot (Red): Bot instance
user_id (int): ID of the user on the watchlist
watchlist_number (int): The watchlist number this user represents
reason (str): Reason for being on the watchlist
added_by (int): Moderator/Administrator that added this user to the watchlist
message (discord.Message, optional): Message object on the watchlist. Defaults to None.
amended_by (int, optional): User ID of user who edited this watchlist user. Defaults to None.
amended_time (int, optional): When changes were last made to this watchlist user. Defaults to None.
"""
self.user_id = user_id
self.message = message
self.watchlist_number = watchlist_number
self.reason = reason
self.added_by = added_by
self.amended_by = amended_by
self.amended_time = amended_time
self.bot = bot
async def create_embed(self, amended_by: discord.Member = None):
"""
Create a discord Embed that represents this user on the watchlist
Args:
amended_by (discord.Member, optional): User who amended this watchlist user. Defaults to None.
Returns:
discord.Embed: The embed representing this user
"""
user = self.bot.get_user(self.user_id)
if not user:
user = await self.bot.fetch_user(self.user_id)
added_by = self.bot.get_user(self.added_by)
if not added_by:
added_by = await self.bot.fetch_user(self.added_by)
if not user:
title = f"#{self.watchlist_number} Unknown / not found user ({self.user_id})"
avatar = None
else:
title = f"#{self.watchlist_number} {user} ({user.id})"
avatar = user.avatar_url_as(static_format="png")
embed = discord.Embed(color=discord.Color.blue(), title=title, description=self.reason)
if avatar:
embed.set_thumbnail(url=avatar)
embed.add_field(
name="Added by",
value=f"{added_by if added_by is not None else 'Unknown / not found user ({self.added_by})'}",
)
if amended_by is not None:
self.amended_by = amended_by.id
self.amended_time = int(datetime.datetime.now().timestamp())
embed.add_field(name="Amended by", value=f"{amended_by} at <t:{self.amended_time}:f>")
else:
amended_by = self.bot.get_user(self.amended_by)
if not amended_by and self.amended_by is not None:
amended_by = await self.bot.fetch_user(self.amended_by)
if amended_by is not None:
embed.add_field(name="Amended by", value=f"{amended_by} at <t:{self.amended_time}:f>")
return embed
async def send_watchlist_message(self, channel: discord.TextChannel = None):
"""
Send (or resend) watchlist message
Args:
channel (discord.TextChannel, optional): The channel to send the message to
Raises:
AttributeError: If there is no channel provided and internal message is not set
"""
if channel:
message = await channel.send(embed=(await self.create_embed()))
self.message = message
elif self.message is not None:
channel = self.message.channel
try:
await self.message.delete()
except:
pass
message = await channel.send(embed=(await self.create_embed()))
self.message = message
else:
raise AttributeError("Must provide a channel if there is no message for this user on watchlist.")
async def delete_watchlist_message(self):
"""
Deletes message on the watchlist
Returns:
bool: True if successful, False otherwise
"""
if self.message is None:
return False
try:
await self.message.delete()
return True
except:
return False
async def update_reason(self, member: discord.Member, reason: str):
"""
Update the reason for this user
Args:
member (discord.Member): The user that requested this update.
reason (str): The new reason
Returns:
bool: True if successful, False otherwise
"""
self.reason = reason
new_embed = await self.create_embed(member)
try:
await self.message.edit(embed=new_embed)
return True
except:
return False
async def update_embed(self):
"""
Update embeds with new user information
Returns:
bool: True if successful, False otherwise
"""
new_embed = await self.create_embed()
try:
await self.message.edit(embed=new_embed)
return True
except:
return False
def to_dict(self):
"""
Converts data for this object into dictionary
Returns:
dict: The object data as a dictionary
"""
data = {
"user_id": self.user_id,
"added_by": self.added_by,
"channel_id": self.message.channel.id if self.message else None,
"message_id": self.message.id if self.message else None,
"watchlist_number": self.watchlist_number,
"reason": self.reason,
"amended_by": self.amended_by,
"amended_time": self.amended_time,
}
return data
@staticmethod
async def from_dict(bot, data: dict):
"""
Create a new WatchlistUser object from data dictionary
Args:
bot (Red): Bot instance
data (dict): Data for watchlist user
Raises:
AttributeError: If there is a missing key in the data dictionary
Returns:
WatchlistUser: The watchlist user object
"""
needed_keys = [
"user_id",
"added_by",
"channel_id",
"message_id",
"watchlist_number",
"reason",
"amended_by",
"amended_time",
]
for k in needed_keys:
if k not in data:
raise AttributeError(f"{k} missing from dictionary!")
channel = bot.get_channel(data["channel_id"])
if not channel:
message = None
else:
message = await channel.fetch_message(data["message_id"])
return WatchlistUser(
bot,
data["user_id"],
data["watchlist_number"],
data["reason"],
data["added_by"],
message=message,
amended_by=data["amended_by"],
amended_time=data["amended_time"],
)
class Watchlist(commands.Cog):
"""
Watchlist of persons of interest
"""
def __init__(self, bot):
self.bot = bot
self.config = Config.get_conf(self, identifier=8946115618891655613, force_registration=True)
# watchlist user data will contain list of dictionaries that cna be converted to a watchlistuser class object
default_guild = {
"watchlist_users": [],
"removed_users": [],
"channel": None,
"alert_channel": None,
"watchlist_num": 0,
}
self.config.register_guild(**default_guild)
# store cached watchlist for each guild
self.watchlist = {}
self.task = asyncio.create_task(self.init())
def cog_unload(self):
if self.task:
self.task.cancel()
async def init(self):
await self.bot.wait_until_ready()
for guild in self.bot.guilds:
watch_list = await self.config.guild(guild).watchlist_users()
self.watchlist[guild.id] = []
for w in watch_list:
try:
self.watchlist[guild.id].append(await WatchlistUser.from_dict(self.bot, w))
except AttributeError as e:
print(e)
while True:
for guild in self.bot.guilds:
if guild.id not in self.watchlist:
self.watchlist[guild.id] = []
for watchlist_user in self.watchlist[guild.id]:
await watchlist_user.update_embed()
await asyncio.sleep(28800) # update every 8 hours
@commands.group(name="watchlist")
@commands.guild_only()
@checks.admin_or_permissions(administrator=True)
async def watchlist(self, ctx):
"""
Manage guild watchlist
"""
pass
@watchlist.command(name="channel")
async def watchlist_channel(self, ctx, *, channel: discord.TextChannel = None):
"""
Change the watchlist channel
"""
if not channel:
await self.config.guild(ctx.guild).channel.clear()
await ctx.send(info("Watchlist channel cleared."))
else:
await self.config.guild(ctx.guild).channel.set(channel.id)
await ctx.tick()
@watchlist.command(name="alert")
async def watchlist_alert(self, ctx, *, channel: discord.TextChannel = None):
"""
Change the watchlist alert channel
"""
if not channel:
await self.config.guild(ctx.guild).alert_channel.clear()
await ctx.send(info("Watchlist channel cleared."))
else:
await self.config.guild(ctx.guild).alert_channel.set(channel.id)
await ctx.tick()
@watchlist.command(name="add")
async def watchlist_add(self, ctx, user_id: int, *, reason: str = None):
"""
Add a user to the watchlist, Reason is optional
Must use their user id!
"""
if ctx.guild.id not in self.watchlist:
self.watchlist[ctx.guild.id] = []
user = self.bot.get_user(user_id)
if not user:
user = await self.bot.fetch_user(user_id)
watch_list_ids = [w.user_id for w in self.watchlist[ctx.guild.id]]
if not user:
await ctx.send(error(f"Could not find user with id `{user_id}`!"))
return
elif user_id in watch_list_ids:
await ctx.send(error(f"User {user} already in the watchlist!"))
return
if reason is None:
reason = "Use `[p]watchlist reason <watchlist number>` to add a reason."
watchlist_num = await self.config.guild(ctx.guild).watchlist_num()
channel_id = await self.config.guild(ctx.guild).channel()
channel = ctx.guild.get_channel(channel_id)
alert_channel = await self.config.guild(ctx.guild).alert_channel()
alert_channel = ctx.guild.get_channel(alert_channel)
if not channel:
await ctx.send(error(f"Could not find watchlist channel, please set it using `[p]watchlist channel` !"))
return
if not alert_channel:
await ctx.send(
warning(
"No alert channel set, you will not get alerts if this user joins! Please set it using `[p]watchlist alert`"
)
)
watchlist_user = WatchlistUser(self.bot, user.id, watchlist_num, reason, ctx.author.id)
await watchlist_user.send_watchlist_message(channel)
self.watchlist[ctx.guild.id].append(watchlist_user)
async with self.config.guild(ctx.guild).watchlist_users() as watchlist_users:
watchlist_users.append(watchlist_user.to_dict())
await self.config.guild(ctx.guild).watchlist_num.set(watchlist_num + 1)
await ctx.tick()
@watchlist.command(name="remove")
async def watchlist_remove(self, ctx, watchlist_num: int, *, reason: str = None):
"""
Remove a user from the watchlist.
Reason is optional
"""
if ctx.guild.id not in self.watchlist:
self.watchlist[ctx.guild.id] = []
watch_list_ids = [w.watchlist_number for w in self.watchlist[ctx.guild.id]]
if watchlist_num not in watch_list_ids:
await ctx.send(error("Unknown watchlist number!"))
return
idx = watch_list_ids.index(watchlist_num)
watchlist_user = self.watchlist[ctx.guild.id][idx]
if reason:
watchlist_user.reason = f"Removed from watchlist by {ctx.author.mention} (id: {ctx.author.id}) because: {reason}\nOriginal reason: {watchlist_user.reason}"
else:
watchlist_user.reason = f"Removed from watchlist by {ctx.author.mention} (id: {ctx.author.id})\nOriginal reason: {watchlist_user.reason}"
async with self.config.guild(ctx.guild).removed_users() as removed_users:
removed_users.append(watchlist_user.to_dict())
# delete message from watchlist channel
status = await watchlist_user.delete_watchlist_message()
if not status:
await ctx.send(warning("There was an issue removing the message from the watchlist channel for this user!"))
del self.watchlist[ctx.guild.id][idx]
async with self.config.guild(ctx.guild).watchlist_users() as watchlist_users:
ids = [w["watchlist_number"] for w in watchlist_users]
idx = ids.index(watchlist_num)
del watchlist_users[idx]
await ctx.tick()
@watchlist.command(name="reason")
async def watchlist_reason(self, ctx, watchlist_num: int, *, reason):
"""
Change the reason for a watchlist user
Use the watchlist number to specify the user to change the reason for
"""
if ctx.guild.id not in self.watchlist:
self.watchlist[ctx.guild.id] = []
watchlist_numbers = [w.watchlist_number for w in self.watchlist[ctx.guild.id]]
if watchlist_num not in watchlist_numbers:
await ctx.send(error("Unknown watchlist number!"))
return
idx = watchlist_numbers.index(watchlist_num)
watchlist_user = self.watchlist[ctx.guild.id][idx]
status = await watchlist_user.update_reason(ctx.author, reason)
if not status:
await ctx.send(error("There was an issue updating the reason!"))
else:
await ctx.tick()
@watchlist.command(name="list")
async def watchlist_list(self, ctx):
"""
List removed users
"""
removed_users = await self.config.guild(ctx.guild).removed_users()
if len(removed_users) < 1:
await ctx.send(info("No one has been removed from the watchlist in your guild."))
return
msg = ""
for data in removed_users:
user = self.bot.get_user(data["user_id"])
if not user:
user = await self.bot.fetch_user(data["user_id"])
if user is None:
msg += f"Unknown user (id: {data['user_id']})\n"
else:
msg += f"{user.mention} (id: {user.id})\n"
msg += f"{data['reason']}\n"
msg += ("=" * 10) + "\n"
raw = list(
pagify(
msg,
page_length=1700,
delims=["\n"],
priority=True,
)
)
pages = []
for i, page in enumerate(raw):
pages.append(f"{page}\n\n-----------------\nPage {i+1} of {len(raw)}")
if not pages:
await ctx.send("No one has their birthday set in your server!")
else:
await menu(ctx, pages, DEFAULT_CONTROLS)
@commands.Cog.listener()
async def on_member_join(self, member: discord.Member):
guild = member.guild
if guild.id not in self.watchlist:
self.watchlist[guild.id] = []
watchlist_ids = [w.user_id for w in self.watchlist[guild.id]]
if not member.id in watchlist_ids:
return
idx = watchlist_ids.index(member.id)
watchlist_user = self.watchlist[guild.id][idx]
alert_channel = await self.config.guild(guild).alert_channel()
channel = guild.get_channel(alert_channel)
if not channel:
return
admin_roles = " ".join([r.mention for r in (await self.bot.get_admin_roles(guild))])
mod_roles = " ".join([r.mention for r in (await self.bot.get_mod_roles(guild))])
if not admin_roles or not mod_roles:
await channel.send(
f"**__Watchlist Alert for #{watchlist_user.watchlist_number}__**\n@everyone\n\nUser {member.mention} has joined!\n\n**Watchlist reason:** `{watchlist_user.reason}`",
allowed_mentions=discord.AllowedMentions(everyone=True),
)
else:
await channel.send(
f"**__Watchlist Alert for #{watchlist_user.watchlist_number}__**\n{admin_roles} {mod_roles}\n\nUser {member.mention} has joined!\n\n**Watchlist reason:** `{watchlist_user.reason}`",
allowed_mentions=discord.AllowedMentions(roles=True),
)
@commands.Cog.listener()
async def on_member_remove(self, member: discord.Member):
guild = member.guild
if guild.id not in self.watchlist:
self.watchlist[guild.id] = []
watchlist_ids = [w.user_id for w in self.watchlist[guild.id]]
if not member.id in watchlist_ids:
return
idx = watchlist_ids.index(member.id)
watchlist_user = self.watchlist[guild.id][idx]
alert_channel = await self.config.guild(guild).alert_channel()
channel = guild.get_channel(alert_channel)
if not channel:
return
admin_roles = " ".join([r.mention for r in (await self.bot.get_admin_roles(guild))])
mod_roles = " ".join([r.mention for r in (await self.bot.get_mod_roles(guild))])
if not admin_roles or not mod_roles:
await channel.send(
f"**__Watchlist Alert for #{watchlist_user.watchlist_number}__**\n@everyone\n\nUser {member.mention} has left!\n\n**Watchlist reason:** `{watchlist_user.reason}`",
allowed_mentions=discord.AllowedMentions(everyone=True),
)
else:
await channel.send(
f"**__Watchlist Alert for #{watchlist_user.watchlist_number}__**\n{admin_roles} {mod_roles}\n\nUser {member.mention} has left!\n\n**Watchlist reason:** `{watchlist_user.reason}`",
allowed_mentions=discord.AllowedMentions(roles=True),
)

View file

@ -7,7 +7,9 @@
"name": "Welcome", "name": "Welcome",
"short": "Announces membership events.", "short": "Announces membership events.",
"description": "Announces members joining, leaving, getting banned, and getting unbanned, in a customizable text channel and with customizable messages.", "description": "Announces members joining, leaving, getting banned, and getting unbanned, in a customizable text channel and with customizable messages.",
"requirements": [], "requirements": [
"python-dateutil"
],
"tags": [ "tags": [
"welcome", "welcome",
"greetings", "greetings",
@ -17,4 +19,4 @@
], ],
"min_bot_version": "3.4.0", "min_bot_version": "3.4.0",
"end_user_data_statement": "This cog doesn't store any user data." "end_user_data_statement": "This cog doesn't store any user data."
} }

View file

@ -7,6 +7,7 @@ from typing import Optional, Union, Literal
from redbot.core import Config, checks, commands from redbot.core import Config, checks, commands
from redbot.core.utils.chat_formatting import box, pagify, humanize_list from redbot.core.utils.chat_formatting import box, pagify, humanize_list
from dateutil.tz import tzlocal
from .enums import WhisperType from .enums import WhisperType
from .errors import WhisperError from .errors import WhisperError
@ -815,7 +816,7 @@ class Welcome(commands.Cog):
plural=plural, plural=plural,
roles=roles, roles=roles,
stats=stats, stats=stats,
joined_on=f"<t:{int(user.joined_at.timestamp())}>", joined_on=f"<t:{int(user.joined_at.astimezone(tzlocal()).timestamp())}>",
), ),
allowed_mentions=discord.AllowedMentions.all(), allowed_mentions=discord.AllowedMentions.all(),
) )