async def __main__(self, ExtensionArgs): user = ExtensionArgs.resolved guild = ExtensionArgs.guild author = ExtensionArgs.author response = ExtensionArgs.response if user.bot: raise Error("Bots cannot have Roblox accounts!", hidden=True) try: roblox_user, _ = await get_user(user=user, guild=guild, cache=True, includes=True) except RobloxNotFound: raise Error("This Roblox account doesn't exist.") except RobloxAPIError: raise Error("The Roblox API appears to be down so I was unable to retrieve the information. Please try again later.") except UserNotVerified: raise Error("This user is not linked to Bloxlink!") else: author_accounts = await get_accounts(author) card = Card(user, author, author_accounts, roblox_user, "getinfo", guild, from_interaction=True) await card() card.response = response message = await response.send(files=[card.front_card_file], view=card.view) card.message = message card.view.message = message
async def validate_items(message, content): items = content.replace(" ", "").split(",")[:3] favorite_items = set() for item in items: item_id = catalog_id_regex.search(item) if item_id: item_id = item_id.group(1) else: item_id = item if not item_id.isdigit(): raise Error( f"Unable to resolve `{item_id}` into a **Catalog Item ID.**" ) try: item = await get_catalog_item(item_id) except RobloxNotFound: raise Error( f"Unable to resolve `{item_id}` into a **Catalog Item ID.**" ) favorite_items.add(item_id) return favorite_items
async def __main__(self, CommandArgs): author = CommandArgs.author guild = CommandArgs.guild transfer_to = CommandArgs.parsed_args.get("user") response = CommandArgs.response prefix = CommandArgs.prefix if transfer_to.bot: raise Message("You cannot transfer your premium to bots!", type="confused") author_data = await self.r.db("bloxlink").table("users").get(str(author.id)).run() or {"id": str(author.id)} time_now = time.time() author_premium_data = author_data.get("premium", {}) transfer_cooldown = author_premium_data.get("transferCooldown") or 0 on_cooldown = transfer_cooldown > time_now if on_cooldown: days_left = math.ceil((transfer_cooldown - time_now)/86400) raise Message(f"You recently transferred your premium! You may transfer again in **{days_left}** day{days_left > 1 and 's' or ''}.", type="silly") if author_premium_data.get("transferTo"): raise Message(f"You are currently transferring your premium to another user! Please disable it with `{prefix}transfer " "disable` first.", type="info") elif author_premium_data.get("transferFrom"): raise Message("You may not transfer premium that someone else transferred to you. You must first revoke the transfer " f"with `{prefix}transfer disable`.", type="confused") prem_data, _ = await get_features(author, author_data=author_data, cache=False, rec=False, partner_check=False) if not prem_data.features.get("premium"): raise Error("You must have premium in order to transfer it!") recipient_data = await self.r.db("bloxlink").table("users").get(str(transfer_to.id)).run() or {} recipient_data_premium = recipient_data.get("premium", {}) if recipient_data_premium.get("transferFrom"): raise Error(f"Another user is already forwarding their premium to this user. The recipient must run `{prefix}transfer disable` " "to revoke the external transfer.") await CommandArgs.prompt([{ "prompt": f"Are you sure you want to transfer your premium to **{transfer_to}**?\n" "You will not be able transfer again for **5** days! We also do __not__ " "remove cool-downs for __any reason at all.__\n\nYou will be " f"able to cancel the transfer at anytime with `{prefix}transfer disable`.", "footer": "Please say **yes** to complete the transfer.", "type": "choice", "choices": ("yes",), "name": "_", "embed_title": "Premium Transfer Confirmation", "embed_color": BROWN_COLOR, "formatting": False }]) await transfer_premium(transfer_from=author, transfer_to=transfer_to, guild=guild, apply_cooldown=True) await response.success(f"Successfully **transferred** your premium to **{transfer_to}!**")
async def __main__(self, CommandArgs): target = CommandArgs.parsed_args["target"] or CommandArgs.message.author flags = CommandArgs.flags guild = CommandArgs.message.guild response = CommandArgs.response prefix = CommandArgs.prefix if target.bot: raise Message("Bots can't have Roblox accounts!", type="silly") valid_flags = ["username", "id", "avatar", "premium", "badges", "groups", "description", "age", "banned"] if not all(f in valid_flags for f in flags.keys()): raise Error(f"Invalid flag! Valid flags are: ``{', '.join(valid_flags)}``") #async with response.loading(): if guild: role_binds, group_ids, _ = await get_binds(guild_data=CommandArgs.guild_data, trello_board=CommandArgs.trello_board) else: role_binds, group_ids = {}, {} try: account, accounts = await get_user(*flags.keys(), author=target, guild=guild, group_ids=(group_ids, role_binds), send_embed=True, response=response, everything=not bool(flags), basic_details=not bool(flags)) except UserNotVerified: raise Error(f"**{target}** is not linked to Bloxlink.") else: if not account: raise Message(f"You have no primary account set! Please use ``{prefix}switchuser`` and set one.", type="silly")
async def validate_games(message, content): games = content.replace(" ", "").split(",")[:3] favorite_games = set() for game in games: game_id = game_id_regex.search(game) if game_id: game_id = game_id.group(1) else: game_id = game if not game_id.isdigit(): raise Error( f"Unable to resolve `{game_id}` into a **Game ID.**") try: game = await get_game(game_id) except RobloxNotFound: raise Error( f"Unable to resolve `{game_id}` into a **Game ID.**") favorite_games.add(game_id) return favorite_games
async def __main__(self, CommandArgs): response = CommandArgs.response guild = CommandArgs.guild author = CommandArgs.author roblox_info = CommandArgs.parsed_args["roblox_name"] discord_user = CommandArgs.parsed_args["discord_user"] roblox_id = roblox_name = None if roblox_info and discord_user: raise Message( "Please only specify a Roblox username OR a Discord user!", type="silly") elif not (roblox_info or discord_user): discord_user = CommandArgs.author if roblox_info: roblox_id = self.autocomplete_regex.search(roblox_info) if not roblox_id: roblox_name = roblox_info else: roblox_id = roblox_id.group(1) try: roblox_user, _ = await get_user(user=discord_user, roblox_id=roblox_id, roblox_name=roblox_name, guild=guild, cache=True, includes=True) except RobloxNotFound: raise Error("This Roblox account doesn't exist.") except RobloxAPIError: raise Error( "The Roblox API appears to be down so I was unable to retrieve the information. Please try again later." ) except UserNotVerified: raise Error("This user is not linked to Bloxlink!") else: author_accounts = await get_accounts(author) card = Card(discord_user or author, author, author_accounts, roblox_user, "getinfo", guild, from_interaction=True) await card() card.response = response message = await response.send(files=[card.front_card_file], view=card.view) card.message = message card.view.message = message
async def __main__(self, CommandArgs): response = CommandArgs.response message = CommandArgs.message author = CommandArgs.author guild = CommandArgs.guild prefix = CommandArgs.prefix user = CommandArgs.parsed_args["user"] or author trello_board = CommandArgs.trello_board guild_data = CommandArgs.guild_data inactive_role = await get_inactive_role(guild, guild_data, trello_board) #async with response.loading(): try: roblox_user, _ = await get_user(author=user, guild=guild) except UserNotVerified: if user == author: if message: message.content = f"{CommandArgs.prefix}verify" return await parse_message(message) else: raise Error(f"You're not linked to Bloxlink! Please run `{prefix}verify add`.") else: raise Error(f"**{user}** is not linked to Bloxlink.") else: embed = await get_profile(author=author, user=user, roblox_user=roblox_user, prefix=prefix, inactive_role=inactive_role, guild=guild, guild_data=guild_data) await response.send(embed=embed)
async def __main__(self, ExtensionArgs): user = ExtensionArgs.resolved guild = ExtensionArgs.guild guild_data = ExtensionArgs.guild_data response = ExtensionArgs.response prefix = ExtensionArgs.prefix if user.bot: raise Error("Bots cannot have Roblox accounts!", hidden=True) if guild: role_binds, group_ids, _ = await get_binds(guild_data=guild_data) else: role_binds, group_ids = {}, {} try: account, accounts = await get_user(author=user, guild=guild, group_ids=(group_ids, role_binds), send_embed=True, send_ephemeral=True, response=response, everything=True) except UserNotVerified: raise Error(f"**{user}** is not linked to Bloxlink.", hidden=True) else: if not account: raise Message( f"This Discord user has no primary account set! They may use `{prefix}switchuser` to set one.", type="info", hidden=True)
async def __main__(self, CommandArgs): trello_board = CommandArgs.trello_board guild_data = CommandArgs.guild_data guild = CommandArgs.guild author = CommandArgs.author response = CommandArgs.response prefix = CommandArgs.prefix trello_options = {} if trello_board: trello_options, _ = await get_options(trello_board) guild_data.update(trello_options) try: old_nickname = author.display_name added, removed, nickname, errors, warnings, roblox_user = await guild_obligations( author, guild = guild, guild_data = guild_data, join = True, roles = True, nickname = True, trello_board = CommandArgs.trello_board, cache = False, response = response, dm = False, exceptions = ("BloxlinkBypass", "Blacklisted", "UserNotVerified", "PermissionError", "RobloxDown", "RobloxAPIError") ) except BloxlinkBypass: raise Message("Since you have the `Bloxlink Bypass` role, I was unable to update your roles/nickname.", type="info") except Blacklisted as b: if isinstance(b.message, str): raise Error(f"{author.mention} has an active restriction for: `{b}`") else: raise Error(f"{author.mention} has an active restriction from Bloxlink.") except UserNotVerified: view = discord.ui.View() view.add_item(item=discord.ui.Button(style=discord.ButtonStyle.link, label="Verify with Bloxlink", url=VERIFY_URL, emoji="🔗")) view.add_item(item=discord.ui.Button(style=discord.ButtonStyle.link, label="Stuck? See a Tutorial", emoji="❔", url="https://www.youtube.com/watch?v=0SH3n8rY9Fg&list=PLz7SOP-guESE1V6ywCCLc1IQWiLURSvBE&index=2")) await response.send("To verify with Bloxlink, click the link below.", mention_author=True, view=view) except PermissionError as e: raise Error(e.message) else: welcome_message, embed, view = await format_update_embed(roblox_user, author, added=added, removed=removed, errors=errors, warnings=warnings, nickname=nickname if old_nickname != nickname else None, prefix=prefix, guild_data=guild_data) await post_event(guild, guild_data, "verification", f"{author.mention} ({author.id}) has **verified** as `{roblox_user.username}`.", GREEN_COLOR) await response.send(content=welcome_message, embed=embed, view=view, mention_author=True)
async def lookup(self, CommandArgs): """lookup a case by its ID""" case_id = (await CommandArgs.prompt([ { "prompt": "Please provide a Case ID.", "name": "case_id" } ], last=True))["case_id"] response = CommandArgs.response prefix = CommandArgs.prefix guild = CommandArgs.message.guild addon_data = await self.r.table("addonData").get(str(guild.id)).run() or {"id": str(guild.id)} court_data = addon_data.get("court") or {} cases = court_data.get("cases") or {} if not court_data: raise Error(f"You must set-up this add-on before you can use it! Please use ``{prefix}courtsetup`` " "to begin the set-up.") elif not cases: raise Error(f"This server has no active cases! Cases may be created with ``{prefix}case create``.") for channel_id, case in cases.items(): if case["caseID"] == case_id: break else: raise Error("I was unable to find the selected case! It may have already been closed.") group_members_ = case.get("groupMembers", {}) group_members_str = "None" if group_members_: group_members_str = [] for group, group_members in group_members_.items(): group_members_str.append(f"**{group}** {ARROW} {', '.join([f'<@{m}>' for m in group_members])}") group_members_str = "\n".join(group_members_str) embed = Embed(title=f"Case {case['caseName']}") embed.add_field(name="Case ID", value=case_id) embed.add_field(name="Presiding Judge", value=f"<@{case['presidingJudge']}>") embed.add_field(name="Case Channel", value=f"<#{channel_id}>") embed.add_field(name="Group Members", value=group_members_str, inline=False) await response.send(embed=embed)
async def unmute(self, CommandArgs): """unmute case members""" response = CommandArgs.response prefix = CommandArgs.prefix guild = CommandArgs.message.guild channel = CommandArgs.message.channel author = CommandArgs.message.author addon_data = await self.r.table("addonData").get(str(guild.id)).run() or {"id": str(guild.id)} court_data = addon_data.get("court") or {} groups = court_data.get("groups") or [] current_case = court_data.get("cases", {}).get(str(channel.id)) if not current_case: raise Error("You must run this command in a case channel!") elif int(current_case["presidingJudge"]) != author.id: raise Error("You must be the presiding judge in order to run this command!") elif not groups: raise Error("You need at least one group in order to run this command!") # TODO: add instructions on creating groups parsed_args = await CommandArgs.prompt([ { "prompt": f"Which group should be muted? Available groups: ``{groups}``", "name": "group", "type": "choice", "choices": groups, "formatting": False } ], last=True) group = parsed_args["group"] group_members = current_case["groupMembers"].get(group) or [] if not group_members: raise Error(f"This group has no members associated with it! Please add them with ``{prefix}case add``") if not guild.chunked: await guild.chunk() for member_id in group_members: member = guild.get_member(int(member_id)) if member: await channel.set_permissions(member, read_messages=True, send_messages=True) await response.success(f"Successfully **unmuted** the members from group ``{group}``!")
async def __main__(self, CommandArgs): target = CommandArgs.parsed_args["roblox_name"] flags = CommandArgs.flags response = CommandArgs.response message = CommandArgs.message guild = CommandArgs.guild prefix = CommandArgs.prefix if message and message.mentions and CommandArgs.string_args: message.content = f"{prefix}getinfo {CommandArgs.string_args[0]}" return await parse_message(message) valid_flags = ["username", "id", "avatar", "premium", "badges", "groups", "description", "age", "banned", "devforum"] if not all(f in valid_flags for f in flags.keys()): raise Error(f"Invalid flag! Valid flags are: `{', '.join(valid_flags)}`") username = ID = False if "username" in flags: username = True flags.pop("username") elif target.isdigit(): ID = True else: username = True #async with response.loading(): if guild: role_binds, group_ids, _ = await get_binds(guild_data=CommandArgs.guild_data, trello_board=CommandArgs.trello_board) else: role_binds, group_ids = {}, {} try: _, _ = await get_user(*flags.keys(), username=username and target, roblox_id=ID and target, group_ids=(group_ids, role_binds), send_embed=True, guild=guild, response=response, everything=not bool(flags), basic_details=not bool(flags)) except RobloxNotFound: raise Error("This Roblox account doesn't exist.") except RobloxAPIError: if ID: try: await Bloxlink.fetch_user(int(target)) except NotFound: raise Error("This Roblox account doesn't exist.") else: if message: message.content = f"{prefix}getinfo {target}" return await parse_message(message) else: raise Message(f"To search with Discord IDs, please use the `{prefix}getinfo` command.\n" "This command only searches by Roblox username or ID.", hidden=True, type="info") else: raise Error("This Roblox account doesn't exist.")
async def __main__(self, CommandArgs): trello_board = CommandArgs.trello_board guild_data = CommandArgs.guild_data guild = CommandArgs.guild author = CommandArgs.author response = CommandArgs.response prefix = CommandArgs.prefix trello_options = {} if trello_board: trello_options, _ = await get_options(trello_board) guild_data.update(trello_options) try: old_nickname = author.display_name added, removed, nickname, errors, warnings, roblox_user = await guild_obligations( author, guild = guild, guild_data = guild_data, roles = True, nickname = True, trello_board = CommandArgs.trello_board, cache = False, response = response, dm = False, exceptions = ("BloxlinkBypass", "Blacklisted", "UserNotVerified", "PermissionError", "RobloxDown", "RobloxAPIError") ) except BloxlinkBypass: raise Message("Since you have the `Bloxlink Bypass` role, I was unable to update your roles/nickname.", type="info") except Blacklisted as b: if isinstance(b.message, str): raise Error(f"{author.mention} has an active restriction for: `{b}`") else: raise Error(f"{author.mention} has an active restriction from Bloxlink.") except UserNotVerified: await response.reply("To verify with Bloxlink, please visit our website at " f"<{VERIFY_URL}>. It won't take long!\nStuck? See this video: <https://www.youtube.com/watch?v=hq496NmQ9GU>", hidden=True) except PermissionError as e: raise Error(e.message) else: welcome_message, embed = await format_update_embed(roblox_user, author, added=added, removed=removed, errors=errors, warnings=warnings, nickname=nickname if old_nickname != author.display_name else None, prefix=prefix, guild_data=guild_data) await post_event(guild, guild_data, "verification", f"{author.mention} ({author.id}) has **verified** as `{roblox_user.username}`.", GREEN_COLOR) await response.send(content=welcome_message, embed=embed)
async def cleanup(self, CommandArgs): """free old cases from the database""" guild = CommandArgs.message.guild author = CommandArgs.message.author prefix = CommandArgs.prefix response = CommandArgs.response addon_data = await self.r.table("addonData").get(str(guild.id)).run() or {"id": str(guild.id)} court_data = addon_data.get("court") or {} cases = court_data.get("cases") or {} removed = 0 my_permissions = guild.me.guild_permissions if not (my_permissions.manage_channels and my_permissions.manage_roles): raise Error("I need both the ``Manage Channels`` and ``Manage Roles`` permissions.") elif not court_data: raise Error(f"You must set-up this add-on before you can use it! Please use ``{prefix}courtsetup`` " "to begin the set-up.") elif not cases: raise Message("Cannot clean cases: you have no cases saved to the database.", type="silly") for judge_role_id in court_data.get("judgeRoles", []): if find(lambda r: r.id == int(judge_role_id), author.roles): break else: raise Error("You must have a Judge role in order to run this command!") for channel_id, case in dict(cases).items(): case_channel = guild.get_channel(int(channel_id)) if not case_channel or (case_channel and case_channel.category and case_channel.category.id != case["archiveCategory"]): cases.pop(channel_id) removed += 1 court_data["cases"] = cases addon_data["court"] = court_data await self.r.table("addonData").insert(addon_data, conflict="replace").run() if removed: await response.success(f"Successfully removed **{removed}** old case(s) from the database.") else: await response.silly("No cases to clean: all case channels still exist in your server.")
async def remove(self, CommandArgs): """allow a user or group back in your server""" guild = CommandArgs.guild response = CommandArgs.response restrictions = await get_guild_value(guild, "restrictions") or {} remove_data = CommandArgs.parsed_args["restriction_data"] remove_data_match = self._remove_data_regex.search(remove_data) if not remove_data_match: raise Message("You must select an option from the dropdown!", type="silly") else: directory_name, remove_id = remove_data_match.group(1), remove_data_match.group(2) if directory_name and remove_id: if restrictions.get(directory_name, {}).get(remove_id): restrictions[directory_name].pop(remove_id) if not restrictions[directory_name]: restrictions.pop(directory_name, None) if not restrictions: restrictions = None await set_guild_value(guild, restrictions=restrictions) await response.success(f"Successfully **removed** this **{directory_name[:-1]}** from your restrictions.") else: raise Error(f"This **{directory_name[:-1]}** isn't restricted!")
async def __main__(self, CommandArgs): response = CommandArgs.response author = CommandArgs.message.author guild = CommandArgs.message.guild guild_data = CommandArgs.guild_data disabled_commands = guild_data.get("disabledCommands", {}) disable_type = CommandArgs.parsed_args["disable_type"] command_name = CommandArgs.parsed_args["command_name"] if command_name in ("disable", "enable") or commands[command_name].developer_only: raise Error("You can't disable this command!") enable = disable_where = "" if isinstance(disable_type, TextChannel): channel_id = str(disable_type.id) disabled_commands["channels"] = disabled_commands.get( "channels", {}) disable_where = f"for channel {disable_type.mention}" if disabled_commands["channels"].get(channel_id): disabled_commands["channels"].pop(channel_id) enable = "enabled" else: disabled_commands["channels"][channel_id] = command_name enable = "disabled" else: disabled_commands["global"] = disabled_commands.get("global", []) disable_where = "**globally**" if command_name in disabled_commands["global"]: disabled_commands["global"].remove(command_name) enable = "enabled" else: disabled_commands["global"].append(command_name) enable = "disabled" guild_data["disabledCommands"] = disabled_commands await self.r.table("guilds").insert(guild_data, conflict="replace").run() await set_guild_value(guild, "disabledCommands", disabled_commands) await response.success( f"Successfully **{enable}** command ``{command_name}`` {disable_where} for non-admins.\n" "If you would like to grant a certain person access to use this command, give them a role called ``Bloxlink Bypass``." ) await post_event( guild, guild_data, "configuration", f"{author.mention} ({author.id}) has **{enable}** the command ``{command_name}`` {disable_where}.", BROWN_COLOR)
async def validate(message, content): try: await trello_board.sync(card_limit=TRELLO["CARD_LIMIT"], list_limit=TRELLO["LIST_LIMIT"]) except TrelloNotFound: raise Error( "Something happened to your Trello board! Was it deleted? Set-up cancelled." ) except TrelloUnauthorized: raise Error( "I've lost permissions to view your Trello board! Please run this command " "again. Set-up cancelled.") for List in await trello_board.get_lists(): if List.name == code: return True for card in await List.get_cards(): if code in (card.name, card.desc): return True return None, "Failed to find the code on your Trello board. Please try again."
async def change(self, CommandArgs): """change a server add-on""" response = CommandArgs.response prefix = CommandArgs.prefix guild = CommandArgs.message.guild guild_data = CommandArgs.guild_data guild_addons = guild_data.get("addons", {}) toggleable_addons = [str(x) for x in filter(lambda x: getattr(x, 'toggleable', True), addons.values())] parsed_args = await CommandArgs.prompt([ { "prompt": f"Please choose the add-on you would like to change: ``{toggleable_addons}``", "name": "addon_choice", "type": "choice", "choices": toggleable_addons, "formatting": False }, { "prompt": "Would you like to **enable** or **disable** this add-on?", "name": "enable", "type": "choice", "choices": ["enable", "disable"] }, ], last=True) addon_choice = parsed_args["addon_choice"] enable = parsed_args["enable"] == "enable" if enable: if getattr(addons[addon_choice], "premium", False): donator_profile, _ = await get_features(Object(id=guild.owner_id), guild=guild) if not donator_profile.features.get("premium"): raise Error(f"You must have premium in order to enable this add-on. Please use ``{prefix}donate`` " "for instructions on donating.") await response.success(f"Successfully **{parsed_args['enable']}d** the **{addon_choice.title()}** add-on! You should " f"now see additional commands if you run ``{prefix}help``.") else: await response.success(f"Successfully **{parsed_args['enable']}d** the **{addon_choice.title()}** add-on! These " f"commands have been removed from your ``{prefix}help`` menu.") guild_addons[addon_choice] = enable guild_data["addons"] = guild_addons await self.r.table("guilds").insert(guild_data, conflict="update").run() await set_guild_value(guild, "addons", guild_addons)
async def add(self, CommandArgs): """add a new Magic Role to Bloxlink""" guild_data = CommandArgs.guild_data guild = CommandArgs.guild response = CommandArgs.response author = CommandArgs.author prefix = CommandArgs.prefix premium_status, _ = await get_features(discord.Object(id=guild.owner_id), guild=guild) if not premium_status.features.get("premium"): magic_roles_desc = "\n".join([f'**{x}** {ARROW} {y}' for x,y in MAGIC_ROLES.items()]) raise Error("Customizing Magic Roles is reserved for __Bloxlink Premium subscribers!__ You may find out " f"more information with the `{prefix}donate` command.\n\n" "However, you may manually create a Bloxlink Magic Role " f"and assign it one of these names, then give it to people!\n{magic_roles_desc}") parsed_args = await CommandArgs.prompt([ { "prompt": "Which `role` would you like to use for this Magic Role?", "name": "role", "type": "role" }, { "prompt": "Now the fun part! Let's choose what this Magic Role can do :sparkles:.\n\n" "Please select the features this Magic Role can do.", "name": "features", "components": [discord.ui.Select(max_values=len(MAGIC_ROLES), options=[ discord.SelectOption(label=k, description=v) for k,v in MAGIC_ROLES.items() ])], "type": "choice", "choices": ("Bloxlink Bypass", "Bloxlink Updater", "Bloxlink Admin") } ]) role = parsed_args["role"] features = parsed_args["features"] magic_roles = guild_data.get("magicRoles", {}) magic_roles[str(role.id)] = features guild_data["magicRoles"] = magic_roles await self.r.table("guilds").insert(guild_data, conflict="update").run() await set_guild_value(guild, "magicRoles", magic_roles) await post_event(guild, guild_data, "configuration", f"{author.mention} ({author.id}) has **added** a new `magicRole`!", BROWN_COLOR) await response.success("Successfully **saved** your new Magic Role! Go assign it to some people! :sparkles:")
async def __main__(self, ExtensionArgs): user = ExtensionArgs.resolved guild = ExtensionArgs.guild guild_data = ExtensionArgs.guild_data response = ExtensionArgs.response if user.bot: raise Error("You cannot update bots!", hidden=True) if isinstance(user, discord.User): try: user = await guild.fetch_member(user.id) except discord.errors.NotFound: raise Error("This user isn't in your server!") try: added, removed, nickname, errors, warnings, roblox_user = await guild_obligations( user, guild=guild, guild_data=guild_data, roles=True, nickname=True, cache=False, dm=False, event=True, exceptions=("BloxlinkBypass", "Blacklisted", "CancelCommand", "UserNotVerified", "PermissionError", "RobloxDown", "RobloxAPIError")) await response.send( f"{REACTIONS['DONE']} **Updated** {user.mention}", hidden=True) except BloxlinkBypass: raise Message( "Since this user has the Bloxlink Bypass role, I was unable to update their roles/nickname.", type="info", hidden=True) except Blacklisted as b: if isinstance(b.message, str): raise Error( f"{user.mention} has an active restriction for: `{b}`", hidden=True) else: raise Error( f"{user.mention} has an active restriction from Bloxlink.", hidden=True) except CancelCommand: pass except UserNotVerified: raise Error("This user is not linked to Bloxlink.", hidden=True) except PermissionError as e: raise Error(e.message, hidden=True)
async def __main__(self, CommandArgs): guild = CommandArgs.message.guild target = CommandArgs.parsed_args["target"] flags = CommandArgs.flags response = CommandArgs.response username = ID = False if "username" in flags: username = True elif target.isdigit(): ID = True else: username = True #async with response.loading(): try: account, _ = await get_user(username=username and target, roblox_id=ID and target) except RobloxNotFound: raise Error("This Roblox account doesn't exist.") else: roblox_id = account.id discord_ids = (await self.r.db("bloxlink").table( "robloxAccounts").get(roblox_id).run() or {}).get("discordIDs") results = [] if discord_ids: for discord_id in discord_ids: try: user = await guild.fetch_member(int(discord_id)) except NotFound: pass else: results.append(f"{user.mention} ({user.id})") embed = Embed(title=f"Reverse Search for {account.username}") embed.set_thumbnail(url=account.avatar) if results: embed.description = "\n".join(results) else: embed.description = "No results found." await response.send(embed=embed)
async def remove(self, CommandArgs): """allow a user or group back in your server""" guild = CommandArgs.guild response = CommandArgs.response guild_data = CommandArgs.guild_data restrictions = guild_data.get("restrictions", {}) resolvable = (await CommandArgs.prompt([{ "prompt": "Please either mention a user, give a Group URL, or a Roblox username.", "name": "resolvable", "validation": self.resolve_restriction }]))["resolvable"] if restrictions.get(resolvable[0], {}).get(str(resolvable[3])): restrictions[resolvable[0]].pop(str(resolvable[3])) if not restrictions[resolvable[0]]: restrictions.pop(resolvable[0], None) guild_data["restrictions"] = restrictions await set_guild_value(guild, "restrictions", restrictions) await self.r.table("guilds").insert(guild_data, conflict="replace").run() await response.success( f"Successfully **removed** this **{resolvable[1]}** from your restrictions." ) else: raise Error(f"This **{resolvable[1]}** isn't restricted!")
async def __main__(self, CommandArgs): guild = CommandArgs.guild response = CommandArgs.response guild_data = CommandArgs.guild_data trello_board = CommandArgs.trello_board prefix = CommandArgs.prefix author = CommandArgs.author locale = CommandArgs.locale role_binds_trello, group_ids_trello, trello_binds_list = await get_binds(guild=guild, trello_board=trello_board) bind_count = count_binds(guild_data, role_binds=role_binds_trello, group_ids=group_ids_trello) if bind_count >= FREE_BIND_COUNT: profile, _ = await get_features(Object(id=guild.owner_id), guild=guild) if not profile.features.get("premium"): raise Error(locale("commands.bind.errors.noPremiumBindLimitExceeded", prefix=prefix, free_bind_count=FREE_BIND_COUNT, prem_bind_count=PREM_BIND_COUNT)) if bind_count >= PREM_BIND_COUNT: raise Error(locale("commands.bind.errors.premiumBindLimitExceeded", prefix=prefix, prem_bind_count=PREM_BIND_COUNT)) parsed_args = await CommandArgs.prompt([ { "prompt": f"{locale('commands.bind.prompts.bindTypePrompt.line_1', arrow=ARROW)}\n" f"{locale('commands.bind.prompts.bindTypePrompt.line_2', arrow=ARROW)}\n" f"{locale('commands.bind.prompts.bindTypePrompt.line_3', arrow=ARROW)}\n" f"{locale('commands.bind.prompts.bindTypePrompt.line_4', arrow=ARROW)}\n" f"{locale('commands.bind.prompts.bindTypePrompt.line_5', arrow=ARROW)}", "name": "bind_choice", "type": "choice", "choices": locale("commands.bind.prompts.bindTypePrompt.choices"), "formatting": False }, { "prompt": locale("commands.bind.prompts.nicknamePrompt.line", prefix=prefix, nickname_templates=NICKNAME_TEMPLATES), "name": "nickname", "max": 100, "type": "string", "footer": locale("commands.bind.prompts.nicknamePrompt.footer"), "formatting": False } ]) bind_choice = parsed_args["bind_choice"].lower() nickname = parsed_args["nickname"] if trello_board: trello_binds_list = await trello_board.get_list(lambda l: l.name.lower() == "bloxlink binds") if not trello_binds_list: try: trello_binds_list = await trello_board.create_list(name="Bloxlink Binds") except TrelloUnauthorized: await response.error(locale("commands.bind.errors.trelloError")) except (TrelloNotFound, TrelloBadRequest): pass trello_card_binds, _ = await parse_trello_binds(trello_board=trello_board, trello_binds_list=trello_binds_list) else: trello_binds_list = None trello_group_bind = None trello_card_binds = { "groups": { "entire group": {}, "binds": {} }, "assets": {}, "badges": {}, "gamePasses": {} } if nickname.lower() in (locale("prompt.skip"), locale("prompt.done"), locale("prompt.next")): nickname = None nickname_lower = None else: nickname_lower = nickname.lower() if bind_choice == locale("commands.bind.group"): parsed_args_group = await CommandArgs.prompt([ { "prompt": locale("commands.bind.prompts.groupPrompt.line"), "name": "group", "validation": self.validate_group }, { "prompt": f"{locale('commands.bind.prompts.groupBindMode.line_1', arrow=ARROW)}\n" f"{locale('commands.bind.prompts.groupBindMode.line_2', arrow=ARROW)}\n" f"{locale('commands.bind.prompts.groupBindMode.line_3', arrow=ARROW)}", "name": "type", "type": "choice", "choices": locale("commands.bind.prompts.groupBindMode.choices") } ]) group = parsed_args_group["group"] group_id = group.group_id group_ids = guild_data.get("groupIDs", {}) found_group = trello_card_binds["groups"]["entire group"].get(group_id) or group_ids.get(group_id) trello_group_bind = trello_card_binds["groups"]["entire group"].get(group_id) if parsed_args_group["type"] == locale("commands.bind.entireGroup"): if found_group: if nickname and found_group["nickname"] != nickname: group_ids[group_id] = {"nickname": nickname, "groupName": group.name} guild_data["groupIDs"] = group_ids await self.r.table("guilds").insert(guild_data, conflict="update").run() trello_group_bind = trello_card_binds["groups"]["entire group"].get(group_id) make_trello_card = True if trello_group_bind and trello_group_bind["nickname"]: for card_data in trello_group_bind["trello"].get("cards", []): card = card_data["card"] try: await card.edit(desc=card.description.replace(trello_group_bind["nickname"], nickname)) except TrelloUnauthorized: await response.error("In order for me to edit your Trello binds, please add `@bloxlink` to your " "Trello board.") except (TrelloNotFound, TrelloBadRequest): pass make_trello_card = False if make_trello_card: try: await trello_binds_list.create_card(name="Bloxlink Group Bind", desc=f"Group: {group_id}\nNickname: {nickname}") except TrelloUnauthorized: await response.error("In order for me to edit your Trello binds, please add `@bloxlink` to your " "Trello board.") except (TrelloNotFound, TrelloBadRequest): pass if trello_binds_list: trello_binds_list.parsed_bind_data = None ending_s = group.name.endswith("s") and "'" or "'s" await post_event(guild, guild_data, "bind", f"{author.mention} ({author.id}) has **changed** `{group.name}`{ending_s} nickname template.", BLURPLE_COLOR) await clear_guild_data(guild) raise Message("Since your group is already linked, the nickname was updated.", type="success") else: raise Message("This group is already linked.", type="silly") for roleset in group.rolesets: roleset_name = roleset.get("name") roleset_rank = roleset.get("rank") if roleset_rank: discord_role = find(lambda r: r.name == roleset_name, guild.roles) if not discord_role: try: discord_role = await guild.create_role(name=roleset_name) except Forbidden: raise PermissionError("I was unable to create the Discord role. Please ensure my role has the `Manage Roles` permission.") # add group to guild_data.groupIDs group_ids[group_id] = {"nickname": nickname not in ("skip", "next") and nickname, "groupName": group.name} guild_data["groupIDs"] = group_ids await self.r.table("guilds").insert(guild_data, conflict="update").run() if trello_binds_list: try: await trello_binds_list.create_card(name="Bloxlink Group Bind", desc=f"Group: {group_id}\nNickname: {nickname}") except TrelloUnauthorized: await response.error("In order for me to edit your Trello binds, please add `@bloxlink` to your " "Trello board.") except (TrelloNotFound, TrelloBadRequest): pass await post_event(guild, guild_data, "bind", f"{author.mention} ({author.id}) has **linked** group `{group.name}`.", BLURPLE_COLOR) await clear_guild_data(guild) raise Message("Success! Your group was successfully linked.", type="success") else: # select ranks from their group # ask if they want to auto-create the binds or select a specific role # shows confirmation embed with arrows from rank to discord role discord_role = await CommandArgs.prompt([ { "prompt": "Please provide **Discord role name(s)** for this bind, separated by commas.", "name": "role", "type": "role", "multiple": True, "max": 10 } ]) discord_roles = discord_role["role"] new_ranks = {"binds":[], "ranges": []} role_binds = guild_data.get("roleBinds") or {} if isinstance(role_binds, list): role_binds = role_binds[0] role_binds["groups"] = role_binds.get("groups") or {} # {"groups": {"ranges": {}, "binds": {}}} role_binds["groups"][group_id] = role_binds["groups"].get(group_id) or {} role_binds["groups"][group_id]["binds"] = role_binds["groups"][group_id].get("binds") or {} role_binds["groups"][group_id]["ranges"] = role_binds["groups"][group_id].get("ranges") or {} role_binds["groups"][group_id]["groupName"] = group.name rolesets_embed = Embed(title=f"{group.name} Rolesets", description="\n".join(f"**{x.get('name')}** {ARROW} {x.get('rank')}" for x in group.rolesets if x.get('rank'))) rolesets_embed = await CommandArgs.response.send(embed=rolesets_embed) response.delete(rolesets_embed) failures = 0 while True: if failures == 5: raise Error("Too many failed attempts. Please run this command again.") selected_ranks = await CommandArgs.prompt([ { "prompt": f"Please select the rolesets that should receive the role(s) **{', '.join([r.name for r in discord_roles])}**. " "You may specify the roleset name or ID. You may provide them in a list, " "or as a range. You may also say `everyone` to capture everyone in the group; " "and you can negate the number to catch everyone with the rank _and above._\n" "You can also say `guest` to include **all non-group members**.\n" "Example 1: `1,4,-6,VIP, 10, 50-100, Staff Members, 255`.\nExample 2: `" "-100` means everyone with rank 100 _and above._\nExample 3: `everyone` " "means everyone in the group.\n\n" "For your convenience, your Rolesets' names and IDs were sent above.", "name": "ranks", "formatting": False } ], last=True) pending_roleset_names = [] for rank in selected_ranks["ranks"].split(","): rank = rank.strip() if rank.isdigit(): new_ranks["binds"].append(str(rank)) elif rank in ("all", "everyone"): new_ranks["binds"].append("all") elif rank in ("0", "guest"): new_ranks["binds"].append("0") elif rank[:1] == "-": try: int(rank) except ValueError: pass else: new_ranks["binds"].append(rank) else: range_search = bind_num_range.search(rank) if range_search: num1, num2 = range_search.group(1), range_search.group(2) new_ranks["ranges"].append([num1, num2]) else: # they specified a roleset name as a string pending_roleset_names.append(rank) if pending_roleset_names: found = False for roleset in group.rolesets: roleset_name = roleset.get("name") roleset_rank = roleset.get("rank") if roleset_name in pending_roleset_names and roleset_name not in new_ranks["binds"]: new_ranks["binds"].append(str(roleset_rank)) found = True if not found: response.delete(await response.error("Could not find a matching Roleset name. Please try again.")) failures += 1 continue break if new_ranks["binds"]: for x in new_ranks["binds"]: rank = role_binds["groups"][group_id].get("binds", {}).get(x, {}) if not isinstance(rank, dict): rank = {"nickname": nickname_lower, "roles": [str(rank)]} for discord_role in discord_roles: role_id = str(discord_role.id) if role_id not in rank["roles"]: rank["roles"].append(role_id) else: for discord_role in discord_roles: role_id = str(discord_role.id) if role_id not in rank.get("roles", []): rank["roles"] = rank.get("roles") or [] rank["roles"].append(role_id) if nickname_lower: rank["nickname"] = nickname else: if not rank.get("nickname"): rank["nickname"] = None role_binds["groups"][group_id]["binds"][x] = rank # trello binds: # rank is in list of ranks # update nickname # append role # else: make new card if trello_binds_list: make_binds_card = True if trello_card_binds: trello_bind_group = trello_card_binds["groups"]["binds"].get(group_id, {}).get("binds") if trello_bind_group: card_data_ = trello_bind_group.get(x) if card_data_: for card in card_data_.get("trello", {}).get("cards", []): trello_card = card["card"] trello_ranks = card.get("ranks") or [] if (x in trello_ranks or x == "all") and len(trello_ranks) == 1: trello_bind_roles = card.get("roles", set()) card_bind_data = [ f"Group: {group_id}", f"Nickname: {(nickname != 'skip' and nickname) or rank.get('nickname') or card_data_.get('nickname') or 'None'}", ] for discord_role in discord_roles: trello_bind_roles.add(discord_role.name) card_bind_data.append(f"Roles: {', '.join(trello_bind_roles)}") card_bind_data.append(f"Ranks: {card['trello_str']['ranks']}") trello_card_desc = "\n".join(card_bind_data) if trello_card_desc != trello_card.description: trello_card.description = trello_card_desc try: await trello_card.edit(desc=trello_card_desc) except TrelloUnauthorized: await response.error("In order for me to edit your Trello binds, please add `@bloxlink` to your " "Trello board.") except (TrelloNotFound, TrelloBadRequest): pass trello_binds_list.parsed_bind_data = None make_binds_card = False break if make_binds_card: card_bind_data = [ f"Group: {group_id}", f"Nickname: {nickname != 'skip' and nickname or 'None'}", f"Roles: {', '.join([r.name for r in discord_roles])}", ] if x != "all": card_bind_data.append(f"Ranks: {x}") trello_card_desc = "\n".join(card_bind_data) try: card = await trello_binds_list.create_card(name="Bloxlink Bind", desc=trello_card_desc) except TrelloUnauthorized: await response.error("In order for me to edit your Trello binds, please add `@bloxlink` to your " "Trello board.") except (TrelloNotFound, TrelloBadRequest): pass trello_binds_list.parsed_bind_data = None if new_ranks["ranges"]: role_binds["groups"][group_id]["ranges"] = role_binds["groups"][group_id].get("ranges") or [] for x in new_ranks["ranges"]: # list of dictionaries: [{"high": 10, "low": 1, "nickname": ""},...] range_, num = self.find_range(x, role_binds["groups"][group_id]["ranges"]) found = bool(range_) for discord_role in discord_roles: role_id = str(discord_role.id) if not role_id in range_.get("roles", []): range_["roles"] = range_.get("roles") or [] range_["roles"].append(role_id) if nickname_lower: range_["nickname"] = nickname else: if not range_.get("nickname"): range_["nickname"] = None if found: role_binds["groups"][group_id]["ranges"][num] = range_ else: range_["low"] = int(x[0]) range_["high"] = int(x[1]) role_binds["groups"][group_id]["ranges"].append(range_) if trello_binds_list: make_binds_card = True if trello_card_binds: trello_range_group = trello_card_binds["groups"]["binds"].get(group_id, {}).get("ranges") if trello_range_group: for trello_range in trello_range_group: trello_data = trello_range["trello"] for card in trello_data.get("cards", []): trello_card = card["card"] trello_ranks = card.get("ranks", []) if trello_range["low"] == range_["low"] and trello_range["high"] == range_["high"] and len(trello_ranks) == 1: trello_data = trello_range["trello"] trello_bind_roles = trello_range.get("roles", set()) card_bind_data = [ f"Group: {group_id}", f"Nickname: {(nickname != 'skip' and nickname) or trello_range.get('nickname') or 'None'}", ] for discord_role in discord_roles: trello_bind_roles.add(discord_role.name) card_bind_data.append(f"Roles: {', '.join(trello_bind_roles)}") card_bind_data.append(f"Ranks: {card['trello_str']['ranks']}") trello_card_desc = "\n".join(card_bind_data) if trello_card_desc != trello_card.description: trello_card.description = trello_card_desc try: await trello_card.edit(desc=trello_card_desc) except TrelloUnauthorized: await response.error("In order for me to edit your Trello binds, please add `@bloxlink` to your " "Trello board.") except (TrelloNotFound, TrelloBadRequest): pass trello_binds_list.parsed_bind_data = None make_binds_card = False break if make_binds_card: card_bind_data = [ f"Group: {group_id}", f"Nickname: {nickname != 'skip' and nickname or 'None'}", f"Roles: {', '.join([r.name for r in discord_roles])}", f"Ranks: {range_['low']}-{range_['high']}" ] trello_card_desc = "\n".join(card_bind_data) try: card = await trello_binds_list.create_card(name="Bloxlink Range Bind", desc=trello_card_desc) except TrelloUnauthorized: await response.error("In order for me to edit your Trello binds, please add `@bloxlink` to your " "Trello board.") except (TrelloNotFound, TrelloBadRequest): pass trello_binds_list.parsed_bind_data = None await self.r.table("guilds").insert({ "id": str(guild.id), "roleBinds": role_binds }, conflict="update").run() text = ["Successfully **bound** rank ID(s): `"] if new_ranks["binds"]: text.append(", ".join(new_ranks["binds"])) if new_ranks["ranges"]: text2 = "" if new_ranks["binds"]: text2 = "; " text.append(f"{text2}ranges: {', '.join([r[0] + ' - ' + r[1] for r in new_ranks['ranges']])}") text.append(f"` with Discord role(s) **{', '.join([r.name for r in discord_roles])}**.") text = "".join(text) await post_event(guild, guild_data, "bind", f"{author.mention} ({author.id}) has **bound** group `{group.name}`.", BLURPLE_COLOR) await clear_guild_data(guild) await response.success(text) elif bind_choice in ("asset", "badge", "gamepass"): if bind_choice == "gamepass": bind_choice_title = "GamePass" bind_choice_plural = "gamePasses" else: bind_choice_title = bind_choice.title() bind_choice_plural = f"{bind_choice}s" vg_parsed_args = await CommandArgs.prompt([ { "prompt": f"Please provide the **{bind_choice_title} ID** to use for this bind.", "name": "bind_id", "type": "number", "formatting": False }, { "prompt": "Please provide **Discord role name(s)** for this bind, separated by commas.", "name": "role", "type": "role", "multiple": True, "max": 10 }, ], last=True) discord_roles = vg_parsed_args["role"] bind_id = str(vg_parsed_args["bind_id"]) if bind_choice == "asset": try: text, response_ = await fetch(f"{API_URL}/marketplace/productinfo?assetId={bind_id}") except RobloxNotFound: raise Error(f"An Asset with ID `{bind_id}` does not exist.") json_data = await response_.json() display_name = json_data.get("Name") elif bind_choice == "badge": try: text, response_ = await fetch(f"https://badges.roblox.com/v1/badges/{bind_id}") except RobloxNotFound: raise Error(f"A Badge with ID `{bind_id}` does not exist.") json_data = await response_.json() display_name = json_data.get("displayName") elif bind_choice == "gamepass": bind_choice_title = "GamePass" bind_choice_plural = "gamePasses" try: text, response_ = await fetch(f"http://api.roblox.com/marketplace/game-pass-product-info?gamePassId={bind_id}") except (RobloxNotFound, RobloxAPIError): raise Error(f"A GamePass with ID `{bind_id}` does not exist.") json_data = await response_.json() if json_data.get("ProductType") != "Game Pass": raise Error(f"A GamePass with ID `{bind_id}` does not exist.") display_name = json_data.get("Name") role_binds = guild_data.get("roleBinds") or {} if isinstance(role_binds, list): role_binds = role_binds[0] role_binds[bind_choice_plural] = role_binds.get(bind_choice_plural) or {} role_binds[bind_choice_plural][bind_id] = role_binds[bind_choice_plural].get(bind_id) or {} role_binds[bind_choice_plural][bind_id]["nickname"] = nickname role_binds[bind_choice_plural][bind_id]["displayName"] = display_name role_binds[bind_choice_plural][bind_id]["roles"] = role_binds[bind_choice_plural][bind_id].get("roles", []) roles = role_binds[bind_choice_plural][bind_id]["roles"] for discord_role in discord_roles: role_id = str(discord_role.id) if not role_id in roles: roles.append(role_id) role_binds[bind_choice_plural][bind_id]["roles"] = roles if trello_binds_list: make_binds_card = True if trello_card_binds: trello_bind_vg = trello_card_binds.get(bind_choice_plural, {}).get(bind_id) if trello_bind_vg: trello_bind_roles = set(trello_bind_vg.get("roles", set())) for card in trello_bind_vg.get("trello", {})["cards"]: trello_card = card["card"] card_bind_data = [ f"{bind_choice_title} ID: {bind_id}", f"Display Name: {display_name}", f"Nickname: {(nickname != 'skip' and nickname) or trello_bind_vg.get('nickname') or 'None'}", ] for discord_role in discord_roles: trello_bind_roles.add(discord_role.name) card_bind_data.append(f"Roles: {', '.join(trello_bind_roles)}") trello_card_desc = "\n".join(card_bind_data) if trello_card_desc != trello_card.description: trello_card.description = trello_card_desc try: await trello_card.edit(desc=trello_card_desc) except TrelloUnauthorized: await response.error("In order for me to edit your Trello binds, please add `@bloxlink` to your " "Trello board.") except (TrelloNotFound, TrelloBadRequest): pass trello_binds_list.parsed_bind_data = None make_binds_card = False break if make_binds_card: card_bind_data = [ f"{bind_choice_title} ID: {bind_id}", f"Display Name: {display_name}", f"Nickname: {nickname != 'skip' and nickname or 'None'}", f"Roles: {', '.join([d.name for d in discord_roles])}", ] trello_card_desc = "\n".join(card_bind_data) try: card = await trello_binds_list.create_card(name=f"Bloxlink {bind_choice_title} Bind", desc=trello_card_desc) except TrelloUnauthorized: await response.error("In order for me to edit your Trello binds, please add `@bloxlink` to your " "Trello board.") except (TrelloNotFound, TrelloBadRequest): pass trello_binds_list.parsed_bind_data = None await self.r.table("guilds").insert({ "id": str(guild.id), "roleBinds": role_binds }, conflict="update").run() await post_event(guild, guild_data, "bind", f"{author.mention} ({author.id}) has **bound** {bind_choice_title} `{display_name}`.", BLURPLE_COLOR) await clear_guild_data(guild) await response.success(f"Successfully **bound** {bind_choice_title} `{display_name}` ({bind_id}) with Discord role(s) **{', '.join([r.name for r in discord_roles])}!**")
async def restore(self, CommandArgs): """restore your Server Data""" message = CommandArgs.message author = CommandArgs.message.author guild = CommandArgs.message.guild response = CommandArgs.response prefix = CommandArgs.prefix if author.id == OWNER: if message.attachments: attachment = message.attachments[0] if not attachment.height: file_data = await attachment.read() json_data = file_data.decode("utf8").replace("'", '"') json_data = json.loads(json_data) json_data["id"] = str(guild.id) if json_data.get("roleBinds"): role_map = {} for bind_type, bind_data in json_data.get("roleBinds", {}).items(): if bind_type == "groups": for group_id, group_data in bind_data.items(): for rank, rank_data in group_data.get("binds", {}).items(): for role_id in rank_data.get("roles", []): if not guild.get_role(int(role_id)): role_map_find = role_map.get(role_id) if not role_map_find: role = await guild.create_role(name=rank*6) role_map[role_id] = str(role.id) role_map_find = str(role.id) json_data["roleBinds"]["groups"][group_id]["binds"][rank]["roles"].remove(role_id) json_data["roleBinds"]["groups"][group_id]["binds"][rank]["roles"].append(role_map_find) await self.r.table("guilds").insert(json_data, conflict="replace").run() return await response.success("Successfully **restored** this server's data.") else: raise Error("You must supply a non-image file for data restore.") user_data = await self.r.db("bloxlink").table("users").get(str(author.id)).run() or {} user_backups = user_data.get("backups", []) if not user_backups: raise Message(f"You don't have any backups created! You may create them with ``{prefix}data backup``.", type="silly") embed = Embed(title="Bloxlink Data Restore", description="Please select the backup you could like to restore with the reactions.") for i, backup in enumerate(user_backups): guild_data = backup["data"] backup_name = backup["backupName"] timestamp = datetime.datetime.fromtimestamp(backup["timestamp"]) trello_board = await get_board(guild_data=guild_data, guild=guild) prefix, _ = await get_prefix(guild=guild, trello_board=trello_board) backup["prefix"] = prefix backup["trello_board"] = trello_board, backup["timestamp"] = timestamp backup["nickname_template"] = guild_data.get("nicknameTemplate", DEFAULTS.get("nicknameTemplate")) if trello_board: trello_options, _ = await get_options(trello_board) guild_data.update(trello_options) len_role_binds = count_binds(guild_data) backup["len_role_binds"] = len_role_binds embed.add_field(name=f"{INT_REACTIONS[i]} {ARROW} {backup_name}", value="\n".join([ f"**Role Binds** {ARROW} {len_role_binds}", f"**Prefix** {ARROW} {prefix}", f"**Nickname Template** {ARROW} {backup['nickname_template']}", f"**Created on ** {timestamp.strftime('%b. %d, %Y (%A)')}" ])) message = await response.send(embed=embed) if message: response.delete(message) for i, _ in enumerate(user_backups): emote_string = INT_REACTIONS[i] try: await message.add_reaction(emote_string) except Forbidden: raise PermissionError("I'm missing permission to add reactions to your message!") try: reaction, _ = await Bloxlink.wait_for("reaction_add", timeout=PROMPT["PROMPT_TIMEOUT"], check=self._reaction_check(author)) except TimeoutError: raise CancelledPrompt(f"timeout ({PROMPT['PROMPT_TIMEOUT']}s)") else: chosen_backup = None str_reaction = str(reaction) for i, reaction_string in enumerate(INT_REACTIONS): if str_reaction == reaction_string: chosen_backup = user_backups[i] if chosen_backup: parsed_args = await CommandArgs.prompt([ { "prompt": "**Warning!** This will **__restore__ ALL OF YOUR SETTINGS** including:\n" f"**{chosen_backup['len_role_binds']}** Role Binds\n" f"**{chosen_backup['prefix']}** prefix\n" f"**{chosen_backup['nickname_template']}** Nickname Template\n" "Continue? ``Y/N``", "name": "confirm", "type": "choice", "formatting": False, "choices": ("yes", "no"), "embed_title": "Warning!", "embed_color": ORANGE_COLOR, "footer": "Say **yes** to continue, or **no** to cancel." } ]) if parsed_args["confirm"] == "yes": await self._restore(guild, chosen_backup) await response.success("Successfully **restored** your backup!") else: raise CancelledPrompt("cancelled restore")
async def __main__(self, CommandArgs): response = CommandArgs.response user_slash = CommandArgs.parsed_args.get("user") role_slash = CommandArgs.parsed_args.get("role") users_ = CommandArgs.parsed_args.get("users") or ( [user_slash, role_slash] if user_slash or role_slash else None) prefix = CommandArgs.prefix message = CommandArgs.message author = CommandArgs.author guild = CommandArgs.guild guild_data = CommandArgs.guild_data users = [] if not (users_ and CommandArgs.has_permission): if not users_: if message: message.content = f"{prefix}getrole" return await parse_message(message) else: raise Message( f"To update yourself, please run the `{prefix}getrole` command.", hidden=True, type="info") else: raise Message( "You do not have permission to update users; you need the `Manage Roles` permission, or " "a role called `Bloxlink Updater`.", type="info", hidden=True) if isinstance(users_[0], Role): if not guild.chunked: await guild.chunk() for role in users_: users += role.members if not users: raise Error("These role(s) have no members in it!", hidden=True) else: users = users_ len_users = len(users) if self.redis: redis_cooldown_key = self.REDIS_COOLDOWN_KEY.format( release=RELEASE, id=guild.id) on_cooldown = await self.redis.get(redis_cooldown_key) if len_users > 3 and on_cooldown: cooldown_time = math.ceil( await self.redis.ttl(redis_cooldown_key) / 60) if not cooldown_time or cooldown_time == -1: await self.redis.delete(redis_cooldown_key) on_cooldown = None if on_cooldown: if on_cooldown == 1: raise Message(f"This server is still queued.") elif on_cooldown == 2: raise Message( "This server's scan is currently running.") elif on_cooldown == 3: cooldown_time = math.ceil( await self.redis.ttl(redis_cooldown_key) / 60) raise Message( f"This server has an ongoing cooldown! You must wait **{cooldown_time}** more minutes." ) donator_profile, _ = await get_features(Object(id=guild.owner_id), guild=guild) premium = donator_profile.features.get("premium") if not premium: donator_profile, _ = await get_features(author) premium = donator_profile.features.get("premium") cooldown = 0 if len_users > 10: if not premium: raise Error( "You need premium in order to update more than 10 members at a time! " f"Use `{prefix}donate` for instructions on donating.") if len_users >= 100: cooldown = math.ceil(((len_users / 1000) * 120) * 60) else: cooldown = 120 if self.redis: await self.redis.set(redis_cooldown_key, 2, ex=86400) trello_board = CommandArgs.trello_board #async with response.loading(): if len_users > 1: for user in users: if not user.bot: try: added, removed, nickname, errors, warnings, roblox_user = await guild_obligations( user, guild=guild, guild_data=guild_data, trello_board=trello_board, roles=True, nickname=True, dm=False, exceptions=("BloxlinkBypass", "UserNotVerified", "Blacklisted", "PermissionError", "RobloxDown"), cache=False) except BloxlinkBypass: if len_users <= 10: await response.info( f"{user.mention} **bypassed**") except UserNotVerified: if len_users <= 10: await response.send( f"{REACTIONS['ERROR']} {user.mention} is **not linked to Bloxlink**" ) except PermissionError as e: raise Error(e.message) except Blacklisted as b: if len_users <= 10: await response.send( f"{REACTIONS['ERROR']} {user.mention} has an active restriction." ) else: if len_users <= 10: await response.send( f"{REACTIONS['DONE']} **Updated** {user.mention}" ) else: user = users[0] if user.bot: raise Message("Bots can't have Roblox accounts!", type="silly") old_nickname = user.display_name try: added, removed, nickname, errors, warnings, roblox_user = await guild_obligations( user, guild=guild, guild_data=guild_data, trello_board=trello_board, roles=True, nickname=True, cache=False, dm=False, event=True, exceptions=("BloxlinkBypass", "Blacklisted", "CancelCommand", "UserNotVerified", "PermissionError", "RobloxDown", "RobloxAPIError")) _, embed = await format_update_embed( roblox_user, user, added=added, removed=removed, errors=errors, warnings=warnings, nickname=nickname if old_nickname != nickname else None, prefix=prefix, guild_data=guild_data) await response.send(embed=embed) except BloxlinkBypass: raise Message( "Since this user has the Bloxlink Bypass role, I was unable to update their roles/nickname.", type="info") except Blacklisted as b: if isinstance(b.message, str): raise Error( f"{user.mention} has an active restriction for: `{b}`" ) else: raise Error( f"{user.mention} has an active restriction from Bloxlink." ) except CancelCommand: pass except UserNotVerified: raise Error("This user is not linked to Bloxlink.") except PermissionError as e: raise Error(e.message) if cooldown: await self.redis.set(redis_cooldown_key, 3, ex=cooldown) if len_users > 10: await response.success("All users updated.")
async def __main__(self, CommandArgs): guild = CommandArgs.message.guild author = CommandArgs.message.author response = CommandArgs.response prefix = CommandArgs.prefix guild_data = CommandArgs.guild_data group_ids = guild_data.get("groupIDs", {}) settings_buffer = [] parsed_args_1 = {} parsed_args_2 = {} parsed_args_3 = {} parsed_args_4 = {} nickname = None parsed_args_1 = await CommandArgs.prompt([{ "prompt": "**Thank you for choosing Bloxlink!** In a few simple prompts, **we'll configure Bloxlink for your server.**\n\n" "**Pre-configuration:**\nBefore continuing, please ensure that Bloxlink has all the proper permissions, " "such as the ability to ``manage roles, nicknames, channels``, etc. If you do not set these " "permissions, you may encounter issues with using certain commands.", "name": "_", "footer": "Say **next** to continue.", "type": "choice", "choices": ["next"], "embed_title": "Setup Prompt" }, { "prompt": "Would you like to link a **Roblox group** to this Discord server? Please provide the **Group URL, or Group ID**.", "name": "group", "footer": "Say **skip** to leave as-is.", "embed_title": "Setup Prompt", "validation": self.validate_group }, { "prompt": "Would you like to change the **Verified role** (the role people are given if they're linked to Bloxlink) name to something else?\n" "Default: ``Verified``", "name": "verified_role", "footer": "Say **disable** to disable the Verified role.\nSay **skip** to leave as-is.", "embed_title": "Setup Prompt", "max": 50 }, { "prompt": "Would you like to link a **Trello.com board** to this server? You'll be able to change Bloxlink settings and binds from " "the board. Please either provide the **Trello board ID, or the board URL.**", "name": "trello_board", "footer": "Say **disable** to disable/clear a saved board.\nSay **skip** to leave as-is.", "embed_title": "Setup Prompt", "validation": self.validate_trello_board }], dm=True, no_dm_post=False) for k, v in parsed_args_1.items(): if k != "_": settings_buffer.append(f"**{k}** {ARROW} {v}") group = parsed_args_1["group"] verified = parsed_args_1["verified_role"] trello_board = parsed_args_1["trello_board"] if group not in ("next", "skip"): group_ids[group.group_id] = { "nickname": nickname, "groupName": group.name } parsed_args_2 = await CommandArgs.prompt([{ "prompt": "Should these members be given a nickname? Please create a nickname using these templates. You may " f"combine templates. The templates MUST match exactly.\n\n**Templates:** ```{NICKNAME_TEMPLATES}```", "name": "nickname", "embed_title": "Setup Prompt", "footer": "Say **disable** to not have a nickname.\nSay **skip** to leave this as the default.", "formatting": False }, { "prompt": "Would you like to automatically transfer your Roblox group ranks to Discord roles?\nValid choices:\n" "``merge`` — This will **NOT** remove any roles. Your group Rolesets will be **merged** with your current roles.\n" "``replace`` — **This will REMOVE and REPLACE your CURRENT ROLES** with your Roblox group Rolesets. You'll " "need to configure permissions and colors yourself.\n" "``skip`` — nothing will be changed.\n\nValid choices: (merge/replace/skip)", "name": "merge_replace", "type": "choice", "choices": ["merge", "replace", "skip", "next"], "footer": "Say either **merge**, **replace**, or **skip**", "embed_title": "Setup Prompt" }], dm=True, no_dm_post=True) if parsed_args_2["merge_replace"] == "next": parsed_args_2["merge_replace"] = "skip" nickname = parsed_args_2["nickname"] nickname_lower = nickname.lower() if nickname_lower == "skip": if group.group_id in group_ids: nickname = group_ids[group.group_id]["nickname"] else: nickname = NICKNAME_DEFAULT elif nickname_lower == "disable": nickname = None group_ids[group.group_id] = { "nickname": nickname, "groupName": group.name } for k, v in parsed_args_2.items(): if k != "_": settings_buffer.append(f"**{k}** {ARROW} {v}") if trello_board not in ("skip", "disable"): trello_code = generate_code() parsed_args_3 = await CommandArgs.prompt([{ "prompt": "We'll now attempt to verify that you own this Trello board. To begin, please add ``[email protected]`` (@bloxlink) " "to your Trello board. Then, say ``next`` to continue.", "name": "trello_continue", "type": "choice", "choices": ["next"], "footer": "Say **next** to continue.", "embed_title": "Trello Verification" }, { "prompt": f"Now, please make a card _anywhere_ on your Trello board with this code as the card name or description:```{trello_code}```\n" "Please note that up to **100** cards will be loaded from your Trello board, and up to **10** lists will be " "loaded. So, if you have more cards or lists, you'll need to archive some or use a different Trello board.", "name": "trello_continue", "type": "choice", "choices": ["next", "done"], "footer": "Say **done** after the code is on your Trello board.", "embed_title": "Trello Verification", "validation": await self.verify_trello_board(trello_board, trello_code) }], dm=True, no_dm_post=True) parsed_args_4 = await CommandArgs.prompt([{ "prompt": "You have reached the end of the setup. Here are your current settings:\n" + "\n".join(settings_buffer), "name": "setup_complete", "type": "choice", "footer": "Please say **done** to complete the setup.", "choices": ["done"], "embed_title": "Setup Prompt Confirmation", "embed_color": BROWN_COLOR, "formatting": False }], dm=True, no_dm_post=True) if group and group != "skip": merge_replace = parsed_args_2.get("merge_replace") if merge_replace not in ("skip", "next"): if merge_replace == "replace": for role in list(guild.roles): try: if not (role in guild.me.roles or role.is_default()): try: await role.delete( reason= f"{author} chose to replace roles through {prefix}setup" ) except Forbidden: pass except HTTPException: pass except AttributeError: # guild.me is None -- bot kicked out raise CancelCommand sorted_rolesets = sorted(group.rolesets, key=lambda r: r.get("rank"), reverse=True) for roleset in sorted_rolesets: roleset_name = roleset.get("name") # roleset_rank = roleset.get("rank") if not find(lambda r: r.name == roleset_name, guild.roles): try: await guild.create_role(name=roleset_name) except Forbidden: raise Error( "Please ensure I have the ``Manage Roles`` permission; setup aborted." ) if verified: if verified == "disable": guild_data["verifiedRoleEnabled"] = False elif verified not in ("next", "skip"): guild_data["verifiedRoleName"] = verified guild_data["verifiedRoleEnabled"] = True if trello_board: update_trello = False if trello_board == "disable": trello_board = None update_trello = True elif trello_board in ("skip", "next"): trello_board = guild_data.get("trelloID") else: trello_board = trello_board.id update_trello = True if update_trello: guild_data["trelloID"] = trello_board if group_ids: guild_data["groupIDs"] = group_ids await self.r.table("guilds").insert(guild_data, conflict="replace").run() await post_event( guild, guild_data, "configuration", f"{author.mention} ({author.id}) has **set-up** the server.", BROWN_COLOR) await clear_guild_data(guild) await response.success( "Your server is now **configured** with Bloxlink!", dm=True, no_dm_post=True) if trello_board and update_trello: trello_info_embed = Embed(title="Trello Information") trello_info_embed.description = "Now that you've linked your Trello board, you may now modify Bloxlink settings and Role Binds from " \ "the board. **No cards will be created right away;** cards are created **as you use commands**. For " \ f"example, if you use ``{prefix}settings`` or ``{prefix}bind``, then cards will be created/edited.\n\n" \ "Any pre-existing settings or binds will be simply be **merged** with any Trello settings/binds **unless " \ f"you switch \"``trelloBindMode``\" to ``replace`` through the ``{prefix}settings`` command.**" await response.send(embed=trello_info_embed, dm=True, no_dm_post=True)
async def __main__(self, CommandArgs): choice = CommandArgs.parsed_args["choice"] guild_data = CommandArgs.guild_data groups = CommandArgs.guild_data.get("groupLock", {}) guild = CommandArgs.guild author = CommandArgs.author prefix = CommandArgs.prefix response = CommandArgs.response if choice == "add": args = await CommandArgs.prompt([{ "prompt": "Please specify either the **Group ID** or **Group URL** that you would like " "to set as a requirement for new joiners.", "name": "group", "validation": self.validate_group }, { "prompt": "Should only a **specific Roleset** be allowed to join your server? You may specify a Roleset name or ID. You may " "provide them in a list, and you may negate the number to capture everyone catch everyone with the rank _and above_.\n" "Example: `-10, 5, VIP` means people who are ranked 5, VIP, or if their roleset is greater than 10 can join your server.", "name": "rolesets", "footer": "Say **skip** to skip this option; if skipped, the roleset people have wouldn't matter, they'll be able to enter " "your server as long as they're in your group.", "type": "list", "exceptions": ("skip", ), "max": 10, }, { "prompt": "Would you like people who are kicked to receive a custom DM? Please specify either `yes` or `no`.\n\n" "Note that Unverified users will receive a different DM on instructions to linking to Bloxlink.", "name": "dm_enabled", "type": "choice", "choices": ["yes", "no"] }]) group = args["group"] dm_enabled = args["dm_enabled"] == "yes" rolesets_raw = args[ "rolesets"] if args["rolesets"] != "skip" else None parsed_rolesets = [] if rolesets_raw: for roleset in rolesets_raw: if roleset.isdigit(): parsed_rolesets.append(int(roleset)) elif roleset[:1] == "-": try: roleset = int(roleset) except ValueError: pass else: parsed_rolesets.append(roleset) else: range_search = self._range_search.search(roleset) if range_search: num1, num2 = range_search.group( 1), range_search.group(2) parsed_rolesets.append([int(num1), int(num2)]) else: # they specified a roleset name as a string roleset_find = group.rolesets.get(roleset.lower()) if roleset_find: parsed_rolesets.append(roleset_find[1]) if not parsed_rolesets: raise Error( "Could not resolve any valid rolesets! Please make sure you're typing the Roleset name correctly." ) if len(groups) >= 15: raise Message( "15 groups is the max you can add to your group-lock! Please delete some before adding any more.", type="silly") profile, _ = await get_features(Object(id=guild.owner_id), guild=guild) if len(groups) >= 3 and not profile.features.get("premium"): raise Message( "If you would like to add more than **3** groups to your group-lock, then you need Bloxlink Premium.\n" f"Please use `{prefix}donate` for instructions on receiving Bloxlink Premium.\n" "Bloxlink Premium members may lock their server with up to **15** groups.", type="info") if dm_enabled: dm_message = (await CommandArgs.prompt([{ "prompt": "Please specify the text of the DM that people who are kicked will receive. A recommendation " "is to provide your Group Link and any other instructions for them.", "name": "dm_message", "max": 1500 }], last=True) )["dm_message"] else: dm_message = None groups[group.group_id] = { "groupName": group.name, "dmMessage": dm_message, "roleSets": parsed_rolesets } await self.r.table("guilds").insert( { "id": str(guild.id), "groupLock": groups }, conflict="update").run() await post_event( guild, guild_data, "configuration", f"{author.mention} ({author.id}) has **added** a group to the `server-lock`.", BROWN_COLOR) await set_guild_value(guild, "groupLock", groups) await response.success( f"Successfully added group **{group.name}** to your Server-Lock!" ) elif choice == "delete": group = (await CommandArgs.prompt([{ "prompt": "Please specify either the **Group URL** or **Group ID** to delete.", "name": "group", "validation": self.validate_group }], last=True))["group"] if not groups.get(group.group_id): raise Message("This group isn't in your server-lock!") del groups[group.group_id] guild_data["groupLock"] = groups if groups: await self.r.table("guilds").insert(guild_data, conflict="replace").run() else: guild_data.pop("groupLock") await self.r.table("guilds").insert(guild_data, conflict="replace").run() await post_event( guild, guild_data, "configuration", f"{author.mention} ({author.id}) has **deleted** a group from the `server-lock`.", BROWN_COLOR) await set_guild_value(guild, "groupLock", groups) await response.success( "Successfully **deleted** your group from the Server-Lock!") elif choice == "view": if not groups: raise Message("You have no groups added to your Server-Lock!", type="info") embed = Embed(title="Bloxlink Server-Lock") embed.set_footer(text="Powered by Bloxlink", icon_url=Bloxlink.user.avatar_url) embed.set_author(name=guild.name, icon_url=guild.icon_url) for group_id, data in groups.items(): embed.add_field(name=f"{data['groupName']} ({group_id})", value=data["dmMessage"], inline=False) await response.send(embed=embed)
async def __main__(self, CommandArgs): author = CommandArgs.author response = CommandArgs.response prefix = CommandArgs.prefix if not SELF_HOST: author_data = await self.r.db("bloxlink").table("users").get(str(author.id)).run() or {"id": str(author.id)} try: primary_account, accounts = await get_user("username", author=author, everything=False, basic_details=True) if accounts: parsed_accounts = await parse_accounts(accounts) parsed_accounts_str = ", ".join(parsed_accounts.keys()) parsed_args = await CommandArgs.prompt([ { "prompt": "This command will allow you to switch into an account you verified as in the past.\n" f"If you would like to link __a new account__, then please use `{prefix}verify add`.\n\n" "**__WARNING:__** This will remove __all of your roles__ in the server and give you " "new roles depending on the server configuration.", "footer": "Say **next** to continue.", "type": "choice", "choices": ["next"], "name": "_", "formatting": False }, { "prompt": "Are you trying to change your account for _this_ server? If so, simply say `next`.\nIf not, please provide " "the __Server ID__ of the server to switch as. Please see this article to find the Server ID: " "[click here](https://support.discordapp.com/hc/en-us/articles/206346498-Where-can-I-find-my-User-Server-Message-ID->).", "name": "guild", "validation": self.validate_server, }, { "prompt": "We'll switch your account for the server **{guild.name}**.\n" "Please select an account to switch into:```" + parsed_accounts_str + "```", "name": "account", "type": "choice", "choices": parsed_accounts.keys() }, { "prompt": "Would you like to make this your __primary__ account? Please say **yes** or **no**.", "name": "primary", "type": "choice", "choices": ("yes", "no") } ], last=True) guild = parsed_args["guild"] username = parsed_args["account"] roblox_id = (parsed_accounts.get(username)).id guild_data = await self.r.table("guilds").get(str(guild.id)).run() or {"id": str(guild.id)} trello_board = await get_board(guild_data=guild_data, guild=guild) if trello_board: options_trello, _ = await get_options(trello_board) guild_data.update(options_trello) allow_reverify = guild_data.get("allowReVerify", DEFAULTS.get("allowReVerify")) roblox_accounts = author_data.get("robloxAccounts", {}) if guild and not allow_reverify: guild_accounts = roblox_accounts.get("guilds", {}) chosen_account = guild_accounts.get(str(guild.id)) if chosen_account and chosen_account != roblox_id: raise Error("You already selected your account for this server. `allowReVerify` must be " "enabled for you to change it.") try: member = await guild.fetch_member(author.id) except (Forbidden, NotFound): await verify_member(author, roblox_id, guild=guild, author_data=author_data, allow_reverify=allow_reverify, primary_account=parsed_args["primary"] == "yes") raise Message("You're not a member of the provided server, so I was only able to update your account internally.", type="success") try: username = await verify_as( member, guild, response = response, primary = parsed_args["primary"] == "yes", roblox_id = roblox_id, trello_board = trello_board, update_user = False) except Message as e: if e.type == "error": await response.error(e) else: await response.send(e) except Error as e: await response.error(e) else: role_binds, group_ids, _ = await get_binds(guild_data=guild_data, trello_board=trello_board) if count_binds(guild_data, role_binds=role_binds, group_ids=group_ids) and not find(lambda r: r.name == "Bloxlink Bypass", member.roles): for role in list(member.roles): if role != guild.default_role and role.name != "Muted": try: await member.remove_roles(role, reason="Switched User") except Forbidden: pass try: added, removed, nickname, errors, roblox_user = await update_member( member, guild = guild, roles = True, nickname = True, response = response, cache = False) except BloxlinkBypass: await response.info("Since you have the `Bloxlink Bypass` role, I was unable to update your roles/nickname; however, your account was still changed.") return except Blacklisted as b: if str(b): raise Error(f"{author.mention} has an active restriction for: `{b}`.") else: raise Error(f"{author.mention} has an active restriction from Bloxlink.") else: welcome_message = guild_data.get("welcomeMessage") or DEFAULTS.get("welcomeMessage") welcome_message = await get_nickname(author, welcome_message, guild_data=guild_data, roblox_user=roblox_user, is_nickname=False) await post_event(guild, guild_data, "verification", f"{author.mention} ({author.id}) has **switched their user** to `{username}`.", GREEN_COLOR) await CommandArgs.response.send(welcome_message) else: raise Message(f"You only have one account linked! Please use `{prefix}verify add` to add another.", type="silly") except UserNotVerified: raise Error(f"You're not linked to Bloxlink. Please use `{prefix}verify add`.") else: raise Message(f"{author.mention}, to verify with Bloxlink, please visit our website at " \ f"<{VERIFY_URL}>. It won't take long!\nStuck? See this video: <https://www.youtube.com/watch?v=hq496NmQ9GU>")
async def change(self, CommandArgs): """change your Bloxlink settings""" if not CommandArgs.has_permission: raise PermissionError("You do not have the required permissions to change server settings.") prefix = CommandArgs.prefix response = CommandArgs.response message = CommandArgs.message author = CommandArgs.message.author guild = CommandArgs.message.guild guild_data = CommandArgs.guild_data parsed_args = await CommandArgs.prompt([{ "prompt": "What value would you like to change? Note that some settings you can't change " "from this command due to the extra complexity, but I will tell you the " f"appropriate command to use.\n\nOptions: ``{options_strings}``\n\nPremium-only options: ``{premium_options_strings}``", "name": "choice", "type": "choice", "formatting": False, "footer": f"Use ``{prefix}settings help`` to view a description of all choices.", "choices": options_combined }]) choice = parsed_args["choice"] if choice == "trelloID": raise Message(f"You can link your Trello board from ``{prefix}setup``!", type="success") elif choice == "Linked Groups": raise Message(f"You can link your group from ``{prefix}bind``!", type="success") elif choice == "joinDM": message.content = f"{prefix}joindm" return await parse_message(message) elif choice == "groupShoutChannel": message.content = f"{prefix}shoutproxy" return await parse_message(message) elif choice == "whiteLabel": message.content = f"{prefix}whitelabel" return await parse_message(message) option_find = OPTIONS.get(choice) if option_find: if option_find[3]: profile, _ = await get_features(Object(id=guild.owner_id), guild=guild) if not profile.features.get("premium"): raise Error("This option is premium-only! The server owner must have premium for it to be changed.\n" f"Use ``{prefix}donate`` for more instructions on getting premium.") option_type = option_find[1] trello_board = CommandArgs.trello_board card = success_text = parsed_value = None desc = option_find[4].format(prefix=CommandArgs.prefix, templates=NICKNAME_TEMPLATES) if trello_board: options_trello_data, trello_binds_list = await get_options(trello_board, return_cards=True) options_trello_find = options_trello_data.get(choice) if options_trello_find: card = options_trello_find[1] if option_type == "boolean": parsed_value = await CommandArgs.prompt([{ "prompt": f"Would you like to **enable** or **disable** ``{choice}``?\n\n" f"**Option description:**\n{desc}", "name": "choice", "type": "choice", "footer": "Say **clear** to set as the default value.", "formatting": False, "choices": ("enable", "disable", "clear") }]) parsed_bool_choice = parsed_value["choice"] if parsed_bool_choice == "clear": parsed_value = DEFAULTS.get(choice) else: parsed_value = parsed_bool_choice == "enable" await self.r.table("guilds").insert({ "id": str(guild.id), choice: parsed_value }, conflict="update").run() success_text = f"Successfully **{parsed_bool_choice}d** ``{choice}``!" elif option_type == "string": parsed_value = (await CommandArgs.prompt([{ "prompt": f"Please specify a new value for ``{choice}``.\n\n" f"**Option description:**\n{desc}", "name": "choice", "type": "string", "footer": "Say **clear** to set as the default value.", "formatting": False, "max": option_find[2] }]))["choice"] if parsed_value == "clear": parsed_value = DEFAULTS.get(choice) await self.r.table("guilds").insert({ "id": str(guild.id), choice: parsed_value }, conflict="update").run() success_text = f"Successfully saved your new {choice}!" elif option_type == "role": parsed_value = (await CommandArgs.prompt([{ "prompt": f"Please specify a role for ``{choice}``.\n\n" f"**Option description:**\n{desc}", "name": "role", "type": "role", "exceptions": ("clear",), "footer": "Say **clear** to set as the default value.", "formatting": False }]))["role"] if parsed_value == "clear": parsed_value = DEFAULTS.get(choice) else: parsed_value = str(parsed_value.id) await self.r.table("guilds").insert({ "id": str(guild.id), choice: parsed_value }, conflict="update").run() success_text = f"Successfully saved your new ``{choice}``!" elif option_type == "number": parsed_value = (await CommandArgs.prompt([{ "prompt": f"Please specify a new integer for ``{choice}``.\n\n" f"**Option description:**\n{desc}", "name": "choice", "type": "number", "footer": "Say **clear** to set as the default value.", "formatting": False, "exceptions": ("clear",), "max": option_find[2] }]))["choice"] if parsed_value == "clear": parsed_value = DEFAULTS.get(choice) await self.r.table("guilds").insert({ "id": str(guild.id), choice: parsed_value }, conflict="update").run() success_text = f"Successfully saved your new ``{choice}``!" elif option_type == "choice": choices = ", ".join(option_find[2]) parsed_value = (await CommandArgs.prompt([{ "prompt": f"Please pick a new value for ``{choice}``: ``{choices}``\n\n" f"**Option description:**\n{desc}", "name": "choice", "type": "choice", "footer": "Say **clear** to set as the default value.", "formatting": False, "exceptions": ["clear"], "choices": option_find[2] }]))["choice"] if parsed_value == "clear": parsed_value = DEFAULTS.get(choice) await self.r.table("guilds").insert({ "id": str(guild.id), choice: parsed_value }, conflict="update").run() success_text = f"Successfully saved your new ``{choice}``!" else: raise Error("An unknown type was specified.") else: raise Error("An unknown option was specified.") if trello_board: try: if card: if card.name == choice: await card.edit(desc=str(parsed_value)) else: await card.edit(name=f"{choice}:{parsed_value}") else: trello_settings_list = await trello_board.get_list(lambda L: L.name == "Bloxlink Settings") \ or await trello_board.create_list(name="Bloxlink Settings") await trello_settings_list.create_card(name=choice, desc=str(parsed_value)) if trello_binds_list: await trello_binds_list.sync(card_limit=TRELLO["CARD_LIMIT"]) except TrelloUnauthorized: await response.error("In order for me to edit your Trello settings, please add ``@bloxlink`` to your " "Trello board.") except (TrelloNotFound, TrelloBadRequest): pass await set_guild_value(guild, choice, parsed_value) await post_event(guild, guild_data, "configuration", f"{author.mention} ({author.id}) has **changed** the ``{choice}`` option.", BROWN_COLOR) raise Message(success_text, type="success")
async def __main__(self, CommandArgs): guild = CommandArgs.guild response = CommandArgs.response trello_board = CommandArgs.trello_board group_id = str(CommandArgs.parsed_args["group_id"]) role = CommandArgs.parsed_args["role"] nickname = CommandArgs.parsed_args["nickname"] remove_roles = [ str(r.id) for r in CommandArgs.parsed_args["remove_roles"] ] if (CommandArgs.parsed_args["remove_roles"] and CommandArgs.parsed_args["remove_roles"] != "skip") else [] remove_roles_trello = [ str(r) for r in CommandArgs.parsed_args["remove_roles"] ] if remove_roles and CommandArgs.parsed_args[ "remove_roles"] != "skip" else [] nickname_lower = nickname and nickname.lower() role_id = str(role.id) try: group = await get_group(group_id, full_group=False) except RobloxNotFound: raise Error( f"A group with ID `{group_id}` does not exist. Please try again." ) guild_data = CommandArgs.guild_data if trello_board: trello_binds_list = await trello_board.get_list( lambda l: l.name.lower() == "bloxlink binds") if not trello_binds_list: try: trello_binds_list = await trello_board.create_list( name="Bloxlink Binds") except TrelloUnauthorized: await response.error( "In order for me to create Trello binds, please add `@bloxlink` to your " "Trello board.") except (TrelloNotFound, TrelloBadRequest): pass trello_card_binds, _ = await parse_trello_binds( trello_board=trello_board, trello_binds_list=trello_binds_list) else: trello_binds_list = None trello_card_binds = {"groups": {"entire group": {}, "binds": {}}} role_binds = guild_data.get("roleBinds") or {} if isinstance(role_binds, list): role_binds = role_binds[0] role_binds["groups"] = role_binds.get("groups") or { } # {"groups": {"ranges": {}, "binds": {}}} role_binds["groups"][group_id] = role_binds["groups"].get( group_id) or {} role_binds["groups"][group_id][ "binds"] = role_binds["groups"][group_id].get("binds") or {} x = "0" rank = role_binds["groups"][group_id].get("binds", {}).get(x, {}) if not isinstance(rank, dict): rank = { "nickname": nickname if nickname and nickname_lower not in ("skip", "done") else None, "roles": [str(rank)], "removeRoles": remove_roles } if role_id not in rank["roles"]: rank["roles"].append(role_id) else: if role_id not in rank.get("roles", []): rank["roles"] = rank.get("roles") or [] rank["roles"].append(role_id) if nickname and nickname_lower not in ("skip", "done"): rank["nickname"] = nickname else: if not rank.get("nickname"): rank["nickname"] = None rank["removeRoles"] = remove_roles role_binds["groups"][group_id]["binds"][x] = rank if trello_binds_list: make_binds_card = True if trello_card_binds: trello_bind_group = trello_card_binds["groups"]["binds"].get( group_id, {}).get("binds") if trello_bind_group: card_data_ = trello_bind_group.get(x) if card_data_: for card in card_data_["trello"]["cards"]: trello_card = card["card"] trello_ranks = card.get("ranks", []) if (x in trello_ranks or x == "all") and len(trello_ranks) == 1: trello_bind_roles = card.get("roles", set()) card_bind_data = [ f"Group: {group_id}", f"Nickname: {(nickname != 'skip' and nickname) or rank.get('nickname') or card_data_.get('nickname') or 'None'}", ] if remove_roles: card_bind_data.append( f"Remove roles: {', '.join(remove_roles_trello)}" ) for role_ in trello_bind_roles: if role_ in (role_id, role.name): break else: trello_bind_roles.add(role.name) card_bind_data.append( f"Roles: {', '.join(trello_bind_roles)}" ) card_bind_data.append( f"Ranks: {card['trello_str']['ranks']}") trello_card_desc = "\n".join(card_bind_data) if trello_card_desc != trello_card.description: trello_card.description = trello_card_desc try: await trello_card.edit( desc=trello_card_desc) except TrelloUnauthorized: await response.error( "In order for me to edit your Trello binds, please add `@bloxlink` to your " "Trello board.") except (TrelloNotFound, TrelloBadRequest): pass trello_binds_list.parsed_bind_data = None make_binds_card = False break if make_binds_card: card_bind_data = [ f"Group: {group_id}", f"Nickname: {nickname != 'skip' and nickname or 'None'}", f"Roles: {role.name}", ] if remove_roles: card_bind_data.append( f"Remove roles: {', '.join(remove_roles_trello)}") if x != "all": card_bind_data.append(f"Ranks: {x}") trello_card_desc = "\n".join(card_bind_data) try: card = await trello_binds_list.create_card( name="Bloxlink Bind", desc=trello_card_desc) except TrelloUnauthorized: await response.error( "In order for me to edit your Trello binds, please add `@bloxlink` to your " "Trello board.") except (TrelloNotFound, TrelloBadRequest): pass trello_binds_list.parsed_bind_data = None await self.r.table("guilds").insert( { "id": str(guild.id), "roleBinds": role_binds }, conflict="update").run() await response.success( f"Successfully bound this **Guest Role** to role **{role.name}!**")