Ejemplo n.º 1
0
async def lingering_secondaries(client):
    start_time = time()
    if client.is_ready():
        potentials = None
        with concurrent.futures.ThreadPoolExecutor() as pool:
            potentials = await client.loop.run_in_executor(pool, get_potentials)
        potentials = potentials.split('\n')
        if potentials:
            potentials = set(potentials[-10000:])  # Sets apparently give better performance. Discard all but last 10k.
            for guild in func.get_guilds(client):
                settings = utils.get_serv_settings(guild)
                if not settings['enabled'] or not settings['auto_channels']:
                    continue
                secondaries = func.get_secondaries(guild, settings=settings, include_jc=True)
                voice_channels = [x for x in guild.channels if isinstance(x, discord.VoiceChannel)]
                for v in voice_channels:
                    if v.id not in secondaries and str(v.id) in potentials and not func.channel_is_requested(v):
                        if v.name not in ['⌛', '⚠']:
                            try:
                                await v.edit(name='⚠')
                                log("Remembering channel {}".format(v.id), guild)
                                await func.admin_log(
                                    "⚠ Remembering channel `{}` in guild **{}**".format(v.id, guild.name), client
                                )
                            except discord.errors.NotFound:
                                pass
                            except Exception:
                                traceback.print_exc()
        end_time = time()
        fn_name = "lingering_secondaries"
        cfg.TIMINGS[fn_name] = end_time - start_time
        if cfg.TIMINGS[fn_name] > 5:
            await func.log_timings(client, fn_name)
 async def check_empty(guild, settings):
     # Delete empty secondaries, in case they didn't get caught somehow (e.g. errors, downtime)
     secondaries = func.get_secondaries(guild, settings)
     voice_channels = [x for x in guild.channels if isinstance(x, discord.VoiceChannel)]
     for v in voice_channels:
         if v.name != "⌛":  # Ignore secondary channels that are currently being created
             if v.id in secondaries:
                 if not v.members:
                     await func.delete_secondary(guild, v)
Ejemplo n.º 3
0
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):
    params_str = ctx['clean_paramstr']
    guild = ctx['guild']
    settings = ctx['settings']
    author = ctx['message'].author

    new_name = params_str.replace('\n',
                                  ' ')  # Can't have newlines in channel name.
    new_name = new_name.strip()

    secondaries = func.get_secondaries(guild, settings)

    first_word = new_name.split(' ')[0]
    try:
        cid = int(first_word)
    except ValueError:
        return False, (
            "`{}` is not a valid channel ID. Please run `{}help rename` "
            "to learn how to use this command.".format(first_word,
                                                       ctx['print_prefix']))

    target_c = guild.get_channel(cid)
    if target_c is None:
        return False, "I can't find any channel with the ID `{}`.".format(cid)
    if cid not in secondaries:
        return False, "Sorry, that's not one of my channels."

    new_name = new_name[len(str(cid)):].strip()

    if new_name:
        return await func.custom_name(guild, target_c, author, new_name)
    else:
        return False, (
            "You need to specify a new name for the channel, e.g. '{0}rename {1} <new name>'.\n"
            "Run '{0}help template' for a full list of variables you can use like "
            "`@@game_name@@`, `@@creator@@` and `@@num_others@@`.".format(
                ctx['print_prefix'], cid))
Ejemplo n.º 5
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_voice_state_update(member, before, after):
    if not client.is_ready():
        return

    if before.channel == after.channel:
        # Ignore mute/unmute events
        return

    guild = member.guild
    guilds = func.get_guilds(client)
    if guild not in guilds:
        return

    settings = utils.get_serv_settings(guild)
    if not settings['enabled']:
        return

    if not settings['auto_channels']:
        # No channels have been set up, do nothing
        return

    secondaries = func.get_secondaries(guild, settings)
    join_channels = func.get_join_channels(guild, settings)

    if after.channel:
        if after.channel.id in settings['auto_channels']:
            await func.create_secondary(guild, after.channel, member)
        elif after.channel.id in secondaries:
            if after.channel.name != "⌛":
                await func.update_text_channel_role(guild, member,
                                                    after.channel, "join")
                bitrate = await func.update_bitrate(after.channel, settings)
                await func.server_log(
                    guild, "➡ {} (`{}`) joined \"**{}**\" (`{}`)".format(
                        func.user_hash(member), member.id, after.channel.name,
                        after.channel.id) +
                    (" ⤏ {}kbps".format(round(bitrate /
                                              1000)) if bitrate else ""), 3,
                    settings)
        elif after.channel.id in join_channels:
            sv = join_channels[after.channel.id]
            msg_channel = guild.get_channel(sv['msgs'])
            vc = guild.get_channel(sv['vc'])
            creator = guild.get_member(sv['creator'])
            if msg_channel and creator and vc:
                try:
                    m = await msg_channel.send(
                        "Hey {},\n{} would like to join your private voice channel. React with:\n"
                        "• ✅ to **allow**.\n"
                        "• ❌ to **deny** this time.\n"
                        "• ⛔ to deny and **block** future requests from them.".
                        format(creator.mention, member.mention))
                    cfg.JOINS_IN_PROGRESS[member.id] = {
                        "creator": creator,
                        "requester": member,
                        "vc": vc,
                        "jc": after.channel,
                        "msg": m,
                        "mid": m.id
                    }
                    log(
                        "{} ({}) requests to join {}".format(
                            member.display_name, member.id,
                            creator.display_name), guild)
                    try:
                        await m.add_reaction('✅')
                        await m.add_reaction('❌')
                        await m.add_reaction('⛔')
                    except discord.errors.Forbidden:
                        pass
                except Exception as e:
                    log(
                        "Failed to send join-request message ({})".format(
                            type(e).__name__), guild)
                else:
                    cfg.JOINS_IN_PROGRESS[member.id]

    if before.channel:
        if before.channel.id in secondaries:
            members = [m for m in before.channel.members if not m.bot]
            bitrate = None
            if members:
                await func.update_text_channel_role(guild, member,
                                                    before.channel, "leave")
                bitrate = await func.update_bitrate(before.channel,
                                                    settings,
                                                    user_left=member)
            await func.server_log(
                guild, "🚪 {} (`{}`) left \"**{}**\" (`{}`)".format(
                    func.user_hash(member), member.id, before.channel.name,
                    before.channel.id) +
                (" [bitrate: {}kbps]".format(round(bitrate /
                                                   1000)) if bitrate else ""),
                3, settings)
            if not members:
                await func.delete_secondary(guild, before.channel)
