Esempio n. 1
0
    async def update_nickname(self, name):
        await self.wait_until_ready()
        log.debug(
            f"{self.user} ({self.pk_member_id}): Updating nickname in guilds")
        return_value = 0

        if len(name or "") > 32:
            return -1

        if name is None or name == "":
            name = self.user.display_name[2:]

        conn.execute(
            "UPDATE members SET nickname = ? WHERE id == ?",
            [name, self.user.id],
        )
        conn.commit()

        for guild in self.guilds:
            try:
                await guild.get_member(self.user.id).edit(nick=name)
                log.debug(
                    f"{self.user} ({self.pk_member_id}): Updated nickname to {name} on guild {guild.name}"
                )
            except AttributeError:
                log.debug(
                    f"{self.user} ({self.pk_member_id}): Failed to update nickname to {name} on guild {guild.name}"
                )
                return_value += 1
                pass

        return return_value
Esempio n. 2
0
    async def disable(self, ctx: commands.context,
                      system_member: discord.Member):
        """
        Disables a system member permanently by deleting it from the database and kicking it from the server. Bot token cannot be reused.

        :param ctx: Discord Context
        :param system_member: System Member
        """
        await ctx.message.delete()
        member = conn.execute(
            "SELECT * FROM members WHERE id = ?",
            [system_member.id],
        ).fetchone()
        if member is not None:
            log.debug(f"Disabling {system_member}")
            confirmation = BotConfirmation(ctx, discord.Color.red())
            await confirmation.confirm(
                f"Disable member __{system_member}__ **permanently?**")
            if confirmation.confirmed:
                await confirmation.message.delete()
                conn.execute(
                    "DELETE FROM members WHERE id = ?",
                    [system_member.id],
                )
                conn.commit()
                await ctx.send(embed=discord.Embed(
                    description=
                    f":ballot_box_with_check: {system_member.mention} **permanently disabled** by {ctx.author.mention}",
                    color=discord.Color.dark_green(),
                ))
                log.info(
                    f"{system_member} has been permanently by {ctx.message.author}"
                )
            else:
                await confirmation.message.delete()
                await ctx.send(
                    embed=discord.Embed(
                        description=
                        f":information_source: {system_member.mention} was __not__ disabled",
                        color=discord.Color.blue(),
                    ),
                    delete_after=10,
                )
                log.info(f"{system_member} not disabled")
        else:
            await ctx.send(
                embed=discord.Embed(
                    description=
                    f":x: {system_member.mention} not found in database",
                    color=discord.Color.red(),
                ),
                delete_after=10,
            )
Esempio n. 3
0
    async def edit(
        self,
        ctx: commands.context,
        message: Optional[discord.Message] = None,
        *,
        content: str,
    ):
        """
        edit (message id) [message]: Edits the last message or message with ID

        :param ctx: Discord Context
        :param message: (optional) ID of message
        :param content: Message Content
        """
        await ctx.message.delete()
        if message is not None:
            member = conn.execute(
                "SELECT * FROM members WHERE main_account_id == ? AND id == ? AND member_enabled = 1",
                [ctx.author.id, message.author.id],
            ).fetchone()
            if member:
                log.debug(
                    f"Editing message {message.id} by {message.author} for {ctx.author}"
                )
                while await helper.edit_as(
                        message,
                        content,
                        member["token"],
                ) is False:
                    await reset()
        else:
            log.debug(f"Editing last Polyphony message for {ctx.author}")
            member_ids = [
                member["id"] for member in conn.execute(
                    "SELECT * FROM members WHERE main_account_id == ?",
                    [ctx.author.id],
                ).fetchall()
            ]
            async for message in ctx.channel.history():
                if message.author.id in member_ids:
                    while await helper.edit_as(
                            message,
                            content,
                            conn.execute(
                                "SELECT * FROM members WHERE id == ?",
                                [message.author.id],
                            ).fetchone()["token"],
                    ) is False:
                        await reset()
                    break
