2018-12-23 19:03:40 +00:00
|
|
|
|
import discord
|
2019-02-28 22:10:30 +00:00
|
|
|
|
from discord.ext.commands import Cog
|
2018-12-23 19:03:40 +00:00
|
|
|
|
import json
|
2019-02-26 13:03:21 +00:00
|
|
|
|
import re
|
2018-12-23 19:03:40 +00:00
|
|
|
|
import config
|
2018-12-27 11:56:13 +00:00
|
|
|
|
from helpers.restrictions import get_user_restrictions
|
2019-03-05 17:33:19 +00:00
|
|
|
|
from helpers.checks import check_if_staff
|
2018-12-23 19:03:40 +00:00
|
|
|
|
|
2019-03-03 18:08:46 +00:00
|
|
|
|
|
2019-02-28 22:10:30 +00:00
|
|
|
|
class Logs(Cog):
|
2018-12-23 19:03:40 +00:00
|
|
|
|
"""
|
|
|
|
|
Logs join and leave messages, bans and unbans, and member changes.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, bot):
|
|
|
|
|
self.bot = bot
|
2020-04-20 22:05:32 +00:00
|
|
|
|
self.invite_re = re.compile(
|
|
|
|
|
r"((discord\.gg|discordapp\.com/" r"+invite)/+[a-zA-Z0-9-]+)", re.IGNORECASE
|
|
|
|
|
)
|
2019-02-27 08:03:45 +00:00
|
|
|
|
self.name_re = re.compile(r"[a-zA-Z0-9].*")
|
2020-04-20 22:05:32 +00:00
|
|
|
|
self.clean_re = re.compile(r"[^a-zA-Z0-9_ ]+", re.UNICODE)
|
2019-02-27 08:12:07 +00:00
|
|
|
|
# All lower case, no spaces, nothing non-alphanumeric
|
2020-04-20 22:05:32 +00:00
|
|
|
|
susp_hellgex = "|".join(
|
|
|
|
|
[r"\W*".join(list(word)) for word in config.suspect_words]
|
|
|
|
|
)
|
2019-03-03 19:41:06 +00:00
|
|
|
|
self.susp_hellgex = re.compile(susp_hellgex, re.IGNORECASE)
|
2019-03-03 19:39:06 +00:00
|
|
|
|
|
2019-02-28 22:10:30 +00:00
|
|
|
|
@Cog.listener()
|
2018-12-23 19:03:40 +00:00
|
|
|
|
async def on_member_join(self, member):
|
|
|
|
|
await self.bot.wait_until_ready()
|
2019-12-27 15:43:47 +00:00
|
|
|
|
|
2020-04-20 22:05:32 +00:00
|
|
|
|
if member.guild.id not in config.guild_whitelist:
|
2019-12-27 15:43:47 +00:00
|
|
|
|
return
|
|
|
|
|
|
2018-12-23 19:03:40 +00:00
|
|
|
|
log_channel = self.bot.get_channel(config.log_channel)
|
|
|
|
|
# We use this a lot, might as well get it once
|
|
|
|
|
escaped_name = self.bot.escape_message(member)
|
|
|
|
|
|
2019-03-02 23:40:43 +00:00
|
|
|
|
# Attempt to correlate the user joining with an invite
|
|
|
|
|
with open("data/invites.json", "r") as f:
|
|
|
|
|
invites = json.load(f)
|
|
|
|
|
|
|
|
|
|
real_invites = await member.guild.invites()
|
|
|
|
|
|
|
|
|
|
# Add unknown active invites. Can happen if invite was manually created
|
|
|
|
|
for invite in real_invites:
|
|
|
|
|
if invite.id not in invites:
|
2019-03-03 14:12:30 +00:00
|
|
|
|
invites[invite.id] = {
|
|
|
|
|
"uses": 0,
|
|
|
|
|
"url": invite.url,
|
|
|
|
|
"max_uses": invite.max_uses,
|
2020-04-20 22:05:32 +00:00
|
|
|
|
"code": invite.code,
|
2019-03-03 14:12:30 +00:00
|
|
|
|
}
|
2019-03-02 23:40:43 +00:00
|
|
|
|
|
|
|
|
|
probable_invites_used = []
|
|
|
|
|
items_to_delete = []
|
|
|
|
|
# Look for invites whose usage increased since last lookup
|
|
|
|
|
for id, invite in invites.items():
|
|
|
|
|
real_invite = next((x for x in real_invites if x.id == id), None)
|
|
|
|
|
|
|
|
|
|
if real_invite is None:
|
|
|
|
|
# Invite does not exist anymore. Was either revoked manually
|
|
|
|
|
# or the final use was used up
|
|
|
|
|
probable_invites_used.append(invite)
|
|
|
|
|
items_to_delete.append(id)
|
|
|
|
|
elif invite["uses"] < real_invite.uses:
|
|
|
|
|
probable_invites_used.append(invite)
|
|
|
|
|
invite["uses"] = real_invite.uses
|
|
|
|
|
|
|
|
|
|
# Delete used up invites
|
|
|
|
|
for id in items_to_delete:
|
|
|
|
|
del invites[id]
|
|
|
|
|
|
|
|
|
|
# Save invites data.
|
|
|
|
|
with open("data/invites.json", "w") as f:
|
|
|
|
|
f.write(json.dumps(invites))
|
|
|
|
|
|
|
|
|
|
# Prepare the invite correlation message
|
|
|
|
|
if len(probable_invites_used) == 1:
|
2019-04-24 09:03:57 +00:00
|
|
|
|
invite_used = probable_invites_used[0]["code"]
|
2019-03-02 23:40:43 +00:00
|
|
|
|
elif len(probable_invites_used) == 0:
|
|
|
|
|
invite_used = "Unknown"
|
|
|
|
|
else:
|
2019-03-03 14:12:30 +00:00
|
|
|
|
invite_used = "One of: "
|
|
|
|
|
invite_used += ", ".join([x["code"] for x in probable_invites_used])
|
2019-03-02 23:40:43 +00:00
|
|
|
|
|
2018-12-23 19:03:40 +00:00
|
|
|
|
# Check if user account is older than 15 minutes
|
|
|
|
|
age = member.joined_at - member.created_at
|
|
|
|
|
if age < config.min_age:
|
|
|
|
|
try:
|
2020-04-20 22:05:32 +00:00
|
|
|
|
await member.send(
|
|
|
|
|
f"Your account is too new to "
|
|
|
|
|
f"join {member.guild.name}."
|
|
|
|
|
" Please try again later."
|
|
|
|
|
)
|
2018-12-23 19:03:40 +00:00
|
|
|
|
sent = True
|
|
|
|
|
except discord.errors.Forbidden:
|
|
|
|
|
sent = False
|
|
|
|
|
await member.kick(reason="Too new")
|
2019-03-02 23:40:43 +00:00
|
|
|
|
|
2020-04-20 22:05:32 +00:00
|
|
|
|
msg = (
|
|
|
|
|
f"🚨 **Account too new**: {member.mention} | "
|
|
|
|
|
f"{escaped_name}\n"
|
|
|
|
|
f"🗓 __Creation__: {member.created_at}\n"
|
|
|
|
|
f"🕓 Account age: {age}\n"
|
|
|
|
|
f"✉ Joined with: {invite_used}\n"
|
|
|
|
|
f"🏷 __User ID__: {member.id}"
|
|
|
|
|
)
|
2018-12-23 19:03:40 +00:00
|
|
|
|
if not sent:
|
2020-04-20 22:05:32 +00:00
|
|
|
|
msg += (
|
|
|
|
|
"\nThe user has disabled direct messages, "
|
|
|
|
|
"so the reason was not sent."
|
|
|
|
|
)
|
2018-12-23 19:03:40 +00:00
|
|
|
|
await log_channel.send(msg)
|
|
|
|
|
return
|
2020-04-20 22:05:32 +00:00
|
|
|
|
msg = (
|
|
|
|
|
f"✅ **Join**: {member.mention} | "
|
|
|
|
|
f"{escaped_name}\n"
|
|
|
|
|
f"🗓 __Creation__: {member.created_at}\n"
|
|
|
|
|
f"🕓 Account age: {age}\n"
|
|
|
|
|
f"✉ Joined with: {invite_used}\n"
|
|
|
|
|
f"🏷 __User ID__: {member.id}"
|
|
|
|
|
)
|
2018-12-23 19:03:40 +00:00
|
|
|
|
|
2018-12-27 11:56:13 +00:00
|
|
|
|
# Handles user restrictions
|
|
|
|
|
# Basically, gives back muted role to users that leave with it.
|
|
|
|
|
rsts = get_user_restrictions(member.id)
|
|
|
|
|
roles = [discord.utils.get(member.guild.roles, id=rst) for rst in rsts]
|
|
|
|
|
await member.add_roles(*roles)
|
2018-12-23 23:53:14 +00:00
|
|
|
|
|
2018-12-23 19:03:40 +00:00
|
|
|
|
# Real hell zone.
|
2018-12-27 11:32:16 +00:00
|
|
|
|
with open("data/userlog.json", "r") as f:
|
2018-12-23 19:03:40 +00:00
|
|
|
|
warns = json.load(f)
|
|
|
|
|
try:
|
2018-12-23 22:36:36 +00:00
|
|
|
|
if len(warns[str(member.id)]["warns"]) == 0:
|
2018-12-23 19:03:40 +00:00
|
|
|
|
await log_channel.send(msg)
|
|
|
|
|
else:
|
2020-04-20 22:05:32 +00:00
|
|
|
|
embed = discord.Embed(
|
|
|
|
|
color=discord.Color.dark_red(), title=f"Warns for {escaped_name}"
|
|
|
|
|
)
|
2018-12-23 19:03:40 +00:00
|
|
|
|
embed.set_thumbnail(url=member.avatar_url)
|
2018-12-23 22:36:36 +00:00
|
|
|
|
for idx, warn in enumerate(warns[str(member.id)]["warns"]):
|
2020-04-20 22:05:32 +00:00
|
|
|
|
embed.add_field(
|
|
|
|
|
name=f"{idx + 1}: {warn['timestamp']}",
|
|
|
|
|
value=f"Issuer: {warn['issuer_name']}"
|
|
|
|
|
f"\nReason: {warn['reason']}",
|
|
|
|
|
)
|
2018-12-23 19:03:40 +00:00
|
|
|
|
await log_channel.send(msg, embed=embed)
|
|
|
|
|
except KeyError: # if the user is not in the file
|
|
|
|
|
await log_channel.send(msg)
|
|
|
|
|
|
2019-02-26 13:03:21 +00:00
|
|
|
|
async def do_spy(self, message):
|
2019-03-02 18:00:55 +00:00
|
|
|
|
if message.author.bot:
|
|
|
|
|
return
|
|
|
|
|
|
2019-03-05 17:33:19 +00:00
|
|
|
|
if check_if_staff(message):
|
|
|
|
|
return
|
|
|
|
|
|
2019-02-26 13:03:21 +00:00
|
|
|
|
alert = False
|
2020-04-20 22:05:32 +00:00
|
|
|
|
cleancont = self.clean_re.sub("", message.content).lower()
|
|
|
|
|
msg = (
|
|
|
|
|
f"🚨 Suspicious message by {message.author.mention} "
|
|
|
|
|
f"({message.author.id}):"
|
|
|
|
|
)
|
2019-02-26 13:03:21 +00:00
|
|
|
|
|
2019-03-05 21:55:06 +00:00
|
|
|
|
invites = self.invite_re.findall(message.content)
|
2019-02-26 13:03:21 +00:00
|
|
|
|
for invite in invites:
|
|
|
|
|
msg += f"\n- Has invite: https://{invite[0]}"
|
|
|
|
|
alert = True
|
|
|
|
|
|
2020-04-09 18:54:27 +00:00
|
|
|
|
for susp_word in config.suspect_words:
|
2020-04-20 22:05:32 +00:00
|
|
|
|
if susp_word in cleancont and not any(
|
|
|
|
|
ok_word in cleancont for ok_word in config.suspect_ignored_words
|
|
|
|
|
):
|
2019-02-27 08:12:07 +00:00
|
|
|
|
msg += f"\n- Contains suspicious word: `{susp_word}`"
|
|
|
|
|
alert = True
|
|
|
|
|
|
2019-02-26 13:03:21 +00:00
|
|
|
|
if alert:
|
2019-02-27 08:12:07 +00:00
|
|
|
|
msg += f"\n\nJump: <{message.jump_url}>"
|
2019-02-26 13:11:52 +00:00
|
|
|
|
spy_channel = self.bot.get_channel(config.spylog_channel)
|
2019-03-03 18:02:08 +00:00
|
|
|
|
|
2019-03-03 19:39:06 +00:00
|
|
|
|
# Bad Code :tm:, blame retr0id
|
|
|
|
|
message_clean = message.content.replace("*", "").replace("_", "")
|
2020-04-20 22:05:32 +00:00
|
|
|
|
regd = self.susp_hellgex.sub(
|
|
|
|
|
lambda w: "**{}**".format(w.group(0)), message_clean
|
|
|
|
|
)
|
2019-03-03 19:39:06 +00:00
|
|
|
|
|
2019-03-03 18:02:08 +00:00
|
|
|
|
# Show a message embed
|
2019-03-03 19:39:06 +00:00
|
|
|
|
embed = discord.Embed(description=regd)
|
2020-04-20 22:05:32 +00:00
|
|
|
|
embed.set_author(
|
|
|
|
|
name=message.author.display_name, icon_url=message.author.avatar_url
|
|
|
|
|
)
|
2019-03-03 18:02:08 +00:00
|
|
|
|
|
|
|
|
|
await spy_channel.send(msg, embed=embed)
|
2019-02-26 13:03:21 +00:00
|
|
|
|
|
2019-02-27 08:03:45 +00:00
|
|
|
|
async def do_nickcheck(self, message):
|
|
|
|
|
compliant = self.name_re.fullmatch(message.author.display_name)
|
|
|
|
|
if compliant:
|
|
|
|
|
return
|
|
|
|
|
|
2020-04-20 22:05:32 +00:00
|
|
|
|
msg = (
|
|
|
|
|
f"R11 violating name by {message.author.mention} " f"({message.author.id})."
|
|
|
|
|
)
|
2019-02-27 08:03:45 +00:00
|
|
|
|
|
|
|
|
|
spy_channel = self.bot.get_channel(config.spylog_channel)
|
|
|
|
|
await spy_channel.send(msg)
|
|
|
|
|
|
2019-02-28 22:10:30 +00:00
|
|
|
|
@Cog.listener()
|
2019-02-26 13:03:21 +00:00
|
|
|
|
async def on_message(self, message):
|
|
|
|
|
await self.bot.wait_until_ready()
|
|
|
|
|
if message.channel.id not in config.spy_channels:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
await self.do_spy(message)
|
|
|
|
|
|
2019-02-28 22:10:30 +00:00
|
|
|
|
@Cog.listener()
|
2019-01-29 10:51:43 +00:00
|
|
|
|
async def on_message_edit(self, before, after):
|
|
|
|
|
await self.bot.wait_until_ready()
|
2019-02-05 20:52:09 +00:00
|
|
|
|
if after.channel.id not in config.spy_channels or after.author.bot:
|
2019-01-29 10:51:43 +00:00
|
|
|
|
return
|
|
|
|
|
|
2019-02-14 22:10:47 +00:00
|
|
|
|
# If content is the same, just skip over it
|
|
|
|
|
# This usually means that something embedded.
|
|
|
|
|
if before.clean_content == after.clean_content:
|
|
|
|
|
return
|
|
|
|
|
|
2019-02-26 13:03:21 +00:00
|
|
|
|
await self.do_spy(after)
|
|
|
|
|
|
2019-10-21 22:12:09 +00:00
|
|
|
|
# U+200D is a Zero Width Joiner stopping backticks from breaking the formatting
|
|
|
|
|
before_content = before.clean_content.replace("`", "`\u200d")
|
|
|
|
|
after_content = after.clean_content.replace("`", "`\u200d")
|
|
|
|
|
|
2019-01-29 10:51:43 +00:00
|
|
|
|
log_channel = self.bot.get_channel(config.log_channel)
|
2020-04-21 10:59:33 +00:00
|
|
|
|
|
2020-04-20 22:05:32 +00:00
|
|
|
|
msg = (
|
2020-05-17 20:40:23 +00:00
|
|
|
|
"📝 **Message edit**: \n"
|
|
|
|
|
f"from {self.bot.escape_message(after.author.name)} "
|
|
|
|
|
f"({after.author.id}), in {after.channel.mention}:\n"
|
2020-04-21 10:59:33 +00:00
|
|
|
|
f"```{before_content}``` → ```{after_content}```"
|
2020-04-20 22:05:32 +00:00
|
|
|
|
)
|
2019-02-14 22:10:47 +00:00
|
|
|
|
|
|
|
|
|
# If resulting message is too long, upload to hastebin
|
|
|
|
|
if len(msg) > 2000:
|
2019-02-14 22:14:09 +00:00
|
|
|
|
haste_url = await self.bot.haste(msg)
|
|
|
|
|
msg = f"📝 **Message edit**: \nToo long: <{haste_url}>"
|
2019-02-14 22:10:47 +00:00
|
|
|
|
|
2019-01-29 10:51:43 +00:00
|
|
|
|
await log_channel.send(msg)
|
|
|
|
|
|
2019-02-28 22:10:30 +00:00
|
|
|
|
@Cog.listener()
|
2019-01-29 10:51:43 +00:00
|
|
|
|
async def on_message_delete(self, message):
|
|
|
|
|
await self.bot.wait_until_ready()
|
2019-02-05 20:52:09 +00:00
|
|
|
|
if message.channel.id not in config.spy_channels or message.author.bot:
|
2019-01-29 10:51:43 +00:00
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
log_channel = self.bot.get_channel(config.log_channel)
|
2020-04-20 22:05:32 +00:00
|
|
|
|
msg = (
|
|
|
|
|
"🗑️ **Message delete**: \n"
|
|
|
|
|
f"from {self.bot.escape_message(message.author.name)} "
|
|
|
|
|
f"({message.author.id}), in {message.channel.mention}:\n"
|
|
|
|
|
f"`{message.clean_content}`"
|
|
|
|
|
)
|
2019-02-14 22:10:47 +00:00
|
|
|
|
|
|
|
|
|
# If resulting message is too long, upload to hastebin
|
|
|
|
|
if len(msg) > 2000:
|
2019-02-14 22:14:09 +00:00
|
|
|
|
haste_url = await self.bot.haste(msg)
|
|
|
|
|
msg = f"🗑️ **Message delete**: \nToo long: <{haste_url}>"
|
2019-02-14 22:10:47 +00:00
|
|
|
|
|
2019-01-29 10:51:43 +00:00
|
|
|
|
await log_channel.send(msg)
|
|
|
|
|
|
2019-02-28 22:10:30 +00:00
|
|
|
|
@Cog.listener()
|
2018-12-23 19:03:40 +00:00
|
|
|
|
async def on_member_remove(self, member):
|
|
|
|
|
await self.bot.wait_until_ready()
|
2019-12-27 15:43:47 +00:00
|
|
|
|
|
2020-04-20 22:05:32 +00:00
|
|
|
|
if member.guild.id not in config.guild_whitelist:
|
2019-12-27 15:43:47 +00:00
|
|
|
|
return
|
|
|
|
|
|
2018-12-23 19:03:40 +00:00
|
|
|
|
log_channel = self.bot.get_channel(config.log_channel)
|
2020-04-20 22:05:32 +00:00
|
|
|
|
msg = (
|
|
|
|
|
f"⬅️ **Leave**: {member.mention} | "
|
|
|
|
|
f"{self.bot.escape_message(member)}\n"
|
|
|
|
|
f"🏷 __User ID__: {member.id}"
|
|
|
|
|
)
|
2018-12-23 19:03:40 +00:00
|
|
|
|
await log_channel.send(msg)
|
|
|
|
|
|
2019-02-28 22:10:30 +00:00
|
|
|
|
@Cog.listener()
|
2018-12-25 11:13:02 +00:00
|
|
|
|
async def on_member_ban(self, guild, member):
|
2018-12-23 19:03:40 +00:00
|
|
|
|
await self.bot.wait_until_ready()
|
2019-12-27 15:43:47 +00:00
|
|
|
|
|
2020-04-20 22:05:32 +00:00
|
|
|
|
if guild.id not in config.guild_whitelist:
|
2019-12-27 15:43:47 +00:00
|
|
|
|
return
|
|
|
|
|
|
2019-02-04 22:54:40 +00:00
|
|
|
|
log_channel = self.bot.get_channel(config.modlog_channel)
|
2020-04-20 22:05:32 +00:00
|
|
|
|
msg = (
|
|
|
|
|
f"⛔ **Ban**: {member.mention} | "
|
|
|
|
|
f"{self.bot.escape_message(member)}\n"
|
|
|
|
|
f"🏷 __User ID__: {member.id}"
|
|
|
|
|
)
|
2018-12-23 19:03:40 +00:00
|
|
|
|
await log_channel.send(msg)
|
|
|
|
|
|
2019-02-28 22:10:30 +00:00
|
|
|
|
@Cog.listener()
|
2018-12-25 11:13:02 +00:00
|
|
|
|
async def on_member_unban(self, guild, user):
|
2018-12-23 19:03:40 +00:00
|
|
|
|
await self.bot.wait_until_ready()
|
2019-12-27 15:43:47 +00:00
|
|
|
|
|
2020-04-20 22:05:32 +00:00
|
|
|
|
if guild.id not in config.guild_whitelist:
|
2019-12-27 15:43:47 +00:00
|
|
|
|
return
|
|
|
|
|
|
2019-02-04 22:54:40 +00:00
|
|
|
|
log_channel = self.bot.get_channel(config.modlog_channel)
|
2020-04-20 22:05:32 +00:00
|
|
|
|
msg = (
|
|
|
|
|
f"⚠️ **Unban**: {user.mention} | "
|
|
|
|
|
f"{self.bot.escape_message(user)}\n"
|
|
|
|
|
f"🏷 __User ID__: {user.id}"
|
|
|
|
|
)
|
2018-12-23 19:03:40 +00:00
|
|
|
|
# if user.id in self.bot.timebans:
|
|
|
|
|
# msg += "\nTimeban removed."
|
|
|
|
|
# self.bot.timebans.pop(user.id)
|
|
|
|
|
# with open("data/timebans.json", "r") as f:
|
|
|
|
|
# timebans = json.load(f)
|
|
|
|
|
# if user.id in timebans:
|
|
|
|
|
# timebans.pop(user.id)
|
|
|
|
|
# with open("data/timebans.json", "w") as f:
|
|
|
|
|
# json.dump(timebans, f)
|
|
|
|
|
await log_channel.send(msg)
|
|
|
|
|
|
2019-02-28 22:10:30 +00:00
|
|
|
|
@Cog.listener()
|
2018-12-23 19:03:40 +00:00
|
|
|
|
async def on_member_update(self, member_before, member_after):
|
|
|
|
|
await self.bot.wait_until_ready()
|
2019-12-27 15:43:47 +00:00
|
|
|
|
|
2020-04-20 22:05:32 +00:00
|
|
|
|
if member_after.guild.id not in config.guild_whitelist:
|
2019-12-27 15:43:47 +00:00
|
|
|
|
return
|
|
|
|
|
|
2018-12-23 19:03:40 +00:00
|
|
|
|
msg = ""
|
|
|
|
|
log_channel = self.bot.get_channel(config.log_channel)
|
|
|
|
|
if member_before.roles != member_after.roles:
|
|
|
|
|
# role removal
|
|
|
|
|
role_removal = []
|
|
|
|
|
for index, role in enumerate(member_before.roles):
|
|
|
|
|
if role not in member_after.roles:
|
|
|
|
|
role_removal.append(role)
|
|
|
|
|
# role addition
|
|
|
|
|
role_addition = []
|
|
|
|
|
for index, role in enumerate(member_after.roles):
|
|
|
|
|
if role not in member_before.roles:
|
|
|
|
|
role_addition.append(role)
|
|
|
|
|
|
|
|
|
|
if len(role_addition) != 0 or len(role_removal) != 0:
|
|
|
|
|
msg += "\n👑 __Role change__: "
|
|
|
|
|
roles = []
|
|
|
|
|
for role in role_removal:
|
|
|
|
|
roles.append("_~~" + role.name + "~~_")
|
|
|
|
|
for role in role_addition:
|
|
|
|
|
roles.append("__**" + role.name + "**__")
|
|
|
|
|
for index, role in enumerate(member_after.roles):
|
|
|
|
|
if role.name == "@everyone":
|
|
|
|
|
continue
|
|
|
|
|
if role not in role_removal and role not in role_addition:
|
|
|
|
|
roles.append(role.name)
|
|
|
|
|
msg += ", ".join(roles)
|
|
|
|
|
|
|
|
|
|
if member_before.name != member_after.name:
|
2020-04-20 22:05:32 +00:00
|
|
|
|
msg += (
|
|
|
|
|
"\n📝 __Username change__: "
|
|
|
|
|
f"{self.bot.escape_message(member_before)} → "
|
|
|
|
|
f"{self.bot.escape_message(member_after)}"
|
|
|
|
|
)
|
2018-12-23 19:03:40 +00:00
|
|
|
|
if member_before.nick != member_after.nick:
|
|
|
|
|
if not member_before.nick:
|
|
|
|
|
msg += "\n🏷 __Nickname addition__"
|
|
|
|
|
elif not member_after.nick:
|
|
|
|
|
msg += "\n🏷 __Nickname removal__"
|
|
|
|
|
else:
|
|
|
|
|
msg += "\n🏷 __Nickname change__"
|
2020-04-20 22:05:32 +00:00
|
|
|
|
msg += (
|
|
|
|
|
f": {self.bot.escape_message(member_before.nick)} → "
|
|
|
|
|
f"{self.bot.escape_message(member_after.nick)}"
|
|
|
|
|
)
|
2018-12-23 19:03:40 +00:00
|
|
|
|
if msg:
|
2020-04-20 22:05:32 +00:00
|
|
|
|
msg = (
|
|
|
|
|
f"ℹ️ **Member update**: {member_after.mention} | "
|
|
|
|
|
f"{self.bot.escape_message(member_after)}{msg}"
|
|
|
|
|
)
|
2018-12-23 19:03:40 +00:00
|
|
|
|
await log_channel.send(msg)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def setup(bot):
|
|
|
|
|
bot.add_cog(Logs(bot))
|