async def unsilence(ctx, target): if common.isDiscordVoiceRegulator(ctx.message.author) != True: return target_id = discord.get_targeted_id(ctx) target_member = discord.default_server.get_member(target_id) if allow_regulation(ctx, target_id) is False: await discord.bot.say( "тЪая╕П{} Permissions denied to regulate user".format( ctx.message.author.mention)) return if target_member is not None: await discord.bot.server_voice_state(target_member, mute=0) journal.update_journal_regulator(invoker=ctx.message.author.id, target=target_id, eventdata="None", action="VOICE_UNSILENCE", messageid=ctx.message.id) await discord.bot.say( "тЬФя╕П {} was unsilenced by {} in voice chat!".format( target_member.mention, ctx.message.author.mention)) else: await discord.bot.say( "ЁЯЫС {} unable to moderate user. Target did not resolve to a valid Discord Member." .format(ctx.message.author.mention))
async def cleanup(ctx, target=None, history=100): start = analytics.getEventTime() if common.isDiscordRegulator(ctx.message.author) != True: return #make user input something usable first, conform outlandish commands to be sane if target is None and history is None: await discord.bot.say("Syntax: `!cleanup @mention <1-1000>` where # is last x chat messages to check per channel. Defaults to 100.") return elif target is None: await discord.bot.say("Error: You must target a user by `@mention` or raw ID.") return elif history > 1000: await discord.bot.say("Warning: This command only supports up to the last 1000 channel messages. Limiting your request to 1000.") history = 1000 else: pass #get the raw mention or just pass the ID depending on what the use supplied. member only needed for protection target_id = discord.get_targeted_id(ctx) target_member = discord.default_server.get_member(target_id) # Only use the Member lookup for Reg/Admin protection #if xiphirx = soy if target_id == discord.bot.user.id: await discord.bot.say("⚠️{0.message.author.mention} you are not allowed to cleanup after the bot!".format(ctx)) return #dont allow this to be called on regulators/administrators if target_member is not None: if common.isDiscordRegulator(target_member): logger.warn("cleanup() called and is exiting: ID:{0.message.author.id} attempted to call cleanup on ID:{1} but was denied by: IS_REGULATOR_PROTECTED".format(ctx, target_id)) await discord.bot.say("⚠️{0.message.author.mention} you are not allowed to call that on other admins/regulators!".format(ctx)) return #while this runs lets not flood the mod channel with messages deleted.IGNORE_IDS.append(target_id) await discord.bot.say("{0.message.author.mention} starting now ID:`{1}` and will @ you when task completed.".format(ctx, target_id)) logger.info("cleanup() {0.message.author.name} started deletion on ID:{1}. Checking all channels last {2} messages.".format(ctx, target_id, history)) for channel in ctx.message.server.channels: if channel.type == discord.discord.ChannelType.text: logger.info("cleanup() {0.message.author.name} started deletion on channel ID: {0.message.channel.id} for user ID:{1}".format(ctx, target_id)) await delete_messages_from_channel(ctx, channel, target_id, history) #no need to keep the id around deleted.IGNORE_IDS.remove(target_id) #create both a journal entry for the user targeted journal.update_journal_event(module=__name__, event="MESSAGE_CLEANUP_MODERATOR", userid=target_id, contents="Invoked by: {0.author.name} in channel {0.channel}".format(ctx.message)) journal.update_journal_regulator(invoker=ctx.message.author.id, target=target_id, eventdata=history, action="MESSAGE_CLEANUP_MODERATOR", messageid=ctx.message.id) secs_taken = int(analytics.getEventTime() - start) await discord.bot.say("{0.message.author.mention} cleanup on ID `{1}` completed! Took ~{2} seconds".format(ctx, target_id, secs_taken))
async def warn(ctx, target, *, reason): if common.isDiscordRegulator(ctx.message.author) != True: return # convert the target into a usable ID target_id = discord.get_targeted_id(ctx) target_member = await discord.get_user_info(target_id) if target_member is None: await discord.bot.say( "{} тЪая╕П I was not able to lookup that user ID.".format( ctx.message.author.mention)) return # warn reason has to have at least a word in it if len(reason) < 4: await discord.bot.say( "тЪая╕П Warning reason must be longer than 4 characters. `!warn @username reason goes here`" ) return # warn reason has to be a reasonable length if len(reason) > 200: await discord.bot.say( "тЪая╕П Reason too long - please limit to 200 characters or less.") return if allow_regulation(ctx, target_id): # return what is happening in the same channel to alert the user await discord.bot.say( "тЬФя╕П {} is warning {} with the reason of `{}`.".format( ctx.message.author.mention, target_member.mention, reason)) # update the internal bot log journal.update_journal_regulator(invoker=ctx.message.author.id, target=target_id, eventdata=reason, action="WARN_MEMBER", messageid=ctx.message.id) # update the internal user log journal.update_journal_event(module=__name__, event="ON_MEMBER_WARN", userid=target_id, contents="{0.name}#{0.discriminator} " "warned by {1.name}#{1.discriminator} " "with reason '{2}'".format( target_member, ctx.message.author, reason)) else: await discord.bot.say( "ЁЯЫС {} unable to moderate user. Either you failed to tag the user properly or the member is protected from regulators." .format(ctx.message.author.mention))
async def kick(ctx, target, *, reason): if common.isDiscordRegulator(ctx.message.author) != True: return #convert the target into a usable ID target_id = discord.get_targeted_id(ctx) target_member = discord.default_server.get_member(target_id) if target_member is None: await discord.bot.say( "{} тЪая╕П I was not able to lookup that user ID.".format( ctx.message.author.mention)) return #kick reason has to have at least a word in it if len(reason) < 4: await discord.bot.say( "тЪая╕П Kick reason must be longer than 4 characters. `!kick @username reason goes here`" ) return # kick reason has to be a reasonable length if len(reason) > 200: await discord.bot.say( "тЪая╕П Reason too long - please limit to 200 characters or less.") return if allow_regulation(ctx, target_id): #return what is happening in the same channel to alert the user, wait 5 seconds and fire the kick command await discord.bot.say( "тЬФя╕П {} is kicking {} with the reason of `{}`.".format( ctx.message.author.mention, target_member.mention, reason)) await asyncio.sleep(5) await discord.bot.kick(target_member) #update the internal bot log, since we cant send kick reasons via this api version #TODO: in rewrite add that journal.update_journal_regulator(invoker=ctx.message.author.id, target=target_id, eventdata=reason, action="KICK_MEMBER", messageid=ctx.message.id) else: await discord.bot.say( "ЁЯЫС {} unable to moderate user. Either you failed to tag the user properly or the member is protected from regulators." .format(ctx.message.author.mention))
async def silence(ctx, target, *, reason): if common.isDiscordVoiceRegulator(ctx.message.author) != True: return target_id = discord.get_targeted_id(ctx) target_member = discord.default_server.get_member(target_id) # silence reason has to have at least a word in it if len(reason) < 4: await discord.bot.say( "тЪая╕П Silence reason must be longer than 4 characters. `!silence @username reason goes here`" ) return # silence reason has to have at least a word in it if len(reason) > 200: await discord.bot.say( "тЪая╕П Reason too long - please limit to 200 characters or less.") return if allow_regulation(ctx, target_id) is False: await discord.bot.say( "тЪая╕П{} Permissions denied to regulate user".format( ctx.message.author.mention)) return if target_member is not None: await discord.bot.server_voice_state(target_member, mute=1) journal.update_journal_regulator(invoker=ctx.message.author.id, target=target_id, eventdata=reason, action="VOICE_SILENCE", messageid=ctx.message.id) await discord.bot.say( "тЬФя╕П {} was silenced by {} in voice chat".format( target_member.mention, ctx.message.author.mention)) else: await discord.bot.say( "ЁЯЫС {} unable to moderate user. Target did not resolve to a valid Discord Member." .format(ctx.message.author.mention))
async def unban(ctx, target): target_id = discord.get_targeted_id(ctx) target_member = discord.default_server.get_member(target_id) if common.isDiscordAdministrator(ctx.message.author): #if the user is an admin, let them blindly call IDs, unban blindly from discord unban_status_discord = await common.unban_id(target_id) #also check the retroactive table, this can be useful if the user was retro banned but never rejoied database.cursor.execute('SELECT * FROM bans_retroactive WHERE id=%s', (target_id, )) unban_status_retroactive = database.cursor.fetchone() if unban_status_retroactive is None: unban_status_retroactive = False else: database.cursor.execute('DELETE FROM bans_retroactive WHERE id=%s', (target_id, )) database.connection.commit() #unban message logic if unban_status_discord is False and unban_status_retroactive is False: await discord.bot.say( "тЪая╕П Unable to find ID in either Discord or Retroactive table. Try again?" ) elif unban_status_discord is True and unban_status_retroactive is False: await discord.bot.say( "тЬФя╕П User was found banned on Discord. Removed ban.") elif unban_status_discord is False and unban_status_retroactive is True: await discord.bot.say( "тЬФя╕П User was found banned in Retroactive table. Removed from database." ) else: await discord.bot.say( "тЬФя╕П User was found banned on Discord and Retroactive ban table. Removed ban and database entry." ) elif common.isDiscordRegulator(ctx.message.author): #regulators have a slightly different logic to follow to allow. Use internal audit log to make decisions database.cursor.execute( 'SELECT * FROM journal_regulators WHERE id_targeted=%s AND id_invoker=%s AND action="BAN_MEMBER" ORDER BY time ASC', (target_id, ctx.message.author.id)) regulator_target_history = database.cursor.fetchone() #if there is nothing in the database there is no reason to even try with the logic if regulator_target_history is None: logger.warn( "{0} attempted unban of {1} ID but failed due to reason: DB_RETURNED_NONE" .format(ctx.message.author.id, target_id)) await discord.bot.say( "тЪая╕П {0.message.author.mention} Unable to find any record of you banning ID `{1}` (ever). You can only unban people you yourself banned." .format(ctx, target_id)) return #get how many mins ago the ban was to see if they are able to be unbanned banned_mins_ago = int( (time.time() - regulator_target_history['time']) / 60) #handle if we should process the unban_id() if banned_mins_ago > config.cfg['regulators']['unban_grace_mins']: over_grace_mins = int(banned_mins_ago - config.cfg['regulators']['unban_grace_mins']) logger.warn( "{0} attempted unban of {1} ID but failed due to reason: OVER_GRACE_PERIOD by {2} minutes" .format(ctx.message.author.id, target_id, over_grace_mins)) await discord.bot.say( "тЪая╕П {0.message.author.mention} The user ID `{1}` is over the grace period for regulator unbans by `{2}` minutes. Contact an Administrator." .format(ctx, target_id, over_grace_mins)) else: unban_status_discord = await common.unban_id(target_id) if unban_status_discord is False: logger.warn( "{0} attempted unban of {1} ID but failed due to reason: DISCORD_RETURNED_NOT_BANNED" .format(ctx.message.author.id, target_id)) await discord.bot.say( "тЪая╕П {0.message.author.mention} Discord returned no ban found for ID `{1}` (ever). The user was already unbanned or something is wrong :/" .format(ctx, target_id)) else: logger.info( "{0} unbanned {1}. Time elapsed since ban {2}".format( ctx.message.author.id, target_id, banned_mins_ago)) await discord.bot.say( "тЬФя╕П {0.message.author.mention} ID `{1}` has been unbanned!" .format(ctx, target)) journal.update_journal_regulator(invoker=ctx.message.author.id, target=target_id, eventdata=banned_mins_ago, action="UNBAN_MEMBER_GRACE", messageid=ctx.message.id)
async def inviteban(ctx, target, *, reason): if common.isDiscordRegulator(ctx.message.author) != True: return # ban reason has to be a reasonable length if len(reason) > 200: await discord.bot.say( "тЪая╕П Invite ban reason must be longer than 4 characters. `!inviteban inviteCode reason goes here`" ) return # ban reason has to be a reasonable length if len(reason) > 200: await discord.bot.say( "тЪая╕П Reason too long - please limit to 200 characters or less.") return #get anyone who joined via the invite database.cursor.execute( "SELECT * FROM journal_events WHERE event='ON_MEMBER_JOINED_WITH_INVITE' AND contents=%s", (target, )) invitees = database.cursor.fetchall() #if there is not anyone, dont even do anything if len(invitees) == 0 or target.lower() == "none": await discord.bot.say( "тЪая╕П {} Invalid invite code! 0 members joined via internal journal. Retype invite or bother Cake." .format(ctx.message.author.mention)) return #get the invite data, make sure it is valid and then get who made it. They purge first. try: invite = await discord.bot.get_invite(target) except Exception as e: invite = None await discord.bot.say( "тЪая╕П {} unable to look up invite code: {} for ownership! Moving on to blind mode!" .format(ctx.message.author.mention, e)) #ban the inviter if we know who they are if invite is not None: banned_inviter = await common.ban_verbose( invite.inviter, "{} from invite {} - '{}'".format(ctx.message.author.name, target, reason)) if banned_inviter == False: pass await discord.bot.say( "тЭУ Something's f****d! Unable to issue ban to Discord API for root invite creator. Bother cake. (protected user?)" ) else: journal.update_journal_regulator( invoker=ctx.message.author.id, target=invite.inviter.id, eventdata=target, action="BAN_MEMBER_VIA_INVITE_MAKER", messageid=ctx.message.id) await discord.bot.say( "тЬФя╕П {} is dropping the hammer on {} and anyone who joined via their invite `{}`. Let the bombs drop! " .format(ctx.message.author.mention, invite.inviter.mention, target)) for invited in invitees: #attempt to turn the ID into a valid member object server = discord.bot.get_server(config.cfg['discord']['server']) invited_member = server.get_member(str(invited['userid'])) #if that cant happen, the user probably left or was banned already. Add to retroactive table (TODO) if invited_member == None: database.cursor.execute( 'INSERT IGNORE INTO bans_retroactive(id, name, reason) VALUES(%s,%s,%s)', (invited['userid'], "UNKNOWN_INVITEBAN_CALLED", reason)) database.connection.commit() await discord.bot.say( "тЪая╕П `{}` is getting banned for joining on invite `{}` (retroactive fallback. User either left or already banned) " .format(invited['userid'], target)) else: banned_inviter = await common.ban_verbose( invited_member, "{} inviteban via {} - '{}'".format(ctx.message.author.name, target, reason)) if banned_inviter == False: await discord.bot.say( "тЭУ Something's f****d! Unable to issue ban to Discord API for {}. Bother cake." .format(invited_member.mention)) else: await discord.bot.say( "тЬФя╕П {} is getting banned for joining on invite `{}` ". format(invited_member.mention, target)) #take a break between to prevent r8 limited await asyncio.sleep(2) await discord.bot.say( "Invite ban completed. {} members removed. Thanks for playing!".format( len(invitees)))
async def timedban(ctx, target, duration, *, reason): if common.isDiscordRegulator(ctx.message.author) != True: return #convert the target into a usable ID target_id = discord.get_targeted_id(ctx) target_member = discord.default_server.get_member(target_id) #ban reason has to have at least a word in it if len(reason) < 4: await discord.bot.say( "тЪая╕П {0.message.author.mention} ban reason must be longer than 4 characters. `!ban @username 1d reason goes here`" .format(ctx)) return #if the user cannot be banned if allow_regulation(ctx, target_id) is False: await discord.bot.say( "ЁЯЫС {0.message.author.mention} unable to moderate user. (no permissions)" .format(ctx)) return #verify the ban duration, and get the durations in 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 `!ban @username 60s|1m|7d could not stop using gamer words`" .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} Ban duration is out of range. Have some mercy! 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 #if we have a Member object, it is not retroactive and can be run live against discord if target_member is not None: #get the time to unban, make a db entry for unban now = int(time.time()) time_to_fire = now + duration_length when_nice = time.strftime("%A, %B %d %Y %H:%M %Z", time.localtime(time_to_fire)) scheduler.set_future_task(invoker=ctx.message.author.id, target=target_member.id, channel=ctx.message.channel.id, timestamp=time_to_fire, event="UNBAN_MEMBER", msg=reason) await discord.bot.say( "тЬФя╕П {0.message.author.mention} is **BANNING** {1} with the reason of `{2}`. (Duration: {3}, Unban at:`{4}`)" .format(ctx, target_member.mention, reason, duration, when_nice)) await asyncio.sleep(5) #ship the ban off to discord res = await common.ban_verbose( target_member, "{0.message.author.name} - '{1}'".format(ctx, reason)) if res is False: await discord.bot.say( "тЭУ Something's f****d! Unable to issue ban to Discord API. Bother <@{}>" .format(config.cfg['bernard']['owner'])) else: journal.update_journal_regulator(invoker=ctx.message.author.id, target=target_id, eventdata=reason, action="BAN_MEMBER", messageid=ctx.message.id) #if a raw mention exists but not the Member object, handle it as retroactive elif target_id is not None and target_member is None: await discord.bot.say( "тЪая╕П {0.message.author.mention} You cannot issue retroactive timed bans. Please use normal !ban." .format(ctx)) else: await discord.bot.say( "ЁЯЫС {0.message.author.mention} unable to moderate user. Target did not resolve to a valid Discord Member." .format(ctx))
async def ban(ctx, target, *, reason): if common.isDiscordRegulator(ctx.message.author) != True: return #convert the target into a usable ID target_id = discord.get_targeted_id(ctx) target_member = discord.default_server.get_member(target_id) #ban reason has to have at least a word in it if len(reason) < 4: await discord.bot.say( "тЪая╕П ban reason must be longer than 4 characters. `!ban @username reason goes here`" ) return # ban reason has to be a reasonable length if len(reason) > 200: await discord.bot.say( "тЪая╕П Reason too long - please limit to 200 characters or less.") return #if the user cannot be banned if allow_regulation(ctx, target_id) is False: await discord.bot.say( "ЁЯЫС {} unable to moderate user. (no permissions)".format( ctx.message.author.mention)) return #if we have a Member object, it is not retroactive and can be run live against discord if target_member is not None: await discord.bot.say( "тЬФя╕П {} is **BANNING** {} with the reason of `{}`.".format( ctx.message.author.mention, target_member.mention, reason)) await asyncio.sleep(5) res = await common.ban_verbose( target_member, "{} - '{}'".format(ctx.message.author.name, reason)) if res is False: await discord.bot.say( "тЭУ Something's f****d! Unable to issue ban to Discord API. Bother <@{}>" .format(config.cfg['bernard']['owner'])) else: journal.update_journal_regulator(invoker=ctx.message.author.id, target=target_id, eventdata=reason, action="BAN_MEMBER", messageid=ctx.message.id) #if a raw mention exists but not the Member object, handle it as retroactive and log who did it. elif target_id is not None and target_member is None: await discord.bot.say( "тЬФя╕П {} is (retroactive) **BANNING** userid `{}` with the reason of `{}`." .format(ctx.message.author.mention, target_id, reason)) journal.update_journal_regulator(invoker=ctx.message.author.id, target=target_id, eventdata=reason, action="BAN_MEMBER_RETROACTIVE", messageid=ctx.message.id) retroactive_reason = "VIA_RETROACTIVE: {} - '{}'".format( ctx.message.author.name, reason) database.cursor.execute( 'INSERT INTO bans_retroactive (id, name, reason) VALUES (%s,%s,%s)', (target_id, "N/A", retroactive_reason)) database.connection.commit() else: await discord.bot.say( "ЁЯЫС {} unable to moderate user. Target did not resolve to a valid Discord Member." .format(ctx.message.author.mention))
async def slur_filter(message): # get the score of the message contents regex_result = regex_scoring_msg(message.content) # if the message scored 0, we can ignore if regex_result['score'] == 0: return # if we got this far, we need to lookup their account age account_min_old = int((datetime.datetime.utcnow().timestamp() - message.author.created_at.timestamp()) / 60) account_age_score = account_age_scoring(account_min_old) # and the amount of time in the discord account_member_score = account_join_time(message.author.id) # and how many times they have recently been AUTomodded account_previous_score = previous_automod_scoring(message.author.id) multiplier = account_age_score + account_member_score if multiplier is not 0: final_score = (regex_result['score'] * multiplier) + account_previous_score else: final_score = regex_result['score'] + account_previous_score if final_score == 0: return # fail safe if the score is 0 # if we made it here, time to push some punishments await discord.bot.delete_message(message ) # delete the message from Discord journal.update_journal_event( module=__name__, event="ON_MESSAGE_DELETE_AUTOMOD", userid=message.author.id, eventid=message.id, contents=message.content) # update the users journal # decide how hard to punish the user, and punish them punishment = get_punishment_tier(final_score) # Get a censored string of what the user said to get automodded clean_gamerwords = [] for gamerword in regex_result['words']: clean_gamerwords.append(censor_word(gamerword)) clean_modded_words = ','.join(clean_gamerwords) # alert the user to what they have done await discord.bot.send_message( message.channel, "{0.author.mention} is being automoderated with enforcement **{1}**: `({2})`" .format(message, punishment, clean_modded_words)) await asyncio.sleep(2) # sleep for 2 seconds until the ban is fired if punishment == "BAN_PERMA": punishment_journal_type = "BAN_MEMBER" ban = await common.ban_verbose( message.author, "AUTOMOD BAN_PERMA SCORE:{}".format(final_score)) if ban is False: await discord.bot.send_message( message.channel, "❓ Something's f****d! Unable to issue ban to Discord API. Bother <@{}>" .format(config.cfg['bernard']['owner'])) elif punishment == "BAN_24H" or punishment == "BAN_1H": punishment_journal_type = "BAN_MEMBER" ban = await common.ban_verbose( message.author, "AUTOMOD {0} SCORE:{1}".format(punishment, final_score)) if ban is False: await discord.bot.send_message( message.channel, "❓ Something's f****d! Unable to issue ban to Discord API. Bother <@{}>" .format(config.cfg['bernard']['owner'])) else: if punishment == "BAN_24H": time_to_fire = time.time() + 86400 else: time_to_fire = time.time() + 3600 # don't forget to set the unban event scheduler.set_future_task(invoker=discord.bot.user.id, target=message.author.id, channel=message.channel.id, timestamp=time_to_fire, event="UNBAN_MEMBER", msg="Automod SCORE:{0} WORDS:{1}".format( final_score, clean_modded_words)) elif punishment == "KICK": punishment_journal_type = "KICK_MEMBER" await discord.bot.kick(message.author) elif punishment == "WARN_HARD": punishment_journal_type = "WARN_MEMBER" await discord.bot.send_message( message.channel, "{0.author.mention} Please review the rules! Your message is not allowed and has been removed. A rapsheet warning has been issued." .format(message, punishment, final_score)) else: punishment_journal_type = None await discord.bot.send_message( message.channel, "{0.author.mention} Please review the rules! Your message is not allowed and has been removed. Next time I may not be as nice. (unoffical warning)" .format(message, punishment, final_score)) # update the regulator log if punishment_journal_type is not None: journal.update_journal_regulator( invoker=discord.bot.user.id, target=message.author.id, eventdata="Automod POLICY:{0} SCORE:{1} WORDS:{2}".format( punishment, final_score, clean_modded_words), action=punishment_journal_type, messageid=message.id) # log the action to the database database.cursor.execute( 'INSERT INTO automod_gamerwords' '(score_final, score_regex, score_prev_infractions, multiply_age_member, multiply_age_account, action, time, id_targeted, message)' 'VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s)', (final_score, regex_result['score'], account_previous_score, account_member_score, account_age_score, punishment, time.time(), message.author.id, message.content)) database.connection.commit()