async def removetagalias(self, ctx, *, alias: OwnedTag):
        """Remove an alias from a tag"""

        flow = Flow(self.bot, ctx)

        if alias.invoked_with == alias.name:
            return await ctx.send(
                f":x: That is not an alias, but the tag's name. "
                f"Try {config.BOT_PREFIX}tag delete {alias.invoked_with} instead!"
            )

        are_you_sure = await ctx.send(
            f":information_source: Are you sure that you want to remove the alias "
            f"`{config.BOT_PREFIX}{alias.invoked_with}` "
            f"from `{config.BOT_PREFIX}{alias.name}`?")

        reaction = await flow.get_yes_no_reaction_confirm(are_you_sure, 200)

        if reaction is None:
            return

        if not reaction:
            return await ctx.send("Aborted.")

        elif reaction:
            async with self.bot.db.acquire() as con:
                async with con.transaction():
                    await con.execute(
                        "DELETE FROM guild_tags_alias WHERE alias = $1 AND tag_id = $2",
                        alias.invoked_with, alias.id)
                    await ctx.send(
                        f":white_check_mark: Successfully removed the alias "
                        f"`{config.BOT_PREFIX}{alias.invoked_with}` from "
                        f"`{config.BOT_PREFIX}{alias.name}`.")
    async def removetag(self, ctx, *, tag: OwnedTag):
        """Remove a tag"""

        flow = Flow(self.bot, ctx)

        are_you_sure = await ctx.send(
            f":information_source: Are you sure that you want to remove the tag "
            f"`{config.BOT_PREFIX}{tag.name}`?")

        reaction = await flow.get_yes_no_reaction_confirm(are_you_sure, 200)

        if reaction is None:
            return

        if not reaction:
            return await ctx.send("Aborted.")

        elif reaction:
            async with self.bot.db.acquire() as con:
                async with con.transaction():
                    await con.execute(
                        "DELETE FROM guild_tags_alias WHERE tag_id = $1",
                        tag.id)
                    await con.execute(
                        "DELETE FROM guild_tags WHERE name = $1 AND guild_id = $2",
                        tag.name, ctx.guild.id)
                    await ctx.send(
                        f":white_check_mark: `{config.BOT_PREFIX}{tag.name}` was removed."
                    )
예제 #3
0
    async def addrole(self, ctx):
        """Add a role to this server's `-roles` list"""

        await ctx.send(":information_source: Reply with the name of the role you want to create.")

        flow = Flow(self.bot, ctx)
        role_name = await flow.get_new_role(240)

        if isinstance(role_name, str):
            await ctx.send(
                f":white_check_mark: I will **create a new role** on this server named `{role_name}` for this.")
            try:
                discord_role = await ctx.guild.create_role(name=role_name)
            except discord.Forbidden:
                raise exceptions.ForbiddenError(exceptions.ForbiddenTask.CREATE_ROLE, role_name)

        else:
            discord_role = role_name

            await ctx.send(
                f":white_check_mark: I'll use the **pre-existing role** named `{discord_role.name}` for this.")

        await ctx.send(":information_source: Reply with a short message the user should see when they get the role.")

        role_join_message = await flow.get_text_input(300)

        if not role_join_message:
            return

        await self.bot.db.execute("INSERT INTO roles (guild_id, role_id, join_message) VALUES ($1, $2, $3) "
                                  "ON CONFLICT (guild_id, role_id) DO UPDATE set join_message = $3",
                                  ctx.guild.id, discord_role.id, role_join_message)

        await ctx.send(f':white_check_mark: `{discord_role.name}` was added as a selfrole or its join message was '
                       f'updated in case the selfrole already existed.')
    async def edittag(self, ctx, *, tag: OwnedTag):
        """Edit one of your tags"""

        flow = Flow(self.bot, ctx)

        embed_q = await ctx.send(
            ":information_source: Should the tag be sent as an embed?")

        embed_bool = await flow.get_yes_no_reaction_confirm(embed_q, 300)

        if embed_bool is None:
            return

        if embed_bool:
            is_embedded = True
        else:
            is_embedded = False

        await ctx.send(
            ":information_source: Reply with the updated **title** of this tag."
        )
        new_title = await flow.get_text_input(300)

        if new_title is None:
            return

        if len(new_title) > 256:
            return await ctx.send(
                ":x: The title cannot be longer than 256 characters.")

        await ctx.send(
            ":information_source: Reply with the updated **content** of this tag."
        )
        new_content = await flow.get_tag_content(300)

        if new_content is None:
            return

        if len(new_content) > 2048:
            return await ctx.send(
                ":x: The content cannot be longer than 2048 characters.")

        are_you_sure = await ctx.send(
            f":information_source: Are you sure that you want to edit your "
            f"`{config.BOT_PREFIX}{tag.name}` tag?")

        reaction = await flow.get_yes_no_reaction_confirm(are_you_sure, 200)

        if reaction is None:
            return

        if not reaction:
            return await ctx.send("Aborted.")

        else:
            await self.bot.db.execute(
                "UPDATE guild_tags SET content = $1, title = $3, is_embedded = $4 WHERE id = $2",
                new_content, tag.id, new_title, is_embedded)
            await ctx.send(":white_check_mark: Your tag was edited.")
