Skip to content

Commit

Permalink
add: 3.0.2 code
Browse files Browse the repository at this point in the history
  • Loading branch information
V3ntus committed Dec 16, 2021
1 parent 5cb2da3 commit fa5ec48
Show file tree
Hide file tree
Showing 8 changed files with 416 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.vscode/

# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
Expand Down
58 changes: 57 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,58 @@
# discord-py-interactions_boilerplate
Boilerplate template for the discord-py-interactions library
<h3 align=center>Boilerplate template for the discord-py-interactions library</h3>
<hr>

- **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**

# Overview
> `main.py:`
- 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.
- 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.
- 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.

> `src/logutil.py:`
- Functions here exist to aid the user in simplifying `logging` configuration. Here, all log messages go to standard output.
- 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.

> `cogs/template.py:`
- 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.

> `config.py:`
- This module houses the basic configuration options for your bot, including DEBUG switches and the bot prefix.

# Installation
> 1. Clone this repository. To switch to a different version, `cd` into this cloned repository and run `git checkout -b [branch name/version here]`
> 2. Create a Discord bot token from [here](https://discord.com/developers/applications/)
> **Register it for slash commands:**
> - Under *OAuth2 > General*, set the Authorization Method to "In-app Authorization"
> - Tick `bot` and `applications.commands`
> - Go to *OAuth2 > URL Generator*, tick `bot` and `applications.commands`. For Bot Permissions, tick:
> > - General: Read Messages/View Channels
> > - Text Permissions: Send Messages, Manage Messages, and Embed Links
> - Copy the generated URL at the bottom of the page to invite it to desired servers
> 3. Make a new file called `.env` inside the repo folder and paste the below code block in the file
> ```
> TOKEN="[paste Discord bot token here]"
> DEV_GUILD=[paste your bot testing server ID here]
> ```
> 4. Run `pip install -r requirements.txt` to install packages. You'll need Python 3.6.8 or later
> 5. Once that's done, run the bot by executing `python3 main.py` in the terminal
>
> <hr />
>
> *If you aren't sure how to obtain your server ID, check out [this article](https://www.alphr.com/discord-find-server-id/)*
>
> *If you get errors related to missing token environment variables, run `source .env`*
# FAQ
## Why aren't my slash commands getting registered?
> There could be many reasons, but let's narrow it down
> - 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.
> - 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.
<hr />
## Why am I getting a `HTTP 403 - 50001 Missing Access`?
> Again, like above, this could be caused by many different reasons, but here are a couple things you can try
> - Follow the above steps to ensure your slash commands are registering properly (making sure `applications.command` is enabled, etc.)
> - Reinvite your bot
51 changes: 51 additions & 0 deletions cogs/helloworld.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
Example cog for real world use
This is safe to delete
"""

import os

from discord.ext import commands
from discord_slash import cog_ext

from src import logutil # pylint: disable=import-error
Cog = commands.Cog

logger = logutil.init_logger("helloworld.py")

DEV_GUILD = int(os.environ.get("DEV_GUILD"))


class HelloWorld(commands.Cog):
"Main class for the bot"
def __init__(self, client):
self.client = client

@Cog.listener()
async def on_ready(self):
"Called when cog is loaded"
logger.info("HelloWorld slash cog registered")

async def command(self, ctx):
"""
Your command code goes here
"""
await ctx.send("Hello world!")

@commands.command(name="helloworld")
async def _reg_prefixed(self, ctx):
"This function registers your command as a normal bot command"
await self.command(ctx,)

@cog_ext.cog_slash(name="helloworld",
description="Hello world!",
guild_ids=[DEV_GUILD])
async def _slash_prefixed(self, ctx,):
"This function registers your command as a slash command"
await self.command(ctx,)


def setup(bot):
"Called when this cog initializes"
bot.add_cog(HelloWorld(bot))
66 changes: 66 additions & 0 deletions cogs/template.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""
This file provides a template for future commands. This file will not be loaded as a cog or module
"""

import os

from discord.ext import commands
# You will need to import cog_ext from discord_slash in order to use slash commands
from discord_slash import cog_ext

# Import required Discord libraries
# Highly recommended - we suggest providing proper debug logging
from src import logutil # pylint: disable=import-error
Cog = commands.Cog

# Change this - this labels log messages for debug mode
logger = logutil.init_logger("template.py")

# Use the DEV_GUILD environment variable to instantly load slash commands in your testing guild
# Global slash commands are usually cached for an hour due to Discord API restrictions.
DEV_GUILD = int(os.environ.get("DEV_GUILD"))


# Rename this class to whatever you'd like. Rename it again below at setup()
class Command(commands.Cog):
"Main class for bot"
def __init__(self, client):
self.client = client

@Cog.listener()
async def on_ready(self): # code to execute when cog has been loaded
"Called when cog is initialized"
logger.info("Command slash cog registered")

# Pass all arguments as "args" and default to NoneType
async def command(self, ctx, *, args: str = None):
"""
Your command code goes here
"""

# These two functions exist so that your command will respond both to the bot prefix
# and slash commands.
# --
# Set this to a command name. This will be called with your server's prefix (eg. rf.command)
@commands.command(name="command")
async def _reg_prefixed(self, ctx):
"This function registers your command as a normal bot command"
# If your function name is different, change it here
await self.command(ctx,)

# Set this to your command name (eg: /command) and add a meaningful description.
# Don't forget to specify a value of [DEV_GUILD] to the guild_ids argument or
# your slash commands won't instantly load!
@cog_ext.cog_slash(name="command",
description="Some description for the command",
guild_ids=[DEV_GUILD])
async def _slash_prefixed(self, ctx,):
"This function registers your command as a slash command"
# If your function name is different, change it here
await self.command(ctx,)


def setup(bot):
"Called when cog is initializing"
# Required for cog to register
bot.add_cog(Command(bot)) # Don't forget to rename the class here!
8 changes: 8 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"Enable DEBUG messages for logging"
DEBUG = False

"Enable verbose DEBUG Discord.py messages for logging"
DEBUG_DISCORD = False

"Bot prefix to respond to"
PREFIX = "#"
159 changes: 159 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
"""
Main script to run
This script initializes cogs and starts the bot
Code taken from my contributions in:
https://github.com/savioxavier/repo-finder-bot/
Additional thanks to savioxavier
"""
import os
import sys

import discord
from discord.ext import commands
from discord.ext.commands.errors import ExtensionFailed, NoEntryPointError
from discord_slash import SlashCommand
from dotenv import load_dotenv

from src import logutil
from config import DEBUG, DEBUG_DISCORD
from config import PREFIX as bot_prefix

load_dotenv()

# Configure logging for this main.py handler
logger = logutil.init_logger("main.py")

# Configure logging for Discord.py
if DEBUG:
logging = logutil.get_logger("discord")

logger.warning("Debug mode is %s; Discord debug is %s", DEBUG, DEBUG_DISCORD)

# Instantiate environment variables
DEV_GUILD = [int(os.environ.get("DEV_GUILD"))]
TOKEN = os.environ.get("TOKEN")

intents = discord.Intents.default()
intents.members = True # pylint: disable=assigning-non-slot # noqa

# Define the client
client = commands.Bot(command_prefix=bot_prefix,
case_insensitive=True,
intents=intents,
help_command=None,
)

# Define the slash command handler
slash = SlashCommand(client,
sync_commands=True,
sync_on_cog_reload=True,)


# BEGIN on_ready
async def on_ready():
"Called when bot is ready to receive interactions"
logger.info(
"Logged in as %s#%s",
client.user.name,
client.user.discriminator
)
# END on_ready

# BEGIN command_help
async def command_help(ctx):
"Help command"
logger.debug("%s - initiated help command",
ctx.message.author)

try:
_created_at = ctx.message.created_at
except AttributeError:
_created_at = ctx.created_at

help_embed = discord.Embed(
title="Help",
description="Fill me in!",
timestamp=_created_at
)

help_embed.set_thumbnail(url=client.user.avatar_url)
help_embed.set_footer(text="Boilerplate Bot")

await ctx.send(embed=help_embed)
# END command_help

# BEGIN on_command_error
async def on_command_error(ctx: commands.Context, error):
"Gets called when a command fails"
if isinstance(error, commands.CommandOnCooldown):
# handle cooldown
logger.debug(
"%s - initiated a command on cooldown [!]"
)
await ctx.send(
f"This command is on cooldown. Try again after `{round(error.retry_after)}` seconds.",
delete_after=5
)
else:
# handle anything else
logger.warning(
"A discord.py command error occured:\n%s",
error
)
await ctx.send(
f"A discord.py command error occured:\n{error}",
delete_after=10
)
# END on_command_error

# BEGIN cogs_dynamic_loader

# Fill this array with Python files in /cogs
# This omits __init__.py, template.py, and excludes files without a py file extension
command_modules = [
module[:-3]
for module in os.listdir(f"{os.path.dirname(__file__)}/cogs")
if module not in ("__init__.py", "template.py") and module[-3:] == ".py"
]

if command_modules or command_modules == []:
logger.info(
"Importing %s cogs: %s",
len(command_modules),
', '.join(command_modules)
)
else:
logger.warning("Could not import any cogs!")

for module in command_modules:
try:
client.load_extension("cogs." + module)
except NoEntryPointError:
logger.error(
"Could not import cog %s: The cog has no setup function - NoEntryPointError",
module
)
logger.debug(str(sys.exc_info()[2]))
except ExtensionFailed:
logger.error(
"Could not import cog %s: The cog failed to execute",
module
)
logger.debug(str(sys.exc_info()[2]))
except Exception as e: # pylint: disable=broad-except
logger.error(
"Could not import cog %s:\n%s",
module,
e
)

logger.info("Cog initialization complete")
logger.debug(
"Cogs incoming:\n%s\n",
',\n'.join(command_modules)
)
# END cogs_dynamic_loader

client.run(TOKEN)
6 changes: 6 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
discord-py-interactions==3.0.2
discord.py==1.7.3
requests==2.26.0
discord==1.7.3
python-dotenv==0.19.1
aiohttp[speedups]
Loading

0 comments on commit fa5ec48

Please sign in to comment.