Edit on GitHub

tickets_plus.cogs.staff

Staff-only commands cog for Tickets+.

Seperated from the main cog to keep things clean. Keeps the staff-only commands in clear view.

Typical usage example:
from tickets_plus import bot
bot_instance = bot.TicketsPlusBot(...)
await bot_instance.load_extension("tickets_plus.cogs.staff")
  1"""Staff-only commands cog for Tickets+.
  2
  3Seperated from the main cog to keep things clean.
  4Keeps the staff-only commands in clear view.
  5
  6Typical usage example:
  7    ```py
  8    from tickets_plus import bot
  9    bot_instance = bot.TicketsPlusBot(...)
 10    await bot_instance.load_extension("tickets_plus.cogs.staff")
 11    ```
 12"""
 13# License: EPL-2.0
 14# SPDX-License-Identifier: EPL-2.0
 15# Copyright (c) 2021-present The Tickets+ Contributors
 16# This Source Code may also be made available under the following
 17# Secondary Licenses when the conditions for such availability set forth
 18# in the Eclipse Public License, v. 2.0 are satisfied: GPL-3.0-only OR
 19# If later approved by the Initial Contributor, GPL-3.0-or-later.
 20
 21import datetime
 22import logging
 23import string
 24
 25import discord
 26from discord import app_commands, utils
 27from discord.ext import commands
 28
 29from tickets_plus import bot
 30from tickets_plus.ext import checks, exceptions
 31
 32
 33class StaffCmmd(commands.Cog, name="StaffCommands"):
 34    """Staff-only commands for Tickets+.
 35
 36    This cog contains all staff-only commands. These commands are
 37    only available to users with the staff role, and are used to
 38    manage the bot and the tickets.
 39    """
 40
 41    def __init__(self, bot_instance: bot.TicketsPlusBot) -> None:
 42        """Initialises the cog instance.
 43
 44        We store some attributes here for later use.
 45
 46        Args:
 47            bot_instance: The bot instance.
 48        """
 49        self._bt = bot_instance
 50        logging.info("Loaded %s", self.__class__.__name__)
 51
 52    @app_commands.command(name="respond", description="Respond to a ticket as the bot.")
 53    @app_commands.guild_only()
 54    @checks.is_staff_check()
 55    @app_commands.describe(message="The message to send to the ticket.")
 56    async def respond(self, ctx: discord.Interaction, message: str) -> None:
 57        """Respond to a ticket as the bot.
 58
 59        This command is used to respond to a ticket as the bot.
 60        It can be used in a ticket channel or a staff notes thread.
 61        It then sends the message to the ticket as the bot.
 62
 63        Args:
 64            ctx: The interaction context.
 65            message: The message to send to the ticket.
 66
 67        Raises:
 68            `tickets_plus.ext.exceptions.InvalidLocation`: Wrong location.
 69                Raised if the command is used in a channel that is not a ticket
 70                or a staff notes thread.
 71        """
 72        await ctx.response.defer(ephemeral=True)
 73        async with self._bt.get_connection() as confg:
 74            guild = await confg.get_guild(ctx.guild_id)  # type: ignore # checked in decorator
 75            if not ctx.user.resolved_permissions.mention_everyone:
 76                message = utils.escape_mentions(message)
 77            if isinstance(ctx.channel, discord.Thread):
 78                ticket = await confg.fetch_ticket(ctx.channel.parent.id)
 79                if ticket is None:
 80                    raise exceptions.InvalidLocation("The parent channel is not a ticket.")
 81                if ticket.staff_note_thread != ctx.channel.id:
 82                    raise exceptions.InvalidLocation("This channel is not the designated staff"
 83                                                     " notes thread.")
 84                await ctx.followup.send(f"Responding to ticket with message:\n{message}")
 85                await ctx.channel.parent.send(  # type: ignore
 86                    f"**{guild.staff_team_name}:** {message}")
 87
 88            elif isinstance(ctx.channel, discord.TextChannel):
 89                ticket = await confg.fetch_ticket(ctx.channel.id)
 90                if ticket is None:
 91                    raise exceptions.InvalidLocation("This channel is not a ticket."
 92                                                     " If it is, use /register.")
 93                await ctx.followup.send(
 94                    f"Responding to ticket with message:\n{message}",
 95                    ephemeral=True,
 96                )
 97                await ctx.channel.send(f"**{guild.staff_team_name}:** {message}")
 98
 99            await confg.close()
