예제 #1
0
    async def tokens(self, ctx: commands.context, *tokens: str):
        """
        Add tokens to queue

        :param ctx: Discord Context
        :param tokens: List of tokens
        """
        async def session(self, author: discord.Member):
            self.token_session.append(author)
            await asyncio.sleep(300)
            self.token_session.remove(author)

        if (ctx.channel.type is discord.ChannelType.private
                and ctx.message.author in self.token_session):
            await ctx.send("Adding tokens...")
            log.debug(tokens)
            for index, token in enumerate(tokens):
                logger = LogMessage(ctx, title=f"Adding token #{index+1}...")
                await logger.init()
                # Check token
                await logger.log(f"Checking token #{index+1}...")
                check_result, client_id = await check_token(token)
                if not check_result:
                    logger.title = f"Token #{index+1} Invalid"
                    logger.color = discord.Color.red()
                    await logger.log("Bot token is invalid")
                else:
                    await logger.log("Token valid")
                    if get_token(token) is None:
                        log.info("Adding new token to database")
                        insert_token(token, False)
                        logger.title = f"Bot token #{index+1} added"
                        logger.color = discord.Color.green()
                        c.execute("SELECT * FROM tokens WHERE used = 0")
                        slots = c.fetchall()
                        from polyphony.bot import bot

                        await logger.send(
                            f"[Invite to Server]({discord.utils.oauth_url(client_id, permissions=discord.Permissions(DEFAULT_INSTANCE_PERMS), guild=bot.get_guild(GUILD_ID))})\n\n**Client ID:** {client_id}\nThere are now {len(slots)} slot(s) available"
                        )
                    else:
                        logger.title = f"Token #{index+1} already in database"
                        logger.color = discord.Color.orange()
                        await logger.log("Bot token already in database")
        elif ctx.channel.type is not discord.ChannelType.private:
            await ctx.message.delete()
            if any(role.name in MODERATOR_ROLES
                   for role in ctx.message.author.roles):
                await ctx.message.author.send(
                    f"Token mode enabled for 5 minutes. Add tokens with `{self.bot.command_prefix}tokens [token] (more tokens...)` right here.\n\n*Don't paste a bot token in a server*"
                )
                await session(self, ctx.message.author)
        elif any(role.name in MODERATOR_ROLES
                 for role in ctx.message.author.roles):
            ctx.message.delete()
            await ctx.channel.send(
                f"To add tokens, execute `{self.bot.command_prefix}tokens` as a moderator on a server **WITHOUT A BOT TOKEN**. Then in DMs, use `{self.bot.command_prefix}tokens [token] (more tokens...)`\n\n*Seriously don't paste a bot token in a server*"
            )
예제 #2
0
    async def member(self, ctx: commands.context, system_member: discord.User):
        """
        Sync system member with PluralKit

        :param system_member: User to sync
        :param ctx: Discord Context
        """
        logger = LogMessage(ctx,
                            title=f":hourglass: Syncing {system_member}...")
        logger.color = discord.Color.orange()
        for instance in instances:
            if (instance.user.id == system_member.id
                    and instance.main_user_account_id == ctx.author.id):
                await logger.log(
                    f":hourglass: Syncing {instance.user.mention}...")
                try:
                    await instance.sync()
                    logger.content[
                        -1] = f":white_check_mark: Synced {instance.user.mention}"
                except TypeError:
                    logger.content[
                        -1] = f":x: Failed to sync {instance.user.mention}"
        logger.title = ":white_check_mark: Sync Complete"
        logger.color = discord.Color.green()
        await logger.update()
