Ejemplo n.º 1
0
    async def __main__(self, CommandArgs):
        choice = CommandArgs.string_args and CommandArgs.string_args[0].lower()

        if choice not in ("view", "change", "reset"):
            parsed_args = await CommandArgs.prompt([
                {
                    "prompt": "Would you like to **view** your settings, **change** them, or "
                              "**reset** all of your settings?\nIf you don't understand an option, "
                              "then say **help**.\nValid choices: (change/view/reset/help)",
                    "name": "choice",
                    "type": "choice",
                    "choices": ("change", "view", "reset", "help")
                }
            ])

            choice = parsed_args["choice"]

        if choice in ("change", "reset"):
            if not CommandArgs.has_permission:
                raise PermissionError("You do not have the required permissions to change server settings.")

        if choice == "view":
            await self.view(CommandArgs)
        elif choice == "change":
            await self.change(CommandArgs)
        elif choice == "reset":
            await self.reset(CommandArgs)
        elif choice == "help":
            await self.help(CommandArgs)
Ejemplo n.º 2
0
    async def __main__(self, CommandArgs):
        channel = CommandArgs.channel
        response = CommandArgs.response
        locale = CommandArgs.locale

        t_1 = time.perf_counter()

        if response.webhook_only:
            m = await response.send(locale("commands.ping.pinging"))
        else:
            try:
                await channel.trigger_typing()
            except NotFound:
                pass

        t_2 = time.perf_counter()
        time_delta = round((t_2 - t_1) * 1000)

        if response.webhook_only:
            try:
                await m.delete()
            except NotFound:
                pass
            except Forbidden:
                raise PermissionError(
                    locale("permissions.oneError",
                           permission="Manage Messages"))
            else:
                await response.send(
                    locale("commands.ping.pong", time_delta=time_delta))
        else:
            await response.send(
                locale("commands.ping.pong", time_delta=time_delta))
Ejemplo n.º 3
0
    async def __main__(self, CommandArgs):
        response = CommandArgs.response

        author = CommandArgs.author

        guild = CommandArgs.guild
        guild_data = CommandArgs.guild_data

        new_prefix = CommandArgs.parsed_args.get("new_prefix")

        if new_prefix:
            if not CommandArgs.has_permission:
                raise PermissionError("You do not meet the required permissions for this command.")

            if RELEASE == "PRO":
                prefix_name = "proPrefix"
            else:
                prefix_name = "prefix"

            await self.r.table("guilds").insert({
                "id": str(guild.id),
                prefix_name: new_prefix
            }, conflict="update").run()

            trello_board = CommandArgs.trello_board

            if trello_board:
                _, card = await get_prefix(guild=guild, trello_board=trello_board)

                if card:
                    try:
                        if card.name == prefix_name:
                            await card.edit(desc=new_prefix)
                        else:
                            await card.edit(name=f"{prefix_name}:{new_prefix}")
                    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
                    else:
                        await trello_board.sync(card_limit=TRELLO["CARD_LIMIT"], list_limit=TRELLO["LIST_LIMIT"])

            await post_event(guild, guild_data, "configuration", f"{author.mention} ({author.id}) has **changed** the `prefix` option.", BROWN_COLOR)

            await set_guild_value(guild, prefix_name, new_prefix)

            await response.success("Your prefix was successfully changed!")

        else:
            old_prefix = CommandArgs.real_prefix

            await response.send(f"Your prefix used for Bloxlink: `{old_prefix}`.\n"
                                 "Change it with `@Bloxlink prefix <new prefix>`.")
Ejemplo n.º 4
0
    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")