예제 #5
0
    async def tagcreation(self, ctx):
        """Allow everyone to make tags on this server, or just Administrators"""

        is_allowed = await self.bot.checks.is_tag_creation_allowed(ctx.guild.id
                                                                   )

        pretty_is_allowed = "Only Administrators" if not is_allowed else "Everyone"

        embed = self.bot.embeds.embed_builder(
            title=f":pencil:  Tag Creation on {ctx.guild.name}",
            description=f"React with the {config.GUILD_SETTINGS_GEAR} emoji"
            f" to change this setting.",
            has_footer=False)
        embed.add_field(name="Allowed Tag Creators", value=pretty_is_allowed)

        info_embed = await ctx.send(embed=embed)
        flow = Flow(self.bot, ctx)

        if await flow.gear_reaction_confirm(info_embed, 300):
            everyone = "\U0001f468\U0000200d\U0001f468\U0000200d\U0001f467\U0000200d\U0001f467"
            only_admins = "\U0001f46e"

            status_question = await ctx.send(
                f":information_source: Who should be able to create new tags "
                f"on this server with `-tag add`, "
                f"**everyone** or **just the Administrators** of this server?\n\n"
                f"React with {everyone} for everyone, or with {only_admins} for just "
                f"Administrators.")

            reaction, user = await flow.get_emoji_choice(
                everyone, only_admins, status_question, 240)

            if reaction is None:
                return

            if str(reaction) == everyone:
                await self.bot.db.execute(
                    "UPDATE guilds SET tag_creation_allowed = true WHERE id = $1",
                    ctx.guild.id)
                await ctx.send(
                    ":white_check_mark: Everyone can now make tags with `-tag add` on this server."
                )

            elif str(reaction) == only_admins:
                await self.bot.db.execute(
                    "UPDATE guilds SET tag_creation_allowed = false WHERE id = $1",
                    ctx.guild.id)
                await ctx.send(
                    ":white_check_mark: Only Administrators can now make"
                    " tags with `tag -add` on this server.")

            await self.bot.cache.update_guild_config_cache()
예제 #6
0
    async def veto(self, ctx, bill_ids: Greedy[Bill]):
        """Veto one or multiple bills

        **Example:**
            `-ministry veto 12` will veto Bill #12
            `-ministry veto 45 46 49 51 52` will veto all those bills"""

        if not bill_ids:
            return await ctx.send_help(ctx.command)

        bills = bill_ids
        flow = Flow(self.bot, ctx)

        error_messages = []

        for _bill in bills:
            error = await self.verify_bill(_bill)
            if error:
                error_messages.append((_bill, error))

        if error_messages:
            # Remove bills that did not pass verify_bill from bills list
            bills[:] = [b for b in bills if b not in list(map(list, zip(*error_messages)))[0]]

            error_messages = '\n'.join(
                [f"-  **{_bill.name}** (#{_bill.id}): _{reason}_" for _bill, reason in error_messages])
            await ctx.send(f":warning: The following bills can not be vetoed.\n{error_messages}")

        # If all bills failed verify_bills, return
        if not bills:
            return

        pretty_bills = '\n'.join([f"-  **{_bill.name}** (#{_bill.id})" for _bill in bills])
        are_you_sure = await ctx.send(f":information_source: Are you sure that you want to veto the following bills?"
                                      f"\n{pretty_bills}")

        reaction = await flow.get_yes_no_reaction_confirm(are_you_sure, 200)

        if reaction is None:
            return

        if not reaction:
            return await ctx.send("Aborted.")

        elif reaction:
            async with ctx.typing():
                for _bill in bills:
                    await _bill.veto()
                    self.veto_scheduler.add(_bill)

                await ctx.send(":white_check_mark: All bills were vetoed.")
