Last active
October 28, 2022 14:42
-
-
Save Rapptz/67aa27cb2c0b1a94a53f9e0c2b99eb74 to your computer and use it in GitHub Desktop.
Showing off custom commands using discord.ext
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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') |
This doesn't work.
This isn't a rewrite version.
Working on a rewrite vers.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This doesn't work.