async def on_message_edit(before: discord.Message, after: discord.Message): if not should_log(before.guild) or before.author.bot: return # Run edited message against censor. bad_message = await check_censor(after) if bad_message: return # Prevent embedding of content from triggering the log if before.content == after.content: return try: chan = client.get_channel(config.SYS_LOG) mes = f":pencil: **{str(before.author)}** modified in <#{before.channel.id}>: `{before.content}` to `{after.content}`" await message_send_helper(mes, chan) # If user is on watchlist, then post it there as well watching = watch.should_note(after.author.id) if watching: watchchan = client.get_channel(config.WATCHLIST_CHAN) await message_send_helper(mes, watchchan) except discord.errors.HTTPException as e: print( f"Unknown error with editing message. This message was unable to post for this reason: {e}\n" )
async def on_member_update(before: discord.Member, after: discord.Member): if not should_log(before.guild): return # If nickname has changed if before.nick != after.nick: # If they don't have an ending nickname, they reset to their actual username if not after.nick: mes = f"**:spy: {str(after)}** has reset their username" else: mes = f"**:spy: {str(after)}** is now known as `{after.nick}`" chan = client.get_channel(config.SYS_LOG) await chan.send(mes) # If role quantity has changed elif before.roles != after.roles: # Determine role difference, post about it removed = [r.name for r in before.roles if r not in after.roles] added = [r.name for r in after.roles if r not in before.roles] mes = "" if removed: removed_str = ', '.join(removed) mes += f":no_entry_sign: **{str(after)}** had the role(s) `{removed_str}` removed.\n" if added: added_str = ', '.join(added) mes += f":new: **{str(after)}** had the role(s) `{added_str}` added." chan = client.get_channel(config.SYS_LOG) if mes != "": await chan.send(mes)
async def on_voice_state_update(member: discord.Member, before: discord.VoiceState, after: discord.VoiceState): if not should_log(member.guild) or member.bot: return if not after.channel: mes = f":mute: **{str(member)}** has left voice channel {before.channel.name}" chan = client.get_channel(config.SYS_LOG) await chan.send(mes) elif not before.channel: mes = f":loud_sound: **{str(member)}** has joined voice channel {after.channel.name}" chan = client.get_channel(config.SYS_LOG) await chan.send(mes)
async def check_tattletale(reaction: discord.Reaction): if reaction.emoji != REACTION_EMOJI or reaction.count < REACTION_THRESHOLD: return m = reaction.message if m.author.bot: return # If enough users have flagged a message, take the following actions: # - Remove the flagged message # - Time out the flagged user # - Notify staff, both of the offending message as well as who reacted, for potential abuse # Limited rollout: Only count reactions by users with certain role reactors = [x async for x in reaction.users()] num_valid_reactors = len([x for x in reactors if check_roles(x, TTL_ROLES)]) if num_valid_reactors < REACTION_THRESHOLD: return reactor_list = "\n".join([str(x) for x in reactors]) if not m.author.is_timed_out(): await m.author.timeout(timedelta(minutes=TIMEOUT_MIN)) try: await m.delete() msg_txt = combine_message(m) staff_chan = client.get_channel(MAILBOX) report = f"Users have flagged a message by <@{m.author.id}> in <#{m.channel.id}>: {msg_txt}.\n\nThese are the users who flagged:\n {reactor_list}" await staff_chan.send(report) except discord.NotFound: pass
async def on_member_join(member: discord.Member): if not should_log(member.guild): return mes = f":confetti_ball: **{str(member)} ({member.id})** has joined" chan = client.get_channel(config.SYS_LOG) await chan.send(mes)
async def delete_message_helper(message: discord.Message): mes = f":no_mobile_phones: **{str(message.author)}** deleted in <#{message.channel.id}>: `{message.content}`" chan = client.get_channel(config.SYS_LOG) # Adds URLs for any attachments that were included in deleted message # These will likely become invalid, but it's nice to note them anyway if message.attachments != []: for item in message.attachments: mes += '\n' + item.url await message_send_helper(mes, chan)
async def on_reaction_remove(reaction: discord.Reaction, user: discord.Member): if user.bot: return emoji_name = reaction.emoji if type( reaction.emoji) == str else reaction.emoji.name chan = client.get_channel(config.SYS_LOG) await chan.send( f":face_in_clouds: {str(user)} ({user.id}) removed the `{emoji_name}` emoji" )
async def on_member_remove(member: discord.Member): if not should_log(member.guild): return # We can remove left users from our answering machine commands.am.remove_entry(member.id) # Remember that the user has left, in case we want to log after they're gone username = f"{str(member)}" commands.ul.add_ban(member.id, username) mes = f":wave: **{username} ({member.id})** has left" chan = client.get_channel(config.SYS_LOG) await chan.send(mes)
async def on_member_ban(server: discord.Guild, member: discord.Member): if not should_log(server): return # We can remove banned user from our answering machine and watch list (if they exist) commands.am.remove_entry(member.id) watch.remove_user(member.id) # Keep a record of their banning, in case the log is made after they're no longer here username = f"{str(member)}" commands.ul.add_ban(member.id, username) mes = f":newspaper2: **{username} ({member.id})** has been banned." chan = client.get_channel(config.SYS_LOG) await chan.send(mes)
async def on_ready(): print('Logged in as') print(client.user.name) print(client.user.id) # Set Bouncer's activity status activity_object = discord.Activity(name="for your reports!", type=discord.ActivityType.watching) await client.change_presence(activity=activity_object) spam.set_channel() # Upload our DB file to a private channel as a backup if not dbg.is_debug_bot(): chan = client.get_channel(config.LOG_CHAN) currentTime = datetime.now(timezone.utc) filename = f"bouncer_backup_{commonbot.utils.format_time(currentTime)}.db" with open(config.DATABASE_PATH, 'rb') as db_file: await chan.send(file=discord.File(db_file, filename=filename))
async def on_message(message: discord.Message): # Bouncer should not react to its own messages if message.author.id == client.user.id: return try: # Allows the owner to enable debug mode if dbg.check_toggle(message): await dbg.toggle_debug(message) return if dbg.should_ignore_message(message): return # If bouncer detects a private DM sent to it if type(message.channel) is discord.channel.DMChannel: # Store who the most recent user was, for $reply ^ commands.am.set_recent_reply(message.author) content = commonbot.utils.combine_message(message) # If not blocked, send message along to specified mod channel if not commands.bu.is_in_blocklist(message.author.id): mes = "" chan = None # If we share the main server, treat that as a DM if config.HOME_SERVER in [ x.id for x in message.author.mutual_guilds ]: mes = f"<@{message.author.id}>: {content}" chan = client.get_channel(config.MAILBOX) # The only other server we should share is the ban appeal server else: mes = f"{str(message.author)} ({message.author.id}): {content}" chan = client.get_channel(config.BAN_APPEAL) logMes = await chan.send(mes) # Send them a message so they know something actually happened await message.channel.send("Your message has been forwarded!") # Lets also add/update them in answering machine mes_entry = AnsweringMachineEntry(f"{str(message.author)}", message.created_at, content, logMes.jump_url) commands.am.update_entry(message.author.id, mes_entry) return # Remove spam spam_message = await spam.check_spammer(message) if spam_message: return # Run message against censor bad_message = await check_censor(message) if bad_message: return # Check if user is on watchlist, and should be tracked watching = watch.should_note(message.author.id) if watching: chan = client.get_channel(config.WATCHLIST_CHAN) content = commonbot.utils.combine_message(message) mes = f"**{str(message.author)}** (ID: {message.author.id}) said in <#{message.channel.id}>: {content}" await message_send_helper(mes, chan) # If a user pings bouncer, log into mod channel, unless it's us if client.user in message.mentions and message.channel.category_id not in config.INPUT_CATEGORIES: content = commonbot.utils.combine_message(message) mes = f"**{str(message.author)}** (ID: {message.author.id}) pinged me in <#{message.channel.id}>: {content}\n{message.jump_url}" chan = client.get_channel(config.MAILBOX) await message_send_helper(mes, chan) # Only allow moderators to invoke commands, and only in staff category if message.content.startswith(config.CMD_PREFIX): if commonbot.utils.check_roles( message.author, config.VALID_ROLES ) and message.channel.category_id in config.INPUT_CATEGORIES: cmd = commonbot.utils.strip_prefix(message.content, config.CMD_PREFIX) cmd = commonbot.utils.get_first_word(cmd) if cmd in FUNC_DICT: func = FUNC_DICT[cmd][0] arg = FUNC_DICT[cmd][1] await func(message, arg) except discord.errors.Forbidden as e: if e.code == 50007: chan = client.get_channel(config.MAILBOX) logMes = await chan.send( "Unable to send message - Can't send messages to that user") else: print(traceback.format_exc()) except discord.errors.HTTPException as e: print(traceback.format_exc())
def set_channel(self): self.notification = client.get_channel(SPAM_CHAN)