async def lingering_secondaries(client): lingering_secondaries.last_run = datetime.now(pytz.utc) start_time = time() if client.is_ready(): potentials = None with concurrent.futures.ThreadPoolExecutor() as pool: potentials = await client.loop.run_in_executor( pool, get_potentials) potentials = potentials.split('\n') if potentials: potentials = set( potentials[-10000:] ) # Sets apparently give better performance. Discard all but last 10k. for guild in func.get_guilds(client): settings = utils.get_serv_settings(guild) if not settings['enabled'] or not settings['auto_channels']: continue secondaries = func.get_secondaries(guild, settings=settings, include_jc=True) voice_channels = [ x for x in guild.channels if isinstance(x, discord.VoiceChannel) ] for v in voice_channels: if v.id not in secondaries and str( v.id ) in potentials and not func.channel_is_requested(v): if v.name not in ['⌛', '⚠']: try: await v.edit(name='⚠') log("Remembering channel {}".format(v.id), guild) await func.admin_log( "⚠ Remembering channel `{}` in guild **{}**" .format(v.id, guild.name), client) except discord.errors.NotFound: pass except Exception: traceback.print_exc() end_time = time() fn_name = "lingering_secondaries" cfg.TIMINGS[fn_name] = end_time - start_time if cfg.TIMINGS[fn_name] > 5: await func.log_timings(client, fn_name)
async def on_reaction_remove(reaction, user): if not client.is_ready(): return if user.bot: return guild = reaction.message.guild guilds = func.get_guilds(client) if guild not in guilds: return if reaction.message.id in cfg.VOTEKICKS: if reaction.emoji == '✅': vk = cfg.VOTEKICKS[reaction.message.id] if user in vk['in_favor'] and user in vk['participants']: vk['in_favor'].remove(user) return
async def analytics(client): if client.is_ready() and cfg.SAPPHIRE_ID is None: fp = os.path.join(cfg.SCRIPT_DIR, "analytics.json") guilds = func.get_guilds(client) if not os.path.exists(fp): analytics = {} else: analytics = utils.read_json(fp) analytics[datetime.now(pytz.timezone( cfg.CONFIG['log_timezone'])).strftime("%Y-%m-%d %H:%M")] = { 'nc': utils.num_active_channels(guilds), 'tt': round(cfg.TICK_TIME, 2), 'tr': main_loop.seconds, 'ng': len(guilds), 'm': round(psutil.virtual_memory().used / 1024 / 1024 / 1024, 2), } with concurrent.futures.ThreadPoolExecutor() as pool: await client.loop.run_in_executor(pool, utils.write_json, fp, analytics)
async def dynamic_tickrate(client): start_time = time() if client.is_ready(): current_channels = utils.num_active_channels(func.get_guilds(client)) new_tickrate = current_channels / 7 new_tickrate = max(3, min(100, new_tickrate)) new_seed_interval = current_channels / 45 new_seed_interval = max(1.5, min(15, new_seed_interval)) if cfg.SAPPHIRE_ID is None: print("New tickrate is {0:.1f}s, seed interval is {1:.2f}m".format(new_tickrate, new_seed_interval)) main_loop.change_interval(seconds=new_tickrate) creation_loop.change_interval(seconds=new_tickrate) deletion_loop.change_interval(seconds=new_tickrate * 2) update_seed.change_interval(minutes=new_seed_interval) cfg.TICK_RATE = new_tickrate end_time = time() fn_name = "dynamic_tickrate" cfg.TIMINGS[fn_name] = end_time - start_time if cfg.TIMINGS[fn_name] > 20: await func.log_timings(client, fn_name)
def for_looper(client): for guild in func.get_guilds(client): settings = utils.get_serv_settings( guild) # Need fresh in case some were deleted dead_secondaries = [] for p in settings['auto_channels']: for sid, sv in settings['auto_channels'][p][ 'secondaries'].items(): s = client.get_channel(sid) if s is None: dying = sv['dying'] + 1 if 'dying' in sv else 1 settings['auto_channels'][p]['secondaries'][sid][ 'dying'] = dying cfg.GUILD_SETTINGS[ guild. id] = settings # Temporarily settings, no need to write to disk. log("{} is dead ({})".format(sid, dying), guild) if dying >= 3: dead_secondaries.append(sid) else: if 'dying' in sv: settings['auto_channels'][p]['secondaries'][sid][ 'dying'] = 0 cfg.GUILD_SETTINGS[ guild. id] = settings # Temporarily settings, no need to write to disk. if dead_secondaries: for p, pv in settings['auto_channels'].items(): tmp = {} for s, sv in pv['secondaries'].items(): if s not in dead_secondaries: tmp[s] = sv settings['auto_channels'][p]['secondaries'] = tmp utils.set_serv_settings(guild, settings) for s in dead_secondaries: if s in cfg.ATTEMPTED_CHANNEL_NAMES: del cfg.ATTEMPTED_CHANNEL_NAMES[s]
async def first_start(client): global ADMIN_CHANNEL global ADMIN while not client.is_ready(): await asyncio.sleep(1) if not cfg.FIRST_RUN_COMPLETE: cfg.FIRST_RUN_COMPLETE = True guilds = func.get_guilds(client) if guilds: text = 'vc/help' if len(guilds) == 1 and guilds[0].id in cfg.PREFIXES: text = cfg.PREFIXES[guilds[0].id] + 'help' else: text = "🚧Nothing🚧" await client.change_presence(activity=discord.Activity(name=text, type=discord.ActivityType.watching)) if 'admin_channel' in cfg.CONFIG: ADMIN_CHANNEL = client.get_channel(cfg.CONFIG['admin_channel']) if ADMIN is None: ADMIN = client.get_user(cfg.CONFIG['admin_id'])
async def update_status(client): if client.is_ready(): guilds = func.get_guilds(client) if guilds: prefix = 'vc/' if len(guilds) == 1 and guilds[0].id in cfg.PREFIXES: prefix = cfg.PREFIXES[guilds[0].id] nc = utils.num_active_channels(guilds) text = "{}help | {} channel{}".format(prefix, nc, ("s" if nc != 1 else "")) else: text = "🚧Nothing🚧" old_text = "" try: old_text = client.guilds[0].me.activity.name except (IndexError, AttributeError, TypeError): pass if text != old_text: try: await client.change_presence(activity=discord.Activity(name=text, type=discord.ActivityType.watching)) log("Changing status to: {}".format(text.replace(' ', ' '))) except Exception as e: log("Failed to update status: {}".format(type(e).__name__))
async def on_ready(self): if self.ready_once: return asyncio.create_task(self.start_chunking()) print('=' * 24) curtime = datetime.now(pytz.timezone( cfg.CONFIG['log_timezone'])).strftime("%Y-%m-%d %H:%M") print(curtime) print('Logged in as') print(self.user.name) print(self.user.id) if cfg.SAPPHIRE_ID is not None: print("Sapphire:", cfg.SAPPHIRE_ID) print("discordpy version: {}".format(discord.__version__)) print('-' * 24) shards = {} for g in func.get_guilds(self): if g.shard_id in shards: shards[g.shard_id] += 1 else: shards[g.shard_id] = 1 settings = utils.get_serv_settings(g) if 'prefix' in settings: cfg.PREFIXES[g.id] = settings['prefix'] print("Shards:", len(shards)) for s in shards: print("s{}: {} guilds".format(s, shards[s])) print('=' * 24) if 'disable_ready_message' in cfg.CONFIG and cfg.CONFIG[ 'disable_ready_message']: log("READY") else: await func.admin_log("🟥🟧🟨🟩 **Ready** 🟩🟨🟧🟥", self)
async def on_reaction_add(reaction, user): if not client.is_ready(): return if user.bot: return guild = reaction.message.guild guilds = func.get_guilds(client) if guild not in guilds: return if reaction.message.id in cfg.VOTEKICKS: if reaction.emoji == '✅': vk = cfg.VOTEKICKS[reaction.message.id] if time() < vk['end_time']: if user not in vk['in_favor'] and user in vk['participants']: vk['in_favor'].append(user) log( "{} voted to kick {}".format( user.display_name, vk['offender'].display_name), guild) return to_delete = [] joins_in_progress = list(cfg.JOINS_IN_PROGRESS.keys()) for uid in joins_in_progress: try: j = cfg.JOINS_IN_PROGRESS[uid] except KeyError: print("Ignoring error:") traceback.print_exc() continue if reaction.message.id == j['mid'] and user.id == j['creator'].id: reacted = False if reaction.emoji == '✅': reacted = True try: await j['vc'].set_permissions(j['requester'], connect=True) await j['requester'].move_to(j['vc']) except discord.errors.Forbidden: await j['msg'].edit( content= ":warning: I don't have permission to move {} to **{}** :(" .format(j['requester'].mention, func.esc_md(j['vc'].name))) except discord.errors.HTTPException as e: await j['msg'].edit( content= ":warning: Unable to move {} to {}'s channel ({})". format(j['requester'].mention, j['creator'].mention, e.text)) else: await j['msg'].delete(delay=5) elif reaction.emoji in ['❌', '⛔']: reacted = True try: await j['requester'].move_to(None) except discord.errors.Forbidden: pass except discord.errors.HTTPException: pass else: await j['msg'].edit( content="Sorry {}, your request to join {} was denied." .format(j['requester'].mention, j['creator'].mention)) if reaction.emoji == '⛔': try: await j['jc'].set_permissions(j['requester'], connect=False) except Exception as e: await j['msg'].edit( content="{}\nFailed to block user ({}).".format( j['msg'].content, type(e).__name__)) if reacted: to_delete.append(uid) try: await j['msg'].remove_reaction('✅', guild.me) await j['msg'].remove_reaction('❌', guild.me) await j['msg'].remove_reaction('⛔', guild.me) except discord.errors.Forbidden: # Shouldn't have an issue removing your own reactions, but apparently sometimes you do. pass except discord.errors.NotFound: pass for uid in to_delete: try: del cfg.JOINS_IN_PROGRESS[uid] except KeyError: pass # Already deleted
async def on_message(message): if not client.is_ready(): return if message.author.bot: # Don't respond to self or bots return guilds = func.get_guilds(client) admin = ADMIN admin_channels = [] if admin is not None: admin_channels = [admin.dm_channel] if 'admin_channel' in cfg.CONFIG and ADMIN_CHANNEL is not None: admin_channels.append(ADMIN_CHANNEL) if message.channel in admin_channels: split = message.content.split(' ') cmd = split[0].split('\n')[0].lower() params_str = message.content[len(cmd):].strip() params = params_str.split(' ') if cmd == 'reload': m = utils.strip_quotes(params_str) success = await reload_modules(m) await func.react(message, '✅' if success else '❌') else: ctx = { 'client': client, 'admin': admin, 'message': message, 'params': params, 'params_str': params_str, 'guilds': guilds, 'LAST_COMMIT': LAST_COMMIT, } await admin_commands.admin_command(cmd, ctx) return if not message.guild: # DM if 'help' in message.content and len( message.content) <= len("@Auto Voice Channels help"): await message.channel.send( "Sorry I don't respond to commands in DMs, " "you need to type the commands in a channel in your server.\n" "If you've tried that already, then make sure I have the right permissions " "to see and reply to your commands in that channel.") elif message.content.lower().startswith("power-overwhelming"): channel = message.channel params_str = message.content[len("power-overwhelming"):].strip() if not params_str: await channel.send( "You need to specify a guild ID. " "Try typing `who am I` to get a list of guilds we're both in" ) return auth_guilds = params_str.replace(' ', '\n').split('\n') for auth_guild in auth_guilds: try: g = client.get_guild(int(auth_guild)) if g is None: await channel.send( "`{}` is not a guild I know about, " "maybe you need to invite me there first?".format( auth_guild)) return except ValueError: await channel.send( "`{}` is not a valid guild ID, try typing " "`who am I` to get a list of guilds we're both in.". format(auth_guild)) return except Exception as e: error_text = "Auth Error `{}`\nUser `{}`\nCMD `{}`".format( type(e).__name__, message.author.id, message.content) await func.admin_log(error_text, ctx['client']) log(error_text) error_text = traceback.format_exc() await func.admin_log(error_text, ctx['client']) log(error_text) return False, ( "A `{}` error occured :(\n" "An admin has been notified and will be in touch.\n" "In the meantime, try asking for help in the support server: " "https://discord.gg/qhMrz6u".format(type(e).__name__)) ctx = { 'message': message, 'channel': channel, 'client': client, } auth_guilds = [int(g) for g in auth_guilds] success, response = await func.power_overwhelming(ctx, auth_guilds) if success or response != "NO RESPONSE": log("DM CMD {}: {}".format("Y" if success else "F", message.content)) if success: if response: if response != "NO RESPONSE": await echo(response, channel, message.author) else: await func.react(message, '✅') else: if response != "NO RESPONSE": await func.react(message, '❌') if response: await echo(response, channel, message.author) elif message.content.lower() in ["who am i", "who am i?"]: in_guilds = [] for g in client.guilds: if message.author in g.members: in_guilds.append("`{}` **{}**".format(g.id, g.name)) if in_guilds: await message.channel.send( "We're both in the following guilds:\n{}".format( '\n'.join(in_guilds))) else: await message.channel.send( "I'm not in any of the same guilds as you.") else: await admin_channels[-1].send( embed=discord.Embed(title="DM from **{}** [`{}`]:".format( message.author.name, message.author.id), description=message.content)) return if message.guild not in guilds: return prefix_m = message.guild.me.mention prefix_mx = "<@!" + prefix_m[2:] if message.guild.id in cfg.PREFIXES: prefix_p = cfg.PREFIXES[message.guild.id] else: prefix_p = 'vc/' prefix = None if message.content.startswith(prefix_m): prefix = prefix_m print_prefix = "@{} ".format(message.guild.me.display_name) elif message.content.startswith(prefix_mx): prefix = prefix_mx print_prefix = "@{} ".format(message.guild.me.display_name) elif message.content.lower().startswith(prefix_p.lower()): prefix = prefix_p print_prefix = prefix_p # Commands if prefix: msg = message.content[len(prefix):].strip() # Remove prefix split = msg.split(' ') cmd = split[0].lower() params = split[1:] params_str = ' '.join(params) clean_paramstr = ' '.join( message.clean_content[len(prefix):].strip().split(' ')[1:]) guild = message.guild channel = message.channel settings = utils.get_serv_settings(guild) if channel.id not in func.get_voice_context_channel_ids( guild, settings): settings['last_channel'] = channel.id utils.set_serv_settings(guild, settings) ctx = { 'client': client, 'guild': guild, 'prefix': prefix, 'print_prefix': print_prefix, 'prefix_p': prefix_p, 'command': cmd, 'gold': func.is_gold(guild), 'sapphire': func.is_sapphire(guild), 'settings': settings, 'message': message, 'channel': channel, 'clean_paramstr': clean_paramstr, } # Restricted commands perms = message.author.permissions_in(channel) perms_required = [ perms.manage_channels, perms.manage_roles, ] ctx['admin'] = all(perms_required) success, response = await commands.run(cmd, ctx, params) if success or response != "NO RESPONSE": log("CMD {}: {}".format("Y" if success else "F", msg), guild) if success: if response: if response != "NO RESPONSE": await echo(response, channel, message.author) else: await func.react(message, '✅') else: if response != "NO RESPONSE": await func.react(message, '❌') if response: await echo(response, channel, message.author)
async def create_join_channels(client): start_time = time() if not client.is_ready(): return to_remove = [] priv_channels = list(cfg.PRIV_CHANNELS.keys()) for pc in priv_channels: try: pcv = cfg.PRIV_CHANNELS[pc] except KeyError: print("Ignoring error:") traceback.print_exc() continue if 'request_time' in pcv and time() - pcv['request_time'] > 120: # Unable to create join channel for 120s to_remove.append(pc) await pcv['text_channel'].send( ":warning: {} For some reason I was unable to create your \"⇩ Join\" channel, please try again later. " "Your channel is still private, but there's now no way for anyone to join you. " "Use `{}public` to make it public again." "".format(pcv['creator'].mention, pcv['prefix'])) log("Failed to create join-channel, timed out.") continue guild = client.get_guild(pcv['guild_id']) if guild not in func.get_guilds(client): continue settings = utils.get_serv_settings(guild) for p, pv in settings['auto_channels'].items(): for s, sv in pv['secondaries'].items(): if 'priv' in sv and 'jc' not in sv: creator = pcv['creator'].display_name vc = pcv['voice_channel'] c_position = vc.position overwrites = vc.overwrites k = guild.default_role v = overwrites[ k] if k in overwrites else discord.PermissionOverwrite( ) v.update(connect=True) overwrites[k] = v try: jc = await guild.create_voice_channel( "⇩ Join {}".format( creator), # TODO creator can change category=vc.category, overwrites=overwrites) except discord.errors.Forbidden: to_remove.append(pc) try: await pcv['text_channel'].send( ":warning: {} I don't have permission to make the \"⇩ Join\" channel for you anymore." "".format(pcv['creator'].mention)) except: log("Failed to create join-channel, and failed to notify {}" .format(creator)) break utils.permastore_secondary(jc.id) settings['auto_channels'][p]['secondaries'][s][ 'jc'] = jc.id utils.set_serv_settings(guild, settings) to_remove.append(pc) try: # Set position again, sometimes create_voice_channel gets it wrong. await jc.edit(position=c_position) except discord.errors.Forbidden: # Harmless error, no idea why it sometimes throws this, seems like a bug. pass break for i in to_remove: try: del cfg.PRIV_CHANNELS[i] except KeyError: # Already deleted somehow. print("Ignoring error:") traceback.print_exc() pass end_time = time() fn_name = "create_join_channels" cfg.TIMINGS[fn_name] = end_time - start_time if cfg.TIMINGS[fn_name] > 10: await func.log_timings(client, fn_name)
async def check_votekicks(client): start_time = time() if client.is_ready(): to_remove = [] votekicks = list(cfg.VOTEKICKS.keys()) for mid in votekicks: try: vk = cfg.VOTEKICKS[mid] except KeyError: print("Ignoring error:") traceback.print_exc() continue guild = vk['message'].guild guilds = func.get_guilds(client) if guild not in guilds: return in_favor = len(vk['in_favor']) log( "TESTVOTEKICK: {} ({}/{})".format(vk['offender'].display_name, in_favor, vk['required_votes']), guild) if in_favor >= vk['required_votes']: to_remove.append(mid) log( "Kicked {} from {} ({}/{})".format( vk['offender'].display_name, vk['voice_channel'].name, in_favor, len(vk['participants']) + 1), guild) try: await vk['offender'].move_to(None) # Kick except Exception as e: to_remove.append(mid) await vk['message'].edit( content= "‼ **Votekick** failed - A `{}` error was encountered." .format(type(e).__name__)) continue banned = True try: await vk['voice_channel'].set_permissions(vk['offender'], connect=False) except discord.errors.Forbidden: banned = False await vk['message'].edit(content=( "‼ **Votekick** ‼\n" "{} was **kicked** from {}'s channel{}.{}". format(vk['offender'].mention, vk['initiator'].mention, ( ", but could not be banned from the channel as I don't have the *Manage Roles* permission." if not banned else ""), ("\nReason: **{}**".format( vk['reason']) if vk['reason'] else "")))) await func.server_log( guild, "👢 {} (`{}`) has been **kicked** from {}'s channel.". format(func.user_hash(vk['offender']), vk['offender'].id, vk['initiator']), 1, utils.get_serv_settings(guild)) elif time() > vk['end_time'] + 5: to_remove.append(mid) log( "VOTEKICK TIMED OUT: {} ({}/{}) {} {}".format( vk['offender'].display_name, in_favor, vk['required_votes'], mid, type(mid)), guild) await vk['message'].edit( content= "‼ **Votekick** timed out: Insufficient votes received " "({0}/{1}), required: {2}/{1}.".format( in_favor, len(vk['participants']) + 1, vk['required_votes'])) for mid in to_remove: log( "REMOVING VOTEKICK: {} {} len:{} keys:{}".format( mid, type(mid), len(cfg.VOTEKICKS), ', '.join([ str(k) + " " + str(type(k)) for k in cfg.VOTEKICKS.keys() ])), guild) del cfg.VOTEKICKS[mid] print("... len:{} keys:{}".format( len(cfg.VOTEKICKS), ', '.join([ str(k) + " " + str(type(k)) for k in cfg.VOTEKICKS.keys() ]))) end_time = time() fn_name = "check_votekicks" cfg.TIMINGS[fn_name] = end_time - start_time if cfg.TIMINGS[fn_name] > 20: await func.log_timings(client, fn_name)
async def on_voice_state_update(member, before, after): if not client.is_ready(): return if before.channel == after.channel: # Ignore mute/unmute events return guild = member.guild guilds = func.get_guilds(client) if guild not in guilds: return settings = utils.get_serv_settings(guild) if not settings['enabled']: return if not settings['auto_channels']: # No channels have been set up, do nothing return secondaries = func.get_secondaries(guild, settings) join_channels = func.get_join_channels(guild, settings) if after.channel: if after.channel.id in settings['auto_channels']: await func.create_secondary(guild, after.channel, member) elif after.channel.id in secondaries: if after.channel.name != "⌛": await func.update_text_channel_role(guild, member, after.channel, "join") bitrate = await func.update_bitrate(after.channel, settings) await func.server_log( guild, "➡ {} (`{}`) joined \"**{}**\" (`{}`)".format( func.user_hash(member), member.id, after.channel.name, after.channel.id) + (" ⤏ {}kbps".format(round(bitrate / 1000)) if bitrate else ""), 3, settings) elif after.channel.id in join_channels: sv = join_channels[after.channel.id] msg_channel = guild.get_channel(sv['msgs']) vc = guild.get_channel(sv['vc']) creator = guild.get_member(sv['creator']) if msg_channel and creator and vc: try: m = await msg_channel.send( "Hey {},\n{} would like to join your private voice channel. React with:\n" "• ✅ to **allow**.\n" "• ❌ to **deny** this time.\n" "• ⛔ to deny and **block** future requests from them.". format(creator.mention, member.mention)) cfg.JOINS_IN_PROGRESS[member.id] = { "creator": creator, "requester": member, "vc": vc, "jc": after.channel, "msg": m, "mid": m.id } log( "{} ({}) requests to join {}".format( member.display_name, member.id, creator.display_name), guild) try: await m.add_reaction('✅') await m.add_reaction('❌') await m.add_reaction('⛔') except discord.errors.Forbidden: pass except Exception as e: log( "Failed to send join-request message ({})".format( type(e).__name__), guild) else: cfg.JOINS_IN_PROGRESS[member.id] if before.channel: if before.channel.id in secondaries: members = [m for m in before.channel.members if not m.bot] bitrate = None if members: await func.update_text_channel_role(guild, member, before.channel, "leave") bitrate = await func.update_bitrate(before.channel, settings, user_left=member) await func.server_log( guild, "🚪 {} (`{}`) left \"**{}**\" (`{}`)".format( func.user_hash(member), member.id, before.channel.name, before.channel.id) + (" [bitrate: {}kbps]".format(round(bitrate / 1000)) if bitrate else ""), 3, settings) if not members: await func.delete_secondary(guild, before.channel)
async def on_message(message): if not client.is_ready(): return if message.author.bot: # Don't respond to self or bots return guilds = func.get_guilds(client) admin = ADMIN admin_channels = [] if admin is not None: admin_channels = [admin.dm_channel] if 'admin_channel' in cfg.CONFIG: admin_channels.append(ADMIN_CHANNEL) if message.channel in admin_channels: split = message.content.split(' ') cmd = split[0].split('\n')[0].lower() params_str = message.content[len(cmd):].strip() params = params_str.split(' ') if cmd == 'stop': m = utils.strip_quotes(params_str) success = await reload_modules(m) await func.react(message, '⌛' if success else '❌') await asyncio.sleep(3) await func.react(message, '🌑' if success else '❌') await asyncio.sleep(1) await func.react(message, '🌓' if success else '❌') await asyncio.sleep(1) await func.react(message, '🌔' if success else '❌') await asyncio.sleep(1) await func.react(message, '✅' if success else '❌') await message.channel.send("restarting..") await client.logout() else: ctx = { 'client': client, 'admin': admin, 'message': message, 'params': params, 'params_str': params_str, 'guilds': guilds, 'LAST_COMMIT': LAST_COMMIT, } await admin_commands.admin_command(cmd, ctx) return if not message.guild: # DM if 'help' in message.content and len( message.content) <= len("@Auto Voice Channels help"): await message.channel.send( "Sorry I don't respond to commands in DMs, " "you need to type the commands in a channel in your server.\n" "If you've tried that already, then make sure I have the right permissions " "to see and reply to your commands in that channel.") else: await admin_channels[-1].send( embed=discord.Embed(title="DM from **{}** [`{}`]:".format( message.author.name, message.author.id), description=message.content)) return if message.guild not in guilds: return prefix_m = message.guild.me.mention prefix_mx = "<@!" + prefix_m[2:] if message.guild.id in cfg.PREFIXES: prefix_p = cfg.PREFIXES[message.guild.id] else: prefix_p = 'vc/' prefix = None if message.content.startswith(prefix_m): prefix = prefix_m print_prefix = "@{} ".format(message.guild.me.display_name) elif message.content.startswith(prefix_mx): prefix = prefix_mx print_prefix = "@{} ".format(message.guild.me.display_name) elif message.content.lower().startswith(prefix_p.lower()): prefix = prefix_p print_prefix = prefix_p # Commands if prefix: msg = message.content[len(prefix):].strip() # Remove prefix split = msg.split(' ') cmd = split[0].lower() params = split[1:] params_str = ' '.join(params) clean_paramstr = ' '.join( message.clean_content[len(prefix):].strip().split(' ')[1:]) guild = message.guild channel = message.channel settings = utils.get_serv_settings(guild) if channel.id not in func.get_voice_context_channel_ids( guild, settings): settings['last_channel'] = channel.id utils.set_serv_settings(guild, settings) ctx = { 'client': client, 'guild': guild, 'prefix': prefix, 'print_prefix': print_prefix, 'prefix_p': prefix_p, 'command': cmd, 'gold': func.is_gold(guild), 'sapphire': func.is_sapphire(guild), 'settings': settings, 'message': message, 'channel': channel, 'clean_paramstr': clean_paramstr, } # Restricted commands perms = message.author.permissions_in(channel) perms_required = [ perms.manage_channels, perms.manage_roles, ] ctx['admin'] = all(perms_required) success, response = await commands.run(cmd, ctx, params) if success or response != "NO RESPONSE": log("CMD {}: {}".format("Y" if success else "F", msg), guild) if success: if response: if response != "NO RESPONSE": await echo(response, channel, message.author) else: await func.react(message, '✅') else: if response != "NO RESPONSE": await func.react(message, '❌') if response: await echo(response, channel, message.author)
async def check_votekicks(client): await client.wait_until_ready() check_votekicks.last_run = datetime.now(pytz.utc) start_time = time() if client.is_ready(): to_remove = [] votekicks = list(cfg.VOTEKICKS.keys()) for mid in votekicks: try: vk = cfg.VOTEKICKS[mid] except KeyError: print("Ignoring error:") traceback.print_exc() continue guild = vk['message'].guild guilds = func.get_guilds(client) if guild not in guilds: return in_favor = len(vk['in_favor']) if in_favor >= vk['required_votes']: to_remove.append(mid) try: await vk['offender'].move_to(None) # Kick except Exception as e: try: await vk['message'].edit( content= "‼ **Votekick** failed - A `{}` error was encountered." .format(type(e).__name__)) except discord.errors.NotFound: continue continue banned = True try: await vk['voice_channel'].set_permissions(vk['offender'], connect=False) except discord.errors.Forbidden: banned = False try: await vk['message'].edit(content=( "‼ **Votekick** ‼\n" "{} was **kicked** from {}'s channel{}.{}".format( vk['offender'].mention, vk['initiator'].mention, ((", but could not be banned from the channel as I don't have the" "*Manage Roles* permission.") if not banned else ""), ("\nReason: **{}**".format(vk['reason']) if vk['reason'] else "")))) except discord.errors.NotFound: continue await func.server_log( guild, "👢 {} (`{}`) has been **kicked** from {}'s channel.". format(func.user_hash(vk['offender']), vk['offender'].id, vk['initiator']), 1, utils.get_serv_settings(guild)) elif time() > vk['end_time'] + 5: to_remove.append(mid) try: await vk['message'].edit( content= "‼ **Votekick** timed out: Insufficient votes received " "({0}/{1}), required: {2}/{1}.".format( in_favor, len(vk['participants']) + 1, vk['required_votes'])) except discord.errors.NotFound: continue for mid in to_remove: del cfg.VOTEKICKS[mid] end_time = time() fn_name = "check_votekicks" cfg.TIMINGS[fn_name] = end_time - start_time if cfg.TIMINGS[fn_name] > 20: await func.log_timings(client, fn_name)