import discord, os, sys, toml, time, datetime, logging, threading, asyncio, readline, importlib from discord.ext import commands with open("config.toml", "r") as f: config = toml.load(f) try: os.remove("main.log") except: pass formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') handler = logging.FileHandler('main.log') handler.setFormatter(formatter) logger = logging.getLogger('') logger.addHandler(handler) logger.setLevel(logging.INFO) #logging.basicConfig(filename='main.log', encoding='utf-8', level=logging.DEBUG) #logging.getLogger().addHandler(logging.StreamHandler()) def log(level, log): if level == "inf": logger.info(log) elif level == "wrn": logger.warning(log) elif level == "dbg": logger.debug(log) elif level == "err": logger.error(log) log("inf", "Logging utility set up.") try: token = os.environ.get('MEETOO_TOKEN') except: log("err", 'Error no token is present type: export MEETOO_TOKEN="[Your token here]" into your terminall NOW!') if token == "": log("err", 'Error no token is present type: export MEETOO_TOKEN="[Your token here]" into your terminall NOW!') log("dbg", f"Token is: {token}") start_time = time.time() bot = commands.Bot(command_prefix='!', intents=discord.Intents.all()) logger.debug("Loaded the client.") #Api used mainly for plugins so getting uptime, loading, unloading, reloading plugins, getting status of plugins, get plugin info, stopping the bot, getting bot name and tag, getting cpm/commands per minute, get if there is web plugin, if there is web plugin register api, get if plugin supports web and so on.. class API: #Returns config.tomls content in a array. def get_config(self): return self.config #Dumps all the toml data from self.config and stores it inside toml.config def save_config(self): try: with open("config.toml", w) as f: self.log("inf", "Saving the configuration file...") toml.dump(self.config, f) self.log("inf", "Config was saved to config.toml") #aok return 0 #In case file doesnt exist except Exception as e: self.log("err", f"Failed to save to config.toml. {e}") return 1 #Connect the main .log file to the api so plugins can log to latest.log w/o risking corrupting the file. Absolutly useless as you can do: self.bot.api.logger("inf", "Hello w/o some useless function") def log(self, status, log): self.logger(str(status), str(log)) #Get the uptime of the root of the bot def uptime(self): uptime = time.time() - self.start_time return uptime def __init__(self, config, bot, log, token): self.start_time = time.time() self.config = config self.bot = bot self.logger = log self.token = token self.guild_ids = config["bot"]["guild_ids"] #This is the most basic interface CLI wich is basically a comand line that appears when you launch the app it will have commands like stop, help, commands, cogs, load, unload and so on.. class CLI: def register_command(self, command, function, description): self.api.logger("inf", f"Trying to register {command} into the CLI commands...") if command in self.cli_cmds: self.api.logger("wrn", f"The command {command} already exists.") return 1 if not callable(function): self.api.logger("err", f"Unable to register {command} as the passed function is not callable.") return 1 try: command = str(command) description = str(description) except: self.api.logger("err", f"Passed cli name or description is not convertable to string.") return 1 dictionary = {"description": description, "function": function} try: self.cli_cmds[command] = dictionary except: return 1 else: self.api.logger("inf", f"CLI Command: {command} registered.") return 0 def remove_command(self, command): self.api.logger("inf", f"Trying to remove {command} from the CLI commands...") if command in self.cli_cmds: self.cli_cmds[command] = None self.api.logger("inf", f"Command {command} removed.") return 0 else: self.api.logger("wrn", f"Command {command} could not be removed as it doesnt exist.") return 1 def handle_input(self, user_input): # Split the user input into command and arguments parts = user_input.strip().split() if not parts: return command_name = parts[0] args = parts[1:] # Find the command command = self.cli_cmds.get(command_name) if not command: # Suggest commands based on input suggestions = [cmd_name for cmd_name in self.cli_cmds if cmd_name.startswith(command_name)] if suggestions: print(f"Unknown command '{command_name}'. Did you mean one of these?") for suggestion in suggestions: print(f" - {suggestion}") else: print(f"Unknown command '{command_name}'") return # Execute the command function = command["function"] try: function(self, *args) except TypeError as e: print(f"Invalid arguments: {e}") def run(self): # Start the CLI loop while True: try: user_input = input(self.prompt) except KeyboardInterrupt: # Handle CTRL+C break except EOFError: # Handle CTRL+D break else: self.handle_input(user_input) #Init of the class where all the values inside self are defined. Takes a loaded api as a arg def __init__(self, api): self.api = api self.cli_cmds = {} self.prompt = "mee2> " @bot.event async def on_ready(): log("inf", f"We have logged in as {bot.user}") @bot.slash_command(guild_ids=config["bot"]["guild_ids"]) async def stop(ctx): await ctx.defer() if ctx.author.guild_permissions.administrator: print("Terminating the bot...") try: await ctx.respond("https://media4.giphy.com/media/CC5MVO9Jx4RqMQRfvT/giphy.gif") except: pass sys.exit("Terminated.") else: await ctx.respond("https://media.tenor.com/Iv6oKRuAhVEAAAAC/hal9000-im-sorry-dave.gif") if __name__ == "__main__": api = API(config, bot, log, token) cli = CLI(api) bot.api = api #CLI commands: #help def cli_help(self, page=1): page_size = 10 start_index = (page - 1) * page_size end_index = start_index + page_size commands = list(self.cli_cmds.keys())[start_index:end_index] if not commands: print("No commands found for this page.") return 1 msg = f"Commands (page {page}):\n" for command in commands: msg += f" {command}: {self.cli_cmds[command]['description']}\n" print(msg) return 0 cli.register_command("help", cli_help, "Shows this page.") #uptime def cli_uptime(self): uptime = self.api.uptime() uptime = str(datetime.timedelta(seconds=uptime)).split(".")[0] print(f"The uptime of the bot is {uptime} HH:MM:SS.") cli.register_command("uptime", cli_uptime, "Shows the uptime of the bot in seconds.") #info def cli_info(self): print(f"You are running version 0.1.0 - Dev. Created by supopur under the GNUv3 license. Git repo: https://git.nazev.eu:8443/supopur/mee2 Our discord link: https://discord.gg/dVVVSM4z") cli.register_command("info", cli_info, "Shows the info about the bot.") #shutdown def cli_sd(self): print("Shutting down the bot. You still must do CTRL+C to exit this terminal!!") try: sys.exit("Terminated from the cli..") except: pass cli.register_command("stop", cli_sd, "Terminates the bot.") def cli_clear(self): os.system('cls' if os.name == 'nt' else 'clear') cli.register_command("clear", cli_clear, "Clears the terminal output") #load def cli_load(self, cog): try: cog = str(cog) except: print("Unable to convert the cog name to string.") return 1 print(f"Trying to load {cog}...") try: cog_module = importlib.import_module(cog) class_name = cog.split(".")[1] print(class_name) cog_class = getattr(cog_module,class_name) cog_instance = cog_class(self.api.bot) self.api.bot.add_cog(cog_instance) except Exception as e: print(f"ERR: Failed to load {cog} becouse: {e}") else: print(f"Loaded {cog} successfully") cli.register_command("load", cli_load, "Loads a cog takes a cog name as a arg example: load cogs.example") #unload def cli_unload(self, cog): try: cog = str(cog) except: print("Unable to convert the cog name to string.") return 1 print(f"Trying to loadd {cog}...") try: cog = cog.split(".")[1] self.api.bot.unload_extension(cog) except Exception as e: print(f"ERR Unable to unload {cog} due to: {e}") else: print(f"Loaded {cog} successfully") cli.register_command("unload", cli_unload, "Unloads a cog takes a cog name as a arg example: unload cogs.example") #start Starts the bot itself. def cli_start(self): print("Starting the bot...") try: self.api.bot.run(self.api.token) except Exception as e: print(e) return 1 else: print("Bot started.") return 0 #cli.register_command("start", cli_start, "Starts the bot.") for x in config["bot"]["cogs"]: log("inf", f"Loading {x}...") try: bot.load_extension(x) except Exception as e: log("wrn", f"Extension {x} failed to load due to: {e}") else: log("inf", f"{x} Loaded.") log("inf", "Loading all cogs completed.") # Create a thread to run the CLI cli_thread = threading.Thread(target=cli.run) cli_thread.start() #uncomment this to enable autostartup bot.run(token)