async def on_member_unban(server, user): msgProcessStart = analytics.getEventTime() if common.isDiscordMainServer(server) is not True: return await discord.bot.send_message( discord.mod_channel(), "{0} **Unbanned User:** {1.mention} (Name:`{1.name}#{1.discriminator}` ID:`{1.id}`)" .format(common.bernardUTCTimeNow(), user)) #check if the user was retroactively rebanned, if they were remove from said list. database.cursor.execute('SELECT * FROM bans_retroactive WHERE id=%s', (user.id, )) retdb = database.cursor.fetchone() if retdb is not None: database.cursor.execute('DELETE FROM bans_retroactive WHERE id=%s', (user.id, )) database.connection.commit() await discord.bot.send_message( discord.mod_channel(), "{0} **Retroactive Unban:** {1.mention} (Name:`{1.name}#{1.discriminator}` ID:`{1.id}`)" .format(common.bernardUTCTimeNow(), user)) journal.update_journal_event(module=__name__, event="RETROACTIVE_UNBAN", userid=user.id, contents=retdb['reason']) #capture the event in the internal log journal.update_journal_event( module=__name__, event="ON_MEMBER_UNBAN", userid=user.id, contents="{0.name}#{0.discriminator}".format(user)) analytics.onMemberProcessTime(msgProcessStart, analytics.getEventTime())
async def on_message_edit(before, after): msgProcessStart = analytics.getEventTime() if common.isDiscordMainServer(before.server) is not True: return if before.content != after.content: if len(before.content) + len(after.content) < 1800: msg = "{0} **Caught Edited Message!**" \ "{1.author.mention} (Name:`{1.author}` ID:`{1.author.id}`) in {1.channel.mention} \n" \ " Original: \"`{1.content}`\" \n\n" \ " Edited: \"`{2.content}`\" \n".format(common.bernardUTCTimeNow(), before, after) msg_sent = await discord.bot.send_message( discord.messages_channel(), msg) else: msgs = [ "{0} **Caught Edited Message! (WARNING: may be truncated)** {1.author.mention} (Name:`{1.author}` ID:`{1.author.id}`) in {1.channel.mention}" .format(common.bernardUTCTimeNow(), before), " Original:```{0}```".format(before.content[:1980]), " Edited:```{0}```".format(after.content[:1980]) ] for msg in msgs: msg_sent = await discord.bot.send_message( discord.messages_channel(), msg) journal_msg = "https://discordapp.com/channels/{0.channel.server.id}/{0.channel.id}/{0.id}".format( msg_sent) journal.update_journal_event(module=__name__, event="ON_MESSAGE_EDIT", userid=before.author.id, eventid=before.id, contents=journal_msg) analytics.onMessageProcessTime(msgProcessStart, analytics.getEventTime())
async def on_member_ban(member): msgProcessStart = analytics.getEventTime() if common.isDiscordMainServer(member.server) is not True: return ignore_depart.append(member.id) await discord.bot.send_message( discord.mod_channel(), "{0} **Banned User:** {1.mention} (Name:`{1.name}#{1.discriminator}` ID:`{1.id}`)" .format(common.bernardUTCTimeNow(), member)) #remove any invites from this user await invites.on_member_leave_invite_cleanup(member) #remove any cached subscriber information subscriber.on_member_remove_purgedb(member) #capture the event in the internal log journal.update_journal_event( module=__name__, event="ON_MEMBER_BANNED", userid=member.id, contents="{0.name}#{0.discriminator}".format(member)) analytics.onMemberProcessTime(msgProcessStart, analytics.getEventTime())
async def on_member_remove(user): global ignore_depart msgProcessStart = analytics.getEventTime() if common.isDiscordMainServer(user.server) is not True: return #if the user was banned or removed for another reason dont issue the depart statement if user.id in ignore_depart: ignore_depart.remove(user.id) return await discord.bot.send_message( discord.mod_channel(), "{0} **Departing User:** {1.mention} (Name:`{1.name}#{1.discriminator}` ID:`{1.id}`)" .format(common.bernardUTCTimeNow(), user)) #remove any invites from this user await invites.on_member_leave_invite_cleanup(user) #remove any cached subscriber information subscriber.on_member_remove_purgedb(user) #capture the event in the internal log journal.update_journal_event( module=__name__, event="ON_MEMBER_REMOVE", userid=user.id, contents="{0.name}#{0.discriminator}".format(user)) analytics.onMemberProcessTime(msgProcessStart, analytics.getEventTime())
async def on_message(message): msgProcessStart = analytics.getEventTime() #process bot DMs into the mod channel if its not the owner. if message.server is None: logger.info( "on_message() got DM from {0.author.name} (ID:{0.author.id}) containing {0.clean_content}" .format(message)) if message.author.id != config.cfg['bernard']['owner']: await discord.bot.send_message( discord.mod_channel(), "{0} **New DM:** {1.author.mention} sent: `{1.clean_content}` " .format(common.bernardUTCTimeNow(), message)) #only reply to the guild set in config file try: if common.isDiscordMainServer(message.server) is not True: await discord.bot.process_commands(message) return except AttributeError: #process messages anyway await discord.bot.process_commands(message) return # start looking for gamer words / banned slurs if regulator.allow_automod(message): await gamerwords.slur_filter(message) #get some basic stats of message sending analytics.setMessageCounter(message) #handoff the message to a function dedicated to its feature see also https://www.youtube.com/watch?v=ekP0LQEsUh0 DO NOT AUDIT OURSELVES BAD THINGS HAPPEN if message.author.id != discord.bot.user.id: await auditing.attachments(message) #message attachment auditing await auditing.discord_invites(message) #discord invites await auditing.blacklisted_domains(message) #url blacklisting #print the message to the console if config.cfg['bernard']['debug']: logger.info( "Channel: {0.channel} User: {0.author} (ID:{0.author.id}) Message: {0.content}" .format(message)) #handle message processing per rate limit, do not reply to ourselves if analytics.rateLimitAllowProcessing(message): if message.author.id != discord.bot.user.id: await discord.bot.process_commands(message) #set the rate limit if message.author.id == discord.bot.user.id: analytics.rateLimitNewMessage(message.channel.id, analytics.getEventTime()) #message processing timings analytics.onMessageProcessTime(msgProcessStart, analytics.getEventTime())
async def remind(ctx, duration, *, text): if common.isDiscordMainServer(ctx.message.server) is not True: return #set a cap of max 5 currently waiting jobs, regs+ are exempt if not common.isDiscordRegulator(ctx.message.author): database.cursor.execute("SELECT * FROM scheduled_tasks WHERE exec_run=0 AND event_type='POST_MESSAGE' AND id_targeted = %s", (ctx.message.author.id,)) waiting_work_count = database.cursor.fetchall() if len(waiting_work_count) >= 5: await discord.bot.say("⚠️{0.message.author.mention} you already have 5 waiting reminders. View them via `!todo`".format(ctx)) return #get the user input to seconds duration_length = scheduler.user_duration_to_seconds(duration) if duration_length is False: await discord.bot.say("⚠️{0.message.author.mention} unable to decode message length. Should be `!remind 60s|1m|4d don't forget about the potatoes`".format(ctx)) return elif duration_length is None: await discord.bot.say("⚠️{0.message.author.mention} unable to decode message length. allowed: seconds (s), minutes (m), hours (h), days (d), weeks (w), years (y)".format(ctx)) return duration_inrange = scheduler.verify_reminder_duration_length(duration_length) if duration_inrange is False: await discord.bot.say("⚠️{0.message.author.mention} Reminder is out of expected range. Current Range {1}m to {2}d".format(ctx, config.cfg['scheduler']['time_range_min_mins'], int(config.cfg['scheduler']['time_range_max_mins'] / 1440))) return #dont allow abusive calls to be made, these should just make API calls to kick the user if "@everyone" in text.lower(): return elif "@here" in text.lower(): return else: text = text.replace("`","'") #limit message to have 3 mentions if len(ctx.message.mentions) > 3: return #calculate the time it should fire now = int(time.time()) time_to_fire = now + duration_length #set the reminder scheduler.set_future_task( invoker=ctx.message.author.id, channel=ctx.message.channel.id, timestamp=time_to_fire, event="POST_MESSAGE", msg=text) when = time.strftime("%A, %B %d %Y %H:%M %Z", time.localtime(time_to_fire)) await discord.bot.say("✔️{0.message.author.mention} reminder set for `{1}`! I'll try not to forget about this, I promise.".format(ctx, when))
async def on_member_update(before, after): msgProcessStart = analytics.getEventTime() if common.isDiscordMainServer(before.server) is not True: return #handle nickname changes if before.nick != after.nick: if before.nick is None: await discord.bot.send_message( discord.mod_channel(), "{0} **Server Nickname Added:** {1.mention} was `{1.name}` is now `{2.nick}` (ID:`{1.id}`)" .format(common.bernardUTCTimeNow(), before, after)) journal.update_journal_event( module=__name__, event="ON_MEMBER_NICKNAME_ADD", userid=after.id, contents="{0.name} -> {1.nick}".format(before, after)) elif after.nick is None: await discord.bot.send_message( discord.mod_channel(), "{0} **Server Nickname Removed:** {1.mention} was `{1.nick}` is now `{2.name}` (ID:`{1.id}`)" .format(common.bernardUTCTimeNow(), before, after)) journal.update_journal_event( module=__name__, event="ON_MEMBER_NICKNAME_REMOVE", userid=after.id, contents="{0.nick} -> {1.name}".format(before, after)) else: await discord.bot.send_message( discord.mod_channel(), "{0} **Server Nickname Changed:** {1.mention} was `{1.nick}` is now `{2.nick}` (ID:`{1.id}`)" .format(common.bernardUTCTimeNow(), before, after)) journal.update_journal_event( module=__name__, event="ON_MEMBER_NICKNAME_CHANGE", userid=after.id, contents="{0.nick} -> {1.nick}".format(before, after)) #handle username changes if before.name != after.name: await discord.bot.send_message( discord.mod_channel(), "{0} **Discord Username Changed:** {1.mention} was `{1.name}` is now `{2.name}` (ID:`{1.id}`)" .format(common.bernardUTCTimeNow(), before, after)) journal.update_journal_event(module=__name__, event="ON_USERNAME_CHANGE", userid=after.id, contents="{0.name} -> {1.name}".format( before, after)) analytics.onMemberProcessTime(msgProcessStart, analytics.getEventTime())
async def on_message_delete(message): msgProcessStart = analytics.getEventTime() if common.isDiscordMainServer(message.server) is not True: return if message.author.id in IGNORE_IDS: logger.info( "on_message_delete() ignoring deleted message user id is in IGNORE_IDS variable" ) return if message.attachments and message.content == "": msg = "{0.author.mention} (Name:`{0.author}` ID:`{0.author.id}`) in {0.channel.mention} \n Deleted Attachment: `{0.attachments[0][filename]}` \n URL: <{0.attachments[0][url]}>".format( message) journal_msg = "Attachment: {0.attachments[0][filename]} Size: {0.attachments[0][size]}".format( message) elif message.attachments and message.content != "": msg = "{0.author.mention} (Name:`{0.author}` ID:`{0.author.id}`) in {0.channel.mention} \n Message: \"`{0.content}`\" \n\n Deleted Attachment: `{0.attachments[0][filename]}` \n URL: <{0.attachments[0][url]}>".format( message) journal_msg = "Text: {0.content} Attachment: {0.attachments[0][filename]} Size: {0.attachments[0][size]}".format( message) else: msg = "{0.author.mention} (Name:`{0.author}` ID:`{0.author.id}`) in {0.channel.mention} \n Message: \"`{0.content}`\" \n\n".format( message) journal_msg = "Text: {0.content}".format(message) await discord.bot.send_message( discord.messages_channel(), "{0} **Caught Deleted Message!** {1}".format( common.bernardUTCTimeNow(), msg)) journal.update_journal_event(module=__name__, event="ON_MESSAGE_DELETE", userid=message.author.id, eventid=message.id, contents=journal_msg) analytics.onMessageProcessTime(msgProcessStart, analytics.getEventTime())
async def on_member_join(user): msgProcessStart = analytics.getEventTime() if common.isDiscordMainServer(user.server) is not True: return #the user got here somehow, get the discord.Invite object we *think* they came from invite = await invites.on_member_join_attempt_invite_source(user) #if the user is retroactively banned, handle it and issue the ban database.cursor.execute('SELECT * FROM bans_retroactive WHERE id=%s', (user.id, )) retdb = database.cursor.fetchone() print(user.id, retdb) if retdb is not None: ignore_depart.append(user.id) await common.ban_verbose( user, "RETROACTIVE_BAN_VIA_BOT_DB - '{}'".format(retdb['reason'])) await discord.bot.send_message( discord.mod_channel(), "{0} **Retroactive Ban:** {1.mention} (Name:`{1.name}#{1.discriminator}` ID:`{1.id}` REASON: `{2}`)" .format(common.bernardUTCTimeNow(), user, retdb['reason'])) journal.update_journal_event(module=__name__, event="RETROACTIVE_BAN", userid=user.id, contents=retdb['reason']) return ##send the message to the admin defined channel await discord.bot.send_message( discord.mod_channel(), "{0} **New User:** {1.mention} (Name:`{1.name}#{1.discriminator}` ID:`{1.id}`) **Account Age:** {2} **From:** `{3}`" .format(common.bernardUTCTimeNow(), user, common.bernardAccountAgeToFriendly(user), invite)) #handle kicking / banning of "new" accounts if config.cfg['auditing']['account_age_min']['enable']: logger.info( "on_member_join() Account age minimum is enabled: Minimum account age required {} minutes." .format( config.cfg['auditing']['account_age_min']['min_age_required'])) account_age_minutes = int((datetime.datetime.utcnow().timestamp() - user.created_at.timestamp()) / 60) #if the users account is too new lets apply logic to kick or ban if account_age_minutes <= config.cfg['auditing']['account_age_min'][ 'min_age_required']: journal.update_journal_event( module=__name__, event="ON_MEMBER_JOIN_ACCOUNT_TOONEW", userid=user.id, contents="{0.name}#{0.discriminator} - {1} mins old".format( user, account_age_minutes)) #await discord.bot.send_message(user, config.cfg['auditing']['account_age_min']['enforcement_dm']) await asyncio.sleep(3) if config.cfg['auditing']['account_age_min'][ 'enforcement'] == "ban": logger.warn( "on_member_join() BANNING ID {0.id} for being too new. Account age was {1} minutes, config is {2} minute age.." .format( user, account_age_minutes, config.cfg['auditing'] ['account_age_min']['min_age_required'])) await common.ban_verbose( user, "BOT_BAN_VIA_ACCOUNT_TOONEW - {} min old".format( account_age_minutes)) else: logger.warn( "on_member_join() KICKING ID {0.id} for being too new. Account age was {1} minutes, config is {2} minute age." .format( user, account_age_minutes, config.cfg['auditing'] ['account_age_min']['min_age_required'])) await discord.bot.kick(user) #do a subscriber check and assign any roles on joining check_sub = subscriber.subscriber_update(user) await check_sub.update_subscriber() #capture the event in the internal log journal.update_journal_event( module=__name__, event="ON_MEMBER_JOIN", userid=user.id, contents="{0.name}#{0.discriminator}".format(user)) analytics.onMemberProcessTime(msgProcessStart, analytics.getEventTime())