Skip to content

Commit fa5ec48

Browse files
committed
add: 3.0.2 code
1 parent 5cb2da3 commit fa5ec48

8 files changed

Lines changed: 416 additions & 1 deletion

File tree

‎.gitignore‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
.vscode/
2+
13
# Byte-compiled / optimized / DLL files
24
__pycache__/
35
*.py[cod]

‎README.md‎

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,58 @@
11
# discord-py-interactions_boilerplate
2-
Boilerplate template for the discord-py-interactions library
2+
<h3 align=center>Boilerplate template for the discord-py-interactions library</h3>
3+
<hr>
4+
5+
- **Currently, this boilerplate supports `discord-py-interactions==3.0.2` but will be updated for future version later on. To switch to a different version, check the branches**
6+
7+
# Overview
8+
> `main.py:`
9+
- A custom, dynamic cog loader is present. Write a cog following the `template.py` in `/cogs/`, place it in the `/cogs/` directory, and it will automatically be loaded when the bot boots.
10+
- Utilizes the logging library and implements an easy to use custom logger and formatter. All you need to do is call `initLogger("script_name")` in a module or cog, and log configuration is taken care of for you.
11+
- Alongside the custom logging utility, exception handling is present and proper debug levels exist. You can configure the level to your liking in `config.py`. Also, this handles command cooldown if you define it in your cogs.
12+
13+
> `src/logutil.py:`
14+
- Functions here exist to aid the user in simplifying `logging` configuration. Here, all log messages go to standard output.
15+
- A custom formatter also applies based on what level logging you desire, whereas DEBUG produces verbose output and is tailored to aid in debugging, showing which module the message is originating from and, in most cases, which line number. Loggging levels are categorized by color.
16+
17+
> `cogs/template.py:`
18+
- This example cog is documented extensively. Please be sure to read over it. This cog will *not* be loaded on boot, so please refrain from writing your code in it.
19+
20+
> `config.py:`
21+
- This module houses the basic configuration options for your bot, including DEBUG switches and the bot prefix.
22+
23+
# Installation
24+
> 1. Clone this repository. To switch to a different version, `cd` into this cloned repository and run `git checkout -b [branch name/version here]`
25+
> 2. Create a Discord bot token from [here](https://discord.com/developers/applications/)
26+
> **Register it for slash commands:**
27+
> - Under *OAuth2 > General*, set the Authorization Method to "In-app Authorization"
28+
> - Tick `bot` and `applications.commands`
29+
> - Go to *OAuth2 > URL Generator*, tick `bot` and `applications.commands`. For Bot Permissions, tick:
30+
> > - General: Read Messages/View Channels
31+
> > - Text Permissions: Send Messages, Manage Messages, and Embed Links
32+
> - Copy the generated URL at the bottom of the page to invite it to desired servers
33+
> 3. Make a new file called `.env` inside the repo folder and paste the below code block in the file
34+
> ```
35+
> TOKEN="[paste Discord bot token here]"
36+
> DEV_GUILD=[paste your bot testing server ID here]
37+
> ```
38+
> 4. Run `pip install -r requirements.txt` to install packages. You'll need Python 3.6.8 or later
39+
> 5. Once that's done, run the bot by executing `python3 main.py` in the terminal
40+
>
41+
> <hr />
42+
>
43+
> *If you aren't sure how to obtain your server ID, check out [this article](https://www.alphr.com/discord-find-server-id/)*
44+
>
45+
> *If you get errors related to missing token environment variables, run `source .env`*
46+
47+
# FAQ
48+
## Why aren't my slash commands getting registered?
49+
> There could be many reasons, but let's narrow it down
50+
> - Ensure your bot token has the `applications.command` scope before you invited your bot. If not, kick the bot from your server(s), follow above directions to enable the permissions scope, and reinvite.
51+
> - The bot uses a guild ID to register the slash commands in a single guild. This ensures it will be registered instantly. In order to use slash commands globally, remove the `guild_ids=[]` in your `@cog_ext.cog_slash` decorators. But **keep in mind this may take a few hours to register.** To refresh it instantly, simply kick the bot from your server and reinvite.
52+
53+
<hr />
54+
55+
## Why am I getting a `HTTP 403 - 50001 Missing Access`?
56+
> Again, like above, this could be caused by many different reasons, but here are a couple things you can try
57+
> - Follow the above steps to ensure your slash commands are registering properly (making sure `applications.command` is enabled, etc.)
58+
> - Reinvite your bot

‎cogs/helloworld.py‎

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""
2+
Example cog for real world use
3+
4+
This is safe to delete
5+
"""
6+
7+
import os
8+
9+
from discord.ext import commands
10+
from discord_slash import cog_ext
11+
12+
from src import logutil # pylint: disable=import-error
13+
Cog = commands.Cog
14+
15+
logger = logutil.init_logger("helloworld.py")
16+
17+
DEV_GUILD = int(os.environ.get("DEV_GUILD"))
18+
19+
20+
class HelloWorld(commands.Cog):
21+
"Main class for the bot"
22+
def __init__(self, client):
23+
self.client = client
24+
25+
@Cog.listener()
26+
async def on_ready(self):
27+
"Called when cog is loaded"
28+
logger.info("HelloWorld slash cog registered")
29+
30+
async def command(self, ctx):
31+
"""
32+
Your command code goes here
33+
"""
34+
await ctx.send("Hello world!")
35+
36+
@commands.command(name="helloworld")
37+
async def _reg_prefixed(self, ctx):
38+
"This function registers your command as a normal bot command"
39+
await self.command(ctx,)
40+
41+
@cog_ext.cog_slash(name="helloworld",
42+
description="Hello world!",
43+
guild_ids=[DEV_GUILD])
44+
async def _slash_prefixed(self, ctx,):
45+
"This function registers your command as a slash command"
46+
await self.command(ctx,)
47+
48+
49+
def setup(bot):
50+
"Called when this cog initializes"
51+
bot.add_cog(HelloWorld(bot))

‎cogs/template.py‎

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
"""
2+
This file provides a template for future commands. This file will not be loaded as a cog or module
3+
"""
4+
5+
import os
6+
7+
from discord.ext import commands
8+
# You will need to import cog_ext from discord_slash in order to use slash commands
9+
from discord_slash import cog_ext
10+
11+
# Import required Discord libraries
12+
# Highly recommended - we suggest providing proper debug logging
13+
from src import logutil # pylint: disable=import-error
14+
Cog = commands.Cog
15+
16+
# Change this - this labels log messages for debug mode
17+
logger = logutil.init_logger("template.py")
18+
19+
# Use the DEV_GUILD environment variable to instantly load slash commands in your testing guild
20+
# Global slash commands are usually cached for an hour due to Discord API restrictions.
21+
DEV_GUILD = int(os.environ.get("DEV_GUILD"))
22+
23+
24+
# Rename this class to whatever you'd like. Rename it again below at setup()
25+
class Command(commands.Cog):
26+
"Main class for bot"
27+
def __init__(self, client):
28+
self.client = client
29+
30+
@Cog.listener()
31+
async def on_ready(self): # code to execute when cog has been loaded
32+
"Called when cog is initialized"
33+
logger.info("Command slash cog registered")
34+
35+
# Pass all arguments as "args" and default to NoneType
36+
async def command(self, ctx, *, args: str = None):
37+
"""
38+
Your command code goes here
39+
"""
40+
41+
# These two functions exist so that your command will respond both to the bot prefix
42+
# and slash commands.
43+
# --
44+
# Set this to a command name. This will be called with your server's prefix (eg. rf.command)
45+
@commands.command(name="command")
46+
async def _reg_prefixed(self, ctx):
47+
"This function registers your command as a normal bot command"
48+
# If your function name is different, change it here
49+
await self.command(ctx,)
50+
51+
# Set this to your command name (eg: /command) and add a meaningful description.
52+
# Don't forget to specify a value of [DEV_GUILD] to the guild_ids argument or
53+
# your slash commands won't instantly load!
54+
@cog_ext.cog_slash(name="command",
55+
description="Some description for the command",
56+
guild_ids=[DEV_GUILD])
57+
async def _slash_prefixed(self, ctx,):
58+
"This function registers your command as a slash command"
59+
# If your function name is different, change it here
60+
await self.command(ctx,)
61+
62+
63+
def setup(bot):
64+
"Called when cog is initializing"
65+
# Required for cog to register
66+
bot.add_cog(Command(bot)) # Don't forget to rename the class here!

‎config.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"Enable DEBUG messages for logging"
2+
DEBUG = False
3+
4+
"Enable verbose DEBUG Discord.py messages for logging"
5+
DEBUG_DISCORD = False
6+
7+
"Bot prefix to respond to"
8+
PREFIX = "#"

‎main.py‎

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
"""
2+
Main script to run
3+
4+
This script initializes cogs and starts the bot
5+
6+
Code taken from my contributions in:
7+
https://github.com/savioxavier/repo-finder-bot/
8+
Additional thanks to savioxavier
9+
"""
10+
import os
11+
import sys
12+
13+
import discord
14+
from discord.ext import commands
15+
from discord.ext.commands.errors import ExtensionFailed, NoEntryPointError
16+
from discord_slash import SlashCommand
17+
from dotenv import load_dotenv
18+
19+
from src import logutil
20+
from config import DEBUG, DEBUG_DISCORD
21+
from config import PREFIX as bot_prefix
22+
23+
load_dotenv()
24+
25+
# Configure logging for this main.py handler
26+
logger = logutil.init_logger("main.py")
27+
28+
# Configure logging for Discord.py
29+
if DEBUG:
30+
logging = logutil.get_logger("discord")
31+
32+
logger.warning("Debug mode is %s; Discord debug is %s", DEBUG, DEBUG_DISCORD)
33+
34+
# Instantiate environment variables
35+
DEV_GUILD = [int(os.environ.get("DEV_GUILD"))]
36+
TOKEN = os.environ.get("TOKEN")
37+
38+
intents = discord.Intents.default()
39+
intents.members = True # pylint: disable=assigning-non-slot # noqa
40+
41+
# Define the client
42+
client = commands.Bot(command_prefix=bot_prefix,
43+
case_insensitive=True,
44+
intents=intents,
45+
help_command=None,
46+
)
47+
48+
# Define the slash command handler
49+
slash = SlashCommand(client,
50+
sync_commands=True,
51+
sync_on_cog_reload=True,)
52+
53+
54+
# BEGIN on_ready
55+
async def on_ready():
56+
"Called when bot is ready to receive interactions"
57+
logger.info(
58+
"Logged in as %s#%s",
59+
client.user.name,
60+
client.user.discriminator
61+
)
62+
# END on_ready
63+
64+
# BEGIN command_help
65+
async def command_help(ctx):
66+
"Help command"
67+
logger.debug("%s - initiated help command",
68+
ctx.message.author)
69+
70+
try:
71+
_created_at = ctx.message.created_at
72+
except AttributeError:
73+
_created_at = ctx.created_at
74+
75+
help_embed = discord.Embed(
76+
title="Help",
77+
description="Fill me in!",
78+
timestamp=_created_at
79+
)
80+
81+
help_embed.set_thumbnail(url=client.user.avatar_url)
82+
help_embed.set_footer(text="Boilerplate Bot")
83+
84+
await ctx.send(embed=help_embed)
85+
# END command_help
86+
87+
# BEGIN on_command_error
88+
async def on_command_error(ctx: commands.Context, error):
89+
"Gets called when a command fails"
90+
if isinstance(error, commands.CommandOnCooldown):
91+
# handle cooldown
92+
logger.debug(
93+
"%s - initiated a command on cooldown [!]"
94+
)
95+
await ctx.send(
96+
f"This command is on cooldown. Try again after `{round(error.retry_after)}` seconds.",
97+
delete_after=5
98+
)
99+
else:
100+
# handle anything else
101+
logger.warning(
102+
"A discord.py command error occured:\n%s",
103+
error
104+
)
105+
await ctx.send(
106+
f"A discord.py command error occured:\n{error}",
107+
delete_after=10
108+
)
109+
# END on_command_error
110+
111+
# BEGIN cogs_dynamic_loader
112+
113+
# Fill this array with Python files in /cogs
114+
# This omits __init__.py, template.py, and excludes files without a py file extension
115+
command_modules = [
116+
module[:-3]
117+
for module in os.listdir(f"{os.path.dirname(__file__)}/cogs")
118+
if module not in ("__init__.py", "template.py") and module[-3:] == ".py"
119+
]
120+
121+
if command_modules or command_modules == []:
122+
logger.info(
123+
"Importing %s cogs: %s",
124+
len(command_modules),
125+
', '.join(command_modules)
126+
)
127+
else:
128+
logger.warning("Could not import any cogs!")
129+
130+
for module in command_modules:
131+
try:
132+
client.load_extension("cogs." + module)
133+
except NoEntryPointError:
134+
logger.error(
135+
"Could not import cog %s: The cog has no setup function - NoEntryPointError",
136+
module
137+
)
138+
logger.debug(str(sys.exc_info()[2]))
139+
except ExtensionFailed:
140+
logger.error(
141+
"Could not import cog %s: The cog failed to execute",
142+
module
143+
)
144+
logger.debug(str(sys.exc_info()[2]))
145+
except Exception as e: # pylint: disable=broad-except
146+
logger.error(
147+
"Could not import cog %s:\n%s",
148+
module,
149+
e
150+
)
151+
152+
logger.info("Cog initialization complete")
153+
logger.debug(
154+
"Cogs incoming:\n%s\n",
155+
',\n'.join(command_modules)
156+
)
157+
# END cogs_dynamic_loader
158+
159+
client.run(TOKEN)

‎requirements.txt‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
discord-py-interactions==3.0.2
2+
discord.py==1.7.3
3+
requests==2.26.0
4+
discord==1.7.3
5+
python-dotenv==0.19.1
6+
aiohttp[speedups]

0 commit comments

Comments
 (0)