예제 #7
0
    async def pass_bill(self, ctx, bill_ids: Greedy[Bill]):
        """Pass one or multiple bills into law

        **Example:**
            `-ministry pass 12` will pass Bill #12 into law
            `-ministry pass 45 46 49 51 52` will pass all those bills into law"""

        bills = bill_ids
        flow = Flow(self.bot, ctx)

        error_messages = []

        for bill in bills:
            error = await self.verify_bill(bill)
            if error:
                error_messages.append((bill, error))

        if error_messages:
            # Remove bills that did not pass verify_bill from bills list
            bills = [b for b in bills if b not in list(map(list, zip(*error_messages)))[0]]

            error_messages = '\n'.join(
                [f"-  **{_bill.name}** (#{_bill.id}): _{reason}_" for _bill, reason in error_messages])
            await ctx.send(f":warning: The following bills can not be passed into law.\n{error_messages}")

        # If all bills failed verify_bills, return
        if not bills:
            return

        pretty_bills = '\n'.join([f"-  **{_bill.name}** (#{_bill.id})" for _bill in bills])
        are_you_sure = await ctx.send(f":information_source: Are you sure that you want "
                                      f"to pass the following bills into law?"
                                      f"\n{pretty_bills}")

        reaction = await flow.get_yes_no_reaction_confirm(are_you_sure, 200)

        if reaction is None:
            return

        if not reaction:
            return await ctx.send("Aborted.")

        elif reaction:
            async with ctx.typing():
                for bill in bills:
                    await bill.pass_into_law()
                    self.pass_scheduler.add(bill)

                await ctx.send(":white_check_mark: All bills were passed into law.")
    async def addtagalias(self, ctx, *, tag: OwnedTag):
        """Add a new alias to a tag"""

        flow = Flow(self.bot, ctx)

        await ctx.send(
            f":information_source: Reply with the new alias for `{config.BOT_PREFIX}{tag.name}`."
        )

        alias = await flow.get_text_input(240)

        if not alias:
            return

        if not await self.validate_tag_name(ctx, alias.lower()):
            return

        are_you_sure = await ctx.send(
            f":information_source: Are you sure that you want to add the "
            f"`{config.BOT_PREFIX}{alias}` alias to `{config.BOT_PREFIX}{tag.name}`?"
        )

        reaction = await flow.get_yes_no_reaction_confirm(are_you_sure, 200)

        if reaction is None:
            return

        if not reaction:
            return await ctx.send("Aborted.")

        elif reaction:
            async with self.bot.db.acquire() as con:
                async with con.transaction():
                    status = await con.execute(
                        "INSERT INTO guild_tags_alias (alias, tag_id, guild_id, global)"
                        " VALUES ($1, $2, $3, $4)", alias.lower(), tag.id,
                        ctx.guild.id, tag.is_global)

        if status == "INSERT 0 1":
            await ctx.send(
                f':white_check_mark: The `{config.BOT_PREFIX}{alias}` alias was added to '
                f'`{config.BOT_PREFIX}{tag.name}`.')
예제 #9
0
    async def updatelink(self, ctx, law_id: Law, new_link: str):
        """Update the link to a law

        Useful for applying amendments to laws if the current Speaker does not own the law's Google Doc.

        **Example**:
            `-law updatelink 16 https://docs.google.com/1/d/ajgh3egfdjfnjdf`
        """

        if not self.bot.laws.is_google_doc_link(new_link):
            return await ctx.send(
                f":x: This does not look like a Google Docs link: `{new_link}`"
            )

        law = law_id  # At this point, law_id is already a Law object, so calling it law_id makes no sense

        are_you_sure = await ctx.send(
            f":information_source: Are you sure that you want to change the link to "
            f"`{law.bill.name}` (#{law.id})?")

        flow = Flow(self.bot, ctx)

        reaction = await flow.get_yes_no_reaction_confirm(are_you_sure, 200)

        if reaction is None:
            return

        if not reaction:
            return await ctx.send("Aborted.")

        elif reaction:
            try:
                await law.amend(new_link)
            except DemocracivBotException as e:
                return await ctx.send(e.message)

            law = await Law.convert(ctx, law.id)
            self.amend_scheduler.add(law)
            await ctx.send(
                f":white_check_mark: The link to `{law.bill.name}` was changed."
            )
    async def addalias(self, ctx, *, party: PoliticalParty):
        """Add a new alias to a political party"""

        flow = Flow(self.bot, ctx)

        await ctx.send(
            f":information_source: Reply with the new alias for `{party.role.name}`."
        )

        alias = await flow.get_text_input(240)

        if not alias:
            return

        async with self.bot.db.acquire() as connection:
            async with connection.transaction():
                status = await connection.execute(
                    "INSERT INTO party_alias (alias, party_id) VALUES ($1, $2)",
                    alias.lower(), party.role.id)

        if status == "INSERT 0 1":
            await ctx.send(f':white_check_mark: Alias `{alias}` for party '
                           f'`{party.role.name}` was added.')
