Skip to content

Instantly share code, notes, and snippets.

@Rapptz
Last active October 28, 2022 14:42
Show Gist options
  • Save Rapptz/67aa27cb2c0b1a94a53f9e0c2b99eb74 to your computer and use it in GitHub Desktop.
Save Rapptz/67aa27cb2c0b1a94a53f9e0c2b99eb74 to your computer and use it in GitHub Desktop.
Showing off custom commands using discord.ext
import discord
from discord.ext import commands
import json, inspect
import functools
import re
# some JSON set up for our storage because >lazy<
class CustomCommandEntry:
__slots__ = ['name', 'content', 'guild_id']
def __init__(self, **kwargs):
self.name = kwargs.pop('name', None)
self.content = kwargs.pop('content', None)
self.guild_id = kwargs.pop('guild_id', None)
def __repr__(self):
return '<CustomCommandEntry name={0.name} content={0.content} guild_id={0.guild_id}>'.format(self)
class CustomCommandEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, CustomCommandEntry):
payload = { '__command__': True }
for val in obj.__slots__:
payload[val] = getattr(obj, val)
return payload
return super().default(obj)
def custom_command_decoder(obj):
if '__command__' in obj:
return CustomCommandEntry(**obj)
return obj
# the actual 'functional' parts
# this is the actual 'callback' that executes the custom command registered
# for 'sanity' sakes, custom commands cannot have arguments. Maybe I will
# modify it so they can in the future, but for now they don't.
async def custom_command_callback(entry, ctx, *args : str):
# in the entry content you can have ${user} and ${mention}
author = ctx.message.author
fixed = entry.content.replace('${user}', author.name).replace('${mention}', author.mention)
# find all ${N} and replace it with args[N]
def getter(obj):
try:
return args[int(obj.group(1))]
except:
return ''
fixed = re.sub(r'\${(\d+)}', getter, fixed)
await ctx.bot.send_message(ctx.message.channel, fixed)
class CustomCommand(commands.Command):
def __init__(self, **kwargs):
self._entries = {}
super().__init__(**kwargs)
async def invoke(self, ctx):
server = ctx.message.server
if server is not None:
entry = self._entries.get(server.id)
if entry is None:
return
# update the callback called
self.callback = functools.partial(custom_command_callback, entry)
self.params = inspect.signature(self.callback).parameters
await super().invoke(ctx)
# that ends the 'functionality'
bot = commands.Bot(command_prefix='>')
def register_custom_command(entry):
"""Registers a custom command to the bot."""
command = bot.get_command(entry.name)
if command is None:
# the command is not registered so we can add it
# for the first time using this entry as its first entry.
command = bot.command(name=entry.name, cls=CustomCommand,
pass_context=True, hidden=True)(custom_command_callback)
if not isinstance(command, CustomCommand):
raise RuntimeError('This is an already registered non-custom command.')
if entry.guild_id in command._entries:
# you can do some permission checking here, but this is outside the example's scope.
raise RuntimeError('This is already registered as a custom command.')
command._entries[entry.guild_id] = entry
def load_custom_commands():
try:
with open('custom_commands.json') as f:
entries = json.load(f, object_hook=custom_command_decoder)
for entry in entries:
register_custom_command(entry)
except FileNotFoundError:
pass
def save_custom_commands():
entries = []
for name, command in bot.commands.items():
if isinstance(command, CustomCommand):
entries.extend(command._entries.values())
with open('custom_commands.json', 'w') as f:
json.dump(entries, f, ensure_ascii=True, cls=CustomCommandEncoder)
@bot.event
async def on_ready():
print('logged on')
print('Name {0.name}\nID: {0.id}'.format(bot.user))
load_custom_commands()
print('loaded custom commands')
@bot.command(pass_context=True, no_pm=True)
async def create(ctx, name, *, content):
"""Creates a custom command for your server.
The following placeholders are available:
- ${user} : replaces the user's name
- ${mention} : replaced with a mention of the user
Custom commands are forced to be lower case.
"""
entry = CustomCommandEntry(name=name.lower(), content=content, guild_id=ctx.message.server.id)
try:
register_custom_command(entry)
except RuntimeError as e:
await bot.say('Error: ' + str(e))
else:
save_custom_commands()
await bot.say('Successfully registered command {0.prefix}{1}'.format(ctx, name))
# removing custom commands exercise to the reader.
bot.run('token')
@Miichel
Copy link

Miichel commented Mar 23, 2020

This doesn't work.

@9u3
Copy link

9u3 commented Apr 20, 2020

This doesn't work.

This isn't a rewrite version.

@9u3
Copy link

9u3 commented Aug 9, 2020

Working on a rewrite vers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment