示例#1
0
async def execute(ctx, params):
    params_str = ' '.join(params)
    guild = ctx['guild']
    settings = ctx['settings']
    limit = utils.strip_quotes(params_str)
    author = ctx['message'].author
    vc = ctx['voice_channel']

    if limit:
        try:
            limit = abs(int(limit))
        except ValueError:
            return False, "`{}` is not a number.".format(limit)
    else:
        limit = len(vc.members)

    if limit > 99:
        return False, "The user limit cannot be higher than 99."

    await vc.edit(user_limit=limit)
    if limit != 0:
        log_msg = "👪 {} (`{}`) set the user limit of \"**{}**\" (`{}`) to {}".format(
            func.user_hash(author), author.id, func.esc_md(vc.name), vc.id,
            limit)
    else:
        log_msg = "👨‍👩‍👧‍👦 {} (`{}`) removed the user limit of \"**{}**\" (`{}`)".format(
            func.user_hash(author), author.id, func.esc_md(vc.name), vc.id)
    await func.server_log(guild, log_msg, 2, settings)
    return True, None
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):
    guild = ctx['guild']
    settings = ctx['settings']
    r = "Name: **{}** \tID: `{}`\n".format(func.esc_md(guild.name), guild.id)
    members = [m for m in guild.members if not m.bot]
    num_members = len(members)
    percent_members_online = len([m for m in members if m.status != discord.Status.offline]) / num_members * 100
    r += "**{}** non-bot members, {}% currently online\n".format(num_members, round(percent_members_online))
    r += "Server is a Gold Patron: **{}**\n".format("Yes" if func.is_gold(guild) else "No")
    r += "Server is a Sapphire Patron: {}\n".format(
        ("**Yes** +private bot" if cfg.SAPPHIRE_ID is not None else "**Yes**") if func.is_sapphire(guild) else "**No**"
    )

    r += "\n**Known Channels:**\n"
    for p in settings['auto_channels']:
        pc = guild.get_channel(p)
        if pc:
            r += "{} (`{}`)".format(func.esc_md(pc.name), pc.id)
            if pc.category:
                r += " in category \"{}\"".format(func.esc_md(pc.category.name))
            r += permission_checks(pc, guild.me)
            secondaries = settings['auto_channels'][p]['secondaries']
            r += "\t {} sub-channel{}".format(len(secondaries), "s" if len(secondaries) != 1 else "")
            r += "\n"
            for s, v in secondaries.items():
                sc = guild.get_channel(s)
                scc = guild.get_member(v['creator'])
                if sc:
                    r += "\t ⮡ \t\"{}\" (`{}`)\t Created by: \"{}\" (\"{}\", `{}`){}\n".format(
                        func.esc_md(sc.name), sc.id,
                        func.esc_md(scc.display_name), func.user_hash(scc), scc.id,
                        permission_checks(sc, guild.me)
                    )
    r = r.replace('➕', '+')  # Make the default plus sign more visible
    return True, r
示例#4
0
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 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 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):
    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."