예제 #11
0
    async def removelaw(self, ctx, law_ids: Greedy[Law]):
        """Repeal one or multiple laws

        **Example:**
            `-law repeal 24` will repeal law #24
            `-law repeal 56 57 58 12 13` will repeal all those laws"""

        if not law_ids:
            return await ctx.send_help(ctx.command)

        laws = law_ids  # At this point, law_id is already a Law object, so calling it law_id makes no sense

        pretty_laws = '\n'.join(
            [f"-  **{_law.bill.name}** (#{_law.id})" for _law in laws])

        are_you_sure = await ctx.send(
            f":information_source: Are you sure that you want repeal the following laws?"
            f"\n{pretty_laws}")

        flow = Flow(self.bot, ctx)

        reaction = await flow.get_yes_no_reaction_confirm(are_you_sure, 200)

        if reaction is None:
            return

        if not reaction:
            return await ctx.send("Aborted.")

        elif reaction:
            for law in laws:
                await law.repeal()
                self.repeal_scheduler.add(law)

            return await ctx.send(f":white_check_mark: All laws were repealed."
                                  )
    async def addtag(self, ctx):
        """Add a tag for this server"""

        flow = Flow(self.bot, ctx)

        await ctx.send(
            ":information_source: Reply with the **name** of the tag. This will be used to access the"
            " tag via the bot's prefix.")

        name = await flow.get_text_input(300)

        if name is None:
            return

        if not await self.validate_tag_name(ctx, name.lower()):
            return

        embed_q = await ctx.send(
            ":information_source: Should the tag be sent as an embed?")

        embed_bool = await flow.get_yes_no_reaction_confirm(embed_q, 300)

        if embed_bool is None:
            return

        if embed_bool:
            is_embedded = True
        else:
            is_embedded = False

        await ctx.send(
            ":information_source: Reply with the **title** of the tag.")
        title = await flow.get_text_input(300)

        if title is None:
            return

        if len(title) > 256:
            return await ctx.send(
                ":x: The title cannot be longer than 256 characters.")

        await ctx.send(
            ":information_source: Reply with the **content** of the tag.")
        content = await flow.get_tag_content(300)

        if content is None:
            return

        if len(content) > 2048:
            return await ctx.send(
                ":x: The content cannot be longer than 2048 characters.")

        is_global = False

        if ctx.author.guild_permissions.administrator and ctx.guild.id == self.bot.democraciv_guild_object.id:
            is_global_msg = await ctx.send(
                ":information_source: Should this tag be global?")

            reaction = await flow.get_yes_no_reaction_confirm(
                is_global_msg, 300)

            if reaction is None:
                return

            if reaction:
                is_global = True

            elif not reaction:
                is_global = False

        are_you_sure = await ctx.send(
            f":information_source: Are you sure that you want to add the tag "
            f"`{config.BOT_PREFIX}{name}`?")

        reaction = await flow.get_yes_no_reaction_confirm(are_you_sure, 200)

        if reaction is None:
            return

        if not reaction:
            return await ctx.send("Aborted.")

        elif reaction:
            async with self.bot.db.acquire() as con:
                async with con.transaction():
                    _id = await con.fetchval(
                        "INSERT INTO guild_tags (guild_id, name, content, title,"
                        " global, author, is_embedded) VALUES "
                        "($1, $2, $3, $4, $5, $6, $7) RETURNING id",
                        ctx.guild.id, name.lower(), content, title, is_global,
                        ctx.author.id, is_embedded)
                    await con.execute(
                        "INSERT INTO guild_tags_alias (tag_id, alias, guild_id, global)"
                        " VALUES ($1, $2, $3, $4)", _id, name.lower(),
                        ctx.guild.id, is_global)
                    await ctx.send(
                        f":white_check_mark: The `{config.BOT_PREFIX}{name}` tag was added."
                    )
예제 #13
0
    async def exportlaws(self, ctx):
        """Generate a Legal Code as a Google Docs document from the list of active laws"""

        flow = Flow(self.bot, ctx)

        query = """SELECT legislature_laws.law_id, legislature_bills.bill_name, legislature_bills.link 
                   FROM legislature_laws JOIN legislature_bills
                   ON legislature_laws.bill_id = legislature_bills.id ORDER BY legislature_laws.law_id;
                """

        await ctx.send(
            ":information_source: Reply with an **edit** link to a Google Docs "
            "document you created. I will then fill that document to make it an up-to-date Legal Code.\n"
            ":warning: Note that I will replace the entire content of your Google Docs document if it "
            "isn't empty.")

        doc_url = await flow.get_private_text_input(120)

        if not doc_url:
            ctx.command.reset_cooldown(ctx)
            return

        if not self.bot.laws.is_google_doc_link(doc_url):
            ctx.command.reset_cooldown(ctx)
            return await ctx.send(
                ":x: That doesn't look like a Google Docs URL.")

        await ctx.send(
            f":white_check_mark: I will generate an up-to-date Legal Code."
            f"\n:arrows_counterclockwise: This may take a few minutes...")

        async with ctx.typing():
            all_laws = await self.bot.db.fetch(query)
            ugly_laws = []

            for record in all_laws:
                ugly_laws.append({
                    'id': record['law_id'],
                    'name': record['bill_name'],
                    'link': record['link']
                })

            date = datetime.datetime.utcnow().strftime("%B %d, %Y at %H:%M")

            result = await self.bot.google_api.run_apps_script(
                script_id="MMV-pGVACMhaf_DjTn8jfEGqnXKElby-M",
                function="generate_legal_code",
                parameters=[
                    doc_url, {
                        'name': self.bot.mk.NATION_FULL_NAME,
                        'date': date
                    }, ugly_laws
                ])

        if result is None or not result['done']:
            ctx.command.reset_cooldown(ctx)
            return await ctx.send(
                ":x: There was an error while generating the document. Are you sure that you "
                "gave me an edit link?")

        if 'error' in result:
            ctx.command.reset_cooldown(ctx)

            error_msg = ("Exception: No item with the given ID could be found,"
                         " or you do not have permission to access it.",
                         "Action not allowed")

            if result['error']['details'][0]['errorMessage'] in error_msg:
                return await ctx.send(
                    ":x: I cannot access that Google Docs document. Are you sure that you "
                    "gave me an edit link?")
            else:
                return await ctx.send(
                    ":x: There was an error while generating the document. Are you sure that you "
                    "gave me an edit link?")

        embed = self.bot.embeds.embed_builder(
            title=f"Generated Legal Code",
            description="This Legal Code is not guaranteed to be correct. Its "
            f"content is based entirely on the list of Laws "
            f"in `{config.BOT_PREFIX}laws`."
            "\n\nRemember to change the edit link you "
            "gave me earlier to not be public.")

        embed.add_field(name="Link to the Legal Code",
                        value=result['response']['result']['view'],
                        inline=False)

        await ctx.send(embed=embed)