100
101    @app_commands.command(name="join", description="Join a ticket's staff notes.")
102    @app_commands.guild_only()
103    @checks.is_staff_check()
104    async def join(self, ctx: discord.Interaction) -> None:
105        """Adds the user to the ticket's staff note thread.
106
107        This command is used to add the user to the ticket's staff notes thread.
108        It's used to allow staff to join notes without the need for the
109        user to have the Manage Threads permission.
110
111        Args:
112            ctx: The interaction context.
113
114        Raises:
115            `tickets_plus.ext.exceptions.InvalidLocation`: Wrong location.
116                Raised if the command is used in a channel that is not a ticket
117                or there is no staff notes thread.
118            `tickets_plus.ext.exceptions.ReferenceNotFound`: Thread missing.
119                Raised if the staff notes thread is missing.
120        """
121        await ctx.response.defer(ephemeral=True)
122        async with self._bt.get_connection() as confg:
123            # We don't need an account for DMs here, due to the guild_only.
124            ticket = await confg.fetch_ticket(ctx.channel.id)  # type: ignore
125            if ticket is None:
126                raise exceptions.InvalidLocation("This channel is not a ticket."
127                                                 " If it is, use /register.")
128            if ticket.staff_note_thread is None:
129                raise exceptions.InvalidLocation("This ticket has no staff notes.")
130            thred = ctx.guild.get_thread(  # type: ignore
131                ticket.staff_note_thread)
132            if thred is None:
133                raise exceptions.ReferenceNotFound("This ticket's staff notes thread is missing."
134                                                   " Was it deleted?")
135            emd = discord.Embed(title="Success!",
136                                description="You have joined the staff notes thread.",
137                                color=discord.Color.green())
138            await thred.add_user(ctx.user)
139            await ctx.followup.send(embed=emd, ephemeral=True)
140
141    @app_commands.command(name="anonymize", description="Toggle anonymous staff responses.")
142    @app_commands.guild_only()
143    @checks.is_staff_check()
144    async def anonymize(self, ctx: discord.Interaction) -> None:
145        """Toggle anonymous staff responses.
146
147        This command is used to toggle anonymous staff responses.
148        The implementation itself is in events.py.
149
150        Args:
151            ctx: The interaction context.
152
153        Raises:
154            `tickets_plus.ext.exceptions.InvalidLocation`: Wrong location.
155                Raised if the command is used in a channel that is not a ticket.
156        """
157        await ctx.response.defer(ephemeral=True)
158        async with self._bt.get_connection() as confg:
159            # Checked by discord in decorator
160            ticket = await confg.fetch_ticket(ctx.channel.id)  # type: ignore
161            if ticket is None:
162                raise exceptions.InvalidLocation("This channel is not a ticket.")
163            ticket.anonymous = not ticket.anonymous
164            status = ticket.anonymous
165            await confg.commit()
166        emd = discord.Embed(title="Success!",
167                            description=f"Anonymous staff responses are now {status}.",
168                            color=discord.Color.green() if status else discord.Color.red())
169        await ctx.followup.send(embed=emd, ephemeral=True)
170
171    @app_commands.command(name="register", description="Register an existing channel as a ticket.")
172    @app_commands.describe(thread="The staff notes thread for the ticket.")
173    @app_commands.guild_only()
174    @checks.is_staff_check()
175    async def register(self, ctx: discord.Interaction, thread: discord.Thread | None = None) -> None:
176        """A migration command to register an existing channel as a ticket.
177
178        We have this command to allow users to migrate from the old version,
179        or add channels that the bot has missed while it was offline.
180        It basically just tells the bot to start tracking the channel.
181
182        Args:
183            ctx: The interaction context.
184            thread: The staff notes thread for the ticket.
185
186        Raises:
187            `tickets_plus.ext.exceptions.InvalidLocation`: Wrong location.
188                If the channel is already a ticket.
189                Or the execution space is not a text channel.
190        """
191        await ctx.response.defer(ephemeral=True)
192        if isinstance(ctx.channel, discord.TextChannel):
193            channel = ctx.channel
194            async with self._bt.get_connection() as confg:
195                guild = await confg.get_guild(ctx.guild_id)  # type: ignore # checked in decorator
196                if thread is None and guild.legacy_threads:
197                    thread = await channel.create_thread(
198                        name="Staff Notes",
199                        reason=f"Staff notes for Ticket {channel.name}",
200                        auto_archive_duration=10080,
201                    )
202                    await thread.send(string.Template(guild.open_message).safe_substitute(channel=channel.mention))
203                new, ticket = await confg.get_ticket(
204                    ctx.channel.id,
205                    ctx.guild_id,  # type: ignore
206                    thread.id)
207                # Unused, we just want to check if it's new and commit it.
208                del ticket
209                if not new:
210                    raise exceptions.InvalidLocation("This channel is already a ticket.")
211                await confg.commit()
212            emd = discord.Embed(title="Success!",
213                                description="Registered channel as a ticket.",
214                                color=discord.Color.green())
215            await ctx.followup.send(embed=emd, ephemeral=True)
216            return
217        raise exceptions.InvalidLocation("Invalid command execution space.")
218
219    @app_commands.command(name="usrstatus", description="Set a new user status.")
220    @app_commands.guild_only()
221    @checks.is_staff_check()
222    @app_commands.describe(
223        target="The user to set the status for.",
224        status="The new status.",
225        days="The new status time days.",
226        hours="The new status time hours.",
227        minutes="The new status time minutes.",
228    )
229    @app_commands.choices(status=[
230        app_commands.Choice(name="None", value=0),
231        app_commands.Choice(name="Support Blocked", value=1),
232        app_commands.Choice(name="Community Support Blocked", value=2),
233    ])
234    async def usrstatus(self,
235                        interaction: discord.Interaction,
236                        target: discord.Member,
237                        status: app_commands.Choice[int],
238                        days: int = 0,
239                        hours: int = 0,
240                        minutes: int = 0) -> None:
241        """Set a new user status.
242
243        This command is used to set a new user status.
244        It's used to block users from opening tickets or from getting support.
245
246        Args:
247            interaction: Discord interaction.
248            target: The user to set the status for.
249            status: The new status.
250            days: The new status time days.
251            hours: The new status time hours.
252            minutes: The new status time minutes.
253        """
254        await interaction.response.defer(ephemeral=True)
255        async with self._bt.get_connection() as confg:
256            member = await confg.get_member(
257                target.id,
258                interaction.guild_id  # type: ignore
259            )
260            member.status = status.value
261            if status.value == 0:
262                member.status_till = None
263                await confg.commit()
264                emd = discord.Embed(title="Success!",
265                                    description=f"Removed status from {target.mention}.",
266                                    color=discord.Color.green())
267                emd2 = discord.Embed(title="Status Removed",
268                                     description=(f"Your status in {target.guild.name} has been removed"
269                                                  f" by {interaction.user.display_name}."),
270                                     color=discord.Color.green())
271            else:
272                if days + hours + minutes == 0:
273                    penalty_time = "Indefinitely"
274                    member.status_till = None
275                else:
276                    penalty_time = datetime.timedelta(days=days, hours=hours, minutes=minutes)
277                    member.status_till = utils.utcnow() + penalty_time
278                if status.value == 1:
279                    if member.guild.support_block is None:
280                        raise exceptions.InvalidParameters("The support block role has not been set.\n"
281                                                           "This may be intentional.\n"
282                                                           "If not please set it up using the /settings.")
283                    await target.add_roles(discord.Object(member.guild.support_block))
284                    emd = discord.Embed(title="Success!",
285                                        description=(f"Blocked {target.mention} from opening tickets"
286                                                     f" for {str(penalty_time)}."),
287                                        color=discord.Color.red())
288                    emd2 = discord.Embed(title="Support Blocked",
289                                         description=("You have been blocked from opening tickets from"
290                                                      f" {target.guild.name} for {str(penalty_time)}."),
291                                         color=discord.Color.red())
292                else:
293                    if member.guild.helping_block is None:
294                        raise exceptions.InvalidParameters("The helping block role has not been set.\n"
295                                                           "This may be intentional.\n"
296                                                           "If not please set it up using the /settings.")
297                    await target.add_roles(discord.Object(member.guild.helping_block))
298                    emd = discord.Embed(title="Success!",
299                                        description=(f"Blocked {target.mention} "
300                                                     f"from providing support for {str(penalty_time)}."),
301                                        color=discord.Color.red())
302                    emd2 = discord.Embed(title="Community Support Blocked",
303                                         description=("You have been blocked from providing "
304                                                      f"support from {target.guild.name}"
305                                                      f" for {str(penalty_time)}."),
306                                         color=discord.Color.red())
307                    if member.guild.strip_roles:
308                        roles = await confg.get_all_community_roles(interaction.guild_id)  # type: ignore
309                        unpck = [discord.Object(rle.role_id) for rle in roles]
310                        await target.remove_roles(*unpck, reason="Community Support Blocked")
311            await confg.commit()
312        await interaction.followup.send(embed=emd, ephemeral=True)
313        try:
314            await target.send(embed=emd2)
315        except discord.Forbidden:
316            logging.debug("Failed to send status message to user.")
317
318
319async def setup(bot_instance: bot.TicketsPlusBot) -> None:
320    """Setup function for the StaffCmmd cog.
321
322    Adds the StaffCmmd cog to the bot.
323    Automatically called by the bot.
324
325    Args:
326        bot_instance: The bot instance.
327    """
328    await bot_instance.add_cog(StaffCmmd(bot_instance))
class StaffCmmd(discord.ext.commands.cog.Cog):
 34class StaffCmmd(commands.Cog, name="StaffCommands"):
 35    """Staff-only commands for Tickets+.
 36
 37    This cog contains all staff-only commands. These commands are
 38    only available to users with the staff role, and are used to
 39    manage the bot and the tickets.
 40    """
 41
 42    def __init__(self, bot_instance: bot.TicketsPlusBot) -> None:
 43        """Initialises the cog instance.
 44
 45        We store some attributes here for later use.
 46
 47        Args:
 48            bot_instance: The bot instance.
 49        """
 50        self._bt = bot_instance
 51        logging.info("Loaded %s", self.__class__.__name__)
 52
 53    @app_commands.command(name="respond", description="Respond to a ticket as the bot.")
 54    @app_commands.guild_only()
 55    @checks.is_staff_check()
 56    @app_commands.describe(message="The message to send to the ticket.")
 57    async def respond(self, ctx: discord.Interaction, message: str) -> None:
 58        """Respond to a ticket as the bot.
 59
 60        This command is used to respond to a ticket as the bot.
 61        It can be used in a ticket channel or a staff notes thread.
 62        It then sends the message to the ticket as the bot.
 63
 64        Args:
 65            ctx: The interaction context.
 66            message: The message to send to the ticket.
 67
 68        Raises:
 69            `tickets_plus.ext.exceptions.InvalidLocation`: Wrong location.
 70                Raised if the command is used in a channel that is not a ticket
 71                or a staff notes thread.
 72        """
 73        await ctx.response.defer(ephemeral=True)
 74        async with self._bt.get_connection() as confg:
 75            guild = await confg.get_guild(ctx.guild_id)  # type: ignore # checked in decorator
 76            if not ctx.user.resolved_permissions.mention_everyone:
 77                message = utils.escape_mentions(message)
 78            if isinstance(ctx.channel, discord.Thread):
 79                ticket = await confg.fetch_ticket(ctx.channel.parent.id)
 80                if ticket is None:
 81                    raise exceptions.InvalidLocation("The parent channel is not a ticket.")
 82                if ticket.staff_note_thread != ctx.channel.id:
 83                    raise exceptions.InvalidLocation("This channel is not the designated staff"
 84                                                     " notes thread.")
 85                await ctx.followup.send(f"Responding to ticket with message:\n{message}")
 86                await ctx.channel.parent.send(  # type: ignore
 87                    f"**{guild.staff_team_name}:** {message}")
 88
 89            elif isinstance(ctx.channel, discord.TextChannel):
 90                ticket = await confg.fetch_ticket(ctx.channel.id)
 91                if ticket is None:
 92                    raise exceptions.InvalidLocation("This channel is not a ticket."
 93                                                     " If it is, use /register.")
 94                await ctx.followup.send(
 95                    f"Responding to ticket with message:\n{message}",
 96                    ephemeral=True,
 97                )
 98                await ctx.channel.send(f"**{guild.staff_team_name}:** {message}")
 99