Ejemplo n.º 5
0
    async def reset(self, CommandArgs):
        """reset either ALL of your settings, or just your binds"""

        if not CommandArgs.has_permission:
            raise PermissionError("You do not have the required permissions to change server settings.")

        prefix = CommandArgs.prefix
        response = CommandArgs.response
        trello_board = CommandArgs.trello_board

        author = CommandArgs.message.author

        guild = CommandArgs.message.guild
        guild_data = CommandArgs.guild_data


        parsed_arg = (await CommandArgs.prompt([{
            "prompt": f"Which setting would you like to clear? Valid choices: ``{RESET_CHOICES}``",
            "name": "choice",
            "type": "choice",
            "formatting": False,
            "choices": RESET_CHOICES
        }]))["choice"]

        if parsed_arg == "everything":
            cont = (await CommandArgs.prompt([{
                "prompt": "**Warning!** This will clear **all of your settings** including binds, "
                          f"saved group information, etc. You'll need to run ``{prefix}setup`` "
                           "and set-up the bot again. Continue? ``Y/N``",
                "name": "continue",
                "choices": ("yes", "no"),
                "embed_title": "Warning!",
                "embed_color": ORANGE_COLOR,
                "type": "choice",
                "footer": "Say **yes** to clear all of your settings, or **no** to cancel."
            }]))["continue"]

            if cont == "no":
                raise CancelledPrompt

            await self.r.table("guilds").get(str(guild.id)).delete().run()

            if trello_board:
                trello_options, _ = await get_options(trello_board, return_cards=True)

                try:
                    if trello_options:
                        for option_name, option in trello_options.items():
                            await option[1].archive()

                    trello_binds_list = await trello_board.get_list(lambda l: l.name == "Bloxlink Binds")

                    if trello_binds_list:
                        for card in await trello_binds_list.get_cards(limit=TRELLO["CARD_LIMIT"]):
                            await card.archive()

                        trello_binds_list.parsed_bind_data = None

                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
                else:
                    await trello_board.sync(card_limit=TRELLO["CARD_LIMIT"], list_limit=TRELLO["LIST_LIMIT"])


            await post_event(guild, guild_data, "configuration", f"{author.mention} ({author.id}) has **deleted** all server information.", BROWN_COLOR)

            await clear_guild_data(guild)

            raise Message("Your server information was successfully cleared.", type="success")


        elif parsed_arg == "binds":
            # delete all binds from db and trello

            cont = (await CommandArgs.prompt([{
                "prompt": "**Warning!** This will clear **all of your binds**. You'll need to "
                         f"run ``{prefix}bind`` to set up your binds again. Continue? ``Y/N``",
                "name": "continue",
                "choices": ("yes", "no"),
                "type": "choice",
                "embed_title": "Warning!",
                "embed_color": ORANGE_COLOR,
                "formatting": False,
                "footer": "Say **yes** to clear all of your binds, or **no** to cancel."
            }]))["continue"]

            if cont == "no":
                raise CancelledPrompt

            guild_data = CommandArgs.guild_data
            role_binds = guild_data.get("roleBinds", {})

            if role_binds:
                role_ids = set()

                for group_id, group_data in role_binds.get("groups", {}).items():
                    for rank_id, rank_data in group_data.get("binds", {}).items():
                        for role_id in rank_data["roles"]:
                            role_ids.add(int(role_id))

                    for range_data in group_data.get("ranges", []):
                        if range_data["roles"]:
                            for role_id in range_data["roles"]:
                                role_ids.add(int(role_id))


                if role_ids:
                    delete_roles = (await CommandArgs.prompt([{
                        "prompt": "Would you like me to **delete these roles from your server as well?** If yes, "
                                  f"then this will delete **{len(role_ids)}** role(s). ``Y/N``",
                        "name": "delete_roles",
                        "choices": ("yes", "no"),
                        "type": "choice",
                        "embed_title": "Warning!",
                        "embed_color": ORANGE_COLOR,
                        "formatting": False,
                        "footer": "Say **yes** to delete these roles, or **no** to cancel."
                    }]))["delete_roles"]

                    if delete_roles == "yes":
                        for role_id in role_ids:
                            role = guild.get_role(role_id)

                            if role:
                                try:
                                    await role.delete(reason=f"{author} chose to delete bound roles through {prefix}settings")
                                except Forbidden:
                                    pass

                        await response.success("Your bound roles were deleted.")

            guild_data.pop("roleBinds", None)
            guild_data.pop("groupIDs", None)

            await self.r.table("guilds").insert(guild_data, conflict="replace").run()

            if trello_board:
                try:
                    trello_binds_list = await trello_board.get_list(lambda l: l.name == "Bloxlink Binds")

                    if trello_binds_list:
                        for card in await trello_binds_list.get_cards(limit=TRELLO["CARD_LIMIT"]):
                            await card.archive()

                        trello_binds_list.parsed_bind_data = None

                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
                else:
                    await trello_board.sync(card_limit=TRELLO["CARD_LIMIT"], list_limit=TRELLO["LIST_LIMIT"])


            await post_event(guild, guild_data, "configuration", f"{author.mention} ({author.id}) has **deleted** all binds.", BROWN_COLOR)

            await clear_guild_data(guild)

            raise Message("Successfully **cleared** all of your bound roles.", type="success")
