async def execute(ctx, params): params_str = ' '.join(params) guild = ctx['guild'] settings = ctx['settings'] author = ctx['message'].author new_word = params_str.replace('\n', ' ') # Can't have newlines in channel name. new_word = utils.strip_quotes(new_word) previous_word = ("text" if 'text_channel_name' not in settings else func.esc_md(settings['text_channel_name'])) if not new_word: return False, ( "You need to define a new name, e.g. `{}textchannelname links` to make " "**links** shown instead of **{}**.".format( ctx['print_prefix'], previous_word)) settings['text_channel_name'] = new_word utils.set_serv_settings(guild, settings) e_new_word = func.esc_md(new_word) await func.server_log( guild, "💬 {} (`{}`) définissez le nom \"text\" du serveur sur **{}**". format(func.user_hash(author), author.id, e_new_word), 2, settings) for p, pv in settings['auto_channels'].items(): for s, sv in pv['secondaries'].items(): if 'tc' in sv: tc = guild.get_channel(sv['tc']) try: await tc.edit(name=utils.nice_cname(new_word)) except discord.errors.Forbidden: pass return True, ( "Done! From now on I'll use **{}** instead of **{}**.".format( e_new_word, previous_word))
async def execute(ctx, params): params_str = ' '.join(params) guild = ctx['guild'] settings = ctx['settings'] author = ctx['message'].author new_word = params_str.replace('\n', ' ') # Can't have newlines in channel name. new_word = utils.strip_quotes(new_word) previous_word = "General" if 'general' not in settings else func.esc_md( settings['general']) if not new_word: return False, ( "You need to define a new word, e.g. `{}general Lounge` to make " "**Lounge** shown instead of **{}**.".format( ctx['print_prefix'], previous_word)) settings['general'] = new_word utils.set_serv_settings(guild, settings) e_new_word = func.esc_md(new_word) await func.server_log( guild, "🎮 {} (`{}`) set the server's \"General\" word to **{}**".format( func.user_hash(author), author.id, e_new_word), 2, settings) return True, ( "Done! From now on I'll use **{}** instead of **{}**.".format( e_new_word, previous_word))
async def execute(ctx, params): guild = ctx['guild'] settings = ctx['settings'] vc = ctx['voice_channel'] for p, pv in settings['auto_channels'].items(): for s, sv in pv['secondaries'].items(): if s == vc.id: if 'priv' not in sv or not sv['priv']: return False, ("Your channel is already public. " "Use `{}private` to make it private instead.".format(ctx['print_prefix'])) try: await vc.set_permissions(guild.default_role, connect=True) except discord.errors.Forbidden: return False, ("I don't have permission to do that." "Please make sure I have the *Manage Roles* permission in this server and category.") settings['auto_channels'][p]['secondaries'][s]['priv'] = False try: jcid = settings['auto_channels'][p]['secondaries'][s]['jc'] del settings['auto_channels'][p]['secondaries'][s]['jc'] except KeyError: jcid = 0 utils.set_serv_settings(guild, settings) if s in cfg.PRIV_CHANNELS: del cfg.PRIV_CHANNELS[s] try: jc = guild.get_channel(jcid) if jc: await jc.delete() except discord.errors.Forbidden: return False, "You channel is now public, but I'm not allowed to delete your **⇩ Join** channel." return True, "Your channel is now public!" return False, "It doesn't seem like you're in a voice channel anymore."
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 execute(ctx, params): params = [p for p in params if p] # Remove any empty params guild = ctx['guild'] settings = ctx['settings'] author = ctx['message'].author cmd = params[0] from commands import commands if cmd not in commands: if 'dcnf' not in ctx['settings'] or ctx['settings']['dcnf'] is False: return False, ( "`{}` is not a recognized command. Run '**{}help**' " "to get a list of commands".format(cmd, ctx['print_prefix'])) else: return False, "NO RESPONSE" if params[1].lower() == "none": if 'restrictions' in settings: try: del settings['restrictions'][cmd] utils.set_serv_settings(guild, settings) await func.server_log( guild, "✋ {} (`{}`) removed all restrictions for the `{}` command." .format(func.user_hash(author), author.id, cmd), 2, settings) return True, "Done! Removed restrictions for the `{}` command.".format( cmd) except KeyError: return False, "There are no restrictions for the `{}` command.".format( cmd) else: return False, "There are no command restrictions." roles = [] for r in params[1:]: try: role = guild.get_role(int(r)) if role is None: raise ValueError except ValueError: return False, ( "`{}` is not a valid role ID. " "Use `{}listroles` to get a list of roles and their IDs.". format(r, ctx['print_prefix'])) roles.append(role) if 'restrictions' not in settings: settings['restrictions'] = {} settings['restrictions'][cmd] = [r.id for r in roles] utils.set_serv_settings(guild, settings) roles_str = ', '.join("**{}**".format(r.name) for r in roles) await func.server_log( guild, "✋ {} (`{}`) set a restriction for the `{}` command: {}".format( func.user_hash(author), author.id, cmd, roles_str), 2, settings) return True, ( "Done! From now on only users with one of the following roles can use the `{}` command: {}" "".format(cmd, roles_str))
async def execute(ctx, params): params_str = ' '.join(params) guild = ctx['guild'] settings = ctx['settings'] author = ctx['message'].author new_prefix = utils.strip_quotes(params_str) if not new_prefix: return False, ("You need to define a new prefix, e.g. `{}prefix avc-` to make " "`avc-` the new prefix.".format(ctx['print_prefix'])) disallowed_characters = ['\n', '\t', '`'] for c in disallowed_characters: if c in new_prefix: return False, "Your prefix can't contain **new lines**, **tab characters**, or **\`**." response = ("Done! My prefix in your server is now `{0}`. Try running `{0}ping` to test it out.\n" "Remember, you can always mention me instead of using my prefix (e.g: **{1} ping**)" ".".format(new_prefix, ctx['message'].guild.me.mention)) if len(new_prefix) == 1: response += ("\n\n:information_source: Note: If you use the **same prefix as another bot**, " "you should also run `{}dcnf` to prevent error messages when using that bot's commands." "".format(new_prefix)) cfg.PREFIXES[guild.id] = new_prefix settings['prefix'] = new_prefix utils.set_serv_settings(guild, settings) await func.server_log( guild, "💬 {} (`{}`) set the server's prefix to `{}`".format( func.user_hash(author), author.id, new_prefix ), 1, settings) return True, response
async def execute(ctx, params): params_str = ' '.join(params) guild = ctx['guild'] settings = ctx['settings'] author = ctx['message'].author nick = utils.strip_quotes(params_str) if nick.lower() == 'reset': try: del settings['custom_nicks'][str(author.id)] utils.set_serv_settings(guild, settings) except KeyError: return False, "You haven't set a custom nickname." return True, "Your custom nickname has been removed." if 'custom_nicks' not in settings: settings['custom_nicks'] = {} settings['custom_nicks'][str(author.id)] = nick utils.set_serv_settings(guild, settings) await func.server_log( guild, "🙋 {} (`{}`) set their custom nick to {}".format( func.user_hash(author), author.id, nick), 2, settings) return True, ( "Done! Channels that show the creator's name will now call you **{}** instead of **{}**." .format(nick, author.display_name))
async def execute(ctx, params): guild = ctx['guild'] settings = ctx['settings'] settings['uniquenames'] = not settings[ 'uniquenames'] if 'uniquenames' in settings else True utils.set_serv_settings(guild, settings) return True, ( "Done. From now on, custom channel names {}. Run this command again to swap back." .format("will **have to be unique**" if settings['uniquenames'] else "**no longer** have to be unique"))
async def execute(ctx, params): settings = ctx['settings'] guild = ctx['guild'] if settings['enabled']: return False, "Already enabled. Use '{}disable' to turn off.".format(ctx['print_prefix']) else: log("Enabling", guild) settings['enabled'] = True utils.set_serv_settings(guild, settings) return True, "Enabling auto voice channels. Turn off with '{}disable'.".format(ctx['print_prefix'])
async def on_guild_remove(guild): num_members = len([m for m in guild.members if not m.bot]) if cfg.SAPPHIRE_ID is None: settings = utils.get_serv_settings(guild) settings['left'] = datetime.now( pytz.timezone( cfg.CONFIG['log_timezone'])).strftime("%Y-%m-%d %H:%M") utils.set_serv_settings(guild, settings) log("Left guild {} `{}` with {} members".format( guild.name, guild.id, num_members)) await func.admin_log( ":new_moon: Left: **{}** (`{}`) - **{}** members".format( func.esc_md(guild.name), guild.id, num_members), client)
async def execute(ctx, params): guild = ctx['guild'] settings = ctx['settings'] asip = not settings['asip'] if 'asip' in settings else True settings['asip'] = asip utils.set_serv_settings(guild, settings) if asip: r = ( "OK, from now on I'll assume channel members without any activity/status are also playing the same thing " "as most other members in the channel.") else: r = ("ASIP is now **OFF** :)") return True, r
async def on_guild_join(guild): num_members = len([m for m in guild.members if not m.bot]) important = num_members > 5000 if cfg.SAPPHIRE_ID is None: settings = utils.get_serv_settings(guild) settings['left'] = False utils.set_serv_settings(guild, settings) log("Joined guild {} `{}` with {} members".format( guild.name, guild.id, num_members)) await func.admin_log( ":bell:{} Joined: **{}** (`{}`) - **{}** members".format( utils.guild_size_icon(num_members), func.esc_md(guild.name), guild.id, num_members), client, important=important)
async def execute(ctx, params): params_str = ' '.join(params) guild = ctx['guild'] settings = ctx['settings'] author = ctx['message'].author bitrate = utils.strip_quotes(params_str) v = author.voice in_vc = v is not None and v.channel.id in func.get_secondaries( guild, settings) if bitrate.lower() == 'reset': try: del settings['custom_bitrates'][str(author.id)] utils.set_serv_settings(guild, settings) except KeyError: return False, "You haven't set a custom bitrate." if in_vc: await func.update_bitrate(v.channel, settings, reset=True) return True, "Your custom bitrate has been reset, the channel default will be used for you from now on." try: bitrate = float(bitrate) except ValueError: return False, "`{}` is not a number.".format(bitrate) if bitrate < 8: return False, "The bitrate must be higher than 8." if bitrate * 1000 > guild.bitrate_limit: return False, "{} is higher than the maximum bitrate in this server ({}).".format( bitrate, guild.bitrate_limit / 1000) if 'custom_bitrates' not in settings: settings['custom_bitrates'] = {} settings['custom_bitrates'][str(author.id)] = bitrate utils.set_serv_settings(guild, settings) if in_vc: await func.update_bitrate(v.channel, settings) await func.server_log( guild, "🎚 {} (`{}`) set their custom bitrate to {}kbps".format( func.user_hash(author), author.id, bitrate), 2, settings) return True, ( "Done! From now on, channels you join will have their bitrate set to {}kbps.\n" "If multiple users in the channel have set custom bitrates, the average will be used.\n\n" "Use `{}channelinfo` to check the current bitrate of your channel.". format(bitrate, ctx['print_prefix']))
async def execute(ctx, params): guild = ctx['guild'] settings = ctx['settings'] textchannels = not settings[ 'text_channels'] if 'text_channels' in settings else True settings['text_channels'] = textchannels utils.set_serv_settings(guild, settings) if textchannels: r = "OK, from now on I'll create private text channels for each voice chat." perms = guild.me.permissions_in(ctx['channel']) if not perms.manage_roles: r += ( "\n:warning: Make sure I have the **Manage Roles** permission in this server and any categories that " "contain my voice channels, otherwise I won't be able to make the text channels." ) else: r = "Text channel creation is now **OFF** :)" return True, r
async def execute(ctx, params): guild = ctx['guild'] settings = ctx['settings'] author = ctx['message'].author vc = ctx['voice_channel'] for p, pv in settings['auto_channels'].items(): for s, sv in pv['secondaries'].items(): if s == vc.id: if 'priv' in sv and sv['priv']: return False, ( "Your channel is already private. " "Use `{}public` to make it public again.".format( ctx['print_prefix'])) try: await vc.set_permissions(author, connect=True) await vc.set_permissions(guild.default_role, connect=False) except discord.errors.Forbidden: return False, ( "I don't have permission to do that." "Please make sure I have the *Manage Roles* permission in this server and category." ) settings['auto_channels'][p]['secondaries'][s]['priv'] = True settings['auto_channels'][p]['secondaries'][s]['msgs'] = ctx[ 'channel'].id utils.set_serv_settings(guild, settings) cfg.PRIV_CHANNELS[s] = { 'creator': author, 'voice_channel': vc, 'primary_id': p, 'text_channel': ctx['channel'], 'guild_id': guild.id, 'request_time': time(), 'prefix': ctx['print_prefix'], } return True, ( "Your channel is now private!\n" "A \"**⇧ Join {}**\" channel will appear below your one shortly. " "When someone enters this channel to request to join you, " "I'll send a message here asking you to approve or deny their request.\n" "Use `{}public` to make it public again." "".format(func.esc_md(author.display_name), ctx['print_prefix'])) return False, "It doesn't seem like you're in a voice channel anymore."
async def execute(ctx, params): guild = ctx['guild'] settings = ctx['settings'] author = ctx['message'].author vc = ctx['voice_channel'] for p, pv in settings['auto_channels'].items(): for s, sv in pv['secondaries'].items(): if s == vc.id: if 'priv' in sv and sv['priv']: return False, ( "Votre chaîne est déjà privée." "Utilisez `{}public` pour le rendre à nouveau public.". format(ctx['print_prefix'])) try: await vc.set_permissions(author, connect=True) await vc.set_permissions(guild.default_role, connect=False) except discord.errors.Forbidden: return False, ( "I don't have permission to do that." "Please make sure I have the *Manage Roles* permission in this server and category." ) settings['auto_channels'][p]['secondaries'][s]['priv'] = True settings['auto_channels'][p]['secondaries'][s]['msgs'] = ctx[ 'channel'].id utils.set_serv_settings(guild, settings) cfg.PRIV_CHANNELS[s] = { 'creator': author, 'voice_channel': vc, 'primary_id': p, 'text_channel': ctx['channel'], 'guild_id': guild.id, 'request_time': time(), 'prefix': ctx['print_prefix'], } return True, ( "Votre canal est désormais privée!\n" "Un canal \"**⇩ Rejoignez {}**\" chaîne apparaîtra au-dessus de votre chaîne sous peu. " "Lorsque quelqu'un entre sur ce canal pour demander à vous rejoindre, " "Je vais envoyer un message ici vous demandant d'approuver ou de refuser leur demande.\n" "Utilisez `{}public` pour le rendre à nouveau public." "".format(func.esc_md(author.display_name), ctx['print_prefix'])) return False, "It doesn't seem like you're in a voice channel anymore."
async def execute(ctx, params): params_str = ' '.join(params) guild = ctx['guild'] settings = ctx['settings'] param = utils.strip_quotes(params_str) if param.lower() == 'none': try: del settings['stct'] utils.set_serv_settings(guild, settings) return ( True, "From now on, new text channels will only be visible to the channel's occupants. " "Existing channels will not be affected.") except KeyError: return False, "Text channels are already invisible to everyone but the channel's occupants." role = None try: param = int(param) role = guild.get_role(param) except ValueError: if param == "@everyone": role = guild.default_role else: for r in guild.roles: if r.mention == param: role = r break if role is None: return False, "I can't find that role. You need to specify either the role ID, or `@mention` it." settings['stct'] = role.id utils.set_serv_settings(guild, settings) return ( True, "From now on, new text channels can be seen by users with the \"{}\" role. " "Existing channels will not be affected.".format( discord.utils.escape_mentions(role.name)))
async def execute(ctx, params): params_str = ' '.join(params) guild = ctx['guild'] settings = ctx['settings'] gsplit = params_str.split('>>') if len(gsplit) != 2 or not gsplit[0] or not gsplit[-1]: return False, ( "Incorrect syntax for alias command. Should be: `{}alias [Actual game name] >> " "[New name]` (without square brackets).".format( ctx['print_prefix'])) else: gname = utils.strip_quotes(gsplit[0]) aname = utils.strip_quotes(gsplit[1]) if gname in settings['aliases']: oaname = settings['aliases'][gname] response = "'{}' already has an alias ('{}'), it will be replaced with '{}'.".format( gname, oaname, aname) else: response = "'{}' will now be shown as '{}'.".format(gname, aname) settings['aliases'][gname] = aname utils.set_serv_settings(guild, settings) return True, response
async def execute(ctx, params): mode = ctx['clean_paramstr'].lower() guild = ctx['guild'] settings = ctx['settings'] c = ctx['voice_channel'] options = { 'primary': 'PRIMARY', 'parent': 'PRIMARY', 'default': 'PRIMARY', 'category': 'CATEGORY', } if mode not in options: try: mode = int(mode) except ValueError: return False, ( "\"{}\" is not a valid option or channel ID. Please run `{}help inheritpermissions` " "to learn how to use this command.".format( ctx['clean_paramstr'], ctx['print_prefix'])) perm_channel = guild.get_channel(mode) if perm_channel is None: return False, ( "`{}` is not a valid channel ID, it looks to me like that channel doesn't exist. " "Maybe I don't have permission to see it?".format(mode)) if not isinstance(perm_channel, discord.VoiceChannel): return False, "Sorry, that channel is not a voice channel." else: mode = options[mode] if mode == 'CATEGORY' and c.category is None: return False, "Your channel is not in a category." for p in settings['auto_channels']: for sid in settings['auto_channels'][p]['secondaries']: if sid == c.id: settings['auto_channels'][p]['inheritperms'] = mode utils.set_serv_settings(guild, settings) return True, "Done! Note that this will only affect new channels."
async def execute(ctx, params): alias = ' '.join(params).strip() guild = ctx['guild'] settings = ctx['settings'] if not settings['aliases']: return True, "You haven't set any aliases yet." keys_to_delete = [] for a, av in settings['aliases'].items(): if (alias == a or alias == av) and a not in keys_to_delete: keys_to_delete.append(a) if keys_to_delete: for k in keys_to_delete: del settings['aliases'][k] utils.set_serv_settings(guild, settings) return True, ("Removed {} alias{}".format( len(keys_to_delete), "es" if len(keys_to_delete) != 1 else "")) else: return False, ( "There is no alias for the game `{}` - please be exact, this operation is case-sensitive. " "Use `{}aliases` to get a list of the aliases in this server.". format(alias, ctx['print_prefix']))
async def admin_command(cmd, ctx): client = ctx['client'] message = ctx['message'] channel = message.channel params = ctx['params'] params_str = ctx['params_str'] guilds = ctx['guilds'] LAST_COMMIT = ctx['LAST_COMMIT'] if cmd == 'log': logfile = "log{}.txt".format("" if cfg.SAPPHIRE_ID is None else cfg.SAPPHIRE_ID) if not os.path.exists(logfile): await channel.send("No log file") return with open(logfile, 'r', encoding="utf8") as f: data = f.read() data = data[-10000:] # Drop everything but the last 10k characters to make string ops quicker data = data.replace(' Creating channel for ', ' ✅') data = data.replace(' Deleting ', ' ❌') data = data.replace(' Renaming ⌛ to ', ' ⏩ ') data = data.replace(' Renaming ', ' 🔄') data = data.replace(' to ', ' ⏩ ') data = data.replace(' CMD Y: ', ' C✔ ') data = data.replace(' CMD F: ', ' C✖ ') data = data.replace(" creating channels too quickly", " creating channels too quickly❗❗") data = data.replace(" where I don't have permissions", " where I don't have permissions❗❗") data = data.replace("Traceback (most recent", "❗❗Traceback (most recent") data = data.replace("discord.errors.", "❗❗discord.errors.") data = data.replace("Remembering channel ", "❗❗Remembering ") data = data.replace("New tickrate is ", "🕐") data = data.replace(", seed interval is ", " 🕐") data = data.replace(' ', ' ') # Reduce indent to save character space today = datetime.now(pytz.timezone(cfg.CONFIG['log_timezone'])).strftime("%Y-%m-%d") data = data.replace(today, 'T') character_limit = 2000 - 17 # 17 for length of ```autohotkey\n at start and ``` at end. data = data[character_limit * -1:] data = data.split('\n', 1)[1] lines = data.split('\n') for i, l in enumerate(lines): # Fake colon (U+02D0) to prevent highlighting the line if " ⏩" in l: lines[i] = l.replace(':', 'ː') elif l.startswith('T '): if '[' in l: s = l.split('[', 1) lines[i] = s[0] + '[' + s[1].replace(':', 'ː') data = '\n'.join(lines) data = '```autohotkey\n' + data data = data + '```' await channel.send(data) if cmd == 'stats': r = await channel.send(". . .") t1 = message.created_at t2 = r.created_at response_time = (t2 - t1).total_seconds() num_users = 0 for g in guilds: num_users += len([m for m in g.members if not m.bot]) lines_of_code = 0 for f in os.listdir(cfg.SCRIPT_DIR): if f.lower().endswith('.py'): lines_of_code += utils.count_lines(os.path.join(cfg.SCRIPT_DIR, f)) elif f == "commands": for sf in os.listdir(os.path.join(cfg.SCRIPT_DIR, f)): if sf.lower().endswith('.py'): lines_of_code += utils.count_lines(os.path.join(cfg.SCRIPT_DIR, f, sf)) cpu = psutil.cpu_percent() mem = psutil.virtual_memory() disk = psutil.disk_usage('/') await r.edit(content=( "Servers: **{tot_servs}** (A:{active_servs} S:{shards}) \t " "Users: **{users}** \t Channels: **{channels}** \n" "Response time: **{rt}** \t Tick rate: **{tr}** \t Tick time: **{tt}** | **{gtt}**\n" "CPU: **{cpu}%** \t MEM: **{memg} ({memp}%)** \t DISK: **{diskg} ({diskp}%)**\n" "**Last commit:** {commit}\n" "**Lines of code:** {lines}\n" "**Timings:** \n{timings}".format( tot_servs=len(guilds), active_servs=utils.num_active_guilds(guilds), shards=utils.num_shards(guilds), users=num_users, channels=utils.num_active_channels(guilds), rt="{0:.2f}s".format(response_time), tr="{0:.1f}s".format(cfg.TICK_RATE), tt="{0:.2f}s".format(cfg.TICK_TIME), gtt="{0:.2f}s".format(cfg.G_TICK_TIME), cpu=cpu, memg="{0:.1f}GB".format(mem.used / 1024 / 1024 / 1024), memp=round(mem.percent), diskg="{0:.1f}GB".format(disk.used / 1024 / 1024 / 1024), diskp=round(disk.percent), commit=LAST_COMMIT, lines=lines_of_code, timings=utils.format_timings() ) )) if cmd == 'ping': r = await channel.send(". . .") t1 = message.created_at t2 = r.created_at response_time = (t2 - t1).total_seconds() e = '🔴🔴🔴' if response_time > 5 else ('🟠🟠' if response_time > 1 else '🟢') await r.edit(content="**{0} {1:.1f}s**".format(e, response_time)) if cmd == 'top': top_guilds = [] for g in guilds: s = func.get_secondaries(g) top_guilds.append({"name": g.name, "size": len([m for m in g.members if not m.bot]), "num": len(s) if s is not None else 0}) top_guilds = sorted(top_guilds, key=lambda x: x['num'], reverse=True)[:10] r = "**Top Guilds:**" for g in top_guilds: r += "\n`{}` {}: \t**{}**".format( g['size'], func.esc_md(g['name']), g['num'] ) r += "\n\n**{}**".format(utils.num_active_channels(guilds)) await channel.send(r) if cmd == 'patrons': if patreon_info is None: await channel.send(content='❌') return patrons = patreon_info.fetch_patrons(force_update=True) if not patrons: await channel.send(content='❌') return fields = {} auths = patreon_info.update_patron_servers(patrons) for p, pv in patrons.items(): pu = client.get_user(p) if pu is not None: pn = pu.name else: pn = "Unknown" gn = "" if str(p) in auths: for s in auths[str(p)]['servers']: gn += "`{}` ".format(s) if 'extra_gold' in auths[str(p)]: for s in auths[str(p)]['extra_gold']: gn += "+g`{}` ".format(s) fields["`{}` **{}** {}".format(p, pn, cfg.TIER_ICONS[pv])] = gn try: for field_chunk in utils.dict_chunks(fields, 25): e = discord.Embed(color=discord.Color.from_rgb(205, 220, 57)) e.title = "{} Patrons".format(len(field_chunk)) for f, fv in field_chunk.items(): fv = fv if fv else "None" e.add_field(name=f, value=fv) await channel.send(embed=e) except: await channel.send(traceback.format_exc()) await func.react(message, '❌') if cmd == 'sapphiredebug': if cfg.SAPPHIRE_ID is None: await channel.send(content='❌ Not a sapphire') return if patreon_info is None: await channel.send(content='❌ No patreon_info') return auths = utils.read_json(os.path.join(cfg.SCRIPT_DIR, "patron_auths.json")) initiator_id = cfg.CONFIG["sapphires"][str(cfg.SAPPHIRE_ID)]['initiator'] msg = ("Sapphire ID: {}\n" "User: `{}`\n" "Actual guilds: {}\n" "Config guilds: {}\n" "Authenticated guilds: {}\n" "get_guilds: {}".format( cfg.SAPPHIRE_ID, initiator_id, ", ".join(['`' + str(g.id) + '`' for g in client.guilds]), ", ".join(['`' + str(g) + '`' for g in cfg.CONFIG["sapphires"][str(cfg.SAPPHIRE_ID)]['servers']]), ", ".join(['`' + str(g) + '`' for g in auths[str(initiator_id)]['servers']]), ", ".join(['`' + str(g.id) + '`' for g in func.get_guilds(client)])) ) await channel.send(msg) if cmd == 'status': g = utils.strip_quotes(params_str) if not g: await func.react(message, '❌') return try: await client.change_presence( activity=discord.Activity(name=g, type=discord.ActivityType.watching) ) await func.react(message, '✅') except: await channel.send(traceback.format_exc()) await func.react(message, '❌') if cmd == 'settings': gid = utils.strip_quotes(params_str) try: int(gid) except ValueError: for x in guilds: if x.name == gid: gid = str(x.id) break fname = gid + '.json' fp = os.path.join(cfg.SCRIPT_DIR, "guilds", fname) if os.path.exists(fp): gid = int(gid) g = client.get_guild(gid) head = "**{}** `{}`{}".format(g.name, gid, ("✅" if g in func.get_guilds(client) else "❌")) head += "💎" if func.is_sapphire(gid) else ("💳" if func.is_gold(gid) else "") s = head s += "\n```json\n" with open(fp, 'r') as f: file_content = f.read() s += file_content s += '```' try: await channel.send(s) except discord.errors.HTTPException: # Usually because message is over character limit haste_url = await utils.hastebin(file_content) await channel.send("{}\n{}".format(head, haste_url)) else: await func.react(message, '❌') if cmd == 'refetch': gid = utils.strip_quotes(params_str) try: gid = int(gid) except ValueError: await func.react(message, '❌') return g = client.get_guild(gid) if g is None: await func.react(message, '❓') return utils.get_serv_settings(g, force_refetch=True) await func.react(message, '✅') return if cmd == 'disable': try: g = client.get_guild(int(utils.strip_quotes(params_str))) settings = utils.get_serv_settings(g) settings['enabled'] = False utils.set_serv_settings(g, settings) log("Force Disabling", g) await func.react(message, '✅') except: await channel.send(traceback.format_exc()) await func.react(message, '❌') if cmd == 'enable': try: g = client.get_guild(int(utils.strip_quotes(params_str))) settings = utils.get_serv_settings(g) settings['enabled'] = True utils.set_serv_settings(g, settings) log("Force Enabling", g) await func.react(message, '✅') except: await channel.send(traceback.format_exc()) await func.react(message, '❌') if cmd == 'info': cid = utils.strip_quotes(params_str) try: c = client.get_channel(int(cid)) members = [m.display_name + " \t {}".format(utils.debug_unicode(m.display_name)) for m in c.members] games = [] for m in c.members: if m.activity: games.append(m.activity.name + " \t {}".format(utils.debug_unicode(m.activity.name))) s = "**__Server:__** {} `{}`\n**__Name:__** {}\n{}\n\n".format( c.guild.name, c.guild.id, c.name, utils.debug_unicode(c.name) ) if c.id in cfg.ATTEMPTED_CHANNEL_NAMES: s += "**__Attempted Name:__** {}\n{}\n\n".format( cfg.ATTEMPTED_CHANNEL_NAMES[c.id], utils.debug_unicode(cfg.ATTEMPTED_CHANNEL_NAMES[c.id]) ) s += "**__{} Members:__**\n".format(len(members)) s += '\n'.join(members) s += '\n\n**__{} Games:__**\n'.format(len(games)) s += '\n'.join(games) s = s.replace('\n\n\n', '\n\n') await channel.send(s) except: await channel.send(traceback.format_exc()) await func.react(message, '❌') if cmd == 'whois': uid = utils.strip_quotes(params_str) try: u = client.get_user(int(uid)) in_guilds = {} for g in client.guilds: if u in g.members: m = g.get_member(int(uid)) in_guilds[g.id] = { "guild_name": func.esc_md(g.name), "guild_size": g.member_count, "patron": "💎" if func.is_sapphire(g) else ("💳" if func.is_gold(g) else ""), "user_name": func.esc_md(m.display_name), "role": m.top_role.name, } if in_guilds: s = "**{}**".format(func.user_hash(u)) s += " \t :b: :regional_indicator_o: :regional_indicator_t:" if u.bot else "" can_dm = True try: await u.create_dm() can_dm = client.user.permissions_in(u.dm_channel).send_messages except discord.errors.Forbidden: can_dm = False s += " \t Can DM: {}".format('✅' if can_dm else '❌') for gid, g in in_guilds.items(): s += "\n{}`{}` **{}** (`{}`) \t {} ({})".format( g['patron'], gid, g['guild_name'], g['guild_size'], g['user_name'], g['role'] ) await echo(s, channel) else: await channel.send("¯\\_(ツ)_/¯") except: await channel.send(traceback.format_exc()) await func.react(message, '❌') if cmd == 'votekicks': try: readable = {} for k, kv in cfg.VOTEKICKS.items(): readable[k] = { "initiator": kv['initiator'].display_name, "participants": [m.display_name for m in kv['participants']], "required_votes": kv['required_votes'], "offender": kv['offender'].display_name, "reason": kv['reason'], "in_favor": [m.display_name for m in kv['in_favor']], "voice_channel": kv['voice_channel'].id, "message": kv['message'].id, "end_time": datetime.fromtimestamp(kv['end_time']).strftime("%Y-%m-%d %H:%M") } s = "```json\n" + json.dumps(readable, indent=1, sort_keys=True) + "```" print(s) try: await channel.send(s) except discord.errors.HTTPException: # Usually because message is over character limit haste_url = await utils.hastebin(s) await channel.send(haste_url) except: await channel.send(traceback.format_exc()) await func.react(message, '❌') if cmd == 'exit': attempts = 0 while attempts < 100: attempts += 1 if not cfg.WRITES_IN_PROGRESS: print("Exiting!") await client.close() sys.exit() break else: print("Failed to close", cfg.WRITES_IN_PROGRESS) await func.react(message, '❌') if cmd == 'loop': mode = params[0] loop_name = params[1] try: loop = ctx['loops'][loop_name] modes = { # Dict of possible loop functions/attrs as [fn, arg] 'current_loop': [loop.current_loop, None], 'next_iteration': [loop.next_iteration, None], 'next_run': [loop.next_iteration, None], # Alias 'start': [loop.start, client], 'stop': [loop.stop, None], 'cancel': [loop.cancel, None], 'restart': [loop.restart, client], 'is_being_cancelled': [loop.is_being_cancelled, None], 'last_run': [loop.last_run, None], } if mode not in modes: await func.react(message, '❓') return fn, arg = modes[mode] if callable(fn): if arg is None: r = fn() else: r = fn(arg) else: r = fn if r is not None: if isinstance(r, date): r = r.astimezone(pytz.timezone(cfg.CONFIG['log_timezone'])) await channel.send(r.strftime("%Y-%m-%d %H:%M:%S")) else: await channel.send(str(r)) await func.react(message, '✅') except: await channel.send(traceback.format_exc()) await channel.send("Loops: \n{}".format('\n'.join(ctx['loops'].keys()))) await func.react(message, '❌') if cmd == 'rename': try: cid = utils.strip_quotes(params[0]) c = client.get_channel(int(cid)) new_name = ' '.join(params[1:]) if not new_name: new_name = "⌛" await c.edit(name=new_name) except: await channel.send(traceback.format_exc()) await func.react(message, '❌') else: await func.react(message, '✅') log("{0} Force Renaming to {1}".format(cid[-4:], new_name), c.guild) if cmd == 'forget': try: cid = int(utils.strip_quotes(params[0])) c = client.get_channel(cid) settings = utils.get_serv_settings(c.guild) for p, pv in settings['auto_channels'].items(): tmp = settings['auto_channels'][p]['secondaries'].copy() for s, sv in pv['secondaries'].items(): if s == cid: del settings['auto_channels'][p]['secondaries'][s] break utils.set_serv_settings(c.guild, settings) except: await channel.send(traceback.format_exc()) await func.react(message, '❌') else: await func.react(message, '✅') if cmd == 'delete': try: cid = int(utils.strip_quotes(params[0])) c = client.get_channel(cid) await c.delete() except: await channel.send(traceback.format_exc()) await func.react(message, '❌') else: await func.react(message, '✅') if cmd == 'whisper': params_str = utils.strip_quotes(params_str) if '\n' not in params_str: await func.react(message, '❌') return uid, msg = params_str.split('\n', 1) try: u = await client.fetch_user(uid) except discord.errors.NotFound: await func.react(message, '❌') return if u.dm_channel is None: await u.create_dm() try: await u.dm_channel.send(msg) except: await channel.send(traceback.format_exc()) await func.react(message, '❌') else: await func.react(message, '✅') if cmd == 'cleanprimaries': try: n_primaries = 0 n_real_primaries = 0 for g in client.guilds: settings = utils.get_serv_settings(g) tmp = {} n_primaries += len(settings['auto_channels']) for p, pv in settings['auto_channels'].items(): c = g.get_channel(p) if c: tmp[p] = pv n_real_primaries += len(tmp) if len(settings['auto_channels']) != len(tmp): settings['auto_channels'] = tmp utils.set_serv_settings(g, settings) await channel.send("Cleaned {} of {} primaries".format(n_real_primaries, n_primaries)) except: await channel.send(traceback.format_exc()) await func.react(message, '❌') if cmd == 'leaveinactive': params_str = utils.strip_quotes(params_str) try: total_guilds = 0 inactive_guilds = 0 cfg.CONFIG['leave_inactive'] = [] for g in client.guilds: total_guilds += 1 if g and (not utils.guild_is_active(g) or g not in guilds): cfg.CONFIG['leave_inactive'].append(g.id) inactive_guilds += 1 if params_str == "go": try: await g.leave() except discord.errors.NotFound: pass if params_str == "go": await channel.send("Left {} of {} guilds.".format(inactive_guilds, total_guilds)) else: await channel.send("Will leave {} of {} guilds. " "Rerun command with 'go' at end to actually leave them.".format( inactive_guilds, total_guilds)) cfg.CONFIG['leave_inactive'] = [] except: await channel.send(traceback.format_exc()) await func.react(message, '❌')
async def execute(ctx, params): ctx['settings']['dcnf'] = True utils.set_serv_settings(ctx['guild'], ctx['settings']) return True, "OK, I'll no longer tell you if you typed a command incorrectly."
async def execute(ctx, params): ctx['settings']['dcnf'] = False utils.set_serv_settings(ctx['guild'], ctx['settings']) return True, "From now on I'll tell you if you typed a command incorrectly."
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 execute(ctx, params): guild = ctx['guild'] settings = ctx['settings'] previous_c = False if 'logging' not in settings else str( settings['logging']) was_previously_enabled = False tc = params[0].lower() if tc == previous_c: if tc is False: return False, "Logging is already disabled." was_previously_enabled = True if tc == 'off': await func.server_log(guild, "📕 Logging is now disabled", 1, settings) settings['logging'] = False utils.set_serv_settings(guild, settings) return True, None elif tc == 'here': tc = ctx['channel'] else: try: tc = int(tc) except ValueError: return False, ( "`{}` is not a valid channel ID. Get the ID by right clicking the channel, " "or just run `{}logging here` in that channel.".format( tc, ctx['print_prefix'])) tmp = guild.get_channel(tc) if tmp is None: return False, ( "`{}` is not a valid channel ID. Get the ID by right clicking the channel, " "or just run `{}logging here` in that channel.".format( tc, ctx['print_prefix'])) tc = tmp level = 1 if 'log_level' not in settings else settings['log_level'] if len(params) > 1: level = params[1] try: level = int(level) except ValueError: return False, "The log level you chose (`{}`) is not a number.".format( level) if not (1 <= level <= 3): return False, "The log level must be between 1 and 3." if level == 3 and not func.is_sapphire(guild): return False, ( "Only Sapphire Patron servers can use level 3 logging, as it may generate a large " "number of messages which may overload the bot and trigger Discord's rate limiting." ) perms = tc.permissions_for(guild.me) if not perms.send_messages: return False, "I don't have permission to send messages to that channel." settings['logging'] = tc.id settings['log_level'] = level utils.set_serv_settings(guild, settings) await func.server_log( guild, ("📘 Logging level set to **{}**".format(level) if was_previously_enabled else "📗 Logging (level **{}**) is now enabled in this channel".format( level)), 1, settings) return True, None
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)