100            await confg.close()
101
102    @app_commands.command(name="join", description="Join a ticket's staff notes.")
103    @app_commands.guild_only()
104    @checks.is_staff_check()
105    async def join(self, ctx: discord.Interaction) -> None:
106        """Adds the user to the ticket's staff note thread.
107
108        This command is used to add the user to the ticket's staff notes thread.
109        It's used to allow staff to join notes without the need for the
110        user to have the Manage Threads permission.
111
112        Args:
113            ctx: The interaction context.
114
115        Raises:
116            `tickets_plus.ext.exceptions.InvalidLocation`: Wrong location.
117                Raised if the command is used in a channel that is not a ticket
118                or there is no staff notes thread.
119            `tickets_plus.ext.exceptions.ReferenceNotFound`: Thread missing.
120                Raised if the staff notes thread is missing.
121        """
122        await ctx.response.defer(ephemeral=True)
123        async with self._bt.get_connection() as confg:
124            # We don't need an account for DMs here, due to the guild_only.
125            ticket = await confg.fetch_ticket(ctx.channel.id)  # type: ignore
126            if ticket is None:
127                raise exceptions.InvalidLocation("This channel is not a ticket."
128                                                 " If it is, use /register.")
129            if ticket.staff_note_thread is None:
130                raise exceptions.InvalidLocation("This ticket has no staff notes.")
131            thred = ctx.guild.get_thread(  # type: ignore
132                ticket.staff_note_thread)
133            if thred is None:
134                raise exceptions.ReferenceNotFound("This ticket's staff notes thread is missing."
135                                                   " Was it deleted?")
136            emd = discord.Embed(title="Success!",
137                                description="You have joined the staff notes thread.",
138                                color=discord.Color.green())
139            await thred.add_user(ctx.user)
140            await ctx.followup.send(embed=emd, ephemeral=True)
141
142    @app_commands.command(name="anonymize", description="Toggle anonymous staff responses.")
143    @app_commands.guild_only()
144    @checks.is_staff_check()
145    async def anonymize(self, ctx: discord.Interaction) -> None:
146        """Toggle anonymous staff responses.
147
148        This command is used to toggle anonymous staff responses.
149        The implementation itself is in events.py.
150
151        Args:
152            ctx: The interaction context.
153
154        Raises:
155            `tickets_plus.ext.exceptions.InvalidLocation`: Wrong location.
156                Raised if the command is used in a channel that is not a ticket.
157        """
158        await ctx.response.defer(ephemeral=True)
159        async with self._bt.get_connection() as confg:
160            # Checked by discord in decorator
161            ticket = await confg.fetch_ticket(ctx.channel.id)  # type: ignore
162            if ticket is None:
163                raise exceptions.InvalidLocation("This channel is not a ticket.")
164            ticket.anonymous = not ticket.anonymous
165            status = ticket.anonymous
166            await confg.commit()
167        emd = discord.Embed(title="Success!",
168                            description=f"Anonymous staff responses are now {status}.",
169                            color=discord.Color.green() if status else discord.Color.red())
170        await ctx.followup.send(embed=emd, ephemeral=True)
171
172    @app_commands.command(name="register", description="Register an existing channel as a ticket.")
173    @app_commands.describe(thread="The staff notes thread for the ticket.")
174    @app_commands.guild_only()
175    @checks.is_staff_check()
176    async def register(self, ctx: discord.Interaction, thread: discord.Thread | None = None) -> None:
177        """A migration command to register an existing channel as a ticket.
178
179        We have this command to allow users to migrate from the old version,
180        or add channels that the bot has missed while it was offline.
181        It basically just tells the bot to start tracking the channel.
182
183        Args:
184            ctx: The interaction context.
185            thread: The staff notes thread for the ticket.
186
187        Raises:
188            `tickets_plus.ext.exceptions.InvalidLocation`: Wrong location.
189                If the channel is already a ticket.
190                Or the execution space is not a text channel.
191        """
192        await ctx.response.defer(ephemeral=True)
193        if isinstance(ctx.channel, discord.TextChannel):
194            channel = ctx.channel
195            async with self._bt.get_connection() as confg:
196                guild = await confg.get_guild(ctx.guild_id)  # type: ignore # checked in decorator
197                if thread is None and guild.legacy_threads:
198                    thread = await channel.create_thread(
199                        name="Staff Notes",
200                        reason=f"Staff notes for Ticket {channel.name}",
201                        auto_archive_duration=10080,
202                    )
203                    await thread.send(string.Template(guild.open_message).safe_substitute(channel=channel.mention))
204                new, ticket = await confg.get_ticket(
205                    ctx.channel.id,
206                    ctx.guild_id,  # type: ignore
207                    thread.id)
208                # Unused, we just want to check if it's new and commit it.
209                del ticket
210                if not new:
211                    raise exceptions.InvalidLocation("This channel is already a ticket.")
212                await confg.commit()
213            emd = discord.Embed(title="Success!",
214                                description="Registered channel as a ticket.",
215                                color=discord.Color.green())
216            await ctx.followup.send(embed=emd, ephemeral=True)
217            return
218        raise exceptions.InvalidLocation("Invalid command execution space.")
219
220    @app_commands.command(name="usrstatus", description="Set a new user status.")
221    @app_commands.guild_only()
222    @checks.is_staff_check()
223    @app_commands.describe(
224        target="The user to set the status for.",
225        status="The new status.",
226        days="The new status time days.",
227        hours="The new status time hours.",
228        minutes="The new status time minutes.",
229    )
230    @app_commands.choices(status=[
231        app_commands.Choice(name="None", value=0),
232        app_commands.Choice(name="Support Blocked", value=1),
233        app_commands.Choice(name="Community Support Blocked", value=2),
234    ])
235    async def usrstatus(self,
236                        interaction: discord.Interaction,
237                        target: discord.Member,
238                        status: app_commands.Choice[int],
239                        days: int = 0,
240                        hours: int = 0,
241                        minutes: int = 0) -> None:
242        """Set a new user status.
243
244        This command is used to set a new user status.
245        It's used to block users from opening tickets or from getting support.
246
247        Args:
248            interaction: Discord interaction.
249            target: The user to set the status for.
250            status: The new status.
251            days: The new status time days.
252            hours: The new status time hours.
253            minutes: The new status time minutes.
254        """
255        await interaction.response.defer(ephemeral=True)
256        async with self._bt.get_connection() as confg:
257            member = await confg.get_member(
258                target.id,
259                interaction.guild_id  # type: ignore
260            )
261            member.status = status.value
262            if status.value == 0:
263                member.status_till = None
264                await confg.commit()
265                emd = discord.Embed(title="Success!",
266                                    description=f"Removed status from {target.mention}.",
267                                    color=discord.Color.green())
268                emd2 = discord.Embed(title="Status Removed",
269                                     description=(f"Your status in {target.guild.name} has been removed"
270                                                  f" by {interaction.user.display_name}."),
271                                     color=discord.Color.green())
272            else:
273                if days + hours + minutes == 0:
274                    penalty_time = "Indefinitely"
275                    member.status_till = None
276                else:
277                    penalty_time = datetime.timedelta(days=days, hours=hours, minutes=minutes)
278                    member.status_till = utils.utcnow() + penalty_time
279                if status.value == 1:
280                    if member.guild.support_block is None:
281                        raise exceptions.InvalidParameters("The support block role has not been set.\n"
282                                                           "This may be intentional.\n"
283                                                           "If not please set it up using the /settings.")
284                    await target.add_roles(discord.Object(member.guild.support_block))
285                    emd = discord.Embed(title="Success!",
286                                        description=(f"Blocked {target.mention} from opening tickets"
287                                                     f" for {str(penalty_time)}."),
288                                        color=discord.Color.red())
289                    emd2 = discord.Embed(title="Support Blocked",
290                                         description=("You have been blocked from opening tickets from"
291                                                      f" {target.guild.name} for {str(penalty_time)}."),
292                                         color=discord.Color.red())
293                else:
294                    if member.guild.helping_block is None:
295                        raise exceptions.InvalidParameters("The helping block role has not been set.\n"
296                                                           "This may be intentional.\n"
297                                                           "If not please set it up using the /settings.")
298                    await target.add_roles(discord.Object(member.guild.helping_block))
299                    emd = discord.Embed(title="Success!",
300                                        description=(f"Blocked {target.mention} "
301                                                     f"from providing support for {str(penalty_time)}."),
302                                        color=discord.Color.red())
303                    emd2 = discord.Embed(title="Community Support Blocked",
304                                         description=("You have been blocked from providing "
305                                                      f"support from {target.guild.name}"
306                                                      f" for {str(penalty_time)}."),
307                                         color=discord.Color.red())
308                    if member.guild.strip_roles:
309                        roles = await confg.get_all_community_roles(interaction.guild_id)  # type: ignore
310                        unpck = [discord.Object(rle.role_id) for rle in roles]
311                        await target.remove_roles(*unpck, reason="Community Support Blocked")
312            await confg.commit()
313        await interaction.followup.send(embed=emd, ephemeral=True)
314        try:
315            await target.send(embed=emd2)
316        except discord.Forbidden:
317            logging.debug("Failed to send status message to user.")