Esempio n. 4
0
    async def on_reaction_add(self, reaction: discord.Reaction,
                              user: discord.Member):
        member = conn.execute(
            "SELECT * FROM members WHERE main_account_id == ? AND id = ? AND member_enabled = 1",
            [user.id, reaction.message.author.id],
        ).fetchone()

        # Check for correct user
        if member is not None:
            # Delete React
            if type(reaction.emoji) is str:
                if (emoji.demojize(reaction.emoji)
                        or "") == ":cross_mark:":  # Discord name: x
                    await reaction.message.delete()

                # Edit React
                if (emoji.demojize(reaction.emoji)
                        or "") == ":memo:":  # Discord name: pencil
                    self.edit_session.append(user.id)
                    embed = discord.Embed(
                        description=
                        f"You are now editing a [message]({reaction.message.jump_url})\nYour next message will replace it's contents.",
                        color=discord.Color.orange(),
                    )
                    embed.set_footer(text='Type "cancel" to cancel edit')
                    instructions = await reaction.message.channel.send(
                        f"{user.mention}", embed=embed)

                    try:

                        # Wait 30 seconds for new message
                        message = await self.bot.wait_for(
                            "message",
                            check=lambda message: message.author.id == member[
                                "main_account_id"],
                            timeout=30,
                        )

                        # On new message, do all the things
                        # If message isn't "cancel" then momentarily switch bot tokens and edit the message
                        if message.content.lower() != "cancel":
                            while await helper.edit_as(
                                    reaction.message, message.content,
                                    member["token"]) is False:
                                await reset()
                        # Delete instructions and edit message with main bot (again, low-level is easier without ctx)
                        await instructions.delete()
                        # bot.http.delete_message(instructions.channel.id, instructions.id),
                        await message.delete()
                        # bot.http.delete_message(message.channel.id, message.id),
                        await reaction.remove(user)

                    # On timeout, delete instructions and reaction
                    except asyncio.TimeoutError:
                        # Delete instructions with main bot
                        await asyncio.gather(instructions.delete(),
                                             reaction.remove(user))

                    self.edit_session.remove(user.id)
Esempio n. 5
0
    async def enable(self, ctx: commands.context,
                     system_member: discord.Member):
        """
        Reintroduce a suspended instance into the wild

        :param ctx: Discord Context
        :param system_member: System Member
        """
        await ctx.message.delete()
        member = conn.execute(
            "SELECT * FROM members WHERE id = ?",
            [system_member.id],
        ).fetchone()
        if member is not None:
            if member["member_enabled"] == 1:
                await ctx.send(
                    embed=discord.Embed(
                        description=
                        f":information_source: {system_member.mention} already enabled",
                        color=discord.Color.blue(),
                    ),
                    delete_after=10,
                )
            else:
                conn.execute(
                    "UPDATE members SET member_enabled = 1 WHERE id = ?",
                    [system_member.id],
                )
                conn.commit()
                await ctx.send(embed=discord.Embed(
                    description=
                    f":white_check_mark: {system_member.mention} enabled by {ctx.author.mention}",
                    color=discord.Color.green(),
                ))
                log.info(
                    f"{system_member} has been enabled by {ctx.message.author}"
                )
        else:
            await ctx.send(
                embed=discord.Embed(
                    description=
                    f":x: {system_member.mention} not found in database",
                    color=discord.Color.red(),
                ),
                delete_after=10,
            )
Esempio n. 6
0
 async def syncall(self, ctx: commands.context):
     if ctx.invoked_subcommand is not None:
         return
     await sync(
         ctx,
         conn.execute(
             "SELECT * FROM members WHERE member_enabled = 1").fetchall(),
     )