예제 #14
0
    async def welcome(self, ctx):
        """Add a welcome message that every new member will see once they join this server"""

        is_welcome_enabled = await self.bot.checks.is_welcome_message_enabled(
            ctx.guild.id)
        current_welcome_channel = await utils.get_welcome_channel(
            self.bot, ctx.guild)
        current_welcome_message = await self.bot.db.fetchval(
            "SELECT welcome_message FROM guilds WHERE id = $1", ctx.guild.id)

        if current_welcome_channel is None:
            current_welcome_channel = "-"
        else:
            current_welcome_channel = current_welcome_channel.mention

        if not current_welcome_message:
            current_welcome_message = "-"
        elif len(current_welcome_message) > 1024:
            current_welcome_message = "*The welcome message is too long to fit in here.*"

        embed = self.bot.embeds.embed_builder(
            title=f":wave:  Welcome Messages on {ctx.guild.name}",
            description=
            f"React with the {config.GUILD_SETTINGS_GEAR} emoji to change"
            f" these settings.",
            has_footer=False)
        embed.add_field(name="Enabled",
                        value=self.emojiy_settings(is_welcome_enabled))
        embed.add_field(name="Welcome Channel", value=current_welcome_channel)
        embed.add_field(name="Welcome Message",
                        value=current_welcome_message,
                        inline=False)

        info_embed = await ctx.send(embed=embed)

        flow = Flow(self.bot, ctx)

        if await flow.gear_reaction_confirm(info_embed, 300):
            status_question = await ctx.send(
                "React with :white_check_mark: to enable welcome messages,"
                " or with :x: to disable welcome messages.")

            reaction = await flow.get_yes_no_reaction_confirm(
                status_question, 240)

            if reaction is None:
                return

            if reaction:
                await self.bot.db.execute(
                    "UPDATE guilds SET welcome = true WHERE id = $1",
                    ctx.guild.id)
                await ctx.send(":white_check_mark: Enabled welcome messages.")

                # Get new welcome channel
                await ctx.send(
                    ":information_source: Reply with the name of the welcome channel."
                )

                channel_object = await flow.get_new_channel(240)

                if isinstance(channel_object, str):
                    raise exceptions.ChannelNotFoundError(channel_object)

                status = await self.bot.db.execute(
                    "UPDATE guilds SET welcome_channel = $2 WHERE id = $1",
                    ctx.guild.id, channel_object.id)

                if status == "UPDATE 1":
                    await ctx.send(
                        f":white_check_mark: Set the welcome channel to {channel_object.mention}."
                    )

                # Get new welcome message
                await ctx.send(
                    f":information_source: Reply with the message that should be sent to {channel_object.mention} "
                    f"every time a new member joins.\n\nWrite `{{member}}` "
                    f"to make the Bot mention the user.")

                welcome_message = await flow.get_text_input(300)

                if welcome_message:
                    status = await self.bot.db.execute(
                        "UPDATE guilds SET welcome_message = $2 WHERE id = $1",
                        ctx.guild.id, welcome_message)

                    if status == "UPDATE 1":
                        await ctx.send(
                            f":white_check_mark: Welcome message was set.")

            elif not reaction:
                await self.bot.db.execute(
                    "UPDATE guilds SET welcome = false WHERE id = $1",
                    ctx.guild.id)
                await ctx.send(
                    ":white_check_mark: Welcome messages were disabled.")

            await self.bot.cache.update_guild_config_cache()