Ejemplo n.º 7
0
async def run(c, ctx, params):
    if c not in commands:
        if 'dcnf' not in ctx['settings'] or ctx['settings']['dcnf'] is False:
            similar = sorted(commands,
                             key=lambda x: SequenceMatcher(None, x, c).ratio(),
                             reverse=True)[0]
            ratio = SequenceMatcher(None, similar, c).ratio()
            return False, "Sorry, `{}` is not a recognised command.{}".format(
                c, " Did you mean `{}{}`?".format(ctx['print_prefix'], similar)
                if ratio > 0.65 else "")
        else:
            return False, "NO RESPONSE"

    cmd = commands[c]

    if cmd.admin_required and not ctx['admin']:
        return False, ("Gabisa bikin channel default, cuma admin yg bisa ")

    restrictions = ctx['settings']['restrictions'] if 'restrictions' in ctx[
        'settings'] else {}
    if not ctx['admin'] and c in restrictions:
        roles = [r.id for r in ctx['message'].author.roles]
        if not any((r in roles) for r in restrictions[c]):
            return False, "You don't have permission to use that command."

    if cmd.sapphire_required and not ctx['sapphire']:
        return False, (
            "That 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 cmd.gold_required and not ctx['gold']:
        return False, (
            "That 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")

    if cmd.voice_required:
        v = ctx['message'].author.voice
        if v is not None and v.channel.id in get_secondaries(
                ctx['guild'], ctx['settings']):
            ctx['voice_channel'] = v.channel
        else:
            return False, "You need to be in one of my voice channels to use that command."

    if cmd.creator_only:
        vc = ctx[
            'voice_channel']  # all creator_only commands will also have voice_required
        creator_id = utils.get_creator_id(ctx['settings'], vc)
        ctx['creator_id'] = creator_id
        if not creator_id == ctx['message'].author.id and not ctx['admin']:
            creator_mention = None
            for m in vc.members:
                if m.id == creator_id:
                    creator_mention = m.mention
                    break
            return False, (
                "Only the person who created this voice channel ({}) is allowed to do that.\n"
                "If you were the creator originally but then left the channel temporarily, "
                "the person at the top of the channel at the time became the new designated creator."
                "".format(
                    creator_mention if creator_mention else "unknown member"))

    if len(params) < cmd.params_required:
        ctx['incorrect_command_usage'] = False
        await help_cmd.command.execute(ctx, [c])
        return False, None

    try:
        r = await cmd.execute(ctx, params)  # Run command
    except discord.errors.Forbidden:
        return False, "I don't have permission to do that :("
    except Exception as e:
        error_text = "Server: `{}`\n`{}` with command `{}`, params_str: `{}`".format(
            ctx['guild'].id,
            type(e).__name__, c, ' '.join(params))
        await admin_log(error_text, ctx['client'])
        log(error_text)
        import traceback
        error_text = traceback.format_exc()
        await admin_log(error_text, ctx['client'])
        log(error_text)
        return False, (
            "A `{}` error occured :(\n"
            "Please ensure I have the correct permissions, check `{}help {}` for the correct command usage, "
            "and then try again. \nIf that still doesn't help, try asking in the support server: "
            "https://discord.gg/qhMrz6u".format(
                type(e).__name__, ctx['print_prefix'], c))

    if r is None:
        # In case command didn't return success/response
        await admin_log(
            "Server: `{}`\nUnknown Error with command `{}`, params_str: {}".
            format(ctx['guild'].id, c, ' '.join(params)),
            ctx['client'],
            important=True)
        return False, (
            "An unknown error occured :(\n"
            "Please ensure I have the correct permissions, check `{}help {}` for the correct command usage, "
            "and then try again. \nIf that still doesn't help, try asking in the support server: "
            "https://discord.gg/qhMrz6u".format(ctx['print_prefix'], c))

    return r