diff --git a/README.md b/README.md index 4c84ea5..f3cc0f0 100755 --- a/README.md +++ b/README.md @@ -14,13 +14,11 @@ Based on https://gitlab.com/ao/dpybotbase - [x] Verification: Actual verification system - [x] Verification: Reset command - [ ] Verification: Using log module from akbbot for logging attempts and removing old attempts -- [ ] Logging: joins -- [ ] Logging: leaves -- [ ] Logging: role changes -- [ ] Logging: message edits -- [ ] Logging: message deletes -- [ ] Logging: bans -- [ ] Logging: kicks +- [x] Logging: joins +- [x] Logging: leaves +- [x] Logging: role changes +- [x] Logging: bans +- [x] Logging: kicks - [x] Moderation: ban - [x] Moderation: silentban - [x] Moderation: kick @@ -32,8 +30,8 @@ Based on https://gitlab.com/ao/dpybotbase - [ ] Moderation: mute-unmute - [ ] Moderation: mutetime - [x] Moderation: playing -- [ ] Moderation: botnickname -- [ ] Moderation: nickname +- [x] Moderation: botnickname +- [x] Moderation: nickname - [ ] Moderation: clear/purge - [ ] Moderation: probate-unprobate - [ ] Moderation: watch-unwatch (using log module from akbbot) @@ -42,6 +40,7 @@ Based on https://gitlab.com/ao/dpybotbase - [ ] Warns: listwarns-listwarnsid - [ ] Warns: clearwarns-clearwarnsid - [ ] Modmail +- [ ] Submiterr - [ ] Moderation: User notes - [x] .serr and .err - [x] Meme commands and pegaswitch (honestly the easiest part) diff --git a/cogs/common.py b/cogs/common.py index 715440c..7759e8d 100644 --- a/cogs/common.py +++ b/cogs/common.py @@ -101,7 +101,7 @@ class Common: def escape_message(self, text: str): """Escapes unfun stuff from messages""" - return text.replace("@", "@ ").replace("#", "# ") + return str(text).replace("@", "@ ").replace("<#", "# ") # This function is based on https://stackoverflow.com/a/35435419/3286892 # by link2110 (https://stackoverflow.com/users/5890923/link2110) diff --git a/cogs/logs.py b/cogs/logs.py new file mode 100644 index 0000000..8a1f78f --- /dev/null +++ b/cogs/logs.py @@ -0,0 +1,150 @@ +import discord +import json +from discord.ext import commands +from sys import argv +from datetime import datetime +import config + + +class Logs: + """ + Logs join and leave messages, bans and unbans, and member changes. + """ + + def __init__(self, bot): + self.bot = bot + + async def on_member_join(self, member): + await self.bot.wait_until_ready() + 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) + + # Check if user account is older than 15 minutes + age = member.joined_at - member.created_at + if age < config.min_age: + try: + await member.send("Your account is too new to join ReSwitched." + " Please try again later.") + sent = True + except discord.errors.Forbidden: + sent = False + await member.kick(reason="Too new") + msg = f"šŸšØ **Account too new**: {member.mention} | "\ + f"{escaped_name}\n"\ + f"šŸ—“ __Creation__: {member.created_at}\n"\ + f"šŸ•“ Account age: {age}\n"\ + f"šŸ· __User ID__: {member.id}" + if not sent: + msg += "\nThe user has disabled direct messages,"\ + " so the reason was not sent." + await log_channel.send(msg) + return + msg = f"āœ… **Join**: {member.mention} | "\ + f"{escaped_name}\n"\ + f"šŸ—“ __Creation__: {member.created_at}\n"\ + f"šŸ•“ Account age: {age}\n"\ + f"šŸ· __User ID__: {member.id}" + + # Real hell zone. + with open("data/warnsv2.json", "r") as f: + warns = json.load(f) + try: + if len(warns[member.id]["warns"]) == 0: + await log_channel.send(msg) + else: + embed = discord.Embed(color=discord.Color.dark_red(), + title=f"Warns for {escaped_name}") + embed.set_thumbnail(url=member.avatar_url) + for idx, warn in enumerate(warns[member.id]["warns"]): + embed.add_field(name=f"{idx + 1}: {warn['timestamp']}", + value=f"Issuer: {warn['issuer_name']}" + f"\nReason: {warn['reason']}") + await log_channel.send(msg, embed=embed) + except KeyError: # if the user is not in the file + await log_channel.send(msg) + + async def on_member_remove(self, member): + await self.bot.wait_until_ready() + log_channel = self.bot.get_channel(config.log_channel) + msg = f"ā¬…ļø **Leave**: {member.mention} | "\ + f"{self.bot.escape_message(member)}\n"\ + f"šŸ· __User ID__: {member.id}" + await log_channel.send(msg) + + async def on_member_ban(self, member): + await self.bot.wait_until_ready() + log_channel = self.bot.get_channel(config.log_channel) + msg = f"ā›” **Ban**: {member.mention} | "\ + f"{self.bot.escape_message(member)}\n"\ + f"šŸ· __User ID__: {member.id}" + await log_channel.send(msg) + + async def on_member_unban(self, server, user): + await self.bot.wait_until_ready() + log_channel = self.bot.get_channel(config.log_channel) + msg = f"āš ļø **Unban**: {user.mention} | "\ + f"{self.bot.escape_message(user)}\n"\ + f"šŸ· __User ID__: {user.id}" + # 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) + + async def on_member_update(self, member_before, member_after): + await self.bot.wait_until_ready() + 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: + msg += "\nšŸ“ __Username change__: "\ + f"{self.bot.escape_message(member_before)} ā†’ "\ + f"{self.bot.escape_message(member_after)}" + 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__" + msg += f": {self.bot.escape_message(member_before.nick)} ā†’ "\ + f"{self.bot.escape_message(member_after.nick)}" + if msg: + msg = f"ā„¹ļø **Member update**: {member_after.mention} | "\ + f"{self.bot.escape_message(member_after)}{msg}" + await log_channel.send(msg) + + +def setup(bot): + bot.add_cog(Logs(bot)) diff --git a/cogs/mod.py b/cogs/mod.py index a9847e5..11f7e03 100644 --- a/cogs/mod.py +++ b/cogs/mod.py @@ -49,8 +49,8 @@ class ModCog: ", it is recommended to use `.ban [reason]`"\ " as the reason is automatically sent to the user." - modlog_channel = self.bot.get_channel(config.modlog_channel) - await modlog_channel.send(chan_message) + log_channel = self.bot.get_channel(config.log_channel) + await log_channel.send(chan_message) @commands.guild_only() @commands.bot_has_permissions(ban_members=True) @@ -88,8 +88,8 @@ class ModCog: ", it is recommended to use `.ban [reason]`"\ " as the reason is automatically sent to the user." - modlog_channel = self.bot.get_channel(config.modlog_channel) - await modlog_channel.send(chan_message) + log_channel = self.bot.get_channel(config.log_channel) + await log_channel.send(chan_message) await ctx.send(f"{safe_name} is now b&. šŸ‘") @commands.guild_only() @@ -116,8 +116,8 @@ class ModCog: ", it is recommended to use `.ban [reason]`"\ " as the reason is automatically sent to the user." - modlog_channel = self.bot.get_channel(config.modlog_channel) - await modlog_channel.send(chan_message) + log_channel = self.bot.get_channel(config.log_channel) + await log_channel.send(chan_message) @commands.guild_only() @commands.check(check_if_staff) @@ -142,7 +142,7 @@ class ModCog: @commands.check(check_if_staff) @commands.command(aliases=["setplaying", "setgame"]) async def playing(self, ctx, *, game: str = ""): - """Sets the currently played game name, staff only. + """Sets the bot's currently played game name, staff only. Just send .playing to wipe the playing state.""" if game: @@ -150,6 +150,32 @@ class ModCog: else: await self.bot.change_presence(activity=None) + @commands.guild_only() + @commands.check(check_if_staff) + @commands.command(aliases=["setbotnick", "botnick", "robotnick"]) + async def botnickname(self, ctx, *, nick: str = ""): + """Sets the bot's nickname, staff only. + + Just send .botnickname to wipe the nickname.""" + + if nick: + await ctx.guild.me.edit(nick=nick, reason=str(ctx.author)) + else: + await ctx.guild.me.edit(nick=None, reason=str(ctx.author)) + + @commands.guild_only() + @commands.check(check_if_staff) + @commands.command(aliases=["setnick", "nick"]) + async def nickname(self, ctx, target: discord.Member, *, nick: str = ""): + """Sets a user's nickname, staff only. + + Just send .nickname to wipe the nickname.""" + + if nick: + await target.edit(nick=nick, reason=str(ctx.author)) + else: + await target.edit(nick=None, reason=str(ctx.author)) + def setup(bot): bot.add_cog(ModCog(bot)) diff --git a/config.py.template b/config.py.template index 787f4fe..b395fa1 100644 --- a/config.py.template +++ b/config.py.template @@ -1,28 +1,34 @@ +import datetime + # Basic bot config prefixes = [".", "!"] token = "token-goes-here" bot_description = "An attempt to rewrite the bot used in ReSwitched" +source_url = "https://github.com/aveao/robocop-ng" +rules_url = "https://reswitched.team/discord/" + # The bot description to be used in .robocop embed embed_desc = "Robocop-NG is developed by [Ave](https://github.com/aveao)"\ " and [tomGER](https://github.com/tumGER), and is a rewrite "\ "of Robocop.\nRobocop is based on Kurisu by 916253 and ihaveamac." + +# Minimum account age required to join the discord +min_age = datetime.timedelta(minutes=15) + guild_whitelist = [ 526372255052201993, # NotSwitched discord 269333940928512010 # ReSwitched discord ] -source_url = "https://github.com/aveao/robocop-ng" -rules_url = "https://reswitched.team/discord/" - bot_manager_role_id = 526372554081042462 # Bot management role in NotSwitched staff_role_ids = [526384077679624192, # Team role in NotSwitched 526372582455508992, # Mod role in NotSwitched 526372554081042462, # Bot management role in NotSwitched 526383985430102016] # Wizard role in NotSwitched -modlog_channel = 526377735908491284 # Log channel in NotSwitched +log_channel = 526377735908491284 # Log channel in NotSwitched welcome_channel = 526372470752673792 # rules-info channel in NotSwitched participant_role = 526378358129557506 # participant role in NotSwitched community_role = 526378381839695872 # community role in NotSwitched diff --git a/data/warnsv2.json b/data/warnsv2.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/data/warnsv2.json @@ -0,0 +1 @@ +{}