예제 #15
0
    async def report(self, ctx):
        """Report something to the Democraciv Moderation

         This command only works in DMs with me."""

        flow = Flow(self.bot, ctx)

        anon_question = await ctx.send(
            "You can report something directly to the mods with this command. Abuse "
            "(i.e. spamming joke reports) will be punished.\n\n\n:information_source: "
            "Do you want this report to be anonymous?")

        is_anon = True

        reaction = await flow.get_yes_no_reaction_confirm(anon_question, 150)

        if reaction is None:
            return

        if reaction:
            is_anon = True

        elif not reaction:
            is_anon = False

        await ctx.send(
            ":information_source: Reply with the details of your report. This will abort after"
            " 10 minutes of no reply.")

        content = await flow.get_text_input(600)

        if not content:
            return

        if len(content) > 2048:
            return await ctx.send(
                ":x: Text cannot be more than 2048 characters.")

        pretty_anon = "be anonymous" if is_anon else "not be anonymous"

        are_you_sure = await ctx.send(
            f":information_source: Are you sure that you want to send this report? "
            f"The report will **{pretty_anon}**.")

        reaction = await flow.get_yes_no_reaction_confirm(are_you_sure, 150)

        if reaction is None:
            return

        if reaction:
            embed = self.bot.embeds.embed_builder(
                title=":exclamation: New Report", description=content)

            if is_anon:
                from_value = "*Anonymous Report*"
            else:
                from_value = str(ctx.author)

            embed.add_field(name="From", value=from_value)

            await mk.get_democraciv_channel(
                self.bot,
                mk.DemocracivChannel.MODERATION_NOTIFICATIONS_CHANNEL).send(
                    content=mk.get_democraciv_role(
                        self.bot, mk.DemocracivRole.MODERATION_ROLE).mention,
                    embed=embed)

            await ctx.send(":white_check_mark: Report was sent.")

        elif not reaction:
            return await ctx.send("Aborted.")
예제 #16
0
    async def defaultrole(self, ctx):
        """Give every new member a specific role once they join this server"""

        is_default_role_enabled = await self.bot.checks.is_default_role_enabled(
            ctx.guild.id)

        current_default_role = await self.bot.db.fetchval(
            "SELECT defaultrole_role FROM guilds WHERE id = $1", ctx.guild.id)

        current_default_role = ctx.guild.get_role(current_default_role)

        if current_default_role is None:
            current_default_role = "-"
        else:
            current_default_role = current_default_role.mention

        embed = self.bot.embeds.embed_builder(
            title=f":partying_face:  Default Role on {ctx.guild.name}",
            description=f"React with the {config.GUILD_SETTINGS_GEAR} emoji to"
            f" change these settings.",
            has_footer=False)
        embed.add_field(name="Enabled",
                        value=self.emojiy_settings(is_default_role_enabled))
        embed.add_field(name="Default Role", value=current_default_role)

        info_embed = await ctx.send(embed=embed)

        flow = Flow(self.bot, ctx)

        if await flow.gear_reaction_confirm(info_embed, 300):

            status_question = await ctx.send(
                "React with :white_check_mark: to enable the default role, or with :x: to disable the default role."
            )

            reaction = await flow.get_yes_no_reaction_confirm(
                status_question, 240)

            if reaction is None:
                return

            if reaction:
                await self.bot.db.execute(
                    "UPDATE guilds SET defaultrole = true WHERE id = $1",
                    ctx.guild.id)
                await ctx.send(":white_check_mark: Enabled the default role.")

                await ctx.send(
                    ":information_source: Reply with the name of the role that every "
                    "new member should get once they join.")

                new_default_role = await flow.get_new_role(240)

                if isinstance(new_default_role, str):
                    await ctx.send(
                        f":white_check_mark: I will **create a new role** on this server named `{new_default_role}`"
                        f" for the default role.")
                    try:
                        new_default_role_object = await ctx.guild.create_role(
                            name=new_default_role)
                    except discord.Forbidden:
                        raise exceptions.ForbiddenError(
                            exceptions.ForbiddenTask.CREATE_ROLE,
                            new_default_role)

                else:
                    new_default_role_object = new_default_role

                    await ctx.send(
                        f":white_check_mark: I'll use the **pre-existing role** named "
                        f"`{new_default_role_object.name}` for the default role."
                    )

                status = await self.bot.db.execute(
                    "UPDATE guilds SET defaultrole_role = $2 WHERE id = $1",
                    ctx.guild.id, new_default_role_object.id)

                if status == "UPDATE 1":
                    await ctx.send(
                        f":white_check_mark: Set the default role to `{new_default_role_object.name}`."
                    )

            elif not reaction:
                await self.bot.db.execute(
                    "UPDATE guilds SET defaultrole = false WHERE id = $1",
                    ctx.guild.id)
                await ctx.send(":white_check_mark: Disabled the default role.")

            await self.bot.cache.update_guild_config_cache()