示例#8
0
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."
示例#9
0
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 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
示例#11
0
async def execute(ctx, params):
    channel = ctx['channel']
    author = ctx['message'].author
    if not params:
        support_server_id = 601015720200896512
        if not ctx['admin'] and ctx['guild'].id != support_server_id:
            e = discord.Embed(color=discord.Color.from_rgb(205, 220, 57))
            e.title = "Auto Voice Channels"
            e.description = (
                "I'm a bot that allows you to dynamically and infinitely create voice channels as you need them, "
                "and automatically delete them as soon as they are no longer used.\n\n"
                "Simply join a voice channel of mine and I'll create a new one for you and move you to it."
            )
            text = (
                " ·  **<PREFIX>lock** - "
                "Lock the user limit of your voice channel so no more people can join. "
                "Use **<PREFIX>unlock** to remove the limit.\n\n"
                " ·  **<PREFIX>limit `N`** - "
                "Set the user limit of your channel to a particular number. "
                "Use **<PREFIX>unlock** to remove the limit.\n\n"
                " ·  **<PREFIX>private** - "
                "Make your voice channel private, preventing anyone from joining you directly. "
                "Creates a \"⇩ Join {}\" channel above yours so people can request to join you.\n\n"
                " ·  **<PREFIX>kick `@USER`** - "
                "Start a votekick to remove someone from your channel.\n\n"
                " ·  **<PREFIX>transfer `@USER`** - "
                "Transfer ownership of your channel to someone else.\n\n".
                format(esc_md(author.display_name)))
            if ctx['gold']:
                text += (
                    " ·  **<PREFIX>name** - Change the name of your voice channel.\n\n"
                    " ·  **<PREFIX>nick** - Set what channels that show the creator's name will call you.\n\n"
                    " ·  **<PREFIX>bitrate** - Set a server-wide custom bitrate (in kbps) for yourself that will be "
                    "used for any channels you join.\n\n")
            text += (
                " ·  **<PREFIX>invite** - Invite me to another server!\n\n"
                " ·  **<PREFIX>help `command`** - Get more info about a particular command."
            )
            text = text.replace('<PREFIX>', ctx['print_prefix'])
            e.add_field(name="Commands:", value=text)
            try:
                await channel.send(embed=e)
            except discord.errors.Forbidden:
                log("Forbidden to echo", channel.guild)
                await dm_user(
                    author, "I don't have permission to send messages in the "
                    "`#{}` channel of **{}**.".format(channel.name,
                                                      channel.guild.name))
                return False, "NO RESPONSE"
            return True, "NO RESPONSE"
        can_embed = channel.permissions_for(ctx['guild'].me).embed_links
        with open("docs.md", 'r', encoding='utf8') as f:
            docs = f.read()
        sections = docs.split('** **')
        for i, s in enumerate(sections):
            s = s.replace("@Auto Voice Channels ",
                          "@{} ".format(ctx['message'].guild.me.display_name))
            s = s.replace("vc/", esc_md(ctx['prefix_p']))
            s = s.replace("@pixaal", author.mention)
            s = s.replace(" :)", " :slight_smile:")
            s = s.replace("**Gold Patron**", ":credit_card: **Gold Patron**")
            s = s.replace("Change the prefix of the bot (default is",
                          "Change the prefix of the bot (currently")
            s = s.replace("<https://www.patreon.com/pixaal>",
                          "https://www.patreon.com/pixaal")  # Always embed
            if s.startswith("\n**-- Commands --**\n") and can_embed:
                lines = [l for l in s.split('\n') if l != ""]
                parts = []
                title = []
                end_of_title = False
                cmds = []
                for l in lines:
                    if not l.startswith('`'):
                        if end_of_title:
                            parts.append(["** **\n" + '\n'.join(title), cmds])
                            title = []
                            cmds = []
                            end_of_title = False
                        title.append(l)
                    else:
                        end_of_title = True
                        cmds.append(l)
                parts.append(["** **\n" + '\n'.join(title), cmds])

                for j, p in enumerate(parts):
                    embed = discord.Embed(
                        color=discord.Color.from_rgb(205, 220, 57))
                    for c in p[1]:
                        cmd_name, cmd_desc = c.split(" - ", 1)
                        embed.add_field(name=" ·  " + cmd_name, value=cmd_desc)
                    try:
                        await channel.send(content=p[0].replace(
                            "Commands --**", "Commands --**\n"),
                                           embed=embed)
                    except discord.errors.Forbidden:
                        log("Forbidden to echo", channel.guild)
                        await dm_user(
                            author,
                            "I don't have permission to send messages in the "
                            "`#{}` channel of **{}**.".format(
                                channel.name, channel.guild.name))
                        return False, "NO RESPONSE"
                continue
            if i == 0:
                s = '\n'.join(
                    s.strip('\n').split('\n')
                    [:-1])  # Remove last line of first section (gfycat embed)
                s += "\nhttps://gfycat.com/latemealyhoneyeater"
            else:
                s = '** **' + s
            echo_success = await echo(s, channel, author)
            if not echo_success:
                return False, "NO RESPONSE"
        return True, "NO RESPONSE"
    else:
        from commands import commands
        c = params[0]
        if c in commands:
            replacements = {
                "<PREFIX>": ctx['print_prefix'],
                "<COMMAND>": c,
                "<USER>": author.mention,
            }
            help_text = commands[c].help_text
            for part in help_text:
                e = discord.Embed(color=discord.Color.from_rgb(205, 220, 57))
                content = None
                if 'incorrect_command_usage' in ctx and part == help_text[0]:
                    content = "Incorrect command usage, here's some info about the `{}` command:".format(
                        c)
                if part == help_text[-1]:
                    e.set_footer(
                        text=
                        "More help: discord.io/DotsBotsSupport  \nSupport me: patreon.com/pixaal",
                        icon_url=ctx['guild'].me.avatar_url_as(size=32))
                for i, p in enumerate(part):
                    t = ("⠀\n" + p[0]
                         ) if i != 0 and not p[0].startswith(" ·  ") else p[0]
                    d = (
                        p[1] + "\n⠀"
                    ) if i == len(part) - 1 and part == help_text[-1] else p[1]
                    if t == 'title':
                        e.title = d
                    else:
                        for r, rv in replacements.items():
                            t = t.replace(r, rv)
                            d = d.replace(r, rv)
                        e.add_field(name=t, value=d, inline=False)
                try:
                    await channel.send(content=content, embed=e)
                except discord.errors.Forbidden:
                    log("Forbidden to echo", channel.guild)
                    await dm_user(
                        author,
                        "I don't have permission to send messages in the "
                        "`#{}` channel of **{}**.".format(
                            channel.name, channel.guild.name))
                    return False, "NO RESPONSE"
                except Exception:
                    log("Failed to echo", channel.guild)
                    print(traceback.format_exc())
                    return False, "NO RESPONSE"

            if commands[c].sapphire_required and not ctx['sapphire']:
                await channel.send(
                    "**Note:** This command is restricted to :gem: **Sapphire Patron** servers.\n"
                    "Become a Sapphire Patron to support the development of this bot and unlock more ~~useless~~ "
                    "amazing features: <https://www.patreon.com/pixaal>")
            elif commands[c].gold_required and not ctx['gold']:
                await channel.send(
                    "**Note:** This command is restricted to :credit_card: **Gold Patron** servers.\n"
                    "Become a Gold Patron to support the development of this bot and unlock more ~~useless~~ "
                    "amazing features: <https://www.patreon.com/pixaal>")
            return True, "NO RESPONSE"
        elif c == 'expressions':
            e = discord.Embed(color=discord.Color.from_rgb(205, 220, 57))
            e.title = "Template Expressions"
            e.description = (
                "Expressions are a powerful way to set the channel name based on certain conditions, such as whether "
                "or not the creator has a particular role, what game is being played, and the party size.\n\n"
                "Expressions must be in the following form:\n"
                "```"
                "{{CONDITION ?? TRUE // FALSE}}"
                "```\n"
                "If the `CONDITION` part is met, whatever you wrote in the `TRUE` part will be added to the channel "
                "name, otherwise the `FALSE` part will be used instead. The `FALSE` part is optional and can be "
                "left out (e.g. `{{CONDITION ?? TRUE}}`).\n\n"
                "Anything at all can be written inside the `TRUE`/`FALSE` parts, including other template variables "
                "like `@@num@@` or `@@game_name@@`, or even other nested expressions, "
                "however only a certain things may be used as the `CONDITION`:\n\n"
            )
            e.add_field(
                name=" ·  `ROLE:role id`",
                value=
                "Check whether or not the creator has a particular role.\n\n")
            e.add_field(
                name=" ·  `GAME=game name`",
                value=
                "Check if the game that users in the channel are playing (the same one that "
                "`@@game_name@@` returns, including aliases) matches **exactly** the text provided.\n"
                "You can also use `!=` instead of `=` to match anything **other** than exactly the text provided, "
                "or `:` to match anything that **contains** the text provided. "
                "E.g. `GAME:Call of Duty` will match with *\"Call of Duty: Modern Warfare\"*, "
                "but `GAME=Call of Duty` will not.\n\n")
            e.add_field(
                name=" ·  `LIVE`",
                value=
                "Whether or not the creator of the channel is streaming. Use `LIVE_DISCORD` to only detect "
                "discord's \"Go Live\" streams, or `LIVE_EXTERNAL` for Twitch. `LIVE` will include both.\n\n"
            )
            e.add_field(
                name=" ·  `PLAYERS>number`",
                value=
                "💎 [*patrons only.*](https://patreon.com/pixaal) Check if the number of players in your game "
                "(determined either by Discord Rich Presence or the game activity statuses of members in the channel) "
                "is greater than the number provided. You can also use `<`, `<=`, `>=`, `=` and `!=`.\n\n"
            )
            e.add_field(
                name=" ·  `MAX>number`",
                value=
                "💎 [*patrons only.*](https://patreon.com/pixaal) Check if the maximum number of players "
                "allowed in your game (determined by Discord Rich Presence or the channel limit) is greater than the "
                "number provided. You can also use `<`, `<=`, `>=`, `=` and `!=`.\n\n"
            )
            e.add_field(
                name=" ·  `RICH`",
                value=
                "💎 [*patrons only.*](https://patreon.com/pixaal) Whether or not the current game uses "
                "Discord Rich Presence, which means `@@num_playing@@`, `@@party_size@@`, `@@party_state@@`, and "
                "`@@party_details@@` should have reliable values.\n\n")
            try:
                await channel.send(embed=e)
            except discord.errors.Forbidden:
                log("Forbidden to echo", channel.guild)
                await dm_user(
                    author, "I don't have permission to send messages in the "
                    "`#{}` channel of **{}**.".format(channel.name,
                                                      channel.guild.name))
                return False, "NO RESPONSE"
            e = discord.Embed(color=discord.Color.from_rgb(205, 220, 57))
            e.title = "Examples"
            e.description = (
                "```{{GAME:Left 4 Dead ?? [@@num_playing@@/4]}}```"
                "```{{LIVE??🔴 @@stream_name@@}}```"
                "```{{PLAYERS=1 ?? LFG}}```"
                "```{{PLAYERS<=20 ?? ## [@@game_name@@] // It's a party!}}```"
                "```{{MAX=@@num_playing@@ ?? (Full) // (@@num_playing@@)}}```"
                "```{{RICH??@@party_details@@{{MAX>1?? (@@num_playing@@/@@party_size@@)}}}}```"
                "```{{ROLE:601025860614750229 ?? {{ROLE:615086491235909643??[UK] // {{ROLE:607913610139664444??[DE] // "
                "[EU]}}}}}}```\n"
                "The spaces around the `??` and `//` improve readability but may not be desired if you do not want any "
                "spaces around the result.\n\n"
                "If you have a question or need any help setting up an expression, "
                "please ask me in the [support server](https://discord.io/DotsBotsSupport). "
                "I'd be happy to add any extra variables you need.")
            await channel.send(embed=e)
            return True, "NO RESPONSE"
        else:
            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(c, ctx['print_prefix']))
            else:
                return False, "NO RESPONSE"
