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*" )
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()
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()
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")
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")
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()
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, )
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())