예제 #17
0
    async def logs(self, ctx):
        """Log important events like message edits & deletions and more to a specific channel"""

        is_logging_enabled = await self.bot.checks.is_logging_enabled(
            ctx.guild.id)
        current_logging_channel = await utils.get_logging_channel(
            self.bot, ctx.guild)

        if current_logging_channel is None:
            current_logging_channel = "-"
        else:
            current_logging_channel = current_logging_channel.mention

        embed = self.bot.embeds.embed_builder(
            title=f":spy:  Event Logging on {ctx.guild.name}",
            description=f"React with the {config.GUILD_SETTINGS_GEAR} emoji "
            f"to change these settings.",
            has_footer=False)

        embed.add_field(name="Enabled",
                        value=self.emojiy_settings(is_logging_enabled))
        embed.add_field(name="Log Channel", value=current_logging_channel)

        info_embed = await ctx.send(embed=embed)

        flow = Flow(self.bot, ctx)

        if await flow.gear_reaction_confirm(info_embed, 300):

            status_question = await ctx.send(
                "React with :white_check_mark: to enable logging, "
                "or with :x: to disable logging.")

            reaction = await flow.get_yes_no_reaction_confirm(
                status_question, 240)

            if reaction is None:
                return

            if reaction:
                await self.bot.db.execute(
                    "UPDATE guilds SET logging = true WHERE id = $1",
                    ctx.guild.id)
                await ctx.send(":white_check_mark: Event logging was enabled.")
                await ctx.send(
                    ":information_source: Reply with the name of the channel"
                    " that I should use to log all events to.")

                channel_object = await flow.get_new_channel(240)

                if isinstance(channel_object, str):
                    raise exceptions.ChannelNotFoundError(channel_object)

                status = await self.bot.db.execute(
                    "UPDATE guilds SET logging_channel = $2 WHERE id = $1",
                    ctx.guild.id, channel_object.id)

                if status == "UPDATE 1":
                    await ctx.send(
                        f":white_check_mark: Set the logging channel to {channel_object.mention}."
                    )

            elif not reaction:
                await self.bot.db.execute(
                    "UPDATE guilds SET logging = false WHERE id = $1",
                    ctx.guild.id)
                await ctx.send(":white_check_mark: Event logging was disabled."
                               )

            await self.bot.cache.update_guild_config_cache()
