diff --git a/config.json b/config.json new file mode 100644 index 0000000..e8d8324 --- /dev/null +++ b/config.json @@ -0,0 +1,4 @@ +{ + "command_prefix": "$", + "token": "" +} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..88b9f66 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +discord.py[voice]==1.7.3 +PyNaCl==1.4.0 diff --git a/src/commands/command.py b/src/commands/command.py new file mode 100644 index 0000000..cd47020 --- /dev/null +++ b/src/commands/command.py @@ -0,0 +1,20 @@ +import discord + +class Command(): + def __init__(self): + # Useful boilerplate + # You should overwrite these values! + + self.name = "command" # Lower case and no spaces please :) + self.display_name = "Command" + self.description = "command description" + self.triggers = ["command"] + + self.hidden = False + + # {} will be .formatted to be the command prefix + self.usage = "{}command" + + async def run(self, bot: discord.Client, config: dict, message: discord.message, content: str): + # Implement this! + pass diff --git a/src/commands/help.py b/src/commands/help.py new file mode 100644 index 0000000..5f8fc16 --- /dev/null +++ b/src/commands/help.py @@ -0,0 +1,36 @@ +import discord + +from commands.command import Command + +class Help(Command): + def __init__(self): + self.name = "help" + self.display_name = "Help" + self.description = "Show a summary of the available bot commands" + self.triggers = ["help", "h"] + + self.usage = "{0}help" + + async def run(self, bot: discord.Client, config: dict, message: discord.Message, content: str): + commands = [] + output = "MemeMan 3 : Electric Boogaloo 2\n\t The finest meme connoisseur\n" + + output = f"{output}\nCommands:" + if config.get("loaded_commands") == None: + output = f"{output}\nNo commands to show!" + else: + for command in config.get("loaded_commands").values(): + command_output = f"\t{command.display_name} : {command.description}\n" + + usage = command.usage.format(config.get("command_prefix")) + + triggers_str = f"${', $'.join(command.triggers)}" + + command_output = f"{command_output}\t\tTriggers: {triggers_str}\n" + command_output = f"{command_output}\t\tUsage: {usage}\n" + + output = f"{output}\n{command_output}" + + output = f"```{output}\n\nMemeMan is open source! Find him at https://gitlab.hodgyj.com/james/mememan```" + + await message.reply(output) diff --git a/src/core/bot.py b/src/core/bot.py new file mode 100644 index 0000000..542037c --- /dev/null +++ b/src/core/bot.py @@ -0,0 +1,16 @@ +import discord + +from core.commands import load_commands +from core.messages import handle_message + +class Bot(discord.Client): + def load_config(self: discord.Client, config: dict): + self.config = config + load_commands(self.config) + + async def on_ready(self: discord.Client): + print("It's showtime!") + print(f"Logged in as {self.user}") + + async def on_message(self: discord.Client, message: discord.Message): + await handle_message(self, self.config, message) diff --git a/src/core/commands.py b/src/core/commands.py new file mode 100644 index 0000000..537772f --- /dev/null +++ b/src/core/commands.py @@ -0,0 +1,69 @@ +import discord + +# Import commands +from commands.help import Help + +def split_message(message: str, config: dict) -> list: + prefix = config.get("command_prefix") + + if prefix == None: + return [] + + if message[0] != prefix: + return [] + + return message.split(prefix, 1) + +def load_commands(config: dict): + print("Loading commands") + + commands = [ + Help + ] + + loaded_commands = {} + + for command in commands: + loaded = command() + + if loaded_commands.get(loaded.name) == None: + loaded_commands[loaded.name] = loaded + print(f"\tLoaded command '{loaded.name}'") + else: + print(f"\tA command with name '{loaded.name}' already exists - skipping") + continue + + config["loaded_commands"] = loaded_commands + +async def dispatch_command(bot: discord.Client, config: dict, message: discord.Message): + split_content = split_message(message.content.lower(), config) + if len(split_content) < 2: + print(split_content) + # no prefix + return + + command_name = split_content[1].split(" ", 1)[0] + + message_content = "" + if len(split_content) > 2: + message_content = split_content[2] + + print(f"Dispatching command '{command_name}'") + + if config.get("loaded_commands") == None: + print("No loaded commands!") + return + + found = False + + for command in config.get("loaded_commands").values(): + print(command.triggers) + if command_name in command.triggers: + print(f"Matched trigger in {command.name}") + found = True + await command.run(bot, config, message, message_content) + break + + if not found: + print(f"Could not find command with trigger {command_name}") + await message.reply(f"`{command_name}` is not a command!") diff --git a/src/core/messages.py b/src/core/messages.py new file mode 100644 index 0000000..147072b --- /dev/null +++ b/src/core/messages.py @@ -0,0 +1,18 @@ +import discord + +from core.commands import dispatch_command + +async def handle_message(bot: discord.Client, config: dict, message: discord.Message): + """ + Handle messages received from Discord + """ + if message.author == bot.user: + # Ignore messages from the bot! + return + + # Ping! + if message.content == "ping": + await message.reply("pong!") + + if message.content[0] == config.get("command_prefix"): + await dispatch_command(bot, config, message) diff --git a/src/main.py b/src/main.py new file mode 100644 index 0000000..50d72f2 --- /dev/null +++ b/src/main.py @@ -0,0 +1,35 @@ +import json + +from core.bot import Bot + +def main(): + + try: + with open("config.json", "rt") as config_file: + config = json.load(config_file) + except OSError as e: + print(f"Could not open config.json! Error: {e.strerror}") + return + except Exception as e: + print(f"Error loading config. Error: {e}") + return + + # Load token from token file if not in JSON config + if config["token"] == "": + try: + with open("token.txt", "rt") as token_file: + config["token"] = token_file.readline() + except OSError as e: + print(f"Could not open token.txt! Error: {e.strerror}") + return + + try: + bot = Bot() + bot.load_config(config) + bot.run(config["token"]) + except Exception as e: + print(f"Exception running bot! Error: {e}") + return + +if __name__ == "__main__": + main()