├── data.json ├── README.md └── main.py /data.json: -------------------------------------------------------------------------------- 1 | {"ticket-counter": 0, "valid-roles": [], "pinged-roles": [], "ticket-channel-ids": [], "verified-roles": []} 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Auroris Ticketing Bot 2 | 3 | This is a Discord bot meant to handle tickets for a server. 4 | 5 | ## Steps to Install: 6 | 7 | 1. Download python from https://www.python.org/downloads/ 8 | 2. Run the installer. 9 | 3. Go into command prompt and enter the following code command 10 | 11 | **Windows:** `py -m pip install discord asyncio` 12 | 13 | **MacOS:** `python3 -m pip install discord asyncio` 14 | 15 | 4. Go to https://discordapp.com/developers/applications and create your application, go to the bot section, and copy your token. Place it into the quotes on the last line of code in main.py, replacing the word **TOKEN**. 16 | 5. Go back to command prompt, and cd (navigate) to the directory where you've downloaded this project. Run the following command once you are in the directory on command prompt: 17 | 18 | **Windows:** `py main.py` 19 | 20 | **MacOS:** `python3 main.py` 21 | 22 | 6. Success! If you see the bot saying that it's running with your username and user ID displayed, you've successfully setup the bot. 23 | 24 | ## How to configure the bot 25 | 26 | There's a variety of commands for administrators to use to configure the bot. The features that you can customize are what roles have access to ticket channels, what roles get pinged when a new ticket is created, and what users can configure the previous options. An important distinction to make is between users who have admin-level permissions for the bot and users who have administrative level permissions for the entire server. In order to provide more customizability, I've incorporated a special subset of roles that can be configured in order to have access to the commands to add/remove roles from tickets and to add/remove roles that get pinged in tickets. That may sound a bit confusing, but here's how it works: 27 | 28 | A user with administrator level permissions sets the bot up. He automatically has access to every single command that the bot offers. However, he may have a few trusted moderators with a Moderator role that he wants to have access to add roles that can be pinged, or to add specific roles to tickets. He can do this via the `.addadminrole` command. This command gives these users access to all of the commands that the actual administrator has, but they do not gain administrative permissions for the *entire* server. 29 | 30 | ## Questions/Bugs/Suggestions? 31 | 32 | If you have any suggestions for additions to the bot, find any bugs, or have any questions, feel free to open an issue or message me on Discord and I'll take a look at it. For updates, follow me on [Twitter](https://twitter.com/ifisq). 33 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | from discord.ext.commands import has_permissions, MissingPermissions 4 | import json 5 | import asyncio 6 | 7 | bot = commands.Bot(command_prefix=".") 8 | bot.remove_command("help") 9 | 10 | @bot.event 11 | async def on_ready(): 12 | print("Bot running with:") 13 | print("Username: ", bot.user.name) 14 | print("User ID: ", bot.user.id) 15 | 16 | @bot.command() 17 | async def help(ctx): 18 | with open("data.json") as f: 19 | data = json.load(f) 20 | 21 | valid_user = False 22 | 23 | for role_id in data["verified-roles"]: 24 | try: 25 | if ctx.guild.get_role(role_id) in ctx.author.roles: 26 | valid_user = True 27 | except: 28 | pass 29 | 30 | if ctx.author.guild_permissions.administrator or valid_user: 31 | 32 | em = discord.Embed(title="Auroris Tickets Help", description="", color=0x00a8ff) 33 | em.add_field(name="`.new `", value="This creates a new ticket. Add any words after the command if you'd like to send a message when we initially create your ticket.") 34 | em.add_field(name="`.close`", value="Use this to close a ticket. This command only works in ticket channels.") 35 | em.add_field(name="`.addaccess `", value="This can be used to give a specific role access to all tickets. This command can only be run if you have an admin-level role for this bot.") 36 | em.add_field(name="`.delaccess `", value="This can be used to remove a specific role's access to all tickets. This command can only be run if you have an admin-level role for this bot.") 37 | em.add_field(name="`.addpingedrole `", value="This command adds a role to the list of roles that are pinged when a new ticket is created. This command can only be run if you have an admin-level role for this bot.") 38 | em.add_field(name="`.delpingedrole `", value="This command removes a role from the list of roles that are pinged when a new ticket is created. This command can only be run if you have an admin-level role for this bot.") 39 | em.add_field(name="`.addadminrole `", value="This command gives all users with a specific role access to the admin-level commands for the bot, such as `.addpingedrole` and `.addaccess`. This command can only be run by users who have administrator permissions for the entire server.") 40 | em.add_field(name="`.deladminrole `", value="This command removes access for all users with the specified role to the admin-level commands for the bot, such as `.addpingedrole` and `.addaccess`. This command can only be run by users who have administrator permissions for the entire server.") 41 | em.set_footer(text="Auroris Development") 42 | 43 | await ctx.send(embed=em) 44 | 45 | else: 46 | 47 | em = discord.Embed(title = "Auroris Tickets Help", description ="", color = 0x00a8ff) 48 | em.add_field(name="`.new `", value="This creates a new ticket. Add any words after the command if you'd like to send a message when we initially create your ticket.") 49 | em.add_field(name="`.close`", value="Use this to close a ticket. This command only works in ticket channels.") 50 | em.set_footer(text="Auroris Development") 51 | 52 | await ctx.send(embed=em) 53 | 54 | @bot.command() 55 | async def new(ctx, *, args = None): 56 | 57 | await bot.wait_until_ready() 58 | 59 | if args == None: 60 | message_content = "Please wait, we will be with you shortly!" 61 | 62 | else: 63 | message_content = "".join(args) 64 | 65 | with open("data.json") as f: 66 | data = json.load(f) 67 | 68 | ticket_number = int(data["ticket-counter"]) 69 | ticket_number += 1 70 | 71 | ticket_channel = await ctx.guild.create_text_channel("ticket-{}".format(ticket_number)) 72 | await ticket_channel.set_permissions(ctx.guild.get_role(ctx.guild.id), send_messages=False, read_messages=False) 73 | 74 | for role_id in data["valid-roles"]: 75 | role = ctx.guild.get_role(role_id) 76 | 77 | await ticket_channel.set_permissions(role, send_messages=True, read_messages=True, add_reactions=True, embed_links=True, attach_files=True, read_message_history=True, external_emojis=True) 78 | 79 | await ticket_channel.set_permissions(ctx.author, send_messages=True, read_messages=True, add_reactions=True, embed_links=True, attach_files=True, read_message_history=True, external_emojis=True) 80 | 81 | em = discord.Embed(title="New ticket from {}#{}".format(ctx.author.name, ctx.author.discriminator), description= "{}".format(message_content), color=0x00a8ff) 82 | 83 | await ticket_channel.send(embed=em) 84 | 85 | pinged_msg_content = "" 86 | non_mentionable_roles = [] 87 | 88 | if data["pinged-roles"] != []: 89 | 90 | for role_id in data["pinged-roles"]: 91 | role = ctx.guild.get_role(role_id) 92 | 93 | pinged_msg_content += role.mention 94 | pinged_msg_content += " " 95 | 96 | if role.mentionable: 97 | pass 98 | else: 99 | await role.edit(mentionable=True) 100 | non_mentionable_roles.append(role) 101 | 102 | await ticket_channel.send(pinged_msg_content) 103 | 104 | for role in non_mentionable_roles: 105 | await role.edit(mentionable=False) 106 | 107 | data["ticket-channel-ids"].append(ticket_channel.id) 108 | 109 | data["ticket-counter"] = int(ticket_number) 110 | with open("data.json", 'w') as f: 111 | json.dump(data, f) 112 | 113 | created_em = discord.Embed(title="Auroris Tickets", description="Your ticket has been created at {}".format(ticket_channel.mention), color=0x00a8ff) 114 | 115 | await ctx.send(embed=created_em) 116 | 117 | @bot.command() 118 | async def close(ctx): 119 | with open('data.json') as f: 120 | data = json.load(f) 121 | 122 | if ctx.channel.id in data["ticket-channel-ids"]: 123 | 124 | channel_id = ctx.channel.id 125 | 126 | def check(message): 127 | return message.author == ctx.author and message.channel == ctx.channel and message.content.lower() == "close" 128 | 129 | try: 130 | 131 | em = discord.Embed(title="Auroris Tickets", description="Are you sure you want to close this ticket? Reply with `close` if you are sure.", color=0x00a8ff) 132 | 133 | await ctx.send(embed=em) 134 | await bot.wait_for('message', check=check, timeout=60) 135 | await ctx.channel.delete() 136 | 137 | index = data["ticket-channel-ids"].index(channel_id) 138 | del data["ticket-channel-ids"][index] 139 | 140 | with open('data.json', 'w') as f: 141 | json.dump(data, f) 142 | 143 | except asyncio.TimeoutError: 144 | em = discord.Embed(title="Auroris Tickets", description="You have run out of time to close this ticket. Please run the command again.", color=0x00a8ff) 145 | await ctx.send(embed=em) 146 | 147 | 148 | 149 | @bot.command() 150 | async def addaccess(ctx, role_id=None): 151 | 152 | with open('data.json') as f: 153 | data = json.load(f) 154 | 155 | valid_user = False 156 | 157 | for role_id in data["verified-roles"]: 158 | try: 159 | if ctx.guild.get_role(role_id) in ctx.author.roles: 160 | valid_user = True 161 | except: 162 | pass 163 | 164 | if valid_user or ctx.author.guild_permissions.administrator: 165 | role_id = int(role_id) 166 | 167 | if role_id not in data["valid-roles"]: 168 | 169 | try: 170 | role = ctx.guild.get_role(role_id) 171 | 172 | with open("data.json") as f: 173 | data = json.load(f) 174 | 175 | data["valid-roles"].append(role_id) 176 | 177 | with open('data.json', 'w') as f: 178 | json.dump(data, f) 179 | 180 | em = discord.Embed(title="Auroris Tickets", description="You have successfully added `{}` to the list of roles with access to tickets.".format(role.name), color=0x00a8ff) 181 | 182 | await ctx.send(embed=em) 183 | 184 | except: 185 | em = discord.Embed(title="Auroris Tickets", description="That isn't a valid role ID. Please try again with a valid role ID.") 186 | await ctx.send(embed=em) 187 | 188 | else: 189 | em = discord.Embed(title="Auroris Tickets", description="That role already has access to tickets!", color=0x00a8ff) 190 | await ctx.send(embed=em) 191 | 192 | else: 193 | em = discord.Embed(title="Auroris Tickets", description="Sorry, you don't have permission to run that command.", color=0x00a8ff) 194 | await ctx.send(embed=em) 195 | 196 | @bot.command() 197 | async def delaccess(ctx, role_id=None): 198 | with open('data.json') as f: 199 | data = json.load(f) 200 | 201 | valid_user = False 202 | 203 | for role_id in data["verified-roles"]: 204 | try: 205 | if ctx.guild.get_role(role_id) in ctx.author.roles: 206 | valid_user = True 207 | except: 208 | pass 209 | 210 | if valid_user or ctx.author.guild_permissions.administrator: 211 | 212 | try: 213 | role_id = int(role_id) 214 | role = ctx.guild.get_role(role_id) 215 | 216 | with open("data.json") as f: 217 | data = json.load(f) 218 | 219 | valid_roles = data["valid-roles"] 220 | 221 | if role_id in valid_roles: 222 | index = valid_roles.index(role_id) 223 | 224 | del valid_roles[index] 225 | 226 | data["valid-roles"] = valid_roles 227 | 228 | with open('data.json', 'w') as f: 229 | json.dump(data, f) 230 | 231 | em = discord.Embed(title="Auroris Tickets", description="You have successfully removed `{}` from the list of roles with access to tickets.".format(role.name), color=0x00a8ff) 232 | 233 | await ctx.send(embed=em) 234 | 235 | else: 236 | 237 | em = discord.Embed(title="Auroris Tickets", description="That role already doesn't have access to tickets!", color=0x00a8ff) 238 | await ctx.send(embed=em) 239 | 240 | except: 241 | em = discord.Embed(title="Auroris Tickets", description="That isn't a valid role ID. Please try again with a valid role ID.") 242 | await ctx.send(embed=em) 243 | 244 | else: 245 | em = discord.Embed(title="Auroris Tickets", description="Sorry, you don't have permission to run that command.", color=0x00a8ff) 246 | await ctx.send(embed=em) 247 | 248 | @bot.command() 249 | async def addpingedrole(ctx, role_id=None): 250 | 251 | with open('data.json') as f: 252 | data = json.load(f) 253 | 254 | valid_user = False 255 | 256 | for role_id in data["verified-roles"]: 257 | try: 258 | if ctx.guild.get_role(role_id) in ctx.author.roles: 259 | valid_user = True 260 | except: 261 | pass 262 | 263 | if valid_user or ctx.author.guild_permissions.administrator: 264 | 265 | role_id = int(role_id) 266 | 267 | if role_id not in data["pinged-roles"]: 268 | 269 | try: 270 | role = ctx.guild.get_role(role_id) 271 | 272 | with open("data.json") as f: 273 | data = json.load(f) 274 | 275 | data["pinged-roles"].append(role_id) 276 | 277 | with open('data.json', 'w') as f: 278 | json.dump(data, f) 279 | 280 | em = discord.Embed(title="Auroris Tickets", description="You have successfully added `{}` to the list of roles that get pinged when new tickets are created!".format(role.name), color=0x00a8ff) 281 | 282 | await ctx.send(embed=em) 283 | 284 | except: 285 | em = discord.Embed(title="Auroris Tickets", description="That isn't a valid role ID. Please try again with a valid role ID.") 286 | await ctx.send(embed=em) 287 | 288 | else: 289 | em = discord.Embed(title="Auroris Tickets", description="That role already receives pings when tickets are created.", color=0x00a8ff) 290 | await ctx.send(embed=em) 291 | 292 | else: 293 | em = discord.Embed(title="Auroris Tickets", description="Sorry, you don't have permission to run that command.", color=0x00a8ff) 294 | await ctx.send(embed=em) 295 | 296 | @bot.command() 297 | async def delpingedrole(ctx, role_id=None): 298 | 299 | with open('data.json') as f: 300 | data = json.load(f) 301 | 302 | valid_user = False 303 | 304 | for role_id in data["verified-roles"]: 305 | try: 306 | if ctx.guild.get_role(role_id) in ctx.author.roles: 307 | valid_user = True 308 | except: 309 | pass 310 | 311 | if valid_user or ctx.author.guild_permissions.administrator: 312 | 313 | try: 314 | role_id = int(role_id) 315 | role = ctx.guild.get_role(role_id) 316 | 317 | with open("data.json") as f: 318 | data = json.load(f) 319 | 320 | pinged_roles = data["pinged-roles"] 321 | 322 | if role_id in pinged_roles: 323 | index = pinged_roles.index(role_id) 324 | 325 | del pinged_roles[index] 326 | 327 | data["pinged-roles"] = pinged_roles 328 | 329 | with open('data.json', 'w') as f: 330 | json.dump(data, f) 331 | 332 | em = discord.Embed(title="Auroris Tickets", description="You have successfully removed `{}` from the list of roles that get pinged when new tickets are created.".format(role.name), color=0x00a8ff) 333 | await ctx.send(embed=em) 334 | 335 | else: 336 | em = discord.Embed(title="Auroris Tickets", description="That role already isn't getting pinged when new tickets are created!", color=0x00a8ff) 337 | await ctx.send(embed=em) 338 | 339 | except: 340 | em = discord.Embed(title="Auroris Tickets", description="That isn't a valid role ID. Please try again with a valid role ID.") 341 | await ctx.send(embed=em) 342 | 343 | else: 344 | em = discord.Embed(title="Auroris Tickets", description="Sorry, you don't have permission to run that command.", color=0x00a8ff) 345 | await ctx.send(embed=em) 346 | 347 | 348 | @bot.command() 349 | @has_permissions(administrator=True) 350 | async def addadminrole(ctx, role_id=None): 351 | 352 | try: 353 | role_id = int(role_id) 354 | role = ctx.guild.get_role(role_id) 355 | 356 | with open("data.json") as f: 357 | data = json.load(f) 358 | 359 | data["verified-roles"].append(role_id) 360 | 361 | with open('data.json', 'w') as f: 362 | json.dump(data, f) 363 | 364 | em = discord.Embed(title="Auroris Tickets", description="You have successfully added `{}` to the list of roles that can run admin-level commands!".format(role.name), color=0x00a8ff) 365 | await ctx.send(embed=em) 366 | 367 | except: 368 | em = discord.Embed(title="Auroris Tickets", description="That isn't a valid role ID. Please try again with a valid role ID.") 369 | await ctx.send(embed=em) 370 | 371 | @bot.command() 372 | @has_permissions(administrator=True) 373 | async def deladminrole(ctx, role_id=None): 374 | try: 375 | role_id = int(role_id) 376 | role = ctx.guild.get_role(role_id) 377 | 378 | with open("data.json") as f: 379 | data = json.load(f) 380 | 381 | admin_roles = data["verified-roles"] 382 | 383 | if role_id in admin_roles: 384 | index = admin_roles.index(role_id) 385 | 386 | del admin_roles[index] 387 | 388 | data["verified-roles"] = admin_roles 389 | 390 | with open('data.json', 'w') as f: 391 | json.dump(data, f) 392 | 393 | em = discord.Embed(title="Auroris Tickets", description="You have successfully removed `{}` from the list of roles that get pinged when new tickets are created.".format(role.name), color=0x00a8ff) 394 | 395 | await ctx.send(embed=em) 396 | 397 | else: 398 | em = discord.Embed(title="Auroris Tickets", description="That role isn't getting pinged when new tickets are created!", color=0x00a8ff) 399 | await ctx.send(embed=em) 400 | 401 | except: 402 | em = discord.Embed(title="Auroris Tickets", description="That isn't a valid role ID. Please try again with a valid role ID.") 403 | await ctx.send(embed=em) 404 | 405 | bot.run("TOKEN") 406 | --------------------------------------------------------------------------------