tickets_plus.cogs.settings
Settings change commands for Tickets+.
This extension provides commands to change the bot's settings. Those settings are guild-specific and are stored in the database.
Typical usage example:
from tickets_plus import bot bot_instance = bot.TicketsPlusBot(...) await bot_instance.load_extension("tickets_plus.cogs.settings")
1"""Settings change commands for Tickets+. 2 3This extension provides commands to change the bot's settings. 4Those settings are guild-specific and are stored in the database. 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.settings") 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 23from typing import List 24 25import discord 26from discord import app_commands 27from discord.ext import commands 28 29from tickets_plus import bot 30from tickets_plus.ext import exceptions 31 32 33@app_commands.guild_only() 34@app_commands.default_permissions(administrator=True) 35class Settings(commands.GroupCog, name="settings", description="Settings for the bot."): 36 """Provides commands to change the bot's settings. 37 38 These settings are guild-specific and are stored in the database. 39 We suggest the settings to be only changed by administrators; 40 however, this can be changed discord-side. 41 This is a group cog, so all commands are under the settings group. 42 """ 43 44 def __init__(self, bot_instance: bot.TicketsPlusBot): 45 """Initializes the cog. 46 47 We initialize the variables we need. 48 Then we call the superclasses __init__ method. 49 50 Args: 51 bot_instance: The bot instance. 52 """ 53 self._bt = bot_instance 54 super().__init__() 55 logging.info("Loaded %s", self.__class__.__name__) 56 57 async def ticket_types_autocomplete(self, ctx: discord.Interaction, arg: str) -> List[app_commands.Choice[str]]: 58 """Autocomplete for ticket types. 59 60 This function is used to autocomplete the ticket types. 61 It is used in the ticket type commands. 62 63 Args: 64 ctx: The interaction context. 65 arg: The argument to autocomplete. 66 67 Returns: 68 A list of choices for the autocomplete. 69 """ 70 async with self._bt.get_connection() as conn: 71 ticket_types = await conn.get_ticket_types(ctx.guild_id # type: ignore 72 ) 73 return [app_commands.Choice(name=t.prefix, value=t.prefix) for t in ticket_types if arg in t.prefix] 74 75 @app_commands.command(name="ticketbot", description="Change the ticket bots for your server.") 76 @app_commands.describe(user="The user to add to ticket bots.") 77 async def change_tracked(self, ctx: discord.Interaction, user: discord.User) -> None: 78 """Changes the ticket bot for the server. 79 80 This command is used to change the ticket bots users. 81 Ticket bots are users that can create tickets to be handled by our bot. 82 83 Args: 84 ctx: The interaction context. 85 user: The user to add/remove to ticket bots. 86 """ 87 await ctx.response.defer(ephemeral=True) 88 async with self._bt.get_connection() as conn: 89 new, ticket_user = await conn.get_ticket_bot( 90 user.id, 91 ctx.guild_id, # type: ignore 92 ) 93 color = discord.Color.green() if new else discord.Color.red() 94 emd = discord.Embed(title="Ticket Bot List Edited", color=color) 95 if not new: 96 await conn.delete(ticket_user) 97 emd.add_field(name="Removed:", value=user.mention) 98 else: 99 emd.add_field(name="Added:", value=user.mention) 100 await conn.commit() 101 await ctx.followup.send(embed=emd, ephemeral=True) 102 103 @app_commands.command(name="staff", description="Change the staff roles.") 104 @app_commands.describe(role="The role to add/remove from staff roles.") 105 async def change_staff(self, ctx: discord.Interaction, role: discord.Role) -> None: 106 """Changes the staff roles for the server. 107 108 This command is used to change the staff roles. 109 Staff roles are roles that can use the staff commands. 110 I mean, it's pretty obvious, isn't it? 111 112 Args: 113 ctx: The interaction context. 114 role: The role to add/remove from staff roles. 115 """ 116 await ctx.response.defer(ephemeral=True) 117 async with self._bt.get_connection() as conn: 118 new, staff_role = await conn.get_staff_role( 119 role.id, 120 ctx.guild_id, # type: ignore 121 ) 122 color = discord.Color.green() if new else discord.Color.red() 123 emd = discord.Embed(title="Staff Role List Edited", color=color) 124 if not new: 125 await conn.delete(staff_role) 126 emd.add_field(name="Removed:", value=role.mention) 127 else: 128 emd.add_field(name="Added:", value=role.mention) 129 await conn.commit() 130 await ctx.followup.send(embed=emd, ephemeral=True) 131 132 @app_commands.command(name="observers", description="Change the observers roles.") 133 @app_commands.describe(role="The role to add/remove from observers roles.") 134 async def change_observers(self, ctx: discord.Interaction, role: discord.Role) -> None: 135 """Changes the observer roles for the server. 136 137 This command is used to change the observer roles. 138 Observer roles are roles that are automatically added to ticket notes. 139 They are to be used in conjunction with the staff roles. 140 As observers don't have access to staff commands. 141 142 Args: 143 ctx: The interaction context. 144 role: The role to add/remove from observer roles. 145 """ 146 await ctx.response.defer(ephemeral=True) 147 async with self._bt.get_connection() as conn: 148 new, obsrvrs = await conn.get_observers_role( 149 role.id, 150 ctx.guild_id, # type: ignore 151 ) 152 color = discord.Color.green() if new else discord.Color.red() 153 emd = discord.Embed(title="Observers Role List Edited", color=color) 154 emd.set_footer(text="Warning! Ticket notes are currently legacy, please use the main bot for ticket notes.") 155 if not new: 156 await conn.delete(obsrvrs) 157 emd.add_field(name="Removed:", value=role.mention) 158 else: 159 emd.add_field(name="Added:", value=role.mention) 160 await conn.commit() 161 await ctx.followup.send(embed=emd, ephemeral=True) 162 163 @app_commands.command(name="communitysupport", description="Change the community support roles.") 164 @app_commands.describe(role="The role to add/remove from community support roles.") 165 async def change_community_roles(self, ctx: discord.Interaction, role: discord.Role) -> None: 166 """Modifies the server's community support roles. 167 168 This command is used to change the community support roles. 169 Community support roles are roles that are automatically added to 170 community support tickets. But do not receive any permissions. 171 They are not pinged when a new ticket is created. 172 173 Args: 174 ctx: The interaction context. 175 role: The role to add/remove from community support roles. 176 """ 177 await ctx.response.defer(ephemeral=True) 178 async with self._bt.get_connection() as conn: 179 new, comsup = await conn.get_community_role( 180 role.id, 181 ctx.guild_id, # type: ignore 182 ) 183 color = discord.Color.green() if new else discord.Color.red() 184 emd = discord.Embed(title="Community Support Role List Edited", color=color) 185 if not new: 186 await conn.delete(comsup) 187 emd.add_field(name="Removed:", value=role.mention) 188 else: 189 emd.add_field(name="Added:", value=role.mention) 190 await conn.commit() 191 await ctx.followup.send(embed=emd, ephemeral=True) 192 193 @app_commands.command(name="communityping", description="Change the community ping roles.") 194 @app_commands.describe(role="The role to add/remove from community ping roles.") 195 async def change_community_ping_roles(self, ctx: discord.Interaction, role: discord.Role) -> None: 196 """Modifies the server's community ping roles. 197 198 This command is used to change the community ping roles. 199 Community ping roles are to be used in conjunction with the community 200 support roles. They are pinged when a new ticket is created but, 201 after the addition of the community support roles. 202 They are not given any permissions, including the ability to see the 203 tickets. They are only pinged. 204 205 Args: 206 ctx: The interaction context. 207 role: The role to add/remove from community ping roles. 208 """ 209 await ctx.response.defer(ephemeral=True) 210 async with self._bt.get_connection() as conn: 211 new, comsup = await conn.get_community_ping( 212 role.id, 213 ctx.guild_id, # type: ignore 214 ) 215 color = discord.Color.green() if new else discord.Color.red() 216 emd = discord.Embed(title="Community Ping Role List Edited", color=color) 217 if not new: 218 await conn.delete(comsup) 219 emd.add_field(name="Removed:", value=role.mention) 220 else: 221 emd.add_field(name="Added:", value=role.mention) 222 await conn.commit() 223 await ctx.followup.send(embed=emd, ephemeral=True) 224 225 @app_commands.command(name="openmsg", description="Change the open message.") 226 @app_commands.describe(message="The new open message.") 227 async def change_openmsg(self, ctx: discord.Interaction, message: str) -> None: 228 """This command is used to change the open message. 229 230 This is the message that opens a new ticket notes thread. 231 It is only visible to the staff team and observers. 232 The message must be less than 200 characters. 233 The default message is "Staff notes for Ticket $channel". 234 Where $channel is the channel mention. 235 236 Args: 237 ctx: The interaction context. 238 message: The new open message. 239 240 Raises: 241 `tickets_plus.ext.exceptions.InvalidParameters`: Message too long. 242 Raised when the message is longer than 200 characters. 243 """ 244 await ctx.response.defer(ephemeral=True) 245 if len(message) > 200: 246 raise exceptions.InvalidParameters("The message must be less than" 247 " 200 characters.") 248 async with self._bt.get_connection() as conn: 249 guild = await conn.get_guild(ctx.guild_id) # type: ignore 250 old = guild.open_message 251 guild.open_message = message 252 await conn.commit() 253 emd = discord.Embed(title="Open Message Changed", color=discord.Color.yellow()) 254 emd.add_field(name="Old message:", value=old) 255 emd.add_field(name="New message:", value=message) 256 emd.set_footer(text="Warning! Ticket notes are currently legacy, please use the main bot for ticket notes.") 257 await ctx.followup.send(embed=emd, ephemeral=True) 258 259 @app_commands.command(name="penrole", description="Change the penalty roles.") 260 @app_commands.describe(role="The role to set as a penalty role.") 261 @app_commands.choices(penal=[ 262 app_commands.Choice(name="Support Block", value=0), 263 app_commands.Choice(name="Helping Block", value=1), 264 ]) 265 async def change_penrole(self, ctx: discord.Interaction, role: discord.Role, 266 penal: app_commands.Choice[int]) -> None: 267 """Change the penalty roles. 268 269 Adjusts the penalty roles. These roles are used to block users from 270 creating tickets or helping in tickets. 271 272 Args: 273 ctx: The interaction context. 274 role: The role to set as a penalty role. 275 penal: The penalty for which to set the role. 276 """ 277 await ctx.response.defer(ephemeral=True) 278 async with self._bt.get_connection() as conn: 279 guild = await conn.get_guild(ctx.guild_id) # type: ignore 280 if penal.value == 0: 281 old = guild.support_block 282 guild.support_block = role.id 283 else: 284 old = guild.helping_block 285 guild.helping_block = role.id 286 await conn.commit() 287 emd = discord.Embed(title="Penalty Role Changed", 288 description=f"Penalty: {penal.name}", 289 color=discord.Color.yellow()) 290 emd.add_field(name="Old role:", value=f"<@&{old}>") 291 emd.add_field(name="New role:", value=role.mention) 292 await ctx.followup.send(embed=emd, ephemeral=True) 293 294 @app_commands.command(name="staffteamname", description="Change the staff team's name.") 295 @app_commands.describe(name="The new staff team's name.") 296 async def change_staffteamname(self, ctx: discord.Interaction, name: str) -> None: 297 """This command is used to change the staff team's name. 298 299 We use this name when the bot sends messages as the staff team. 300 So it is important that it is set to something that makes sense. 301 The default is "Staff Team". But you can change it to whatever you 302 want. It is limited to 40 characters. 303 304 Args: 305 ctx: The interaction context. 306 name: The new staff team's name. 307 308 Raises: 309 `tickets_plus.ext.exceptions.InvalidParameters`: Name too long. 310 Raised when the name is longer than 40 characters. 311 """ 312 await ctx.response.defer(ephemeral=True) 313 if len(name) > 40: 314 raise exceptions.InvalidParameters("Name too long") 315 async with self._bt.get_connection() as conn: 316 guild = await conn.get_guild(ctx.guild_id) # type: ignore 317 old = guild.staff_team_name 318 guild.staff_team_name = name 319 await conn.commit() 320 emd = discord.Embed(title="Staff Team Name Changed", color=discord.Color.yellow()) 321 emd.add_field(name="Old name:", value=old) 322 emd.add_field(name="New name:", value=name) 323 await ctx.followup.send(embed=emd, ephemeral=True) 324 325 @app_commands.command(name="timings", description="Change any internal bot timings.") 326 @app_commands.describe( 327 category="The category of timing to change.", 328 days="The new timing time days.", 329 hours="The new timing time hours.", 330 minutes="The new timing time minutes.", 331 ) 332 @app_commands.choices(category=[ 333 app_commands.Choice(name="First Response Auto-close", value=0), 334 app_commands.Choice(name="Last Response Auto-close", value=1), 335 app_commands.Choice(name="Ticket Close Warning", value=2), 336 ]) 337 async def change_autoclose(self, 338 ctx: discord.Interaction, 339 category: app_commands.Choice[int], 340 days: int = 0, 341 hours: int = 0, 342 minutes: int = 0) -> None: 343 """Set the guild-wide auto-close time. 344 345 This command is used to change the auto-close time. 346 We use this time to correctly calculate the time until a ticket 347 is automatically closed. This is the time displayed 348 in the ticket's description. Additionally, this is the time 349 we may later use to warn the user that their ticket is about 350 to be closed. 351 352 Args: 353 ctx: The interaction context. 354 category: The category of auto-close time to change. 355 days: The new auto-close time days. Default to 0. 356 hours: The new auto-close time hours. Default to 0. 357 minutes: The new auto-close time minutes. Default to 0. 358 """ 359 await ctx.response.defer(ephemeral=True) 360 async with self._bt.get_connection() as conn: 361 guild = await conn.get_guild(ctx.guild_id) # type: ignore 362 prev = None 363 if category.value == 1: 364 changed_close = guild.any_autoclose 365 category_txt = "Last Response" 366 elif category.value == 2: 367 changed_close = guild.warn_autoclose 368 category_txt = "Ticket Close Warning" 369 else: 370 changed_close = guild.first_autoclose 371 category_txt = "First Response" 372 if changed_close is not None: 373 prev = changed_close 374 if days + hours + minutes == 0: 375 changed_close = None 376 emd = discord.Embed(title=f"{category_txt} Disabled", color=discord.Color.red()) 377 emd.add_field(name="Previous auto-close time:", value=f"{str(prev)}") 378 else: 379 changed_close = datetime.timedelta(days=days, hours=hours, minutes=minutes) 380 emd = discord.Embed(title=f"{category_txt} Timings Changed", color=discord.Color.yellow()) 381 emd.add_field(name="Previous auto-close time:", value=f"{str(prev)}") 382 emd.add_field(name="New auto-close time:", value=f"{str(changed_close)}") 383 if category.value == 1: 384 guild.any_autoclose = changed_close 385 elif category.value == 2: 386 guild.warn_autoclose = changed_close 387 else: 388 guild.first_autoclose = changed_close 389 await conn.commit() 390 await ctx.followup.send(embed=emd, ephemeral=True) 391 392 @app_commands.command(name="toggle", description="Toggle a specified True/False value.") 393 @app_commands.describe(value="The value to toggle.") 394 @app_commands.choices(value=[ 395 app_commands.Choice(name="Message Discovery", value=0), 396 app_commands.Choice(name="Button Stripping", value=1), 397 app_commands.Choice(name="Role Stripping", value=2), 398 app_commands.Choice(name="Integrated with Tickets", value=3) 399 ]) 400 async def toggle_value(self, ctx: discord.Interaction, value: app_commands.Choice[int]) -> None: 401 """A generic toggle command. 402 403 A more space efficient way to implement the toggle commands. 404 This command is used to toggle the specified value. 405 406 Args: 407 ctx: The interaction context. 408 value: The value to toggle. 409 """ 410 await ctx.response.defer(ephemeral=True) 411 async with self._bt.get_connection() as conn: 412 guild = await conn.get_guild(ctx.guild_id) # type: ignore 413 if value.value == 0: 414 new_status = not guild.msg_discovery 415 guild.msg_discovery = new_status 416 elif value.value == 1: 417 new_status = not guild.strip_buttons 418 guild.strip_buttons = new_status 419 elif value.value == 2: 420 new_status = not guild.strip_roles 421 guild.strip_roles = new_status 422 else: 423 new_status = not guild.integrated 424 guild.integrated = new_status 425 await conn.commit() 426 emd = discord.Embed(title="Value Toggled", 427 description=f"{value.name} is now {new_status}", 428 color=discord.Color.green() if new_status else discord.Color.red()) 429 await ctx.followup.send(embed=emd, ephemeral=True) 430 431 @app_commands.command(name="tickettype", description="Create/Delete a ticket type.") 432 @app_commands.describe(name=("The name of the ticket type." 433 " This should be the prefix you use for the ticket type." 434 " ie. #<name>-<number>."), 435 comping="Whether or not to ping the community role.", 436 comaccs="Whether or not to give the community role matched ticket.", 437 strpbuttns="Whether or not to strip buttons from the ticket.", 438 ignore="Whether or not to ignore the ticket type.") 439 @app_commands.rename(comping="communityping", comaccs="communityaccess", strpbuttns="stripbuttons") 440 @app_commands.autocomplete(name=ticket_types_autocomplete) 441 async def create_ticket_type(self, 442 ctx: discord.Interaction, 443 name: str, 444 comping: bool = False, 445 comaccs: bool = False, 446 strpbuttns: bool = False, 447 ignore: bool = False) -> None: 448 """Create/Delete a new ticket type. 449 450 This command is used to create a new ticket type. 451 A ticket type is a preset of overrides that are applied to 452 a ticket when it is created. This allows for some more 453 settings customization. 454 455 Args: 456 ctx: Interaction context 457 name: The name of the new ticket type. 458 comping: Whether to ping the community role. 459 comaccs: Whether to give the community role view tickets. 460 strpbuttns: Whether to strip buttons from the ticket. 461 ignore: Whether to ignore the ticket type. 462 """ 463 await ctx.response.defer(ephemeral=True) 464 async with self._bt.get_connection() as conn: 465 new, tick_type = await conn.get_ticket_type( 466 ctx.guild_id, # type: ignore 467 name=name, 468 comping=comping, 469 comaccs=comaccs, 470 strpbuttns=strpbuttns, 471 ignore=ignore) 472 if new: 473 emd = discord.Embed(title="Ticket Type Created", 474 description=f"Ticket type {name} created.", 475 color=discord.Color.green()) 476 else: 477 await conn.delete(tick_type) 478 emd = discord.Embed(title="Ticket Type Deleted", 479 description=f"Ticket type {name} deleted.", 480 color=discord.Color.red()) 481 await conn.commit() 482 await ctx.followup.send(embed=emd, ephemeral=True) 483 484 @app_commands.command(name="edittickettype", description="Edit a ticket type.") 485 @app_commands.describe(name=("The name of the ticket type." 486 " This should be the prefix you use for the ticket type." 487 " ie. #<name>-<number>."), 488 comping="Whether or not to ping the community role.", 489 comaccs="Whether or not to give the community role matched ticket.", 490 strpbuttns="Whether or not to strip buttons from the ticket.", 491 ignore="Whether or not to ignore the ticket type.") 492 @app_commands.rename(comping="communityping", comaccs="communityaccess", strpbuttns="stripbuttons") 493 @app_commands.autocomplete(name=ticket_types_autocomplete) 494 async def edit_ticket_type(self, 495 ctx: discord.Interaction, 496 name: str, 497 comping: bool | None = None, 498 comaccs: bool | None = None, 499 strpbuttns: bool | None = None, 500 ignore: bool | None = None) -> None: 501 """Edit a ticket type. 502 503 This command is used to edit a ticket type. 504 A ticket type is a preset of overrides that are applied to 505 a ticket when it is created. This allows for some more 506 settings customization. 507 508 Args: 509 ctx: Discord interaction context. 510 name: The name of the ticket type. 511 comping: Whether to ping the community role. 512 comaccs: Whether to give the community role view tickets. 513 strpbuttns: Whether to strip buttons from the ticket. 514 ignore: Whether to ignore the ticket type. 515 """ 516 if not any([comping, comaccs, strpbuttns]): 517 raise exceptions.InvalidParameters("You must specify at least one value to edit.") 518 await ctx.response.defer(ephemeral=True) 519 async with self._bt.get_connection() as conn: 520 new, tick_type = await conn.get_ticket_type( 521 ctx.guild_id, # type: ignore 522 name=name, 523 ) 524 if new: 525 raise exceptions.InvalidParameters("Ticket type does not exist.") 526 else: 527 if comping is not None: 528 tick_type.comping = comping 529 if comaccs is not None: 530 tick_type.comaccs = comaccs 531 if strpbuttns is not None: 532 tick_type.strpbuttns = strpbuttns 533 if ignore is not None: 534 tick_type.ignore = ignore 535 emd = discord.Embed(title="Ticket Type Edited", 536 description=f"Ticket type {name} edited.", 537 color=discord.Color.green()) 538 await conn.commit() 539 await ctx.followup.send(embed=emd, ephemeral=True) 540 541 542async def setup(bot_instance: bot.TicketsPlusBot) -> None: 543 """Setup up the settings commands. 544 545 We add the settings cog to the bot. 546 Nothing different from the normal setup function. 547 548 Args: 549 bot_instance: The bot instance. 550 """ 551 await bot_instance.add_cog(Settings(bot_instance))
34@app_commands.guild_only() 35@app_commands.default_permissions(administrator=True) 36class Settings(commands.GroupCog, name="settings", description="Settings for the bot."): 37 """Provides commands to change the bot's settings. 38 39 These settings are guild-specific and are stored in the database. 40 We suggest the settings to be only changed by administrators; 41 however, this can be changed discord-side. 42 This is a group cog, so all commands are under the settings group. 43 """ 44 45 def __init__(self, bot_instance: bot.TicketsPlusBot): 46 """Initializes the cog. 47 48 We initialize the variables we need. 49 Then we call the superclasses __init__ method. 50 51 Args: 52 bot_instance: The bot instance. 53 """ 54 self._bt = bot_instance 55 super().__init__() 56 logging.info("Loaded %s", self.__class__.__name__) 57 58 async def ticket_types_autocomplete(self, ctx: discord.Interaction, arg: str) -> List[app_commands.Choice[str]]: 59 """Autocomplete for ticket types. 60 61 This function is used to autocomplete the ticket types. 62 It is used in the ticket type commands. 63 64 Args: 65 ctx: The interaction context. 66 arg: The argument to autocomplete. 67 68 Returns: 69 A list of choices for the autocomplete. 70 """ 71 async with self._bt.get_connection() as conn: 72 ticket_types = await conn.get_ticket_types(ctx.guild_id # type: ignore 73 ) 74 return [app_commands.Choice(name=t.prefix, value=t.prefix) for t in ticket_types if arg in t.prefix] 75 76 @app_commands.command(name="ticketbot", description="Change the ticket bots for your server.") 77 @app_commands.describe(user="The user to add to ticket bots.") 78 async def change_tracked(self, ctx: discord.Interaction, user: discord.User) -> None: 79 """Changes the ticket bot for the server. 80 81 This command is used to change the ticket bots users. 82 Ticket bots are users that can create tickets to be handled by our bot. 83 84 Args: 85 ctx: The interaction context. 86 user: The user to add/remove to ticket bots. 87 """ 88 await ctx.response.defer(ephemeral=True) 89 async with self._bt.get_connection() as conn: 90 new, ticket_user = await conn.get_ticket_bot( 91 user.id, 92 ctx.guild_id, # type: ignore 93 ) 94 color = discord.Color.green() if new else discord.Color.red() 95 emd = discord.Embed(title="Ticket Bot List Edited", color=color) 96 if not new: 97 await conn.delete(ticket_user) 98 emd.add_field(name="Removed:", value=user.mention) 99 else: 100 emd.add_field(name="Added:", value=user.mention) 101 await conn.commit() 102 await ctx.followup.send(embed=emd, ephemeral=True) 103 104 @app_commands.command(name="staff", description="Change the staff roles.") 105 @app_commands.describe(role="The role to add/remove from staff roles.") 106 async def change_staff(self, ctx: discord.Interaction, role: discord.Role) -> None: 107 """Changes the staff roles for the server. 108 109 This command is used to change the staff roles. 110 Staff roles are roles that can use the staff commands. 111 I mean, it's pretty obvious, isn't it? 112 113 Args: 114 ctx: The interaction context. 115 role: The role to add/remove from staff roles. 116 """ 117 await ctx.response.defer(ephemeral=True) 118 async with self._bt.get_connection() as conn: 119 new, staff_role = await conn.get_staff_role( 120 role.id, 121 ctx.guild_id, # type: ignore 122 ) 123 color = discord.Color.green() if new else discord.Color.red() 124 emd = discord.Embed(title="Staff Role List Edited", color=color) 125 if not new: 126 await conn.delete(staff_role) 127 emd.add_field(name="Removed:", value=role.mention) 128 else: 129 emd.add_field(name="Added:", value=role.mention) 130 await conn.commit() 131 await ctx.followup.send(embed=emd, ephemeral=True) 132 133 @app_commands.command(name="observers", description="Change the observers roles.") 134 @app_commands.describe(role="The role to add/remove from observers roles.") 135 async def change_observers(self, ctx: discord.Interaction, role: discord.Role) -> None: 136 """Changes the observer roles for the server. 137 138 This command is used to change the observer roles. 139 Observer roles are roles that are automatically added to ticket notes. 140 They are to be used in conjunction with the staff roles. 141 As observers don't have access to staff commands. 142 143 Args: 144 ctx: The interaction context. 145 role: The role to add/remove from observer roles. 146 """ 147 await ctx.response.defer(ephemeral=True) 148 async with self._bt.get_connection() as conn: 149 new, obsrvrs = await conn.get_observers_role( 150 role.id, 151 ctx.guild_id, # type: ignore 152 ) 153 color = discord.Color.green() if new else discord.Color.red() 154 emd = discord.Embed(title="Observers Role List Edited", color=color) 155 emd.set_footer(text="Warning! Ticket notes are currently legacy, please use the main bot for ticket notes.") 156 if not new: 157 await conn.delete(obsrvrs) 158 emd.add_field(name="Removed:", value=role.mention) 159 else: 160 emd.add_field(name="Added:", value=role.mention) 161 await conn.commit() 162 await ctx.followup.send(embed=emd, ephemeral=True) 163 164 @app_commands.command(name="communitysupport", description="Change the community support roles.") 165 @app_commands.describe(role="The role to add/remove from community support roles.") 166 async def change_community_roles(self, ctx: discord.Interaction, role: discord.Role) -> None: 167 """Modifies the server's community support roles. 168 169 This command is used to change the community support roles. 170 Community support roles are roles that are automatically added to 171 community support tickets. But do not receive any permissions. 172 They are not pinged when a new ticket is created. 173 174 Args: 175 ctx: The interaction context. 176 role: The role to add/remove from community support roles. 177 """ 178 await ctx.response.defer(ephemeral=True) 179 async with self._bt.get_connection() as conn: 180 new, comsup = await conn.get_community_role( 181 role.id, 182 ctx.guild_id, # type: ignore 183 ) 184 color = discord.Color.green() if new else discord.Color.red() 185 emd = discord.Embed(title="Community Support Role List Edited", color=color) 186 if not new: 187 await conn.delete(comsup) 188 emd.add_field(name="Removed:", value=role.mention) 189 else: 190 emd.add_field(name="Added:", value=role.mention) 191 await conn.commit() 192 await ctx.followup.send(embed=emd, ephemeral=True) 193 194 @app_commands.command(name="communityping", description="Change the community ping roles.") 195 @app_commands.describe(role="The role to add/remove from community ping roles.") 196 async def change_community_ping_roles(self, ctx: discord.Interaction, role: discord.Role) -> None: 197 """Modifies the server's community ping roles. 198 199 This command is used to change the community ping roles. 200 Community ping roles are to be used in conjunction with the community 201 support roles. They are pinged when a new ticket is created but, 202 after the addition of the community support roles. 203 They are not given any permissions, including the ability to see the 204 tickets. They are only pinged. 205 206 Args: 207 ctx: The interaction context. 208 role: The role to add/remove from community ping roles. 209 """ 210 await ctx.response.defer(ephemeral=True) 211 async with self._bt.get_connection() as conn: 212 new, comsup = await conn.get_community_ping( 213 role.id, 214 ctx.guild_id, # type: ignore 215 ) 216 color = discord.Color.green() if new else discord.Color.red() 217 emd = discord.Embed(title="Community Ping Role List Edited", color=color) 218 if not new: 219 await conn.delete(comsup) 220 emd.add_field(name="Removed:", value=role.mention) 221 else: 222 emd.add_field(name="Added:", value=role.mention) 223 await conn.commit() 224 await ctx.followup.send(embed=emd, ephemeral=True) 225 226 @app_commands.command(name="openmsg", description="Change the open message.") 227 @app_commands.describe(message="The new open message.") 228 async def change_openmsg(self, ctx: discord.Interaction, message: str) -> None: 229 """This command is used to change the open message. 230 231 This is the message that opens a new ticket notes thread. 232 It is only visible to the staff team and observers. 233 The message must be less than 200 characters. 234 The default message is "Staff notes for Ticket $channel". 235 Where $channel is the channel mention. 236 237 Args: 238 ctx: The interaction context. 239 message: The new open message. 240 241 Raises: 242 `tickets_plus.ext.exceptions.InvalidParameters`: Message too long. 243 Raised when the message is longer than 200 characters. 244 """ 245 await ctx.response.defer(ephemeral=True) 246 if len(message) > 200: 247 raise exceptions.InvalidParameters("The message must be less than" 248 " 200 characters.") 249 async with self._bt.get_connection() as conn: 250 guild = await conn.get_guild(ctx.guild_id) # type: ignore 251 old = guild.open_message 252 guild.open_message = message 253 await conn.commit() 254 emd = discord.Embed(title="Open Message Changed", color=discord.Color.yellow()) 255 emd.add_field(name="Old message:", value=old) 256 emd.add_field(name="New message:", value=message) 257 emd.set_footer(text="Warning! Ticket notes are currently legacy, please use the main bot for ticket notes.") 258 await ctx.followup.send(embed=emd, ephemeral=True) 259 260 @app_commands.command(name="penrole", description="Change the penalty roles.") 261 @app_commands.describe(role="The role to set as a penalty role.") 262 @app_commands.choices(penal=[ 263 app_commands.Choice(name="Support Block", value=0), 264 app_commands.Choice(name="Helping Block", value=1), 265 ]) 266 async def change_penrole(self, ctx: discord.Interaction, role: discord.Role, 267 penal: app_commands.Choice[int]) -> None: 268 """Change the penalty roles. 269 270 Adjusts the penalty roles. These roles are used to block users from 271 creating tickets or helping in tickets. 272 273 Args: 274 ctx: The interaction context. 275 role: The role to set as a penalty role. 276 penal: The penalty for which to set the role. 277 """ 278 await ctx.response.defer(ephemeral=True) 279 async with self._bt.get_connection() as conn: 280 guild = await conn.get_guild(ctx.guild_id) # type: ignore 281 if penal.value == 0: 282 old = guild.support_block 283 guild.support_block = role.id 284 else: 285 old = guild.helping_block 286 guild.helping_block = role.id 287 await conn.commit() 288 emd = discord.Embed(title="Penalty Role Changed", 289 description=f"Penalty: {penal.name}", 290 color=discord.Color.yellow()) 291 emd.add_field(name="Old role:", value=f"<@&{old}>") 292 emd.add_field(name="New role:", value=role.mention) 293 await ctx.followup.send(embed=emd, ephemeral=True) 294 295 @app_commands.command(name="staffteamname", description="Change the staff team's name.") 296 @app_commands.describe(name="The new staff team's name.") 297 async def change_staffteamname(self, ctx: discord.Interaction, name: str) -> None: 298 """This command is used to change the staff team's name. 299 300 We use this name when the bot sends messages as the staff team. 301 So it is important that it is set to something that makes sense. 302 The default is "Staff Team". But you can change it to whatever you 303 want. It is limited to 40 characters. 304 305 Args: 306 ctx: The interaction context. 307 name: The new staff team's name. 308 309 Raises: 310 `tickets_plus.ext.exceptions.InvalidParameters`: Name too long. 311 Raised when the name is longer than 40 characters. 312 """ 313 await ctx.response.defer(ephemeral=True) 314 if len(name) > 40: 315 raise exceptions.InvalidParameters("Name too long") 316 async with self._bt.get_connection() as conn: 317 guild = await conn.get_guild(ctx.guild_id) # type: ignore 318 old = guild.staff_team_name 319 guild.staff_team_name = name 320 await conn.commit() 321 emd = discord.Embed(title="Staff Team Name Changed", color=discord.Color.yellow()) 322 emd.add_field(name="Old name:", value=old) 323 emd.add_field(name="New name:", value=name) 324 await ctx.followup.send(embed=emd, ephemeral=True) 325 326 @app_commands.command(name="timings", description="Change any internal bot timings.") 327 @app_commands.describe( 328 category="The category of timing to change.", 329 days="The new timing time days.", 330 hours="The new timing time hours.", 331 minutes="The new timing time minutes.", 332 ) 333 @app_commands.choices(category=[ 334 app_commands.Choice(name="First Response Auto-close", value=0), 335 app_commands.Choice(name="Last Response Auto-close", value=1), 336 app_commands.Choice(name="Ticket Close Warning", value=2), 337 ]) 338 async def change_autoclose(self, 339 ctx: discord.Interaction, 340 category: app_commands.Choice[int], 341 days: int = 0, 342 hours: int = 0, 343 minutes: int = 0) -> None: 344 """Set the guild-wide auto-close time. 345 346 This command is used to change the auto-close time. 347 We use this time to correctly calculate the time until a ticket 348 is automatically closed. This is the time displayed 349 in the ticket's description. Additionally, this is the time 350 we may later use to warn the user that their ticket is about 351 to be closed. 352 353 Args: 354 ctx: The interaction context. 355 category: The category of auto-close time to change. 356 days: The new auto-close time days. Default to 0. 357 hours: The new auto-close time hours. Default to 0. 358 minutes: The new auto-close time minutes. Default to 0. 359 """ 360 await ctx.response.defer(ephemeral=True) 361 async with self._bt.get_connection() as conn: 362 guild = await conn.get_guild(ctx.guild_id) # type: ignore 363 prev = None 364 if category.value == 1: 365 changed_close = guild.any_autoclose 366 category_txt = "Last Response" 367 elif category.value == 2: 368 changed_close = guild.warn_autoclose 369 category_txt = "Ticket Close Warning" 370 else: 371 changed_close = guild.first_autoclose 372 category_txt = "First Response" 373 if changed_close is not None: 374 prev = changed_close 375 if days + hours + minutes == 0: 376 changed_close = None 377 emd = discord.Embed(title=f"{category_txt} Disabled", color=discord.Color.red()) 378 emd.add_field(name="Previous auto-close time:", value=f"{str(prev)}") 379 else: 380 changed_close = datetime.timedelta(days=days, hours=hours, minutes=minutes) 381 emd = discord.Embed(title=f"{category_txt} Timings Changed", color=discord.Color.yellow()) 382 emd.add_field(name="Previous auto-close time:", value=f"{str(prev)}") 383 emd.add_field(name="New auto-close time:", value=f"{str(changed_close)}") 384 if category.value == 1: 385 guild.any_autoclose = changed_close 386 elif category.value == 2: 387 guild.warn_autoclose = changed_close 388 else: 389 guild.first_autoclose = changed_close 390 await conn.commit() 391 await ctx.followup.send(embed=emd, ephemeral=True) 392 393 @app_commands.command(name="toggle", description="Toggle a specified True/False value.") 394 @app_commands.describe(value="The value to toggle.") 395 @app_commands.choices(value=[ 396 app_commands.Choice(name="Message Discovery", value=0), 397 app_commands.Choice(name="Button Stripping", value=1), 398 app_commands.Choice(name="Role Stripping", value=2), 399 app_commands.Choice(name="Integrated with Tickets", value=3) 400 ]) 401 async def toggle_value(self, ctx: discord.Interaction, value: app_commands.Choice[int]) -> None: 402 """A generic toggle command. 403 404 A more space efficient way to implement the toggle commands. 405 This command is used to toggle the specified value. 406 407 Args: 408 ctx: The interaction context. 409 value: The value to toggle. 410 """ 411 await ctx.response.defer(ephemeral=True) 412 async with self._bt.get_connection() as conn: 413 guild = await conn.get_guild(ctx.guild_id) # type: ignore 414 if value.value == 0: 415 new_status = not guild.msg_discovery 416 guild.msg_discovery = new_status 417 elif value.value == 1: 418 new_status = not guild.strip_buttons 419 guild.strip_buttons = new_status 420 elif value.value == 2: 421 new_status = not guild.strip_roles 422 guild.strip_roles = new_status 423 else: 424 new_status = not guild.integrated 425 guild.integrated = new_status 426 await conn.commit() 427 emd = discord.Embed(title="Value Toggled", 428 description=f"{value.name} is now {new_status}", 429 color=discord.Color.green() if new_status else discord.Color.red()) 430 await ctx.followup.send(embed=emd, ephemeral=True) 431 432 @app_commands.command(name="tickettype", description="Create/Delete a ticket type.") 433 @app_commands.describe(name=("The name of the ticket type." 434 " This should be the prefix you use for the ticket type." 435 " ie. #<name>-<number>."), 436 comping="Whether or not to ping the community role.", 437 comaccs="Whether or not to give the community role matched ticket.", 438 strpbuttns="Whether or not to strip buttons from the ticket.", 439 ignore="Whether or not to ignore the ticket type.") 440 @app_commands.rename(comping="communityping", comaccs="communityaccess", strpbuttns="stripbuttons") 441 @app_commands.autocomplete(name=ticket_types_autocomplete) 442 async def create_ticket_type(self, 443 ctx: discord.Interaction, 444 name: str, 445 comping: bool = False, 446 comaccs: bool = False, 447 strpbuttns: bool = False, 448 ignore: bool = False) -> None: 449 """Create/Delete a new ticket type. 450 451 This command is used to create a new ticket type. 452 A ticket type is a preset of overrides that are applied to 453 a ticket when it is created. This allows for some more 454 settings customization. 455 456 Args: 457 ctx: Interaction context 458 name: The name of the new ticket type. 459 comping: Whether to ping the community role. 460 comaccs: Whether to give the community role view tickets. 461 strpbuttns: Whether to strip buttons from the ticket. 462 ignore: Whether to ignore the ticket type. 463 """ 464 await ctx.response.defer(ephemeral=True) 465 async with self._bt.get_connection() as conn: 466 new, tick_type = await conn.get_ticket_type( 467 ctx.guild_id, # type: ignore 468 name=name, 469 comping=comping, 470 comaccs=comaccs, 471 strpbuttns=strpbuttns, 472 ignore=ignore) 473 if new: 474 emd = discord.Embed(title="Ticket Type Created", 475 description=f"Ticket type {name} created.", 476 color=discord.Color.green()) 477 else: 478 await conn.delete(tick_type) 479 emd = discord.Embed(title="Ticket Type Deleted", 480 description=f"Ticket type {name} deleted.", 481 color=discord.Color.red()) 482 await conn.commit() 483 await ctx.followup.send(embed=emd, ephemeral=True) 484 485 @app_commands.command(name="edittickettype", description="Edit a ticket type.") 486 @app_commands.describe(name=("The name of the ticket type." 487 " This should be the prefix you use for the ticket type." 488 " ie. #<name>-<number>."), 489 comping="Whether or not to ping the community role.", 490 comaccs="Whether or not to give the community role matched ticket.", 491 strpbuttns="Whether or not to strip buttons from the ticket.", 492 ignore="Whether or not to ignore the ticket type.") 493 @app_commands.rename(comping="communityping", comaccs="communityaccess", strpbuttns="stripbuttons") 494 @app_commands.autocomplete(name=ticket_types_autocomplete) 495 async def edit_ticket_type(self, 496 ctx: discord.Interaction, 497 name: str, 498 comping: bool | None = None, 499 comaccs: bool | None = None, 500 strpbuttns: bool | None = None, 501 ignore: bool | None = None) -> None: 502 """Edit a ticket type. 503 504 This command is used to edit a ticket type. 505 A ticket type is a preset of overrides that are applied to 506 a ticket when it is created. This allows for some more 507 settings customization. 508 509 Args: 510 ctx: Discord interaction context. 511 name: The name of the ticket type. 512 comping: Whether to ping the community role. 513 comaccs: Whether to give the community role view tickets. 514 strpbuttns: Whether to strip buttons from the ticket. 515 ignore: Whether to ignore the ticket type. 516 """ 517 if not any([comping, comaccs, strpbuttns]): 518 raise exceptions.InvalidParameters("You must specify at least one value to edit.") 519 await ctx.response.defer(ephemeral=True) 520 async with self._bt.get_connection() as conn: 521 new, tick_type = await conn.get_ticket_type( 522 ctx.guild_id, # type: ignore 523 name=name, 524 ) 525 if new: 526 raise exceptions.InvalidParameters("Ticket type does not exist.") 527 else: 528 if comping is not None: 529 tick_type.comping = comping 530 if comaccs is not None: 531 tick_type.comaccs = comaccs 532 if strpbuttns is not None: 533 tick_type.strpbuttns = strpbuttns 534 if ignore is not None: 535 tick_type.ignore = ignore 536 emd = discord.Embed(title="Ticket Type Edited", 537 description=f"Ticket type {name} edited.", 538 color=discord.Color.green()) 539 await conn.commit() 540 await ctx.followup.send(embed=emd, ephemeral=True)
Provides commands to change the bot's settings.
These settings are guild-specific and are stored in the database. We suggest the settings to be only changed by administrators; however, this can be changed discord-side. This is a group cog, so all commands are under the settings group.
45 def __init__(self, bot_instance: bot.TicketsPlusBot): 46 """Initializes the cog. 47 48 We initialize the variables we need. 49 Then we call the superclasses __init__ method. 50 51 Args: 52 bot_instance: The bot instance. 53 """ 54 self._bt = bot_instance 55 super().__init__() 56 logging.info("Loaded %s", self.__class__.__name__)
Initializes the cog.
We initialize the variables we need. Then we call the superclasses __init__ method.
Arguments:
- bot_instance: The bot instance.
58 async def ticket_types_autocomplete(self, ctx: discord.Interaction, arg: str) -> List[app_commands.Choice[str]]: 59 """Autocomplete for ticket types. 60 61 This function is used to autocomplete the ticket types. 62 It is used in the ticket type commands. 63 64 Args: 65 ctx: The interaction context. 66 arg: The argument to autocomplete. 67 68 Returns: 69 A list of choices for the autocomplete. 70 """ 71 async with self._bt.get_connection() as conn: 72 ticket_types = await conn.get_ticket_types(ctx.guild_id # type: ignore 73 ) 74 return [app_commands.Choice(name=t.prefix, value=t.prefix) for t in ticket_types if arg in t.prefix]
Autocomplete for ticket types.
This function is used to autocomplete the ticket types. It is used in the ticket type commands.
Arguments:
- ctx: The interaction context.
- arg: The argument to autocomplete.
Returns:
A list of choices for the autocomplete.
543async def setup(bot_instance: bot.TicketsPlusBot) -> None: 544 """Setup up the settings commands. 545 546 We add the settings cog to the bot. 547 Nothing different from the normal setup function. 548 549 Args: 550 bot_instance: The bot instance. 551 """ 552 await bot_instance.add_cog(Settings(bot_instance))
Setup up the settings commands.
We add the settings cog to the bot. Nothing different from the normal setup function.
Arguments:
- bot_instance: The bot instance.