Esempio n. 7
0
    async def nick(self,
                   ctx: commands.context,
                   ctx_member: discord.Member,
                   *,
                   nickname: str = ""):
        member = conn.execute(
            "SELECT * FROM members WHERE main_account_id == ? AND id == ?",
            [ctx.author.id, ctx_member.id],
        ).fetchone()
        if member is not None:
            with ctx.channel.typing():
                embed = discord.Embed(
                    description=
                    f":hourglass: {'Updating' if nickname != '' else 'Clearing'} nickname for {ctx_member.mention}...",
                    color=discord.Color.orange(),
                )
                embed.set_author(name=ctx_member.display_name,
                                 icon_url=ctx_member.avatar_url)
                status_msg = await ctx.channel.send(embed=embed)

                # Create instance
                instance = PolyphonyInstance(member["pk_member_id"])
                asyncio.run_coroutine_threadsafe(
                    instance.start(member["token"]), self.bot.loop)
                await instance.wait_until_ready()

                out = await instance.update_nickname(nickname)

                await instance.close()

                if out < 0:
                    embed = discord.Embed(
                        title=f":x: **Could not update nickname**\n",
                        description=
                        f":warning: Nickname `{(nickname[:42] + '...') if len(nickname or '') > 42 else nickname}` is too long",
                        color=discord.Color.red(),
                    )
                    embed.set_author(name=ctx_member.display_name,
                                     icon_url=ctx_member.avatar_url)
                    embed.set_footer(
                        text="Nicknames must be 32 characters or less")
                else:
                    embed = discord.Embed(
                        description=
                        f":white_check_mark: Nickname {'updated' if nickname != '' else 'cleared'} for {ctx_member.mention}",
                        color=discord.Color.green(),
                    )
                    embed.set_author(name=ctx_member.display_name,
                                     icon_url=ctx_member.avatar_url)
                    if out > 0:
                        embed.set_footer(
                            text="With errors: did not update in all guilds")

                await status_msg.edit(embed=embed)
