Compare commits
No commits in common. 'main' and 'testing' have entirely different histories.
@ -1,19 +0,0 @@
|
|||||||
# Ignore Python cache files
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*.pyd
|
|
||||||
|
|
||||||
# Ignore other non-needed Python files
|
|
||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
*.egg-info/
|
|
||||||
dist/
|
|
||||||
|
|
||||||
# Ignore the meetoo async file and build directory
|
|
||||||
meetoo.async
|
|
||||||
build/
|
|
||||||
|
|
||||||
# Ignore the cogs directory
|
|
||||||
cogs/
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
|||||||
# Mee2
|
|
||||||
|
|
||||||
Mee2 is a fully customizable, open-source Discord bot with a web interface to control it. It supports plugins and is heavily customizable. Mee2 is forever free and licensed under GNU V3.
|
|
||||||
|
|
||||||
## Status
|
|
||||||
|
|
||||||
Mee2 is still actively maintained, but it's not yet ready for public use. Pull requests are welcome. You can find the latest version on our Gitea repo: https://git.nazev.eu:8443/
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- Web interface to control the bot
|
|
||||||
- Plugins support
|
|
||||||
- Highly customizable
|
|
||||||
- Fully open-source
|
|
||||||
- Forever free
|
|
||||||
|
|
||||||
## Plugins
|
|
||||||
|
|
||||||
The official plugins can be downloaded at [our gitea repo](https://git.nazev.eu:8443/MEE2_Official_plugins).
|
|
||||||
|
|
||||||
The installation might be diffrent on every plugin so be sure to check README.md of the plugin. But in general here are the install steps of a normal plugin:
|
|
||||||
|
|
||||||
To install a plugin download the git repo and extract the zip. There should be a folder with the name of the plugin. Drag that folder over to {meetooroot}/cogs and open {meetooroot}/config.toml and under section [bot] you should see cogs=[] add the following to the [] brackets"cogs.{pluginname}.{pluginname}".
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
We welcome contributions to Mee2! To contribute, please clone the repository and make your changes. Then, open a pull request on our Gitea repo.
|
|
||||||
|
|
||||||
## Discord
|
|
||||||
|
|
||||||
Join our Discord server to get support, ask questions, or contribute to Mee2 development: https://discord.gg/dVVVSM4z
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
Mee2 is licensed under the GNU V3 license. See LICENSE.md for details.
|
|
@ -1,3 +1,21 @@
|
|||||||
|
#The config is auto generated in the web interface do not change unless you know what you are doing.
|
||||||
|
|
||||||
|
|
||||||
|
[webserver]
|
||||||
|
#set to false if you dont have a certificate
|
||||||
|
usessl = true
|
||||||
|
port = "5000"
|
||||||
|
#DO NOT USE IN PRODUCTION. Do not even use flask it is recomended to use a production ready wsgi server such as gunicorn
|
||||||
|
debug = true
|
||||||
|
#Leave it like this if you dont know what your doing.
|
||||||
|
ip = "0.0.0.0"
|
||||||
|
crt = "certs/certificate.crt"
|
||||||
|
key = "certs/private.key"
|
||||||
|
|
||||||
[bot]
|
[bot]
|
||||||
cogs=["cogs.Essentials.commands"]
|
|
||||||
guild_ids=["823188238260633600"]
|
|
||||||
|
[commands]
|
||||||
|
hello=false
|
||||||
|
stop=false
|
||||||
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
token="MTAwNDgwODA4OTgzNDM3MzIzMA.GeCLNB.JmmGESI2peVqyaaPFlFJUEVG-6SI7LGh3x-Ysc"
|
@ -1,346 +1,65 @@
|
|||||||
import discord, os, sys, toml, time, datetime, logging, threading, asyncio, readline, importlib
|
import discord, flask, os, quart, sys, toml, time, datetime
|
||||||
|
from quart import render_template
|
||||||
|
from quart.helpers import make_response
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
|
|
||||||
with open("config.toml", "r") as f:
|
with open("config.toml", "r") as f:
|
||||||
config = toml.load(f)
|
config = toml.load(f)
|
||||||
|
|
||||||
try:
|
with open("creds.toml", "r") as f:
|
||||||
os.remove("main.log")
|
creds = toml.load(f)
|
||||||
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()
|
start_time = time.time()
|
||||||
|
|
||||||
|
print(creds)
|
||||||
|
intents = discord.Intents.default()
|
||||||
|
intents.members = True
|
||||||
|
|
||||||
|
bot = commands.Bot(command_prefix='!', intents=intents)
|
||||||
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
|
@bot.event
|
||||||
async def on_ready():
|
async def on_ready():
|
||||||
log("inf", f"We have logged in as {bot.user}")
|
print(f"We have logged in as {bot.user}")
|
||||||
|
|
||||||
|
|
||||||
|
@bot.slash_command(guild_ids=["823188238260633600"])
|
||||||
|
async def hello(ctx):
|
||||||
|
await ctx.respond("Hello!")
|
||||||
|
|
||||||
@bot.slash_command(guild_ids=config["bot"]["guild_ids"])
|
@bot.slash_command(guild_ids=["823188238260633600"])
|
||||||
async def stop(ctx):
|
async def stop(ctx):
|
||||||
await ctx.defer()
|
|
||||||
if ctx.author.guild_permissions.administrator:
|
if ctx.author.guild_permissions.administrator:
|
||||||
print("Terminating the bot...")
|
print("Terminating the bot...")
|
||||||
try:
|
|
||||||
await ctx.respond("https://media4.giphy.com/media/CC5MVO9Jx4RqMQRfvT/giphy.gif")
|
await ctx.respond("https://media4.giphy.com/media/CC5MVO9Jx4RqMQRfvT/giphy.gif")
|
||||||
except:
|
|
||||||
pass
|
|
||||||
sys.exit("Terminated.")
|
sys.exit("Terminated.")
|
||||||
else:
|
else:
|
||||||
await ctx.respond("https://media.tenor.com/Iv6oKRuAhVEAAAAC/hal9000-im-sorry-dave.gif")
|
await ctx.respond("https://media.tenor.com/Iv6oKRuAhVEAAAAC/hal9000-im-sorry-dave.gif")
|
||||||
|
#webserver
|
||||||
|
app = quart.Quart(__name__)
|
||||||
|
#The index.
|
||||||
|
@app.route('/', methods=['GET'])
|
||||||
|
async def index():
|
||||||
|
return await render_template('index.html')
|
||||||
|
|
||||||
|
@app.route('/configuration', methods=['GET'])
|
||||||
|
async def configuration():
|
||||||
|
return await render_template('configuration.html')
|
||||||
|
|
||||||
|
@app.route('/dashboard', methods=['GET'])
|
||||||
|
async def dashboard():
|
||||||
|
return await render_template('dashboard.html')
|
||||||
|
|
||||||
|
@app.route('/plugins', methods=['GET'])
|
||||||
|
async def plugins():
|
||||||
|
return await render_template('plugins.html')
|
||||||
|
|
||||||
|
@app.route('/stop', methods=['GET'])
|
||||||
|
async def stop():
|
||||||
|
sys.exit("Web terminated.")
|
||||||
|
|
||||||
|
|
||||||
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.")
|
bot.loop.create_task(app.run_task('0.0.0.0', 5000))
|
||||||
|
|
||||||
#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(creds["token"])
|
||||||
bot.run(token)
|
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #2c3e50;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 25;
|
||||||
|
height: 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #3e4982;
|
||||||
|
color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
header h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td,
|
||||||
|
#status th {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:nth-child(even) {
|
||||||
|
background-color: #1c567d;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:hover {
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
text-align: left;
|
||||||
|
background-color: #04AA6D;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
background-color: #34495e;
|
||||||
|
width: 200px;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav li a {
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 10px 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav li a:hover {
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 20px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
@ -0,0 +1,88 @@
|
|||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #2c3e50;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 25;
|
||||||
|
height: 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #3e4982;
|
||||||
|
color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
header h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td,
|
||||||
|
#status th {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:nth-child(even) {
|
||||||
|
background-color: #1c567d;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:hover {
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
text-align: left;
|
||||||
|
background-color: #04AA6D;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
background-color: #34495e;
|
||||||
|
width: 200px;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav li a {
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 10px 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav li a:hover {
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 20px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
@ -0,0 +1,61 @@
|
|||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #2c3e50;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.selected {
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
.logo {
|
||||||
|
width: 25;
|
||||||
|
height: 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #3e4982;
|
||||||
|
color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
header h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
background-color: #34495e;
|
||||||
|
width: 200px;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav li a {
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 10px 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav li a:hover {
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 20px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
|||||||
|
const form = document.querySelector("form");
|
||||||
|
const tokenInput = form.querySelector("#token");
|
||||||
|
const nameInput = form.querySelector("#name");
|
||||||
|
const statusSelect = form.querySelector("#status");
|
||||||
|
|
||||||
|
// set the token input as password field
|
||||||
|
tokenInput.setAttribute("type", "password");
|
||||||
|
|
||||||
|
// function to get the config values from the server
|
||||||
|
const getConfigValues = async () => {
|
||||||
|
try {
|
||||||
|
const tokenResponse = await fetch('/api/token');
|
||||||
|
const tokenValue = await tokenResponse.text();
|
||||||
|
tokenInput.value = tokenValue;
|
||||||
|
|
||||||
|
const nameResponse = await fetch('/api/name');
|
||||||
|
const nameValue = await nameResponse.text();
|
||||||
|
nameInput.value = nameValue;
|
||||||
|
|
||||||
|
const statusResponse = await fetch('/api/status');
|
||||||
|
const statusValue = await statusResponse.text();
|
||||||
|
statusSelect.value = statusValue;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getConfigValues();
|
||||||
|
|
||||||
|
form.addEventListener("submit", async (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
const token = tokenInput.value;
|
||||||
|
const name = nameInput.value;
|
||||||
|
const status = statusSelect.value;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// send POST request to /api/token endpoint with token as the body
|
||||||
|
if (token !== '') {
|
||||||
|
await fetch('/api/token', {
|
||||||
|
method: 'POST',
|
||||||
|
body: token
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// send POST request to /api/name endpoint with name as the body
|
||||||
|
if (name !== '') {
|
||||||
|
await fetch('/api/name', {
|
||||||
|
method: 'POST',
|
||||||
|
body: name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// send POST request to /api/status endpoint with status as the body
|
||||||
|
if (status !== '') {
|
||||||
|
await fetch('/api/status', {
|
||||||
|
method: 'POST',
|
||||||
|
body: status
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
});
|
@ -0,0 +1,88 @@
|
|||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #2c3e50;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 25;
|
||||||
|
height: 25;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
background-color: #3e4982;
|
||||||
|
color: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
header h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td,
|
||||||
|
#status th {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:nth-child(even) {
|
||||||
|
background-color: #1c567d;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr:hover {
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
text-align: left;
|
||||||
|
background-color: #04AA6D;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
background-color: #34495e;
|
||||||
|
width: 200px;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav li a {
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 10px 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav li a:hover {
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected {
|
||||||
|
background-color: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 20px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>MEE2 Configuration</title>
|
||||||
|
<link href="{{ url_for('static', filename='configuration.css') }}" rel="stylesheet" type="text/css" />
|
||||||
|
<script src="{{ url_for('static', filename='js/configuration.js') }}"></script>
|
||||||
|
<link rel="icon" type="image/x-icon" href="https://cdn.discordapp.com/app-icons/1004808089834373230/e30029e50ec03472f1fe236a43d38684.png?size=512">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>MEE2 Configuration</h1>
|
||||||
|
</header>
|
||||||
|
<div class="container">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Home</a></li>
|
||||||
|
<li><a href="/dashboard">Dashboard</a></li>
|
||||||
|
<li><a href="#" class="selected">Configuration</a></li>
|
||||||
|
<li><a href="/logs">Logs</a></li>
|
||||||
|
<li><a href="/support">Support</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main>
|
||||||
|
<form>
|
||||||
|
<label for="token">Token:</label>
|
||||||
|
<input type="password" id="token" name="token">
|
||||||
|
<br>
|
||||||
|
<label for="name">Bot Name:</label>
|
||||||
|
<input type="text" id="name" name="name">
|
||||||
|
<br>
|
||||||
|
<label for="status">Status:</label>
|
||||||
|
<select id="status" name="status">
|
||||||
|
<option value="online">Online</option>
|
||||||
|
<option value="idle">Idle</option>
|
||||||
|
<option value="dnd">Do Not Disturb</option>
|
||||||
|
<option value="invisible">Invisible</option>
|
||||||
|
</select>
|
||||||
|
<br>
|
||||||
|
<input type="submit" value="Save Changes">
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,54 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>MEE2 Dashboard</title>
|
||||||
|
<link href="{{ url_for('static', filename='dashboard.css') }}" rel="stylesheet" type="text/css" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="https://cdn.discordapp.com/app-icons/1004808089834373230/e30029e50ec03472f1fe236a43d38684.png?size=512">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>MEE2 - Admin Dashboard</h1>
|
||||||
|
</header>
|
||||||
|
<div class="container">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Home</a></li>
|
||||||
|
<li><a href="#" class="selected">Dashboard</a></li>
|
||||||
|
<li><a href="/plugins">Plugins</a></li>
|
||||||
|
<li><a href="/configuration">Configuration</a></li>
|
||||||
|
<li><a href="/logs">Logs</a></li>
|
||||||
|
<li><a href="/support">Support</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main>
|
||||||
|
<p>This is the admin dashboard. Here you can see important thing like the uptime, hits per minute, cogs status etc..</p>
|
||||||
|
<table id="status">
|
||||||
|
<tr>
|
||||||
|
<th>Module</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Module link</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Uptime</td>
|
||||||
|
<td>1 Year</td>
|
||||||
|
<td><a href="/api/uptime">/api/uptime</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Hits Per Minute</td>
|
||||||
|
<td>10</td>
|
||||||
|
<td><a href="/api/hpm">/api/hpm</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Plugin - Fivem</td>
|
||||||
|
<td>Online</td>
|
||||||
|
<td><a href="/plugins/fivem">/plugins/fivem</a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,34 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>MEE2 Index</title>
|
||||||
|
<link rel="icon" type="image/x-icon" href="https://cdn.discordapp.com/app-icons/1004808089834373230/e30029e50ec03472f1fe236a43d38684.png?size=512">
|
||||||
|
<link href="{{ url_for('static', filename='index.css') }}" rel="stylesheet" type="text/css" />
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>MEE2</h1>
|
||||||
|
</header>
|
||||||
|
<div class="container">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li class="selected"><a href="#">Home</a></li>
|
||||||
|
<li><a href="/dashboard">Dashboard</a></li>
|
||||||
|
<li><a href="/plugins">Plugins</a></li>
|
||||||
|
<li><a href="/configuration">Configuration</a></li>
|
||||||
|
<li><a href="/logs">Logs</a></li>
|
||||||
|
<li><a href="/support">Support</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main>
|
||||||
|
<h2>Welcome to the MEE2 index page.</h2>
|
||||||
|
<p>This bot is designed to help you with various tasks and make your life easier</p>
|
||||||
|
<p>Here you can find information about the bot's features, plugins, configuration, and support. All this info is public. If you want you can turn it off in the settings page.</p>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,54 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<title>MEE2 Plugins</title>
|
||||||
|
<link href="{{ url_for('static', filename='plugins.css') }}" rel="stylesheet" type="text/css" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="https://cdn.discordapp.com/app-icons/1004808089834373230/e30029e50ec03472f1fe236a43d38684.png?size=512">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<h1>MEE2 - Plugins</h1>
|
||||||
|
</header>
|
||||||
|
<div class="container">
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/">Home</a></li>
|
||||||
|
<li><a href="/dashboard">Dashboard</a></li>
|
||||||
|
<li><a href="#" class="selected">Plugins</a></li>
|
||||||
|
<li><a href="/configuration">Configuration</a></li>
|
||||||
|
<li><a href="/logs">Logs</a></li>
|
||||||
|
<li><a href="/support">Support</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<main>
|
||||||
|
<p>This is the plugin page here you can disable/enable the individual cogs.</p>
|
||||||
|
<table id="status">
|
||||||
|
<tr>
|
||||||
|
<th>Plugin</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Plugin link</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Uptime</td>
|
||||||
|
<td>1 Year</td>
|
||||||
|
<td><a href="/api/uptime">/api/uptime</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Hits Per Minute</td>
|
||||||
|
<td>10</td>
|
||||||
|
<td><a href="/api/hpm">/api/hpm</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Plugin - Fivem</td>
|
||||||
|
<td>Online</td>
|
||||||
|
<td><a href="/plugins/fivem">/plugins/fivem</a></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
@ -0,0 +1,16 @@
|
|||||||
|
import requests
|
||||||
|
|
||||||
|
def get_player_description(user_id):
|
||||||
|
url = f"https://users.roblox.com/v1/users/{user_id}"
|
||||||
|
response = requests.get(url)
|
||||||
|
if response.status_code == 200:
|
||||||
|
player_data = response.json()
|
||||||
|
return player_data["description"]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
user_id = 156719681 # Replace with the desired user ID
|
||||||
|
description = get_player_description(user_id)
|
||||||
|
|
||||||
|
print(f"Player description: \n{description}")
|
||||||
|
|
Loading…
Reference in New Issue