async def execute(ctx, params):
    settings = ctx['settings']
    asip = settings['asip'] if 'asip' in settings else False
    c = ctx['voice_channel']

    template = settings['channel_name_template']
    for p, v in settings['auto_channels'].items():
        for s in v['secondaries']:
            if s == c.id:
                if 'template' in v:
                    template = v['template']
                break

    r = "Current channel name: **{}**\n".format(func.esc_md(c.name))
    r += "Channel name template: **{}**\n\n".format(func.esc_md(template))

    r += "Bitrate: **{0:.2f}** kbps\n".format(c.bitrate / 1000)
    r += "Custom bitrates:"
    any_bitrates = False
    for m in c.members:
        if 'custom_bitrates' not in settings:
            break
        if str(m.id) in settings['custom_bitrates']:
            any_bitrates = True
            r += "\n:white_small_square: {}: **{}**".format(
                func.esc_md(m.display_name),
                settings['custom_bitrates'][str(m.id)])
    if not any_bitrates:
        r += " **None**"
    r += "\n\n"

    r += "Game activity:"
    games = []
    for m in c.members:
        if m.activity and m.activity.name and not m.bot:
            act = m.activity
            games.append(act.name)
            r += "\n:white_small_square: {}: **{}**".format(
                func.esc_md(m.display_name), func.esc_md(act.name))
            r += '\t'
            if hasattr(act, 'state') and act.state:
                r += " {}".format(func.esc_md(act.state))
            if hasattr(act, 'details') and act.details:
                r += " ({})".format(func.esc_md(act.details))
            if hasattr(act, 'party') and act.party:
                r += " \tParty: "
                r += "" if 'id' not in act.party else "`{}` ".format(
                    act.party['id'])
                r += "" if 'size' not in act.party else ('/'.join(
                    str(v) for v in act.party['size']))
    if not games:
        r += " **None**"

    games = func.get_channel_games(c)
    for g in games:
        party = func.get_party_info(c, g, asip, default="None")
        if party:
            r += "\n\n**{}**\n".format(g)
            r += "Size: **{}**".format(party['size'])
            r += " *(includes {} user{} with no status)*\n".format(
                party['sneakies'], '' if party['sneakies'] == 1 else
                's') if party['sneakies'] else "\n"
            r += "State: **{}**\n".format(party['state'])
            r += "Details: **{}**".format(party['details'])

    aliases = {}
    for g in games:
        if g in settings['aliases']:
            aliases[g] = settings['aliases'][g]
    if aliases:
        r += "\n\n"
        r += "Aliases for these games:"
        for g, a in aliases.items():
            r += "\n:white_small_square: {}: **{}**".format(
                func.esc_md(g), func.esc_md(a))

    return True, r