예제 #3
0
    async def sync(self, ctx: commands.context):
        """
        Sync system members with PluralKit

        :param ctx: Discord Context
        """
        if ctx.invoked_subcommand is not None:
            return
        logger = LogMessage(ctx, title=":hourglass: Syncing All Members...")
        logger.color = discord.Color.orange()
        for instance in instances:
            if instance.main_user_account_id == ctx.author.id:
                await logger.log(
                    f":hourglass: Syncing {instance.user.mention}...")
                try:
                    await instance.sync()
                    logger.content[
                        -1] = f":white_check_mark: Synced {instance.user.mention}"
                except TypeError:
                    logger.content[
                        -1] = f":x: Failed to sync {instance.user.mention}"
        logger.title = ":white_check_mark: Sync Complete"
        logger.color = discord.Color.green()
        await logger.update()
예제 #4
0
    async def syncall(self, ctx: commands.context):
        if ctx.invoked_subcommand is not None:
            return
        log.info("Syncing all...")
        logger = LogMessage(ctx, title=":hourglass: Syncing All Members...")
        logger.color = discord.Color.orange()

        for instance in instances:
            if len(logger.content) > 30:
                logger.title = ":white_check_mark: Batch Complete"
                logger.color = discord.Color.green()
                await logger.update()
                logger = LogMessage(
                    ctx, title=":hourglass: Syncing Next Batch of Members...")
                logger.color = discord.Color.orange()
            await logger.log(f":hourglass: Syncing {instance.user.mention}...")
            sync_state = instance.sync()
            if await sync_state == 0:
                logger.content[
                    -1] = f":white_check_mark: Synced {instance.user.mention}"
            elif sync_state == 1:
                logger.content[
                    -1] = f":x: Failed to sync {instance.user.mention} because member ID `{instance.pk_member_id}` was not found on PluralKit's servers"
            else:
                logger.content[
                    -1] = f":x: Failed to sync {instance.user.mention} becanse main user left server. Instance has been automatically suspended."
        logger.title = ":white_check_mark: Sync Complete"
        logger.color = discord.Color.green()
        await logger.update()
        log.info("Sync all complete")
예제 #5
0
    async def register(
        self,
        ctx: commands.context,
        pluralkit_member_id: str,
        account: discord.Member,
    ):
        """
        Creates a new Polyphony member instance

        :param ctx: Discord Context
        :param pluralkit_member_id: PluralKit system or member id
        :param account: Main Account to be extended from
        """

        log.debug("Registering new member...")

        logger = LogMessage(ctx, title="Registering...")
        await logger.init()

        instance = None

        async with ctx.channel.typing():
            await logger.log("Fetching member from PluralKit...")
            member = await pk_get_member(pluralkit_member_id)

            # Member exists
            if member is not None:
                system_names = [f"{member['name']} (`{member['id']}`)"]
                await logger.log(
                    f"Fetched member -> {member['name']} (`{member['id']}`)")
                tokens = get_unused_tokens()

                # Fail: No Slots Available
                if len(tokens) == 0:
                    logger.title = "Error Registering: No Slots Available"
                    logger.color = discord.Color.red()
                    await logger.log(
                        f"No tokens in queue. Run `{self.bot.command_prefix}tokens` for information on how to add more."
                    )
                    return

                # Confirm add
                confirmation = BotConfirmation(ctx, discord.Color.blue())
                await confirmation.confirm(
                    f"Create member for {account} with member {', '.join(system_names)}?"
                )
                if confirmation.confirmed:
                    await confirmation.message.delete()
                    await logger.log("Adding to database...")
                else:
                    await confirmation.message.delete()
                    logger.title = "Extend Cancelled"
                    logger.color = discord.Color.red()
                    await logger.log("Registration cancelled by user")
                    return

                # Get a token and update as used
                bot_token = tokens[0]
                update_token_as_used(bot_token["token"])

                # Insert new user into users database
                if get_user(account.id) is None:
                    await logger.log(
                        f"{account.mention} is a new user! Registering them with Polyphony"
                    )
                    insert_user(account.id)

                # Insert member unless already registered
                if get_member(pluralkit_member_id) is None:
                    insert_member(
                        bot_token["token"],
                        member["id"],
                        account.id,
                        0,
                        member["name"],
                        member["display_name"],
                        member["avatar_url"],
                        member["proxy_tags"][0],
                        member["keep_proxy"],
                        member_enabled=True,
                    )

                # Fail: Member Already Registered
                else:
                    logger.title = "Member Already Registered with Polyphony"
                    logger.color = discord.Color.light_grey()
                    await logger.log(
                        f"Member ID `{pluralkit_member_id}` was already registered with Polyphony"
                    )
                    return
                await logger.log("Creating member instance...")
                instance = create_member_instance(get_member(member["id"]))

            # Fail: Invalid ID
            else:
                logger.title = "Error Registering: Member ID invalid"
                logger.color = discord.Color.red()
                await logger.log(
                    f"Member ID `{pluralkit_member_id}` was not found")
                return

        # Success State
        logger.title = f"Registration of {member['name']} Successful"
        logger.color = discord.Color.green()
        c.execute("SELECT * FROM tokens WHERE used = 0")
        slots = c.fetchall()
        await logger.log(f"There are now {len(slots)} slots available")
        await logger.log(f"\nUser is {instance.user.mention}")
        log.info("New member instance extended and activated")