Esempio n. 8
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
        """
        await sync(
            ctx,
            conn.execute("SELECT * FROM members WHERE id = ?",
                         [system_member.id]).fetchall(),
        )
Esempio n. 9
0
    async def system(self, ctx: commands.context, main_user: discord.User):
        """
        Sync system members with PluralKit

        :param main_user: User to sync for
        :param ctx: Discord Context
        """
        await sync(
            ctx,
            conn.execute("SELECT * FROM members WHERE main_account_id = ?",
                         [main_user.id]).fetchall(),
        )
Esempio n. 10
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
        await sync(
            ctx,
            conn.execute("SELECT * FROM members WHERE main_account_id = ?",
                         [ctx.author.id]).fetchall(),
        )
Esempio n. 11
0
    async def delete(
        self,
        ctx: commands.context,
        message: Optional[discord.Message] = None,
    ):
        """
        del (id): Deletes the last message unless a message ID parameter is provided. Can be run multiple times. n max limited by config.

        :param ctx: Discord Context
        :param message: ID of message to delete
        """
        await ctx.message.delete()
        if message is not None:
            member = conn.execute(
                "SELECT * FROM members WHERE main_account_id == ? AND id == ? AND member_enabled = 1",
                [ctx.author.id, message.author.id],
            ).fetchone()
            if member:
                log.debug(
                    f"Deleting message {message.id} by {message.author} for {ctx.author}"
                )
                message = await self.bot.get_channel(message.channel.id
                                                     ).fetch_message(message.id
                                                                     )
                await message.delete()
        else:
            log.debug(f"Deleting last Polyphony message for {ctx.author}")
            member_ids = [
                member["id"] for member in conn.execute(
                    "SELECT * FROM members WHERE main_account_id == ?",
                    [ctx.author.id],
                ).fetchall()
            ]
            async for message in ctx.channel.history():
                if message.author.id in member_ids:
                    await message.delete()
                    break
Esempio n. 12
0
 async def autoproxy(self, ctx: commands.context, arg: Union[discord.Member,
                                                             str]):
     await ctx.message.delete()
     embed = None
     if (type(arg) is discord.Member and conn.execute(
             "SELECT * FROM members WHERE main_account_id == ? AND id == ?",
         [ctx.author.id, arg.id],
     ).fetchone() is not None):
         conn.execute(
             "UPDATE users SET autoproxy_mode = 'member', autoproxy = ? WHERE id == ?",
             [arg.id, ctx.author.id],
         )
         conn.commit()
         embed = discord.Embed(
             description=f"Autoproxy set to {arg.mention}")
         embed.set_footer(text="Use ;;ap off to turn autoproxy off")
     elif arg == "latch":
         conn.execute(
             "UPDATE users SET autoproxy_mode = 'latch', autoproxy = NULL WHERE id == ?",
             [ctx.author.id],
         )
         conn.commit()
         embed = discord.Embed(
             description=f"Autoproxy set to **latch mode**")
         embed.set_footer(text="Use ;;ap off to turn autoproxy off")
     elif arg == "off":
         conn.execute(
             "UPDATE users SET autoproxy_mode = NULL WHERE id == ?",
             [ctx.author.id],
         )
         conn.commit()
         embed = discord.Embed(description=f"Autoproxy is now **off**")
     if embed is None and type(arg) is discord.Member:
         embed = discord.Embed(
             description=f"{arg.mention} is not a member of your system",
             color=discord.Color.red(),
         )
     embed.set_author(name=ctx.author, icon_url=ctx.author.avatar_url)
     await ctx.send(embed=embed, delete_after=10)
Esempio n. 13
0
    async def on_message(self, msg: discord.Message):
        start = time.time()  # For benchmark debug message

        # Cancel if using bot command prefix (allows bot commands to run)
        if msg.content.startswith(COMMAND_PREFIX):
            return

        # Skip for edit react
        if msg.author.id in self.edit_session:
            return

        # Get the system
        system = conn.execute(
            "SELECT * FROM members WHERE main_account_id == ? AND member_enabled = 1",
            [msg.author.id],
        ).fetchall()

        ping_suppress = False  # Set to suppress ping. Used to prevent double ping forwarding with a proxied ping.

        # Set datastructures
        member_data = {
            "prefix": None,
            "suffix": None,
        }
        member = None

        # Compile tags with member objects
        tags = []
        for m in system:
            for t in json.loads(m["pk_proxy_tags"]):
                tags.append([t, m])

        # Check tags and set member
        for tag in tags:
            if msg.content.startswith(tag[0].get("prefix")
                                      or "") and msg.content.endswith(
                                          tag[0].get("suffix") or ""):
                member_data["prefix"] = tag[0].get("prefix") or ""
                member_data["suffix"] = tag[0].get("suffix") or ""
                member = tag[1]
                break

        # Get autoproxy status
        ap_data = {"mode": None, "user": None}
        db_user = conn.execute("SELECT * FROM users WHERE id == ?",
                               [msg.author.id]).fetchone()
        if db_user is not None:
            ap_data["mode"] = db_user["autoproxy_mode"]
            ap_data["user"] = db_user["autoproxy"]

        # Check for autoproxy
        if member is None and ap_data["mode"] is not None:
            member = conn.execute(
                "SELECT * FROM members WHERE id == ? AND member_enabled = 1",
                [ap_data["user"]],
            ).fetchone()

        # Send message if member is set
        if member is not None:
            log.debug(
                f"""{member['member_name']} ({member["pk_member_id"]}): Processing new message in {msg.channel} => "{msg.content}" (attachments: {len(msg.attachments)})"""
            )

            # Set autoproxy latch
            if ap_data["mode"] == "latch" and (
                    member_data["prefix"] is not None
                    or member_data["suffix"] is not None):
                log.debug(
                    f"Setting autoproxy latch to {member['display_name']}")

                # Update database to remember current latch
                conn.execute(
                    "UPDATE users SET autoproxy = ? WHERE id == ?",
                    [member["id"], msg.author.id],
                )
                conn.commit()

            # Remove prefix/suffix
            message = msg.content[len(member_data["prefix"] or "") or None:
                                  -len(member_data["suffix"] or "") or None]

            # Send proxied message
            while await helper.send_as(
                    msg,
                    message,
                    member["token"],
                    files=[await file.to_file() for file in msg.attachments],
                    reference=msg.reference,
            ) is False:
                await reset()
            await msg.delete()

            # Server log channel message deletion handler (cleans up logging channel)
            new_proxied_message(msg)

            end = time.time()  # For benchmarking purposes
            log.debug(
                f"{member['member_name']} ({member['pk_member_id']}): Benchmark: {timedelta(seconds=end - start)} | Protocol Roundtrip: {timedelta(seconds=self.bot.latency)}"
            )

            ping_suppress = True  # Message was proxied so suppress it

        # Check for ping
        all_members = conn.execute(
            "SELECT * FROM members WHERE member_enabled = 1").fetchall()
        # Get the database entry for the current member (if any)
        msg_member = conn.execute("SELECT * FROM members WHERE id = ?",
                                  [msg.author.id]).fetchone()
        # Get the system of the current member (if any)
        if msg_member is not None:
            system = conn.execute(
                "SELECT * FROM members WHERE main_account_id = ?",
                [msg_member["main_account_id"]],
            ).fetchall()

        # If a message was proxied, the ping is suppressed to avoid a double-ping from the instance and the original message
        if not ping_suppress:
            for member in all_members:
                # Check for a valid ping
                if (member["id"] in [m.id for m in msg.mentions]
                        and msg.author.id != member["main_account_id"]
                        and msg.author.id != member["id"]
                        and msg.author.id != self.bot.user.id
                        and not msg.content.startswith(COMMAND_PREFIX)):
                    embed = discord.Embed(
                        description=
                        f"Originally to {self.bot.get_user(member['id']).mention}\n[Highlight Message]({msg.jump_url})"
                    )
                    embed.set_author(
                        name=f"From {msg.author}",
                        icon_url=msg.author.avatar_url,
                    )

                    # Check if ping is from another Polyphony instance
                    if msg.author.bot is True and msg.author.id in [
                            m["id"] for m in all_members
                    ]:
                        # Check member isn't part of author's own system
                        if member["id"] not in [m["id"] for m in system]:
                            # Forward Ping from Instance
                            log.debug(
                                f"Forwarding ping from {member['id']} to {member['main_account_id']} (from proxy)"
                            )
                            await self.bot.get_channel(msg.channel.id).send(
                                f"{self.bot.get_user(member['main_account_id']).mention}",
                                embed=embed,
                            )
                    else:
                        # Forward Ping from non-polyphony instance
                        log.debug(
                            f"Forwarding ping from {member['id']} to {member['main_account_id']}"
                        )

                        await self.bot.get_channel(msg.channel.id).send(
                            f"{self.bot.get_user(member['main_account_id']).mention}",
                            embed=embed,
                        )
                    break

        # Delete logging channel message
        if DELETE_LOGS_USER_ID is not None and DELETE_LOGS_CHANNEL_ID is not None:
            if (msg.channel.id == DELETE_LOGS_CHANNEL_ID
                    or msg.author.id == DELETE_LOGS_USER_ID):
                try:
                    embed_text = msg.embeds[0].description
                except IndexError:
                    return

                for oldmsg in recently_proxied_messages:
                    member_ids = [m["id"] for m in system]
                    if str(oldmsg.id) in embed_text and not any([
                            str(member_id) in embed_text
                            for member_id in member_ids
                    ]):
                        log.debug(
                            f"Deleting delete log message {msg.id} (was about {oldmsg.id})"
                        )
                        await msg.delete()
Esempio n. 14
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()
Esempio n. 15
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,
                )
Esempio n. 16
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())