Ejemplo n.º 6
0
    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")
Ejemplo n.º 7
0
    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])}!**")
Ejemplo n.º 8
0
    async def __main__(self, CommandArgs):
        response = CommandArgs.response
        users_ = CommandArgs.parsed_args["users"]
        prefix = CommandArgs.prefix

        message = CommandArgs.message
        author = message.author
        guild = message.guild

        guild_data = CommandArgs.guild_data

        users = []

        if not users_:
            message.content = f"{prefix}getrole"
            return await parse_message(message)

        if not CommandArgs.has_permission:
            if users_[0] == author:
                message.content = f"{prefix}getrole"
                return await parse_message(message)
            else:
                raise PermissionError(
                    "You do not have permission to update arbitrary users or roles!"
                )

        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!")
        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
            #trello_binds_list = trello_board and await trello_board.get_list(lambda l: l.name.lower() == "bloxlink binds")

            #async with response.loading():
            if len_users > 1:
                for user in users:
                    if not user.bot:
                        try:
                            added, removed, nickname, errors, 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"),
                                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)
                        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, 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"))

                    _, embed = await format_update_embed(
                        roblox_user,
                        user,
                        added=added,
                        removed=removed,
                        errors=errors,
                        nickname=nickname
                        if old_nickname != user.display_name else None,
                        prefix=prefix,
                        guild_data=guild_data)

                    if embed:
                        await response.send(embed=embed)
                    else:
                        await response.success(
                            "This user is all up-to-date; no changes were made."
                        )

                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 str(b):
                        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)

            if cooldown:
                await self.redis.set(redis_cooldown_key, 3, ex=cooldown)

            if len_users > 10:
                await response.success("All users updated.")
Ejemplo n.º 9
0
    async def __main__(self, CommandArgs):
        response = CommandArgs.response
        guild_data = CommandArgs.guild_data
        prefix = CommandArgs.prefix

        guild = CommandArgs.message.guild

        try:
            category = find(lambda c: c.name == "Verification", guild.categories) or \
                       await guild.create_category("Verification")

            verify_info = find(lambda t: t.name == "verify-instructions", category.channels) or \
                          await guild.create_text_channel("verify-instructions", category=category)

            verify_channel = find(lambda t: t.name == "verify", category.channels) or \
                             await guild.create_text_channel("verify", category=category)

            sample_channel = await guild.create_text_channel("sample-channel")

        except Forbidden:
            raise PermissionError("I was unable to create the necessary channels. Please ensure I have the "
                                  "``Manage Channels`` permission.")

        except HTTPException:
            raise Error("You have too many channels or categories! Please delete some before continuing.")

        try:
            await verify_info.send("This server uses Bloxlink to manage Roblox verification. In "
                                   "order to unlock all the features of this server, you'll need "
                                   "to verify your Roblox account with your Discord account!\n\nTo "
                                   f"do this, run ``{prefix}verify`` in {verify_channel.mention} and follow the instructions.")

            await sample_channel.send("This is a sample channel that only Verified users " \
                                      "can read. This channel is not important, you may freely delete it.\n" \
                                      "To create another sample channel, right click this channel and click 'Clone " \
                                      "Text Channel', or just run this command again.")

        except (Forbidden, NotFound):
            raise PermissionError("I was unable to send messages to the created channels. Please give me the "
                                  "proper permissions.")


        try:
            await verify_info.set_permissions(guild.me, send_messages=True, read_messages=True)

            verified_role_name = guild_data.get("verifiedRoleName", DEFAULTS.get("verifiedRoleName"))

            for role in guild.roles:
                if role.name != guild.me.name:
                    await verify_info.set_permissions(role, send_messages=False, read_messages=True)
                    await verify_channel.set_permissions(role, send_messages=True, read_messages=True)

                if role.name == verified_role_name:
                    for target, overwrite in sample_channel.overwrites.items():
                        await sample_channel.set_permissions(target, overwrite=None)

                    await sample_channel.set_permissions(guild.default_role, send_messages=False, read_messages=False)
                    await sample_channel.set_permissions(role, send_messages=True, read_messages=True)


        except Forbidden:
            raise PermissionError("Unable to set permissions to the channels. Please ensure I have the "
                                  "``Manage Channels`` and ``Manage Roles`` permission.")

        except NotFound:
            raise Error("Please do not delete the created channels while I'm setting them up...")


        await self.r.table("guilds").insert({
            "id": str(guild.id),
            "verifyChannel": str(verify_channel.id)
        }, conflict="update").run()

        await response.success(f"All done! Your new verification channel is {verify_channel.mention} and " \
                                "is now managed by Bloxlink.")