예제 #6
0
    async def register(
        self,
        ctx: commands.context,
        pluralkit_member_id: str,
        account: discord.Member,
    ):
        """
        Creates a new Polyphony member instance

        :param ctx: Discord Context
        :param pluralkit_member_id: PluralKit system or member id
        :param account: Main Account to be extended from
        """

        log.debug("Registering new member...")

        logger = LogMessage(ctx, title="Registering...")
        await logger.init()

        async with ctx.channel.typing():
            # Error: Account is not a user
            if account.bot is True:
                await logger.set(
                    title="Error Registering: Bad Account Pairing",
                    color=discord.Color.red(),
                )
                await logger.log(f"{account.mention} is a bot user")
                return

            # Get available tokens
            token = conn.execute(
                "SELECT * FROM tokens WHERE used == 0").fetchone()

            # Error: No Slots Available
            if not token:
                await logger.set(
                    title=":x: Error Registering: No Slots Available",
                    color=discord.Color.red(),
                )
                await logger.log(
                    f":x: No tokens in queue. Run `{self.bot.command_prefix}tokens` for information on how to add more."
                )
                return

            # Error: Duplicate Registration
            check_duplicate = conn.execute(
                "SELECT * FROM members WHERE pk_member_id == ?",
                [pluralkit_member_id],
            ).fetchone()
            if check_duplicate:
                await logger.set(
                    title=":x: Error Registering: Member Already Registered",
                    color=discord.Color.red(),
                )
                await logger.log(
                    f":x: Member ID `{pluralkit_member_id}` is already registered with instance {self.bot.get_user(check_duplicate['id'])}"
                )
                return

            # Fetch member from PluralKit
            await logger.log(":hourglass: Fetching member from PluralKit...")
            member = await pk_get_member(pluralkit_member_id)

            # Error: Member not found
            if member is None:
                await logger.set(
                    title=":x: Error Registering: Member ID invalid",
                    color=discord.Color.red(),
                )
                await logger.log(
                    f":x: Member ID `{pluralkit_member_id}` was not found")
                return

            # Error: Missing PluralKit Data
            if (member["name"] is None or member["avatar_url"] is None
                    or member["proxy_tags"] is None):
                await logger.set(
                    title=":x: Error Registering: Missing PluralKit Data",
                    color=discord.Color.red(),
                )
                if member["name"] is None:
                    await logger.log(":warning: Member is missing a name")
                if member["avatar_url"] is None:
                    await logger.log(":warning: Member is missing an avatar")
                if member["proxy_tags"] is None:
                    await logger.log(":warning: Member is missing proxy tags")
                await logger.log(
                    "\n:x: *Please check the privacy settings on PluralKit*")
                return

            system_name = f"__**{member['name']}**__ (`{member['id']}`)"
            await logger.edit(
                -1,
                f":white_check_mark: Fetched member __**{member['name']}**__ (`{member['id']}`)",
            )

            # Confirm add
            confirmation = BotConfirmation(ctx, discord.Color.blue())
            await confirmation.confirm(
                f":grey_question: Create member for {account} with member {system_name}?"
            )
            if confirmation.confirmed:
                await confirmation.message.delete()
            else:
                await confirmation.message.delete()
                await logger.set(title=":x: Registration Cancelled",
                                 color=discord.Color.red())
                await logger.log(":x: Registration cancelled by user")
                return

            # Check if user is new to Polyphony
            if (conn.execute("SELECT * FROM users WHERE id = ?",
                             [account.id]).fetchone() is None):
                await logger.log(
                    f":tada: {account.mention} is a new user! Registering them with Polyphony"
                )
                conn.execute("INSERT INTO users VALUES (?, NULL, NULL)",
                             [account.id])
                conn.commit()

            # Insert member into database
            await logger.log(":hourglass: Adding to database...")
            try:
                insert_member(
                    token["token"],
                    member["id"],
                    account.id,
                    decode_token(token["token"]),
                    member["name"],
                    member["display_name"],
                    member["avatar_url"],
                    member["proxy_tags"],
                    member["keep_proxy"],
                    member_enabled=True,
                )
                await logger.edit(-1, ":white_check_mark: Added to database")

            # Error: Database Error
            except sqlite3.Error as e:
                log.error(e)
                await logger.set(
                    title=":x: Error Registering: Database Error",
                    color=discord.Color.red(),
                )
                await logger.edit(-1, ":x: An unknown database error occurred")
                return

            # Mark token as used
            conn.execute(
                "UPDATE tokens SET used = 1 WHERE token = ?",
                [token["token"]],
            )
            conn.commit()

            # Create Instance
            await logger.log(":hourglass: Syncing Instance...")
            instance = PolyphonyInstance(pluralkit_member_id)
            asyncio.run_coroutine_threadsafe(instance.start(token["token"]),
                                             self.bot.loop)
            await instance.wait_until_ready()

            sync_error_text = ""

            # Update Username
            await logger.edit(-1, f":hourglass: Syncing Username...")
            out = await instance.update_username(member["name"])
            if out != 0:
                sync_error_text += f"> {out}\n"

            # Update Avatar URL
            await logger.edit(-1, f":hourglass: Syncing Avatar...")
            out = await instance.update_avatar(member["avatar_url"])
            if out != 0:
                sync_error_text += f"> {out}\n"

            # Update Nickname
            await logger.edit(-1, f":hourglass: Syncing Nickname...")
            out = await instance.update_nickname(member["display_name"])
            if out < 0:
                sync_error_text += f"> PluralKit display name must be 32 or fewer in length if you want to use it as a nickname"
            elif out > 0:
                sync_error_text += f"> Nick didn't update on {out} guild(s)\n"

            # Update Roles
            await logger.edit(-1, f":hourglass: Updating Roles...")
            out = await instance.update_default_roles()
            if out:
                sync_error_text += f"> {out}\n"

            if sync_error_text == "":
                await logger.edit(-1, ":white_check_mark: Synced instance")
            else:
                await logger.edit(-1, ":warning: Synced instance with errors:")
                await logger.log(sync_error_text)

        # Success State
        logger.content = []
        await logger.set(
            title=f":white_check_mark: Registered __{member['name']}__",
            color=discord.Color.green(),
        )

        slots = conn.execute("SELECT * FROM tokens WHERE used = 0").fetchall()
        await logger.log(f":arrow_forward: **User is {instance.user.mention}**"
                         )
        if sync_error_text != "":
            await logger.log(":warning: Synced instance with errors:")
            await logger.log(sync_error_text)
        await logger.log(f"*There are now {len(slots)} slots available*")
        log.info(
            f"{instance.user} ({instance.pk_member_id}): New member instance registered ({len(slots)} slots left)"
        )
        await instance.close()