예제 #18
0
    async def archiveoldgov(self, ctx):
        """Move all channels in the Government category and #propaganda into the Archives and set the right permissions
        """

        flow = Flow(self.bot, ctx)

        are_you_sure = await ctx.send(
            f":information_source: Are you sure that you want archive every government channel?"
        )

        reaction = await flow.get_yes_no_reaction_confirm(are_you_sure, 200)

        if reaction is None:
            return

        if not reaction:
            return await ctx.send("Aborted.")

        elif reaction:
            async with ctx.typing():
                government_category: discord.CategoryChannel = discord.utils.get(
                    self.bot.democraciv_guild_object.categories,
                    name="Government")

                if government_category is None:
                    return await ctx.send(
                        ":x: There is no category named `Government` for me to archive."
                    )

                def predicate(c):
                    return c.name.lower() == f"mk{self.bot.mk.MARK}-archive"

                archive_category = discord.utils.find(
                    predicate, self.bot.democraciv_guild_object.categories)

                if archive_category is None:
                    return await ctx.send(
                        f":x: There is no category named `MK{self.bot.mk.MARK}-Archive` for me to use."
                    )

                everyone_perms = discord.PermissionOverwrite(
                    read_message_history=False,
                    send_messages=False,
                    read_messages=False)
                everyone_role = self.bot.democraciv_guild_object.default_role
                archive_perms = discord.PermissionOverwrite(
                    read_message_history=True,
                    send_messages=False,
                    read_messages=True)
                archives_role = discord.utils.get(
                    self.bot.democraciv_guild_object.roles, name="Archives")

                if archives_role is None:
                    return await ctx.send(
                        ":x: There is no role named `Archives` for me to use.")

                for channel in government_category.text_channels:
                    await channel.send(
                        f":tada: Thanks for playing Democraciv MK{self.bot.mk.MARK}!"
                    )
                    await channel.edit(
                        name=f"mk{self.bot.mk.MARK}-{channel.name}",
                        overwrites={
                            everyone_role: everyone_perms,
                            archives_role: archive_perms
                        },
                        category=archive_category)

                propaganda_channel = discord.utils.get(
                    self.bot.democraciv_guild_object.text_channels,
                    name="propaganda")

                if propaganda_channel is not None:
                    await propaganda_channel.edit(
                        name=f"mk{self.bot.mk.MARK}-propaganda",
                        category=archive_category,
                        overwrites={
                            everyone_role: everyone_perms,
                            archives_role: archive_perms
                        })

                press_channel = discord.utils.get(
                    self.bot.democraciv_guild_object.text_channels,
                    name="press")

                if press_channel is not None:
                    await press_channel.edit(
                        name=f"mk{self.bot.mk.MARK}-press",
                        category=archive_category,
                        overwrites={
                            everyone_role: everyone_perms,
                            archives_role: archive_perms
                        })

                await ctx.send(":white_check_mark: Done.")
    async def create_new_party(self, ctx) -> typing.Optional[PoliticalParty]:
        await ctx.send(
            ":information_source: Reply with the name of the new party you want to create."
        )

        flow = Flow(self.bot, ctx)
        role_name = await flow.get_new_role(240)

        if isinstance(role_name, str):
            is_updated = False

            await ctx.send(
                f":white_check_mark: I will **create a new role** on this server named `{role_name}`"
                f" for the new party.")
            try:
                discord_role = await ctx.guild.create_role(name=role_name)
            except discord.Forbidden:
                raise exceptions.ForbiddenError(
                    exceptions.ForbiddenTask.CREATE_ROLE, role_name)

        else:
            is_updated = True
            discord_role = role_name

            await ctx.send(
                f":white_check_mark: I'll use the **pre-existing role**"
                f" `{discord_role.name}` for the new party.")

        await ctx.send(
            ":information_source: Reply with the name of the party's leader or representative."
        )
        leader = await flow.get_text_input(240)

        if not leader:
            return

        try:
            leader_member = await commands.MemberConverter().convert(
                ctx, leader)
        except commands.BadArgument:
            raise exceptions.MemberNotFoundError(leader)

        await ctx.send(
            ":information_source: Reply with the invite link to the party's Discord server. "
            "If they don't have one, just reply with gibberish.")

        party_invite = await flow.get_text_input(300)

        if not party_invite:
            return None

        discord_invite_pattern = re.compile(
            r"(?:https?://)?discord(?:app\.com/invite|\.gg)/?[a-zA-Z0-9]+/?")
        if not discord_invite_pattern.fullmatch(party_invite):
            party_invite = None

        is_private = False
        private_question = await ctx.send(
            "Should this new party be **public**, i.e. join-able by everyone? "
            "React with :white_check_mark: if yes, or with :x: if not.")

        reaction = await flow.get_yes_no_reaction_confirm(
            private_question, 240)

        if reaction is None:
            return None

        if reaction:
            is_private = False

        elif not reaction:
            is_private = True

        async with self.bot.db.acquire() as connection:
            async with connection.transaction():
                await connection.execute(
                    "INSERT INTO parties (id, discord_invite, is_private, leader) VALUES ($1, $2, $3, $4)"
                    "ON CONFLICT (id) DO UPDATE SET discord_invite = $2, is_private = $3,"
                    " leader = $4 WHERE parties.id = $1", discord_role.id,
                    party_invite, is_private, leader_member.id)

                await connection.execute(
                    "INSERT INTO party_alias (alias, party_id) VALUES ($1, $2)"
                    " ON CONFLICT DO NOTHING ", discord_role.name.lower(),
                    discord_role.id)

                if not is_updated:
                    await ctx.send(
                        f':white_check_mark: `{discord_role.name}` was added as a new party.'
                    )
                else:
                    await ctx.send(
                        f':white_check_mark: `{discord_role.name}` was added as a new party or its '
                        f'properties were updated if it already existed.')

        return await PoliticalParty.convert(ctx, discord_role.id)
    async def mergeparties(self, ctx, amount_of_parties: int):
        """Merge one or multiple parties into a single, new party"""

        flow = Flow(self.bot, ctx)

        to_be_merged = []

        for i in range(1, amount_of_parties + 1):
            await ctx.send(
                f":information_source: What's the name or alias for political party #{i}?"
            )

            name = await flow.get_text_input(120)

            if not name:
                return

            try:
                party = await PoliticalParty.convert(ctx, name)
            except exceptions.PartyNotFoundError:
                return await ctx.send(
                    f":x: There is no party that matches `{name}`. Aborted.")

            to_be_merged.append(party)

        members_to_merge = {
            member
            for party in to_be_merged for member in party.role.members
        }
        pretty_parties = [f"`{party.role.name}`" for party in to_be_merged]

        are_you_sure = await ctx.send(
            f":information_source: Are you sure that you want to merge"
            f" {', '.join(pretty_parties)} into one, new party?")

        reaction = await flow.get_yes_no_reaction_confirm(are_you_sure, 120)

        if reaction is None:
            return

        if not reaction:
            return await ctx.send("Aborted.")

        try:
            new_party = await self.create_new_party(ctx)
        except exceptions.DemocracivBotException as e:
            return await ctx.send(
                f"{e.message}\n:x: Party creation failed, old parties were not deleted."
            )

        if new_party is None or new_party.role is None:
            return await ctx.send(
                ":x: Party creation failed, old parties were not deleted.")

        async with ctx.typing():
            for member in members_to_merge:
                await member.add_roles(new_party.role)

            for party in to_be_merged:
                # In case the merger keeps the name and thus role of an old party
                if party.role.id == new_party.role.id:
                    continue

                async with self.bot.db.acquire() as connection:
                    async with connection.transaction():
                        await connection.execute(
                            "DELETE FROM party_alias WHERE party_id = $1",
                            party.role.id)
                        await connection.execute(
                            "DELETE FROM parties WHERE id = $1", party.role.id)

                await party.role.delete()

        await ctx.send(":white_check_mark: The old parties were deleted and"
                       " all their members have now the role of the new party."
                       )