Ejemplo n.º 10
0
    async def __main__(self, CommandArgs):
        guild = CommandArgs.guild
        response = CommandArgs.response
        author = CommandArgs.author
        locale = CommandArgs.locale

        role_binds_trello, group_ids_trello = await get_binds(guild=guild)

        bind_count = await count_binds(guild)

        if bind_count >= FREE_BIND_COUNT:
            if not "premium" in (await has_premium(guild=guild)).features:
                raise Error(
                    locale("commands.bind.errors.noPremiumBindLimitExceeded",
                           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",
                           prem_bind_count=PREM_BIND_COUNT))

        parsed_args = await CommandArgs.prompt([{
            "prompt":
            f"{locale('commands.bind.prompts.bindTypePrompt.line_1', arrow=ARROW)}\n",
            "name":
            "bind_choice",
            "type":
            "choice",
            "components": [
                discord.ui.Select(
                    max_values=1,
                    options=[
                        discord.SelectOption(
                            label="Group",
                            description="Users of your group will get a role."
                        ),
                        discord.SelectOption(
                            label="Asset",
                            description="Users need to own this catalog item."
                        ),
                        discord.SelectOption(
                            label="Badge",
                            description="Users need to own this badge."),
                        discord.SelectOption(
                            label="GamePass",
                            description="Users need to own this GamePass."),
                        discord.SelectOption(
                            label="DevForum Members",
                            description=
                            "Users need to be a member of the DevForum."),
                        discord.SelectOption(
                            label="Roblox Staff",
                            description="Users need to be Roblox Staff members."
                        ),
                    ])
            ],
            "choices":
            locale("commands.bind.prompts.bindTypePrompt.choices"),
            "formatting":
            False
        }, {
            "prompt":
            locale("commands.bind.prompts.nicknamePrompt.line",
                   nickname_templates=NICKNAME_TEMPLATES),
            "name":
            "nickname",
            "max":
            100,
            "type":
            "string",
            "footer":
            locale("commands.bind.prompts.nicknamePrompt.footer"),
            "formatting":
            False
        }, {
            "prompt":
            "Should any **additional** roles be **removed from the user** if they meet the bind conditions? You can specify multiple roles.\n\n"
            "Note that this is an **advanced option**, so you most likely should `skip` this. Bloxlink will already remove \"old\" Group roles.\n\n"
            "This option really exists if there are non-group roles that another bot in your server gives.",
            "name":
            "remove_roles",
            "multiple":
            True,
            "type":
            "role",
            "max":
            10,
            "exceptions": ("skip", ),
            "footer":
            "Say **skip** to skip this option."
        }])

        bind_choice = parsed_args["bind_choice"][0].lower()
        nickname = parsed_args["nickname"]

        if "display-name" in nickname:
            display_name_confirm = (await CommandArgs.prompt([{
                "prompt":
                "**Warning!** You chose Display Names for your Nickname Template.\n"
                "Display Names **aren't unique** and can **lead to impersonation.** Are you sure you want to use this? yes/no",
                "type":
                "choice",
                "components": [
                    discord.ui.Select(max_values=1,
                                      options=[
                                          discord.SelectOption(label="Yes"),
                                          discord.SelectOption(label="No"),
                                      ])
                ],
                "choices": ("yes", "no"),
                "name":
                "confirm",
                "embed_title":
                "Display Names Confirmation",
                "embed_color":
                BROWN_COLOR,
                "formatting":
                False
            }]))["confirm"][0]

            if display_name_confirm == "no":
                raise CancelledPrompt

        remove_roles = [str(r.id) for r in parsed_args["remove_roles"]
                        ] if parsed_args["remove_roles"] != "skip" else []

        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",
                "components": [
                    discord.ui.Select(
                        max_values=1,
                        options=[
                            discord.SelectOption(
                                label="Link my entire group",
                                description=
                                "Roleset names must match with Discord role names."
                            ),
                            discord.SelectOption(
                                label="Select specific rolesets",
                                description=
                                "You can choose how the roles are called."),
                        ])
                ],
                "type":
                "choice",
                "choices": ("link my entire group", "select specific rolesets")
            }])

            group = parsed_args_group["group"]
            group_id = group.group_id

            group_ids = await get_guild_value(guild, "groupIDs") or {}
            found_group = group_ids.get(group_id)

            if parsed_args_group["type"][0] == "link my entire group":
                if found_group:
                    if (nickname and found_group["nickname"] != nickname) or (
                            sorted(remove_roles) != sorted(
                                found_group.get("removeRoles", []))):
                        group_ids[group_id] = {
                            "nickname": nickname,
                            "groupName": group.name,
                            "removeRoles": remove_roles
                        }

                        await set_guild_value(group, groupIDs=group_ids)

                        ending_s = group.name.endswith("s") and "'" or "'s"

                        await post_event(
                            guild, "bind",
                            f"{author.mention} ({author.id}) has **changed** `{group.name}`{ending_s} nickname template.",
                            BLURPLE_COLOR)

                        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_data in group.rolesets.items():
                    discord_role = discord.utils.find(
                        lambda r: r.name == roleset_data[0], guild.roles)

                    if not discord_role:
                        try:
                            discord_role = await guild.create_role(
                                name=roleset_data[0])
                        except discord.errors.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,
                    "removeRoles": remove_roles
                }

                await set_guild_value(guild, groupIDs=group_ids)

                await post_event(
                    guild, "bind",
                    f"{author.mention} ({author.id}) has **linked** group `{group.name}`.",
                    BLURPLE_COLOR)

                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 = await get_guild_value(guild, "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
                role_binds["groups"][group_id]["removeRoles"] = remove_roles

                rolesets_embed = discord.Embed(
                    title=f"{group.name} Rolesets",
                    description="\n".join(f"**{x[0]}** {ARROW} {x[1]}"
                                          for x in group.rolesets.values()))
                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\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)

                    for rank in selected_ranks["ranks"].split(","):
                        rank = rank.strip()

                        if rank.isdigit() and rank != "0":
                            if 1 <= int(rank) <= 255:
                                new_ranks["binds"].append(str(rank))
                            else:
                                response.delete(await response.error(
                                    "Ranks must be an integer between [1-255]"
                                ))
                                failures += 1
                                break
                        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)
                                if (1 <= int(num1) <= 255) and (1 <= int(num2)
                                                                <= 255):
                                    new_ranks["ranges"].append([num1, num2])
                                else:
                                    response.delete(await response.error(
                                        "Ranges must be between [1-255]."))
                                    failures += 1
                                    break
                            else:
                                # they specified a roleset name as a string
                                roleset_find = group.rolesets.get(rank.lower())

                                if roleset_find:
                                    new_ranks["binds"].append(
                                        str(roleset_find[1]))
                                else:
                                    response.delete(await response.error(
                                        "Could not find a matching Roleset name. Please try again."
                                    ))
                                    failures += 1
                                    break
                    else:
                        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)],
                                "removeRoles": remove_roles
                            }

                            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

                            rank["removeRoles"] = remove_roles

                        role_binds["groups"][group_id]["binds"][x] = rank

                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

                        range_["removeRoles"] = remove_roles

                        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_)

            await set_guild_value(guild, roleBinds=role_binds)

            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, "bind",
                f"{author.mention} ({author.id}) has **bound** group `{group.name}`.",
                BLURPLE_COLOR)

            await response.success(text)

        else:
            no_id_bind = False

            if bind_choice == "gamepass":
                bind_choice_title = "GamePass"
                bind_choice_plural = "gamePasses"
            elif bind_choice == "devforum members":
                bind_choice_title = "DevForum Members"
                bind_choice_plural = "devForum"
                no_id_bind = True
            elif bind_choice == "roblox staff":
                bind_choice_title = "Roblox Staff"
                bind_choice_plural = "robloxStaff"
                no_id_bind = True
            else:
                bind_choice_title = bind_choice.title()
                bind_choice_plural = f"{bind_choice}s"

            if not no_id_bind:
                vg_parsed_args_1 = await CommandArgs.prompt([{
                    "prompt":
                    f"Please provide the **{bind_choice_title} ID** to use for this bind.",
                    "name":
                    "bind_id",
                    "type":
                    "number",
                    "formatting":
                    False
                }])

            vg_parsed_args_2 = await CommandArgs.prompt([
                {
                    "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_2["role"]
            bind_id = str(
                vg_parsed_args_1["bind_id"]) if not no_id_bind else None
            display_name = None

            if bind_choice == "asset":
                try:
                    json_data, response_ = await fetch(
                        f"{API_URL}/marketplace/productinfo?assetId={bind_id}",
                        json=True)
                except (RobloxNotFound, RobloxAPIError):
                    raise Error(
                        f"An Asset with ID `{bind_id}` does not exist.")

                display_name = json_data.get("Name")

            elif bind_choice == "badge":
                try:
                    json_data, response_ = await fetch(
                        f"https://badges.roblox.com/v1/badges/{bind_id}",
                        json=True)
                except (RobloxNotFound, RobloxAPIError):
                    raise Error(f"A Badge with ID `{bind_id}` does not exist.")

                display_name = json_data.get("displayName")

            elif bind_choice == "gamepass":
                bind_choice_title = "GamePass"
                bind_choice_plural = "gamePasses"

                try:
                    json_data, response_ = await fetch(
                        f"http://api.roblox.com/marketplace/game-pass-product-info?gamePassId={bind_id}",
                        json=True)
                except (RobloxNotFound, RobloxAPIError):
                    raise Error(
                        f"A GamePass with ID `{bind_id}` does not exist.")

                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 = await get_guild_value(guild, "roleBinds") or {}

            if isinstance(role_binds, list):
                role_binds = role_binds[0]

            role_binds[bind_choice_plural] = role_binds.get(
                bind_choice_plural) or {}

            if bind_id:
                role_binds[bind_choice_plural][bind_id] = role_binds[
                    bind_choice_plural].get(bind_id) or {}
                data_point = role_binds[bind_choice_plural][bind_id]
            else:
                role_binds[bind_choice_plural] = role_binds.get(
                    bind_choice_plural) or {}
                data_point = role_binds[bind_choice_plural]

            data_point["nickname"] = nickname
            data_point["displayName"] = display_name
            data_point["removeRoles"] = remove_roles
            data_point["roles"] = data_point.get("roles", [])
            roles = data_point["roles"]

            for discord_role in discord_roles:
                role_id = str(discord_role.id)

                if not role_id in roles:
                    roles.append(role_id)

            await set_guild_value(guild, roleBinds=role_binds)

            if display_name:
                await post_event(
                    guild, "bind",
                    f"{author.mention} ({author.id}) has **bound** {bind_choice_title} `{display_name}`.",
                    BLURPLE_COLOR)
                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])}!**"
                )
            else:
                await post_event(
                    guild, "bind",
                    f"{author.mention} ({author.id}) has **bound** {bind_choice_title}.",
                    BLURPLE_COLOR)
                await response.success(
                    f"Successfully **bound** {bind_choice_title} with Discord role(s) **{', '.join([r.name for r in discord_roles])}!**"
                )