예제 #7
0
    async def tokens(self, ctx: commands.context, *tokens: str):
        """
        Add tokens to queue

        :param ctx: Discord Context
        :param tokens: List of tokens
        """
        async def session(self, author: discord.Member):
            self.token_session.append(author)
            await asyncio.sleep(300)
            self.token_session.remove(author)

        if (ctx.channel.type is discord.ChannelType.private
                and ctx.message.author in self.token_session):
            await ctx.send("Adding tokens...")
            for index, token in enumerate(tokens):
                logger = LogMessage(ctx, title=f"Adding token #{index+1}...")
                await logger.init()
                # Check token
                await logger.log(f"Checking token #{index+1}...")
                all_tokens = conn.execute("SELECT * FROM tokens").fetchall()
                chk = False
                token_client = decode_token(token)
                for chk_token in all_tokens:
                    if decode_token(str(chk_token["token"])) == token_client:
                        chk = True
                check_result, client_id = await check_token(token)
                if chk:
                    logger.title = f"Token #{index + 1} Client ID already in database"
                    logger.color = discord.Color.red()
                    await logger.log("Client ID already exists in database")
                elif not check_result:
                    logger.title = f"Token #{index+1} Invalid"
                    logger.color = discord.Color.red()
                    await logger.log("Bot token is invalid")
                else:
                    await logger.log("Token valid")
                    if (conn.execute("SELECT * FROM tokens WHERE token = ?",
                                     [token]).fetchone() is None):
                        conn.execute("INSERT INTO tokens VALUES(?, ?)",
                                     [token, False])
                        conn.commit()
                        logger.title = f"Bot token #{index+1} added"
                        logger.color = discord.Color.green()
                        c.execute("SELECT * FROM tokens WHERE used = 0")
                        slots = c.fetchall()
                        from polyphony.bot import bot

                        await logger.send(
                            f"[Invite to Server]({discord.utils.oauth_url(client_id, permissions=discord.Permissions(DEFAULT_INSTANCE_PERMS), guild=bot.get_guild(GUILD_ID))})\n\n**Client ID:** {client_id}\nThere are now {len(slots)} slot(s) available"
                        )
                        log.info(
                            f"New token added by {ctx.author} (There are now {len(slots)} slots)"
                        )
                    else:
                        logger.title = f"Token #{index+1} already in database"
                        logger.color = discord.Color.orange()
                        await logger.log("Bot token already in database")
        elif ctx.channel.type is not discord.ChannelType.private:
            await ctx.message.delete()
            if any(role.name in MODERATOR_ROLES
                   for role in ctx.message.author.roles) or ctx.bot.is_owner(
                       ctx.author):
                try:
                    await ctx.message.author.send(
                        f"Token mode enabled for 5 minutes. Add tokens with `{self.bot.command_prefix}tokens [token] (more tokens...)` right here.\n\n*Don't paste a bot token in a server*"
                    )
                except discord.errors.Forbidden:
                    await ctx.send("Enable server DMs to use token command",
                                   delete_after=10.0)
                await session(self, ctx.message.author)
            elif any(role.name in MODERATOR_ROLES
                     for role in ctx.message.author.roles) or ctx.bot.is_owner(
                         ctx.author):
                await ctx.channel.send(
                    f"To add tokens, execute `{self.bot.command_prefix}tokens` as a moderator on a server **WITHOUT A BOT TOKEN**. Then in DMs, use `{self.bot.command_prefix}tokens [token] (more tokens...)`\n\n*Seriously don't paste a bot token in a server*",
                    delete_after=10.0,
                )
