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_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_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 scheduler_unban_member(work): now = int(time.time()) logger.info( "UNBAN_MEMBER JOB ID {0[id]} BANNED BY {0[id_invoker]} BAN HAS EXPIRED, UNBANNING {0[id_targeted]}" .format(work)) #call the unban unban_status_discord = await common.unban_id(str(work['id_targeted'])) if unban_status_discord: logger.info( "UNBAN_MEMBER JOB ID {0[id]} USER ID {0[id_targeted]} BAN SUCCESSFULLY REMOVED FROM DISCORD." .format(work)) #update the modlog channel await discord.bot.send_message( discord.mod_channel(), "{0} **Scheduled Unban:** ID:`{1[id_targeted]}`)".format( common.bernardUTCTimeNow(), work)) #set a user journal record the unban has happened journal.update_journal_event( module=__name__, event="ON_MEMBER_UNBAN_SCHEDULED", userid=work['id_targeted'], contents="SCHEDULER JOBID: {0[id]}".format(work)) else: logger.warn( "UNBAN_MEMBER JOB ID {0[id]} USER ID {0[id_targeted]} BAN WAS NOT REMOVED FROM DISCORD, USER ALREADY UNBANNED?" .format(work)) await scheduler_mark_work_done(work)
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 journal_events_cleanup(): await discord.bot.wait_until_ready() while not discord.bot.is_closed: logger.info( "Starting background task journal_events_cleanup() - Interval {0}". format(config.cfg['housekeeping']['journal_events_cleanup'] ['interval'])) #get the current time, and our tareted lookup time for the database query utc_now = common.bernardUTCEpochTimeNow() seconds_grace = 86400 * config.cfg['housekeeping'][ 'journal_events_cleanup']['days_to_keep'] db_cutoff = int(utc_now - seconds_grace) logger.info( "journal_events_cleanup() attempting purge of deleted messages older than {0} seconds ({1}) days" .format( seconds_grace, config.cfg['housekeeping'] ['journal_events_cleanup']['days_to_keep'])) #delete the records for deleted and edited messages over configured date database.cursor.execute( "DELETE from journal_events where event='ON_MESSAGE_DELETE' OR event = 'ON_MESSAGE_EDIT' and time<%s order by time desc", (db_cutoff, )) deleted_records_count = database.cursor.rowcount #log internally and update the modlog channel if deleted_records_count > 0: logger.warn( "journal_events_cleanup() purging {0} deleted messages from journal_events" .format(deleted_records_count)) await discord.bot.send_message( discord.mod_channel(), "{0} **Pruned Deleted Messages:** {1} messages removed from journal for being older than {2} days" .format( common.bernardUTCTimeNow(), deleted_records_count, config.cfg['housekeeping']['journal_events_cleanup'] ['days_to_keep'])) else: logger.warn( "journal_events_cleanup() nothing to delete at this time.") await asyncio.sleep( config.cfg['housekeeping']['journal_events_cleanup']['interval'])
async def purge_inactive_users(): await discord.bot.wait_until_ready() while not discord.bot.is_closed: job_start = analytics.getEventTime() logger.info( "Starting background task purge_inactive_users() - Interval {0}". format(config.cfg['housekeeping']['purge_inactive_users'] ['interval'])) est = await discord.bot.estimate_pruned_members( server=discord.objectFactory(config.cfg['discord']['server']), days=config.cfg['housekeeping']['purge_inactive_users'] ['inactive_days']) if est > 0: #send the cleanup pruned = await discord.bot.prune_members( server=discord.objectFactory(config.cfg['discord']['server']), days=config.cfg['housekeeping']['purge_inactive_users'] ['inactive_days']) #notify via mod room and console log whos getting purged logger.warn( "Purging {0} inactive users via purge_inactive_users() background job" .format(pruned)) await discord.bot.send_message( discord.mod_channel(), "{0} **Pruned Inactive Users:** {1} users removed for inactivity of {2} days" .format( common.bernardUTCTimeNow(), pruned, config.cfg['housekeeping']['purge_inactive_users'] ['inactive_days'])) logger.info( "Sleeping background task purge_inactive_users() - Interval {0}". format(config.cfg['housekeeping']['purge_inactive_users'] ['interval'])) journal.update_journal_job(module=__name__, job="purge_inactive_users", start=job_start, result=est) await asyncio.sleep( config.cfg['housekeeping']['purge_inactive_users']['interval'])
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())