Staff-only commands for Tickets+.

This cog contains all staff-only commands. These commands are only available to users with the staff role, and are used to manage the bot and the tickets.

StaffCmmd(bot_instance: tickets_plus.bot.TicketsPlusBot)
42    def __init__(self, bot_instance: bot.TicketsPlusBot) -> None:
43        """Initialises the cog instance.
44
45        We store some attributes here for later use.
46
47        Args:
48            bot_instance: The bot instance.
49        """
50        self._bt = bot_instance
51        logging.info("Loaded %s", self.__class__.__name__)

Initialises the cog instance.

We store some attributes here for later use.

Arguments:
  • bot_instance: The bot instance.
respond = <discord.app_commands.commands.Command object>
join = <discord.app_commands.commands.Command object>
anonymize = <discord.app_commands.commands.Command object>
register = <discord.app_commands.commands.Command object>
usrstatus = <discord.app_commands.commands.Command object>
async def setup(bot_instance: tickets_plus.bot.TicketsPlusBot) -> None:
320async def setup(bot_instance: bot.TicketsPlusBot) -> None:
321    """Setup function for the StaffCmmd cog.
322
323    Adds the StaffCmmd cog to the bot.
324    Automatically called by the bot.
325
326    Args:
327        bot_instance: The bot instance.
328    """
329    await bot_instance.add_cog(StaffCmmd(bot_instance))

Setup function for the StaffCmmd cog.

Adds the StaffCmmd cog to the bot. Automatically called by the bot.

Arguments:
  • bot_instance: The bot instance.