예제 #8
0
async def sync(
    ctx: commands.context,
    query: List[sqlite3.Row],
    message=":hourglass: Syncing Members",
) -> NoReturn:
    logger = LogMessage(ctx, message)
    logger.color = discord.Color.orange()
    await logger.init()
    for i, member in enumerate(query):

        # Create instance
        instance = PolyphonyInstance(member["pk_member_id"])
        from polyphony.bot import bot

        asyncio.run_coroutine_threadsafe(instance.start(member["token"]),
                                         bot.loop)

        await instance.wait_until_ready()

        log.debug(f"Syncing {instance.user}")

        await logger.log(
            f":hourglass: Syncing {instance.user.mention}... ({i+1}/{len(query)})"
        )

        # Pull from PluralKit
        pk_member = await pk_get_member(member["pk_member_id"])
        if pk_member is None:
            await logger.edit(
                -1,
                f":x: Failed to sync {instance.user.mention} from PluralKit")
            log.debug(f"Failed to sync {instance.user}")
            await instance.close()
            continue

        await instance.wait_until_ready()

        error_text = ""

        # Update Proxy Tags
        conn.execute(
            "UPDATE members SET pk_proxy_tags = ? WHERE pk_member_id = ?",
            [json.dumps(pk_member.get("proxy_tags")), member["pk_member_id"]],
        )

        # Update Username
        if (instance.user.display_name != pk_member.get("name")
                and pk_member.get("name") is not None):
            await logger.edit(
                -1,
                f":hourglass: Syncing {instance.user.mention} Username... ({i}/{len(query)})",
            )
            conn.execute(
                "UPDATE members SET display_name = ? WHERE pk_member_id = ?",
                [pk_member.get("name"), member["pk_member_id"]],
            )
            out = await instance.update_username(pk_member.get("name"))
            if out != 0:
                error_text += f"> {out}\n"

        # Update Avatar URL
        if pk_member.get("avatar_url") is not None:
            await logger.edit(
                -1,
                f":hourglass: Syncing {instance.user.mention} Avatar... ({i}/{len(query)})",
            )
            conn.execute(
                "UPDATE members SET pk_avatar_url = ? WHERE pk_member_id = ?",
                [pk_member.get("avatar_url"), member["pk_member_id"]],
            )
            out = await instance.update_avatar(pk_member.get("avatar_url"))
            if out != 0:
                error_text += f"> {out}\n"

        # Update Nickname
        # Check if nickname is set
        await logger.edit(
            -1,
            f":hourglass: Syncing {instance.user.mention} Nickname... ({i}/{len(query)})",
        )
        if member["nickname"] != None:
            out = await instance.update_nickname(member["nickname"])
            if out < 0:
                error_text += f"> Nickname must be 32 characters or fewer in length\n"
            elif out > 0:
                error_text += f"> Nick didn't update on {out} guild(s)\n"
        # Otherwise use display_name if it exists
        else:
            conn.execute(
                "UPDATE members SET display_name = ? WHERE pk_member_id = ?",
                [pk_member.get("display_name"), member["pk_member_id"]],
            )
            out = await instance.update_nickname(
                pk_member.get("display_name") or pk_member.get("name"))
            if out < 0:
                error_text += f"> PluralKit display name must be 32 characters or fewer in length if you want to use it as a nickname\n"
            elif out > 0:
                error_text += f"> Nick didn't update on {out} guild(s)\n"

        # Update Roles
        await logger.edit(
            -1,
            f":hourglass: Syncing {instance.user.mention} Roles... ({i}/{len(query)})",
        )
        out = await instance.update_default_roles()
        if out:
            error_text += f"> {out}\n"

        if error_text == "":
            await logger.edit(
                -1, f":white_check_mark: Synced {instance.user.mention}")
        else:
            logger.content[
                -1] = f":warning: Synced {instance.user.mention} with errors:"
            await logger.log(
                error_text if error_text.endswith("\n") else f"{error_text}\n")

        conn.commit()

        log.debug(f"Synced {instance.user}")

        await instance.close()

    await logger.set(":white_check_mark: Sync Complete", discord.Color.green())