Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
273d92ec5e | |||
52bcb60e3a | |||
8ffa9b0175 |
53
.gitignore
vendored
53
.gitignore
vendored
@ -1,52 +1,3 @@
|
||||
# ---> Vim
|
||||
[._]*.s[a-w][a-z]
|
||||
[._]s[a-w][a-z]
|
||||
*.un~
|
||||
Session.vim
|
||||
.netrwhist
|
||||
*~
|
||||
|
||||
# ---> Emacs
|
||||
# -*- mode: gitignore; -*-
|
||||
*~
|
||||
\#*\#
|
||||
/.emacs.desktop
|
||||
/.emacs.desktop.lock
|
||||
*.elc
|
||||
auto-save-list
|
||||
tramp
|
||||
.\#*
|
||||
|
||||
# Org-mode
|
||||
.org-id-locations
|
||||
*_archive
|
||||
|
||||
# flymake-mode
|
||||
*_flymake.*
|
||||
|
||||
# eshell files
|
||||
/eshell/history
|
||||
/eshell/lastdir
|
||||
|
||||
# elpa packages
|
||||
/elpa/
|
||||
|
||||
# reftex files
|
||||
*.rel
|
||||
|
||||
# AUCTeX auto folder
|
||||
/auto/
|
||||
|
||||
# cask packages
|
||||
.cask/
|
||||
|
||||
tmp/
|
||||
__pycache__/*
|
||||
*/__pycache__/*
|
||||
.directory
|
||||
storage/*
|
||||
*.sha512
|
||||
|
||||
log
|
||||
|
||||
.idea
|
||||
*/__pycache__/*
|
||||
Pipfile.lock
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "fractale"]
|
||||
path = fractale
|
||||
url = https://moriya.zapto.org/LCI/fractale
|
2
Makefile
2
Makefile
@ -42,7 +42,7 @@ storage/libs/python: storage/libs storage/libs/get-pip.py storage/libs/dependenc
|
||||
export LD_LIBRARY_PATH=$(mainDir)/storage/libs/libgit2/libgit2-0.27.0/installed/lib;\
|
||||
export LIBGIT2=$(mainDir)/storage/libs/libgit2/libgit2-0.27.0/installed/;\
|
||||
export PYTHONPATH=$(mainDir)/storage/libs/python:${PYTHONPATH};\
|
||||
python3.6 ~/.local/bin/pip3 install -U -t storage/libs/python -r dependencies
|
||||
~/.local/bin/pip3 install -U -t storage/libs/python -r dependencies
|
||||
touch $(mainDir)/storage/libs/python
|
||||
storage:
|
||||
cd $(mainDir);\
|
||||
|
13
Pipfile
Normal file
13
Pipfile
Normal file
@ -0,0 +1,13 @@
|
||||
[[source]]
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
packaging = "*"
|
||||
discord-py = {extras = ["voice"],git = "https://github.com/Rapptz/discord.py",ref = "84c1eac62a775a37b03bd0971b221b0c50724630"}
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[requires]
|
||||
python_version = "3.7"
|
131
README.md
Normal file
131
README.md
Normal file
@ -0,0 +1,131 @@
|
||||
|
||||
Bot discord Python pour le serveur officiel d'e-penser
|
||||
=
|
||||
Ce bot est un supplément au [Bot JS](https://github.com/epenserdiscord/epenser-bot)
|
||||
|
||||
|
||||
Installer le bot
|
||||
-
|
||||
```BASH
|
||||
user : git clone https://github.com/epenserdiscord/epenser-bot-py.git
|
||||
```
|
||||
Les dépendances seront installées au moment du lancement du bot.
|
||||
excepté python (faut pas abuser non plus),
|
||||
puisqu'on utilise la version rewrite de discord.py,
|
||||
il faut python >= 3.5.3
|
||||
|
||||
Lancer le bot
|
||||
-
|
||||
```BASH
|
||||
user : export DISCORD_TOKEN=token Remplacez par le token de votre bot.
|
||||
user : cd path/to/epenser-bot-py/
|
||||
user : while true ; do git fetch --all; git reset --hard origin/master; make; done
|
||||
```
|
||||
On peut aussi lancer le bot sans la boucle :
|
||||
```BASH
|
||||
user : export DISCORD_TOKEN=token Remplacez par le token de votre bot.
|
||||
user : cd path/to/epenser-bot-py/
|
||||
user : make
|
||||
```
|
||||
Daemoniser le bot
|
||||
-
|
||||
Le bot est actuellement organisé de manière à ce que la commande `/restart` quitte le programme et ce qu'un script à part vienne le relancer après avoir, par exemple, mis à jour les sources. Par conséquent, nous avons besoin d'un tel script. Celui qui est utilisé actuellement est séparé en plusieurs parties pour gérer les deux sections (JS et Python) avec :
|
||||
|
||||
`/home/epenser/run` (marqué en executable)
|
||||
```BASH
|
||||
#!/bin/bash
|
||||
export DISCORD_TOKEN=token
|
||||
export GITHUB_TOKEN=token
|
||||
sleep 20 #C'est pour le réseau :3
|
||||
bash /home/epenser/pybot.sh &
|
||||
bash /home/epenser/jsbot.sh
|
||||
```
|
||||
On ne s'intéresse ici pas au `jsbot.sh` mais uniquement `pybot.sh` :
|
||||
|
||||
`/home/epenser/pybot.sh`
|
||||
```BASH
|
||||
#!/bin/bash
|
||||
set -o pipefail
|
||||
cd /home/epenser/epenser-bot/epenser-bot-py/
|
||||
while [ true ] ;do
|
||||
date >> log
|
||||
echo "---- git pull ----" >> log
|
||||
echo >> log
|
||||
git fetch --all 2>&1 | tee -a log
|
||||
git reset --hard origin/master 2>&1 | tee -a log
|
||||
echo >> log
|
||||
./main.py 2>&1 | tee -a log
|
||||
done
|
||||
```
|
||||
Le serveur utilisé pour hoster le bot est un raspberry sous raspbian. Il est donc sous systemd. Je ne me suis pas intéressé à Open-RC même si j'aurais pu.
|
||||
|
||||
`/etc/init.d/epenser`
|
||||
```BASH
|
||||
#!/bin/sh -e
|
||||
### BEGIN INIT INFO
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
### END INIT INFO
|
||||
DAEMON="/home/epenser/run"
|
||||
daemon_OPT=""
|
||||
DAEMONUSER="epenser"
|
||||
daemon_NAME="run"
|
||||
|
||||
PATH="/sbin:/bin:/usr/sbin:/usr/bin"
|
||||
|
||||
test -x $DAEMON || exit 0
|
||||
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
d_start () {
|
||||
log_daemon_msg "Starting system $daemon_NAME Daemon"
|
||||
start-stop-daemon --background --name $daemon_NAME --start --quiet --chuid $DAEMONUSER --exec $DAEMON -- $daemon_OPT
|
||||
log_end_msg $?
|
||||
}
|
||||
|
||||
d_stop () {
|
||||
log_daemon_msg "Stopping system $daemon_NAME Daemon"
|
||||
start-stop-daemon --name $daemon_NAME --stop --retry 5 --quiet --name $daemon_NAME
|
||||
log_end_msg $?
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
|
||||
start|stop)
|
||||
d_${1}
|
||||
;;
|
||||
|
||||
restart|reload|force-reload)
|
||||
d_stop
|
||||
d_start
|
||||
;;
|
||||
|
||||
force-stop)
|
||||
d_stop
|
||||
killall -q $daemon_NAME || true
|
||||
sleep 2
|
||||
killall -q -9 $daemon_NAME || true
|
||||
;;
|
||||
|
||||
status)
|
||||
status_of_proc "$daemon_NAME" "$DAEMON" "system-wide $daemon_NAME" && exit 0 || exit $?
|
||||
;;
|
||||
*)
|
||||
echo "Usage: /etc/init.d/$daemon_NAME {start|stop|force-stop|restart|reload|force-reload|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
```
|
||||
Pensez à remplacer les parties spécifiques.
|
||||
|
||||
(Il y a probablement des erreurs dans cette partie, j'ai jamais rien compris à systemD :( )
|
||||
|
||||
```BASH
|
||||
root : systemctl daemon-reload
|
||||
root : systemctl enable epenser.service
|
||||
root : systemctl add-wants $(systemctl get-default) epenser.service
|
||||
```
|
||||
En théorie, à partir de là, le service est installé. Si on redémarre le système, le service se lancera au démarrage.
|
||||
Seul problème : Impossible de l'arrêter.
|
||||
TODO : Modifier le script pour qu'on puisse l'arrêter.
|
BIN
assets/DejaVuSerif-Bold.ttf
Normal file
BIN
assets/DejaVuSerif-Bold.ttf
Normal file
Binary file not shown.
BIN
assets/Hack-Bold.ttf
Normal file
BIN
assets/Hack-Bold.ttf
Normal file
Binary file not shown.
BIN
assets/ba-dum-tss.mp3
Normal file
BIN
assets/ba-dum-tss.mp3
Normal file
Binary file not shown.
BIN
assets/for-the-damaged-coda.mp3
Normal file
BIN
assets/for-the-damaged-coda.mp3
Normal file
Binary file not shown.
BIN
assets/pillar-men-theme.mp3
Normal file
BIN
assets/pillar-men-theme.mp3
Normal file
Binary file not shown.
BIN
assets/roundabout-long.mp3
Normal file
BIN
assets/roundabout-long.mp3
Normal file
Binary file not shown.
BIN
assets/roundabout-short.mp3
Normal file
BIN
assets/roundabout-short.mp3
Normal file
Binary file not shown.
BIN
assets/see-you-again.mp3
Normal file
BIN
assets/see-you-again.mp3
Normal file
Binary file not shown.
1
config/config.json
Normal file
1
config/config.json
Normal file
@ -0,0 +1 @@
|
||||
{"modules": ["modules"], "prefix": "%"}
|
54
config/log_config.json
Normal file
54
config/log_config.json
Normal file
@ -0,0 +1,54 @@
|
||||
{
|
||||
"version": 1,
|
||||
"disable_existing_loggers": false,
|
||||
"formatters": {
|
||||
"simple": {
|
||||
"format": "%(asctime)s :: %(name)s :: %(levelname)s :: %(message)s"
|
||||
}
|
||||
},
|
||||
|
||||
"handlers": {
|
||||
"console": {
|
||||
"class": "logging.StreamHandler",
|
||||
"level": "DEBUG",
|
||||
"formatter": "simple",
|
||||
"stream": "ext://sys.stdout"
|
||||
},
|
||||
|
||||
"info_file_handler": {
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"level": "INFO",
|
||||
"formatter": "simple",
|
||||
"filename": "info.log",
|
||||
"maxBytes": 1048576,
|
||||
"backupCount": 20,
|
||||
"encoding": "utf8"
|
||||
},
|
||||
|
||||
"error_file_handler": {
|
||||
"class": "logging.handlers.RotatingFileHandler",
|
||||
"level": "ERROR",
|
||||
"formatter": "simple",
|
||||
"filename": "errors.log",
|
||||
"maxBytes": 1048576,
|
||||
"backupCount": 20,
|
||||
"encoding": "utf8"
|
||||
}
|
||||
},
|
||||
|
||||
"loggers": {
|
||||
"LBI": {
|
||||
"level": "INFO",
|
||||
"handlers": ["console", "info_file_handler", "error_file_handler"]
|
||||
},
|
||||
"discord": {
|
||||
"level":"WARNING",
|
||||
"handlers":["console", "info_file_handler", "error_file_handler"]
|
||||
}
|
||||
},
|
||||
|
||||
"root": {
|
||||
"level": "INFO",
|
||||
"handlers": []
|
||||
}
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
https://github.com/Rapptz/discord.py/archive/rewrite.zip#egg=discord.py
|
||||
https://github.com/Rapptz/discord.py/archive/rewrite.zip#egg=discord.py[voice]
|
||||
Pillow==5.2.0
|
||||
|
28
errors.py
Normal file
28
errors.py
Normal file
@ -0,0 +1,28 @@
|
||||
class LBIException(Exception):
|
||||
"""
|
||||
Base exception class for LBI
|
||||
|
||||
All other exceptions are subclasses
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ModuleException(LBIException):
|
||||
"""
|
||||
Base exception class for all module errors
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ModuleNotInstalled(ModuleException):
|
||||
"""
|
||||
Raised when a module is not found in module directory
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class IncompatibleModule(ModuleException):
|
||||
"""
|
||||
Raised when a module is not compatible with bot version
|
||||
"""
|
||||
pass
|
1
fractale
1
fractale
@ -1 +0,0 @@
|
||||
Subproject commit 013814928c567deb2272581f7a27c2b1e15eec2f
|
941
main.py
941
main.py
@ -1,398 +1,621 @@
|
||||
#!/usr/bin/python3
|
||||
|
||||
import importlib
|
||||
import json
|
||||
import logging
|
||||
import logging.config
|
||||
import os
|
||||
import traceback
|
||||
from typing import Dict
|
||||
|
||||
import discord
|
||||
from packaging.version import Version
|
||||
|
||||
client = discord.Client()
|
||||
prefix = ';'
|
||||
modules = {} # format : {'modulename':[module, initializedclass]}
|
||||
owners = [281166473102098433, 318866596502306816, 436105272310759426]
|
||||
from errors import IncompatibleModule
|
||||
from modules.base import BaseClass
|
||||
|
||||
__version__ = "0.1.0"
|
||||
|
||||
|
||||
async def auth(user, module_name):
|
||||
if user.id in owners:
|
||||
class Module:
|
||||
name: str
|
||||
|
||||
def __init__(self, name: str):
|
||||
"""
|
||||
Init module
|
||||
|
||||
:param name: Module name
|
||||
:type name: str
|
||||
"""
|
||||
self.name = name
|
||||
MODULES.update({self.name: self})
|
||||
|
||||
@property
|
||||
def exists(self) -> bool:
|
||||
"""
|
||||
Check if module exists
|
||||
|
||||
:return: True if module is present in modules folders
|
||||
:rtype: bool
|
||||
"""
|
||||
if not os.path.isdir(os.path.join("modules", self.name)):
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def complete(self) -> bool:
|
||||
"""
|
||||
Check if module is complete
|
||||
|
||||
:return: True if module is compatible
|
||||
:rtype: Boolean
|
||||
"""
|
||||
# Check if version.json exists
|
||||
if not os.path.exists(os.path.join("modules", self.name, "version.json")):
|
||||
return False
|
||||
with open(os.path.join("modules", self.name, "version.json")) as file:
|
||||
versions = json.load(file)
|
||||
if "version" not in versions.keys():
|
||||
return False
|
||||
if "dependencies" not in versions.keys():
|
||||
return False
|
||||
if "bot_version" not in versions.keys():
|
||||
return False
|
||||
return True
|
||||
|
||||
@property
|
||||
def version(self) -> Version:
|
||||
"""
|
||||
Returns module version
|
||||
|
||||
:return: current module version
|
||||
:rtype: Version
|
||||
"""
|
||||
with open(os.path.join("modules", self.name, "version.json")) as file:
|
||||
versions = json.load(file)
|
||||
return Version(versions["version"])
|
||||
|
||||
@property
|
||||
def bot_version(self) -> dict:
|
||||
"""
|
||||
returns the min and max version of the bot that is compatible with the module
|
||||
|
||||
:return: Min and max version for bot
|
||||
:rtype: dict
|
||||
:raises IncompatibleModule: If bot_version is not properly formated (there must be min and max keys)
|
||||
"""
|
||||
with open(os.path.join("modules", self.name, "version.json")) as file:
|
||||
versions = json.load(file)
|
||||
try:
|
||||
modules[module_name][1].authlist
|
||||
except:
|
||||
return True
|
||||
for guild in client.guilds:
|
||||
if guild.get_member(user.id):
|
||||
for roleid in modules[module_name][1].authlist:
|
||||
if roleid in [r.id for r in guild.get_member(user.id).roles]:
|
||||
return {"min": Version(versions["bot_version"]["min"]),
|
||||
"max": Version(versions["bot_version"]["max"])}
|
||||
except KeyError:
|
||||
raise IncompatibleModule(f"Module {self.name} is not compatible with bot (version.json does not "
|
||||
f"contain bot_version.max or bot_version.min item)")
|
||||
|
||||
@property
|
||||
def dependencies(self) -> dict:
|
||||
"""
|
||||
return list of dependencies version
|
||||
|
||||
:return: list of dependencies version
|
||||
:rtype: dict
|
||||
:raises IncompatibleModule: If bot_version is not properly formated (there must be min and max keys for each
|
||||
dependencies)
|
||||
"""
|
||||
with open(os.path.join("modules", self.name, "version.json")) as file:
|
||||
versions = json.load(file)
|
||||
try:
|
||||
deps = {}
|
||||
for name, dep in versions["dependencies"].items():
|
||||
dep_ver = {"min": Version(dep["min"]),
|
||||
"max": Version(dep["max"])}
|
||||
deps.update({name: dep_ver})
|
||||
return deps
|
||||
except KeyError:
|
||||
raise IncompatibleModule(f"Module {self.name} is not compatible with bot (version.json does not "
|
||||
f"contain dependencies.modulename.max or dependencies.modulename.min item)")
|
||||
|
||||
@property
|
||||
def compatible(self) -> bool:
|
||||
"""
|
||||
Check if module is compatible with current installation
|
||||
|
||||
:return: True if all dependencies are okays
|
||||
:rtype: bool
|
||||
"""
|
||||
# Check bot version
|
||||
bot_ver = Version(__version__)
|
||||
if bot_ver < self.bot_version["min"]:
|
||||
return False
|
||||
if bot_ver > self.bot_version["max"]:
|
||||
return False
|
||||
for name, dep in self.dependencies.items():
|
||||
if name not in MODULES.keys():
|
||||
Module(name)
|
||||
if MODULES[name].version < dep["min"]:
|
||||
return False
|
||||
if MODULES[name].version > dep["max"]:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_ready():
|
||||
print("Bienvenue, {0.user}, l'heure est venue de faire des fractales.".format(client))
|
||||
panic = False
|
||||
error = None
|
||||
MODULES: Dict[str, Module] = {}
|
||||
|
||||
async def panic_load():
|
||||
print("--PANIC LOAD--")
|
||||
panic = True
|
||||
modules = {}
|
||||
for filename in os.listdir('modules'):
|
||||
if filename.endswith('.py'):
|
||||
try:
|
||||
modules.update({filename[:-3:]: [importlib.import_module('modules.' + filename[:-3:])]})
|
||||
print("Module {0} chargé.".format(filename[:-3:]))
|
||||
except:
|
||||
print("[ERROR] Le module {0} n'a pas pu être chargé.".format(filename))
|
||||
# initialisation
|
||||
for module_name in list(modules.keys()):
|
||||
try:
|
||||
modules[module_name].append(modules[module_name][0].MainClass(client, modules, owners, prefix))
|
||||
print("Module {0} initialisé.".format(module_name))
|
||||
except:
|
||||
print("[ERROR] Le module {0} n'a pas pu être initialisé.".format(module_name))
|
||||
modules.pop(module_name, None)
|
||||
|
||||
if 'modules.py' in os.listdir('modules'):
|
||||
try:
|
||||
modules.update({'modules': [importlib.import_module('modules.' + 'modules')]})
|
||||
print("Module {0} chargé.".format('modules'))
|
||||
try:
|
||||
modules['modules'].append(modules['modules'][0].MainClass(client, modules, owners, prefix))
|
||||
print("Module {0} initialisé.".format('modules'))
|
||||
try:
|
||||
await modules['modules'][1].on_ready()
|
||||
except Exception as e:
|
||||
error = e
|
||||
except Exception as e:
|
||||
print("[ERROR] Le module {0} n'a pas pu être initialisé.".format('modules'))
|
||||
await panic_load()
|
||||
error = e
|
||||
except Exception as e:
|
||||
print("[ERROR] Le module {0} n'a pas pu être chargé.".format('modules.py'))
|
||||
await panic_load()
|
||||
error = e
|
||||
def setup_logging(default_path='config/log_config.json', default_level=logging.INFO, env_key='LBI_LOG_CONFIG'):
|
||||
"""Setup logging configuration
|
||||
"""
|
||||
path = default_path
|
||||
value = os.getenv(env_key, None)
|
||||
if value:
|
||||
path = value
|
||||
if os.path.exists(path):
|
||||
with open(path, 'rt') as f:
|
||||
config = json.load(f)
|
||||
logging.config.dictConfig(config)
|
||||
else:
|
||||
await panic_load()
|
||||
logging.basicConfig(level=default_level)
|
||||
|
||||
if panic:
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_ready' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_ready()
|
||||
|
||||
def modules_edit(func):
|
||||
def wrapper(self, *args, **kwargs):
|
||||
print(func.__name__, ":", self.reloading)
|
||||
if self.reloading:
|
||||
return func(self, *args, **kwargs)
|
||||
else:
|
||||
for moduleName in list(modules.keys()):
|
||||
if (not moduleName == 'modules') and 'on_ready' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_ready()
|
||||
if error:
|
||||
raise error
|
||||
self.reloading = True
|
||||
a = func(self, *args, **kwargs)
|
||||
self.reloading = False
|
||||
return a
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_error(event, *args, **kwargs):
|
||||
def event(func):
|
||||
def wrapper(self, *args, **kwargs):
|
||||
if self.reloading:
|
||||
return lambda: None
|
||||
else:
|
||||
return func(self, *args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
setup_logging()
|
||||
|
||||
log_discord = logging.getLogger('discord')
|
||||
log_LBI = logging.getLogger('LBI')
|
||||
|
||||
debug = log_LBI.debug
|
||||
info = log_LBI.info
|
||||
warning = log_LBI.warning
|
||||
error = log_LBI.error
|
||||
critical = log_LBI.critical
|
||||
|
||||
|
||||
def load_modules_info():
|
||||
for mod in os.listdir("modules"):
|
||||
Module(mod)
|
||||
|
||||
|
||||
class LBI(discord.Client):
|
||||
base_path = "storage"
|
||||
debug = log_LBI.debug
|
||||
info = log_LBI.info
|
||||
warning = log_LBI.warning
|
||||
error = log_LBI.error
|
||||
critical = log_LBI.critical
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.reloading = False
|
||||
self.id = ClientById(self)
|
||||
self.ready = False
|
||||
# Content: {"module_name": {"module": imported module, "class": initialized class}}
|
||||
self.modules = {}
|
||||
self.config = {
|
||||
"modules": ["modules"],
|
||||
"prefix": "%",
|
||||
}
|
||||
self.owners = [281166473102098433, 318866596502306816]
|
||||
self.load_config()
|
||||
self.load_modules()
|
||||
|
||||
def load_config(self, config_file="config/config.json"):
|
||||
if os.path.exists(config_file):
|
||||
with open(config_file, 'rt') as f:
|
||||
config = json.load(f)
|
||||
self.config.update(config)
|
||||
info("Config successfully loaded.")
|
||||
else:
|
||||
with open(config_file, 'w') as f:
|
||||
json.dump(self.config, f)
|
||||
info("Config successfully created.")
|
||||
|
||||
def save_config(self, config_file="config/config.json"):
|
||||
with open(config_file, "w") as f:
|
||||
json.dump(self.config, f)
|
||||
info("Config successfully saved.")
|
||||
|
||||
@modules_edit
|
||||
def load_modules(self):
|
||||
info("Starts to load modules...")
|
||||
e = {}
|
||||
for module in self.config["modules"]:
|
||||
e.update({module: self.load_module(module)})
|
||||
info("Finished to load all modules.")
|
||||
return e
|
||||
|
||||
@modules_edit
|
||||
def load_module(self, module):
|
||||
"""
|
||||
|
||||
Status codes:
|
||||
- 0: Module loaded
|
||||
- 1: Module not in modules folder
|
||||
- 2: Module incomplete
|
||||
- 3: Module incompatible
|
||||
|
||||
:param module: Module name
|
||||
:return: Status code
|
||||
"""
|
||||
|
||||
# Check module compatibility
|
||||
load_modules_info()
|
||||
if not MODULES.get(module):
|
||||
return 1
|
||||
if not MODULES[module].exists:
|
||||
return 1
|
||||
if not MODULES[module].complete:
|
||||
return 2
|
||||
if not MODULES[module].compatible:
|
||||
return 3
|
||||
deps = MODULES[module].dependencies
|
||||
for dep in deps.keys():
|
||||
if dep not in self.modules.keys():
|
||||
self.load_module(dep)
|
||||
try:
|
||||
info("Start loading module {module}...".format(module=module))
|
||||
imported = importlib.import_module('modules.' + module)
|
||||
importlib.reload(imported)
|
||||
initialized_class = imported.MainClass(self)
|
||||
self.modules.update({module: {"imported": imported, "initialized_class": initialized_class}})
|
||||
info("Module {module} successfully imported.".format(module=module))
|
||||
initialized_class.on_load()
|
||||
if module not in self.config["modules"]:
|
||||
self.config["modules"].append(module)
|
||||
self.save_config()
|
||||
except AttributeError as e:
|
||||
error("Module {module} doesn't have MainClass.".format(module=module))
|
||||
return e
|
||||
return 0
|
||||
|
||||
@modules_edit
|
||||
def unload_module(self, module):
|
||||
info("Start unload module {module}...".format(module=module))
|
||||
try:
|
||||
if module in self.config["modules"]:
|
||||
self.config["modules"].remove(module)
|
||||
self.save_config()
|
||||
self.unload_all()
|
||||
self.load_modules()
|
||||
except KeyError as e:
|
||||
error("Module {module} not loaded.").format(module=module)
|
||||
return e
|
||||
|
||||
@modules_edit
|
||||
def reload(self):
|
||||
del self.modules
|
||||
self.load_modules()
|
||||
|
||||
@modules_edit
|
||||
def unload_all(self):
|
||||
del self.modules
|
||||
self.modules = {}
|
||||
|
||||
@event
|
||||
async def on_socket_raw_receive(self, message):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_socket_raw_receive(message)
|
||||
|
||||
@event
|
||||
async def on_socket_raw_send(self, payload):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_socket_raw_send(payload)
|
||||
|
||||
@event
|
||||
async def on_typing(self, channel, user, when):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_typing(channel, user, when)
|
||||
|
||||
@event
|
||||
async def on_message(self, message):
|
||||
print(message.content)
|
||||
try:
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"]._on_message(message)
|
||||
except RuntimeError:
|
||||
info("Liste des modules changée pendant l'execution d'un on_message")
|
||||
|
||||
@event
|
||||
async def on_message_delete(self, message):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_message_delete(message)
|
||||
|
||||
@event
|
||||
async def on_raw_message_delete(self, payload):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_raw_message_delete(payload)
|
||||
|
||||
@event
|
||||
async def on_raw_bulk_message_delete(self, payload):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_raw_bulk_message_delete(payload)
|
||||
|
||||
@event
|
||||
async def on_message_edit(self, before, after):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_message_edit(before, after)
|
||||
|
||||
@event
|
||||
async def on_raw_message_edit(self, payload):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_raw_message_edit(payload)
|
||||
|
||||
@event
|
||||
async def on_reaction_add(self, reaction, user):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_reaction_add(reaction, user)
|
||||
|
||||
@event
|
||||
async def on_raw_reaction_add(self, payload):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_raw_reaction_add(payload)
|
||||
|
||||
@event
|
||||
async def on_reaction_remove(self, reaction, user):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_reaction_remove(reaction, user)
|
||||
|
||||
@event
|
||||
async def on_raw_reaction_remove(self, payload):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_raw_reaction_remove(payload)
|
||||
|
||||
@event
|
||||
async def on_reaction_clear(self, message, reactions):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_reaction_clear(message, reactions)
|
||||
|
||||
@event
|
||||
async def on_raw_reaction_clear(self, payload):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_raw_reaction_clear(payload)
|
||||
|
||||
@event
|
||||
async def on_private_channel_delete(self, channel):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_private_channel_delete(channel)
|
||||
|
||||
@event
|
||||
async def on_private_channel_create(self, channel):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_private_channel_create(channel)
|
||||
|
||||
@event
|
||||
async def on_private_channel_update(self, before, after):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_private_channel_update(before, after)
|
||||
|
||||
@event
|
||||
async def on_private_channel_pins_update(self, channel, last_pin):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_private_channel_pins_update(channel, last_pin)
|
||||
|
||||
@event
|
||||
async def on_guild_channel_delete(self, channel):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_guild_channel_delete(channel)
|
||||
|
||||
@event
|
||||
async def on_guild_channel_create(self, channel):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_guild_channel_create(channel)
|
||||
|
||||
@event
|
||||
async def on_guild_channel_update(self, before, after):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_guild_channel_update(before, after)
|
||||
|
||||
@event
|
||||
async def on_guild_channel_pins_update(self, channel, last_pin):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_guild_channel_pins_update(channel, last_pin)
|
||||
|
||||
@event
|
||||
async def on_member_join(self, member):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_member_join(member)
|
||||
|
||||
@event
|
||||
async def on_member_remove(self, member):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_member_remove(member)
|
||||
|
||||
@event
|
||||
async def on_member_update(self, before, after):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_member_update(before, after)
|
||||
|
||||
@event
|
||||
async def on_guild_join(self, guild):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_guild_join(guild)
|
||||
|
||||
@event
|
||||
async def on_guild_remove(self, guild):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_guild_remove(guild)
|
||||
|
||||
@event
|
||||
async def on_guild_update(self, before, after):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_guild_update(before, after)
|
||||
|
||||
@event
|
||||
async def on_guild_role_create(self, role):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_guild_role_create(role)
|
||||
|
||||
@event
|
||||
async def on_guild_role_delete(self, role):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_guild_role_delete(role)
|
||||
|
||||
@event
|
||||
async def on_guild_role_update(self, before, after):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_guild_role_update(before, after)
|
||||
|
||||
@event
|
||||
async def on_guild_emojis_update(self, guild, before, after):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_guild_emojis_update(guild, before, after)
|
||||
|
||||
@event
|
||||
async def on_guild_available(self, guild):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_guild_available(guild)
|
||||
|
||||
@event
|
||||
async def on_guild_unavailable(self, guild):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_guild_unavailable(guild)
|
||||
|
||||
@event
|
||||
async def on_voice_state_update(self, member, before, after):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_voice_state_update(member, before, after)
|
||||
|
||||
@event
|
||||
async def on_member_ban(self, guild, user):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_member_ban(guild, user)
|
||||
|
||||
@event
|
||||
async def on_member_unban(self, guild, user):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_member_unban(guild, user)
|
||||
|
||||
@event
|
||||
async def on_group_join(self, channel, user):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_group_join(channel, user)
|
||||
|
||||
@event
|
||||
async def on_group_remove(self, channel, user):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_group_remove(channel, user)
|
||||
|
||||
@event
|
||||
async def on_relationship_add(self, relationship):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_relationship_add(relationship)
|
||||
|
||||
@event
|
||||
async def on_relationship_remove(self, relationship):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_relationship_remove(relationship)
|
||||
|
||||
@event
|
||||
async def on_relationship_update(self, before, after):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_relationship_update(before, after)
|
||||
|
||||
@event
|
||||
async def on_connect(self):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_connect()
|
||||
|
||||
@event
|
||||
async def on_shard_ready(self):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_shard_ready()
|
||||
|
||||
@event
|
||||
async def on_resumed(self):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_resumed()
|
||||
|
||||
@event
|
||||
async def on_error(self, event_, *args, **kwargs):
|
||||
print(event_, *args, **kwargs)
|
||||
print(traceback.format_exc())
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_error' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_error(event, *args, **kwargs)
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_error(event_, *args, **kwargs)
|
||||
|
||||
@event
|
||||
async def on_guild_integrations_update(self, guild):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_guild_integrations_update(guild)
|
||||
|
||||
@client.event
|
||||
async def on_socket_raw_receive(msg):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_socket_raw_receive' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_socket_raw_receive(msg)
|
||||
@event
|
||||
async def on_webhooks_update(self, channel):
|
||||
for module in self.modules.values():
|
||||
await module["initialized_class"].on_webhooks_update(channel)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_socket_raw_send(payload):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_socket_raw_send' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_socket_raw_send(payload)
|
||||
class ClientById:
|
||||
client: LBI
|
||||
|
||||
def __init__(self, client_):
|
||||
self.client = client_
|
||||
|
||||
@client.event
|
||||
async def on_typing(channel, user, when):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_typing' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_typing(channel, user, when)
|
||||
async def get_message(self, id_, *args, **kwargs):
|
||||
"""Find a message by id
|
||||
|
||||
:param id_: Id of message to find
|
||||
:type id_: int
|
||||
|
||||
@client.event
|
||||
async def on_message(message):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_message' in modules[moduleName][1].events and message.content.startswith(modules[moduleName][1].command):
|
||||
if await auth(message.author, moduleName):
|
||||
await modules[moduleName][1].on_message(message)
|
||||
:raises discord.NotFound: This exception is raised when a message is not found (or not accessible by bot)
|
||||
|
||||
:rtype: discord.Message
|
||||
:return: discord.Message instance if message is found.
|
||||
"""
|
||||
msg = None
|
||||
for channel in self.client.get_all_channels():
|
||||
try:
|
||||
return await channel.get_message(id_, *args, **kwargs)
|
||||
except discord.NotFound:
|
||||
continue
|
||||
if msg is None:
|
||||
raise discord.NotFound(None, "Message not found")
|
||||
|
||||
@client.event
|
||||
async def on_message_delete(message):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_message_delete' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_message_delete(message)
|
||||
async def edit_message(self, id_, *args, **kwargs):
|
||||
"""Edit message by id_
|
||||
|
||||
:param id_: Id of the message to edit
|
||||
:type id_: int"""
|
||||
message = await self.get_message(id_)
|
||||
return await message.edit(**kwargs)
|
||||
|
||||
@client.event
|
||||
async def on_raw_message_delete(payload):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_raw_message_delete' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_raw_message_delete(payload)
|
||||
async def remove_reaction(self, id_message, *args, **kwargs):
|
||||
"""Remove reaction from message by id
|
||||
|
||||
:param id_message: Id of message
|
||||
:type id_message: int"""
|
||||
message = await self.get_message(id_message)
|
||||
return await message.remove_reaction(*args, **kwargs)
|
||||
|
||||
@client.event
|
||||
async def on_raw_bulk_message_delete(payload):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_raw_bulk_message_delete' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_raw_bulk_message_delete(payload)
|
||||
async def send_message(self, id_, *args, **kwargs):
|
||||
"""Send message by channel id
|
||||
|
||||
:param id_: Id of channel where to send message
|
||||
:type id_: int"""
|
||||
channel = self.client.get_channel(id_)
|
||||
return channel.send(*args, **kwargs)
|
||||
|
||||
@client.event
|
||||
async def on_message_edit(before, after):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_message_edit' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_message_edit(before, after)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_raw_message_edit(payload):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_raw_message_edit' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_raw_message_edit(payload)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_reaction_add(reaction, user):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_reaction_add' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_reaction_add(reaction, user)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_raw_reaction_add(payload):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_raw_reaction_add' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_raw_reaction_add(payload)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_reaction_remove(reaction, user):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_reaction_remove' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_reaction_remove(reaction, user)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_raw_reaction_remove(payload):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_raw_reaction_remove' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_raw_reaction_remove(payload)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_reaction_clear(message, reactions):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_reaction_clear' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_reaction_clear(message, reactions)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_raw_reaction_clear(payload):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_raw_reaction_clear' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_raw_reaction_clear(payload)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_private_channel_delete(channel):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_private_channel_delete' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_private_channel_delete(channel)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_private_channel_create(channel):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_private_channel_create' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_private_channel_create(channel)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_private_channel_update(before, after):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_private_channel_update' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_private_channel_update(before, after)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_private_channel_pins_update(channel, last_pin):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_private_channel_pins_update' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_private_channel_pins_update(channel, last_pin)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_guild_channel_delete(channel):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_guild_channel_delete' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_guild_channel_delete(channel)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_guild_channel_create(channel):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_guild_channel_create' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_guild_channel_create(channel)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_guild_channel_update(before, after):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_guild_channel_update' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_guild_channel_update(before, after)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_guild_channel_pins_update(channel, last_pin):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_guild_channel_pins_update' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_guild_channel_pins_update(channel, last_pin)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_member_join(member):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_member_join' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_member_join(member)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_member_remove(member):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_member_remove' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_member_remove(member)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_member_update(before, after):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_member_update' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_member_update(before, after)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_guild_join(guild):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_guild_join' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_guild_join(guild)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_guild_remove(guild):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_guild_remove' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_guild_remove(guild)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_guild_update(before, after):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_guild_update' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_guild_update(before, after)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_guild_role_create(role):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_guild_role_create' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_guild_role_create(role)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_guild_role_delete(role):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_guild_role_delete' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_guild_role_delete(role)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_guild_role_update(before, after):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_guild_role_update' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_guild_role_update(before, after)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_guild_emojis_update(guild, before, after):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_guild_emojis_update' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_guild_emojis_update(guild, before, after)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_guild_available(guild):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_guild_available' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_guild_available(guild)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_guild_unavailable(guild):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_guild_unavailable' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_guild_unavailable(guild)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_voice_state_update(member, before, after):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_voice_state_update' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_voice_state_update(member, before, after)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_member_ban(guild, user):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_member_ban' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_member_ban(guild, user)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_member_unban(guild, user):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_member_unban' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_member_unban(guild, user)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_group_join(channel, user):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_group_join' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_group_join(channel, user)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_group_remove(channel, user):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_group_remove' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_group_remove(channel, user)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_relationship_add(relationship):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_relationship_add' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_relationship_add(relationship)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_relationship_remove(relationship):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_relationship_remove' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_relationship_remove(relationship)
|
||||
|
||||
|
||||
@client.event
|
||||
async def on_relationship_update(before, after):
|
||||
for moduleName in list(modules.keys()):
|
||||
if 'on_relationship_update' in modules[moduleName][1].events:
|
||||
await modules[moduleName][1].on_relationship_update(before, after)
|
||||
|
||||
|
||||
client.run(os.environ['DISCORD_TOKEN'])
|
||||
client = LBI()
|
||||
client.run('NTUwMDkxOTAyMDY2ODg0NjA4.XKpsPQ.T5emitHQDrt7SxfUNgY1awzX-OY', max_messages=500000)
|
||||
|
0
modules/__init__.py
Normal file
0
modules/__init__.py
Normal file
408
modules/base/__init__.py
Normal file
408
modules/base/__init__.py
Normal file
@ -0,0 +1,408 @@
|
||||
"""Base class for module, never use directly !!!"""
|
||||
import os
|
||||
import pickle
|
||||
import zipfile
|
||||
|
||||
import discord
|
||||
|
||||
|
||||
class Storage:
|
||||
def __init__(self, base_path, client):
|
||||
self.client = client
|
||||
self.base_path = base_path
|
||||
try:
|
||||
os.makedirs(base_path)
|
||||
except FileExistsError:
|
||||
self.client.info("Le dossier {dossier} a déjà été créé.".format(dossier=self.base_path))
|
||||
|
||||
def mkdir(self, directory):
|
||||
try:
|
||||
os.makedirs(self.path(directory))
|
||||
except FileExistsError:
|
||||
self.client.info("Le dossier {dossier} a déjà été créé.".format(dossier=directory))
|
||||
|
||||
def mkzip(self, files, name):
|
||||
with zipfile.ZipFile(self.path(files), 'w', zipfile.ZIP_DEFLATED) as zip_file:
|
||||
for file in files:
|
||||
zip_file.write(self.path(file), compress_type=zipfile.ZIP_DEFLATED)
|
||||
return name
|
||||
|
||||
def open(self, filename, *args, **kwargs):
|
||||
return open(self.path(filename), *args, **kwargs)
|
||||
|
||||
def path(self, filename):
|
||||
return os.path.join(self.base_path, filename)
|
||||
|
||||
def exists(self, filename):
|
||||
return os.path.exists(self.path(filename))
|
||||
|
||||
|
||||
class BaseClass:
|
||||
"""Base class for all modules, Override it to make submodules"""
|
||||
name = ""
|
||||
help = {
|
||||
"description": "",
|
||||
"commands": {
|
||||
|
||||
}
|
||||
}
|
||||
help_active = False
|
||||
color = 0x000000
|
||||
command_text = None
|
||||
authorized_roles = []
|
||||
|
||||
def __init__(self, client):
|
||||
"""Initialize module class
|
||||
|
||||
Initialize module class, always call it to set self.client when you override it.
|
||||
|
||||
:param client: client instance
|
||||
:type client: NikolaTesla"""
|
||||
self.client = client
|
||||
if not os.path.isdir(os.path.join("storage", self.name)):
|
||||
os.makedirs(os.path.join("storage", self.name))
|
||||
self.storage = Storage(os.path.join(self.client.base_path, self.name), client)
|
||||
|
||||
async def send_help(self, channel):
|
||||
embed = discord.Embed(
|
||||
title="[{nom}] - Aide".format(nom=self.name),
|
||||
description=self.help["description"].format(prefix=self.client.config['prefix']),
|
||||
color=self.color
|
||||
)
|
||||
for command, description in self.help["commands"].items():
|
||||
embed.add_field(name=command.format(prefix=self.client.config['prefix'], command=self.command_text),
|
||||
value=description.format(prefix=self.client.config['prefix'], command=self.command_text),
|
||||
inline=False)
|
||||
await channel.send(embed=embed)
|
||||
|
||||
async def auth(self, user, role_list):
|
||||
if type(role_list) == list:
|
||||
if user.id in self.client.owners:
|
||||
return True
|
||||
for guild in self.client.guilds:
|
||||
if guild.get_member(user.id):
|
||||
for role_id in role_list:
|
||||
if role_id in [r.id for r in guild.get_member(user.id).roles]:
|
||||
return True
|
||||
elif type(role_list) == str:
|
||||
module_name = role_list
|
||||
if user.id in self.client.owners:
|
||||
return True
|
||||
authorized_roles = self.client.modules[module_name]["class"].authorized_roles
|
||||
if len(authorized_roles):
|
||||
for guild in self.client.guilds:
|
||||
if guild.get_member(user.id):
|
||||
for role_id in authorized_roles:
|
||||
if role_id in [r.id for r in guild.get_member(user.id).roles]:
|
||||
return True
|
||||
else:
|
||||
return True
|
||||
return False
|
||||
|
||||
async def parse_command(self, message):
|
||||
"""Parse a command_text from received message and execute function
|
||||
%git update
|
||||
com_update(m..)
|
||||
Parse message like `{prefix}{command_text} subcommand` and call class method `com_{subcommand}`.
|
||||
|
||||
:param message: message to parse
|
||||
:type message: discord.Message"""
|
||||
if message.content.startswith(self.client.config["prefix"] + (self.command_text if self.command_text else "")):
|
||||
|
||||
content = message.content.lstrip(
|
||||
self.client.config["prefix"] + (self.command_text if self.command_text else ""))
|
||||
sub_command, args, kwargs = self._parse_command_content(content)
|
||||
sub_command = "com_" + sub_command
|
||||
if sub_command in dir(self):
|
||||
await self.__getattribute__(sub_command)(message, args, kwargs)
|
||||
else:
|
||||
await self.command(message, [sub_command[4:]] + args, kwargs)
|
||||
|
||||
@staticmethod
|
||||
def _parse_command_content(content):
|
||||
"""Parse string
|
||||
|
||||
Parse string like `subcommand argument "argument with spaces" -o -shortwaytopassoncharacteroption --longoption
|
||||
-o "option with argument"`. You can override this function to change parsing.
|
||||
|
||||
:param content: content to parse
|
||||
:type content: str
|
||||
|
||||
:return: parsed arguments: [subcommand, [arg1, arg2, ...], [(option1, arg1), (option2, arg2), ...]]
|
||||
:rtype: list[str, list, list]"""
|
||||
if not len(content.split()):
|
||||
return "", [], []
|
||||
# Sub_command
|
||||
sub_command = content.split()[0]
|
||||
args_ = []
|
||||
kwargs = []
|
||||
if len(content.split()) > 1:
|
||||
# Take the other part of command_text
|
||||
content = content.split(" ", 1)[1].replace("\"", "\"\"")
|
||||
# Splitting around quotes
|
||||
quotes = [element.split("\" ") for element in content.split(" \"")]
|
||||
# Split all sub chains but brute chains and flat the resulting list
|
||||
args = [item.split() if item[0] != "\"" else [item, ] for sublist in quotes for item in sublist]
|
||||
# Second plating
|
||||
args = [item for sublist in args for item in sublist]
|
||||
# args_ are arguments, kwargs are options with arguments
|
||||
i = 0
|
||||
while i < len(args):
|
||||
if args[i].startswith("\""):
|
||||
args_.append(args[i][1:-1])
|
||||
elif args[i].startswith("--"):
|
||||
if i + 1 >= len(args):
|
||||
kwargs.append((args[i].lstrip("-"), None))
|
||||
break
|
||||
if args[i + 1][0] != "-":
|
||||
kwargs.append((args[i].lstrip("-"), args[i + 1].strip("\"")))
|
||||
i += 1
|
||||
else:
|
||||
kwargs.append((args[i].lstrip("-"), None))
|
||||
elif args[i].startswith("-"):
|
||||
if len(args[i]) == 2:
|
||||
if i + 1 >= len(args):
|
||||
break
|
||||
if args[i + 1][0] != "-":
|
||||
kwargs.append((args[i].lstrip("-"), args[i + 1].strip("\"")))
|
||||
i += 1
|
||||
else:
|
||||
kwargs.append((args[i].lstrip("-"), None))
|
||||
else:
|
||||
kwargs.extend([(arg, None) for arg in args[i][1:]])
|
||||
else:
|
||||
args_.append(args[i])
|
||||
i += 1
|
||||
return sub_command, args_, kwargs
|
||||
|
||||
async def _on_message(self, message):
|
||||
"""Override this function to deactivate command_text parsing"""
|
||||
await self.parse_command(message)
|
||||
await self.on_message(message)
|
||||
|
||||
async def command(self, message, args, kwargs):
|
||||
"""Override this function to handle all messages starting with `{prefix}{command_text}`
|
||||
|
||||
Function which is executed for all command_text doesn't match with a `com_{subcommand}` function"""
|
||||
pass
|
||||
|
||||
def save_object(self, object_instance, object_name):
|
||||
"""Save object into pickle file"""
|
||||
with self.storage.open(object_name, "wb") as f:
|
||||
pickler = pickle.Pickler(f)
|
||||
pickler.dump(object_instance)
|
||||
|
||||
def load_object(self, object_name):
|
||||
"""Load object from pickle file"""
|
||||
if self.save_exists(object_name):
|
||||
with self.storage.open(object_name, "rb") as f:
|
||||
unpickler = pickle.Unpickler(f)
|
||||
return unpickler.load()
|
||||
|
||||
def save_exists(self, object_name):
|
||||
"""Check if pickle file exists"""
|
||||
return self.storage.exists(object_name)
|
||||
|
||||
def on_load(self):
|
||||
"""This function is called when module is loaded"""
|
||||
pass
|
||||
|
||||
async def on_socket_raw_receive(self, message):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_socket_raw_send(self, payload):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_typing(self, channel, user, when):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_message(self, message):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_message_delete(self, message):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_raw_message_delete(self, payload):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_raw_bulk_message_delete(self, payload):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_message_edit(self, before, after):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_raw_message_edit(self, payload):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_reaction_add(self, reaction, user):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_raw_reaction_add(self, payload):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_reaction_remove(self, reaction, user):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_raw_reaction_remove(self, payload):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_reaction_clear(self, message, reactions):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_raw_reaction_clear(self, payload):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_private_channel_delete(self, channel):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_private_channel_create(self, channel):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_private_channel_update(self, before, after):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_private_channel_pins_update(self, channel, last_pin):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_guild_channel_delete(self, channel):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_guild_channel_create(self, channel):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_guild_channel_update(self, before, after):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_guild_channel_pins_update(self, channel, last_pin):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_member_join(self, member):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_member_remove(self, member):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_member_update(self, before, after):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_guild_join(self, guild):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_guild_remove(self, guild):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_guild_update(self, before, after):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_guild_role_create(self, role):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_guild_role_delete(self, role):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_guild_role_update(self, before, after):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_guild_emojis_update(self, guild, before, after):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_guild_available(self, guild):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_guild_unavailable(self, guild):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_voice_state_update(self, member, before, after):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_member_ban(self, guild, user):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_member_unban(self, guild, user):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_group_join(self, channel, user):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_group_remove(self, channel, user):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_relationship_add(self, relationship):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_relationship_remove(self, relationship):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_relationship_update(self, before, after):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_ready(self):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_connect(self):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_shard_ready(self):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_resumed(self):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_error(self, event, *args, **kwargs):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_guild_integrations_update(self, guild):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
||||
|
||||
async def on_webhooks_update(self, channel):
|
||||
"""Override this function to handle this event."""
|
||||
pass
|
10
modules/base/version.json
Normal file
10
modules/base/version.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"dependencies": {
|
||||
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
class MainClass:
|
||||
def __init__(self, client, modules, owners, prefix):
|
||||
self.client = client
|
||||
self.modules = modules
|
||||
self.owners = owners
|
||||
self.prefix = prefix
|
||||
self.events = ['on_message'] # events list
|
||||
self.command = "%sdummy" % self.prefix # command prefix (can be empty to catch every single messages)
|
||||
|
||||
self.name = "Dummy"
|
||||
self.description = "Module d'exemple"
|
||||
self.interactive = False
|
||||
self.color = 0x000000
|
||||
self.help = """\
|
||||
Aucune fonction.
|
||||
"""
|
||||
|
||||
async def on_message(self, message):
|
||||
print(message.content)
|
@ -1,112 +0,0 @@
|
||||
import asyncio
|
||||
import collections
|
||||
import os.path
|
||||
import pickle
|
||||
import random
|
||||
import traceback
|
||||
from subprocess import call
|
||||
|
||||
import discord
|
||||
|
||||
moduleFiles = "errors"
|
||||
|
||||
|
||||
class MainClass:
|
||||
def save_object(self, object_instance, object_name):
|
||||
with open("storage/%s/" % moduleFiles + object_name + "tmp", "wb") as pickleFile:
|
||||
pickler = pickle.Pickler(pickleFile)
|
||||
pickler.dump(object_instance)
|
||||
call(['mv', "storage/%s/" % moduleFiles + object_name + "tmp", "storage/%s/" % moduleFiles + object_name])
|
||||
|
||||
def load_object(self, objectname):
|
||||
if self.save_exists(objectname):
|
||||
with open("storage/%s/" % moduleFiles + objectname, "rb") as pickleFile:
|
||||
unpickler = pickle.Unpickler(pickleFile)
|
||||
return unpickler.load()
|
||||
|
||||
def save_exists(self, objectname):
|
||||
return os.path.isfile("storage/%s/" % moduleFiles + objectname)
|
||||
|
||||
def __init__(self, client, modules, owners, prefix):
|
||||
if not os.path.isdir("storage/%s" % moduleFiles):
|
||||
call(['mkdir', 'storage/%s' % moduleFiles])
|
||||
self.errorsDeque = None
|
||||
self.developpement_chan_id = [549662392120901633]
|
||||
self.memes = [
|
||||
"https://cdn.discordapp.com/avatars/436105272310759426/6e6850c03fba976f45295a76410a6699.png?size=64",
|
||||
"https://cdn.discordapp.com/avatars/281166473102098433/6e92bff42c9f409334e1580c9f666228.png?size=64",
|
||||
"https://cdn.discordapp.com/avatars/318866596502306816/8e1792132f5b9ceaca6f7aea1fd489f0.png?size=64"
|
||||
]
|
||||
self.icon = "https://cdn.discordapp.com/attachments/340620490009739265/431569015664803840/photo.png"
|
||||
self.client = client
|
||||
self.modules = modules
|
||||
self.owners = owners
|
||||
self.prefix = prefix
|
||||
self.events = ['on_error', 'on_message', 'on_ready'] # events list
|
||||
self.command = "%slicorne" % self.prefix # command prefix (can be empty to catch every single messages)
|
||||
|
||||
self.name = "Error Handling"
|
||||
self.description = "Module de gestions des erreurs"
|
||||
self.interactive = True
|
||||
self.super_users_list = [431043517217898496]
|
||||
self.color = 0xdb1348
|
||||
self.help = """\
|
||||
</prefix>licorne
|
||||
=> Crée une erreur.
|
||||
"""
|
||||
|
||||
async def on_ready(self):
|
||||
if self.save_exists('errorsDeque'):
|
||||
self.errorsDeque = self.load_object('errorsDeque')
|
||||
else:
|
||||
self.errorsDeque = collections.deque()
|
||||
for i in range(len(self.errorsDeque)):
|
||||
try:
|
||||
messagelst = self.errorsDeque.popleft()
|
||||
channel = self.client.get_channel(messagelst[0])
|
||||
delete_message = await channel.get_message(messagelst[1])
|
||||
await delete_message.delete()
|
||||
except:
|
||||
raise
|
||||
self.save_object(self.errorsDeque, 'errorsDeque')
|
||||
|
||||
async def on_message(self, message):
|
||||
5 / 0
|
||||
|
||||
async def on_error(self, event, *args, **kwargs):
|
||||
embed = discord.Embed(title="Aïe :/", description="```PYTHON\n{0}```".format(traceback.format_exc()),
|
||||
color=self.color).set_image(url=random.choice(self.memes))
|
||||
message_list = None
|
||||
try:
|
||||
message = await args[0].channel.send(
|
||||
embed=embed.set_footer(text="Ce message s'autodétruira dans une minute.", icon_url=self.icon))
|
||||
message_list = [message.channel.id, message.id]
|
||||
self.errorsDeque.append(message_list)
|
||||
except:
|
||||
try:
|
||||
message = args[1].channel.send(
|
||||
embed=embed.set_footer(text="Ce message s'autodétruira dans une minute.", icon_url=self.icon))
|
||||
message_list = [message.channel.id, message.id]
|
||||
self.errorsDeque.append(message_list)
|
||||
except:
|
||||
pass
|
||||
for chanid in self.developpement_chan_id:
|
||||
try:
|
||||
await self.client.get_channel(chanid).send(
|
||||
embed=embed.set_footer(text="Ce message ne s'autodétruira pas.", icon_url=self.icon))
|
||||
except:
|
||||
pass
|
||||
self.save_object(self.errorsDeque, 'errorsDeque')
|
||||
await asyncio.sleep(60)
|
||||
try:
|
||||
channel = self.client.get_channel(message_list[0])
|
||||
delete_message = await channel.get_message(message_list[1])
|
||||
await delete_message.delete()
|
||||
except:
|
||||
raise
|
||||
finally:
|
||||
try:
|
||||
self.errorsDeque.remove(message_list)
|
||||
except ValueError:
|
||||
pass
|
||||
self.save_object(self.errorsDeque, 'errorsDeque')
|
105
modules/errors/__init__.py
Normal file
105
modules/errors/__init__.py
Normal file
@ -0,0 +1,105 @@
|
||||
import asyncio
|
||||
import random
|
||||
import traceback
|
||||
|
||||
import collections
|
||||
import discord
|
||||
|
||||
from modules.base import BaseClass
|
||||
|
||||
|
||||
class MainClass(BaseClass):
|
||||
name = "errors"
|
||||
description = "Error handling"
|
||||
interactive = True
|
||||
super_users_list = [431043517217898496]
|
||||
color = 0xdb1348
|
||||
help = {
|
||||
"description": "Montre toutes les erreurs du bot dans discord.",
|
||||
"commands": {
|
||||
"`{prefix}{command}`": "Renvoie une erreur de test.",
|
||||
}
|
||||
}
|
||||
command_text = "unicorn"
|
||||
|
||||
def __init__(self, client):
|
||||
super().__init__(client)
|
||||
self.errorsDeque = None
|
||||
self.development_chan_id = [456142390726623243, 473637619310264330, 474267318332030987]
|
||||
self.memes = [
|
||||
"https://cdn.discordapp.com/attachments/430408983283761152/430433931272126465/Bruce_3.png",
|
||||
"https://cdn.discordapp.com/attachments/430408983283761152/430431622521946123/LUNE.jpg",
|
||||
"https://cdn.discordapp.com/attachments/326742676672086018/431570139016724480/27th3c.png",
|
||||
"https://cdn.discordapp.com/attachments/326742676672086018/431570538868244492/27th8d.png",
|
||||
"https://cdn.discordapp.com/attachments/326742676672086018/431570620942123009/lemdpc3.png",
|
||||
"https://cdn.discordapp.com/attachments/326742676672086018/431570838026846208/telecharge_15.jpg",
|
||||
"https://cdn.discordapp.com/attachments/326742676672086018/431571174078808070/telecharge_16.jpg",
|
||||
"https://cdn.discordapp.com/attachments/326742676672086018/431571655115145217/unknown.png",
|
||||
"https://cdn.discordapp.com/attachments/326742676672086018/431574206518525963/Bruce_troll_QHwYz39nj7i.png",
|
||||
"https://cdn.discordapp.com/attachments/326742676672086018/431572693910028289/telecharge_19.jpg"
|
||||
"https://cdn.discordapp.com/attachments/434475794631360512/447168326582665241/2akl04.jpg",
|
||||
"https://cdn.discordapp.com/attachments/434475794631360512/447168125067067394/20180519_004620.png",
|
||||
"https://cdn.discordapp.com/attachments/434475794631360512/446441679868788736/Sans_titre_0.png",
|
||||
"https://cdn.discordapp.com/attachments/434475794631360512/446441465611026443/unknown.png",
|
||||
"https://cdn.discordapp.com/attachments/297868535076749323/445789702373638164/image.png",
|
||||
"https://cdn.discordapp.com/attachments/297868535076749323/297875363160129540/unknown.png",
|
||||
"https://cdn.discordapp.com/attachments/326742316456869888/447887603664945163/unknown.png",
|
||||
"https://cdn.discordapp.com/attachments/434475794631360512/460876662414901249/TeslaPLS.png"
|
||||
]
|
||||
self.icon = "https://cdn.discordapp.com/attachments/340620490009739265/431569015664803840/photo.png"
|
||||
|
||||
async def on_ready(self):
|
||||
if self.save_exists('errorsDeque'):
|
||||
self.errorsDeque = self.load_object('errorsDeque')
|
||||
else:
|
||||
self.errorsDeque = collections.deque()
|
||||
for i in range(len(self.errorsDeque)):
|
||||
try:
|
||||
messagelst = self.errorsDeque.popleft()
|
||||
channel = self.client.get_channel(messagelst[0])
|
||||
delete_message = await channel.get_message(messagelst[1])
|
||||
await delete_message.delete()
|
||||
except:
|
||||
raise
|
||||
self.save_object(self.errorsDeque, 'errorsDeque')
|
||||
|
||||
async def command(self, message, args, kwargs):
|
||||
raise BaseException("Si cette erreur apparait, alors tout est normal")
|
||||
|
||||
async def on_error(self, event, *args, **kwargs):
|
||||
embed = discord.Embed(title="Aïe :/", description="```PYTHON\n{0}```".format(traceback.format_exc()),
|
||||
color=self.color).set_image(url=random.choice(self.memes))
|
||||
message_list = None
|
||||
try:
|
||||
message = await args[0].channel.send(
|
||||
embed=embed.set_footer(text="Ce message va s'autodétruire dans une minute.", icon_url=self.icon))
|
||||
message_list = [message.channel.id, message.id]
|
||||
self.errorsDeque.append(message_list)
|
||||
except:
|
||||
try:
|
||||
message = args[1].channel.send(
|
||||
embed=embed.set_footer(text="Ce message va s'autodétruire dans une minute.", icon_url=self.icon))
|
||||
message_list = [message.channel.id, message.id]
|
||||
self.errorsDeque.append(message_list)
|
||||
except:
|
||||
pass
|
||||
for chanid in self.development_chan_id:
|
||||
try:
|
||||
await self.client.get_channel(chanid).send(
|
||||
embed=embed.set_footer(text="Ce message ne s'autodétruira pas.", icon_url=self.icon))
|
||||
except:
|
||||
pass
|
||||
self.save_object(self.errorsDeque, 'errorsDeque')
|
||||
await asyncio.sleep(60)
|
||||
try:
|
||||
channel = self.client.get_channel(message_list[0])
|
||||
delete_message = await channel.get_message(message_list[1])
|
||||
await delete_message.delete()
|
||||
except:
|
||||
raise
|
||||
finally:
|
||||
try:
|
||||
self.errorsDeque.remove(message_list)
|
||||
except ValueError:
|
||||
pass
|
||||
self.save_object(self.errorsDeque, 'errorsDeque')
|
10
modules/errors/version.json
Normal file
10
modules/errors/version.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"dependencies": {
|
||||
"base":"0.1.0"
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,218 +0,0 @@
|
||||
# dummy module
|
||||
import discord
|
||||
import random
|
||||
|
||||
import fractale.source.main
|
||||
from PIL import Image
|
||||
class MainClass():
|
||||
def __init__(self, client, modules, owners, prefix):
|
||||
self.client = client
|
||||
self.modules = modules
|
||||
self.owners = owners
|
||||
self.prefix = prefix
|
||||
self.events = ['on_message'] # events list
|
||||
self.command = "%sfractale" % self.prefix # command prefix (can be empty to catch every single messages)
|
||||
|
||||
self.name = "Fractale"
|
||||
self.description = "Module de génération de fractales"
|
||||
self.interactive = True
|
||||
self.color = 0x78ffc3
|
||||
self.fractals = {
|
||||
"von_koch_curve_flake": {"Type": "Figures", "Max": ((5000, 5000), 5000, 10, 16777215, 16777215, 5000), "Min": ((0, 0), 0, 1, 0, 0, 0),
|
||||
"Default": "(2500 2500) 2000 5 #81ff65 #191919 2",
|
||||
"Indication": "(départ) rayon longueur iterations couleur_trait couleur_fond stroke",
|
||||
"ParseData": "pfixxi"},
|
||||
"von_koch_curve": {"Type": "Figures", "Max": ((5000, 5000), (5000, 5000), 10, 16777215, 16777215, 5000), "Min": ((0, 0), (0, 0), 1, 0, 0, 0),
|
||||
"Default": "(0 2500) (5000 2500) 5 #81ff65 #191919 2",
|
||||
"Indication": "(départ) (arrivée) iterations couleur_trait couleur_fond stroke", "ParseData": "ppixxi"},
|
||||
"blanc_manger": {"Type": "Figures", "Max": ((5000, 5000), (5000, 5000), 10, 16777215, 16777215, 5000), "Min": ((0, 0), (0, 0), 1, 0, 0, 0),
|
||||
"Default": "(1000 1000) (4000 4000) 7 #81ff65 #191919 2",
|
||||
"Indication": "(départ) (arrivée) iterations couleur_trait couleur_fond stroke", "ParseData": "ppixxi"},
|
||||
"dragon": {"Type": "Lsystem", "Max": ((5000, 5000), 2500, 19, 16777215, 16777215, 5000), "Min": ((0, 0), 1, 1, 0, 0, 0),
|
||||
"Default": "(2500 2500) 4 18 #81ff65 #191919 2", "Indication": "(origine) longueur iterations couleur_trait couleur_fond stroke",
|
||||
"ParseData": "pfixxi"},
|
||||
"sierpinski_triangle": {"Type": "Lsystem", "Max": ((5000, 5000), 2500, 11, 16777215, 16777215, 5000), "Min": ((0, 0), 0, 1, 0, 0, 0),
|
||||
"Default": "(0 0) 10 9 #81ff65 #191919 2", "Indication": "(origine) longueur iterations couleur_trait couleur_fond stroke",
|
||||
"ParseData": "pfixxi"},
|
||||
"fractal_plant": {"Type": "Lsystem", "Max": ((5000, 5000), 2500, 8, 16777215, 16777215, 5000), "Min": ((0, 0), 0, 1, 0, 0, 0),
|
||||
"Default": "(0 2500) 6.5 8 #81ff65 #191919 2", "Indication": "(origine) longueur iterations couleur_trait couleur_fond stroke",
|
||||
"ParseData": "pfixxi"},
|
||||
"koch_curve_right_angle": {"Type": "Lsystem", "Max": ((5000, 5000), 2500, 9, 16777215, 16777215, 5000), "Min": ((0, 0), 0, 1, 0, 0, 0),
|
||||
"Default": "(0 5000) 2.25 7 #81ff65 #191919 2",
|
||||
"Indication": "(origine) longueur iterations couleur_trait couleur_fond stroke",
|
||||
"ParseData": "pfixxi"},
|
||||
"fractal_binary_tree": {"Type": "Lsystem", "Max": ((5000, 5000), 2500, 15, 16777215, 16777215, 5000), "Min": ((0, 0), 0, 1, 0, 0, 0),
|
||||
"Default": "(0 2500) 1.2 12 #81ff65 #191919 2", "Indication": "(origine) longueur iterations couleur_trait couleur_fond stroke",
|
||||
"ParseData": "pfixxi"}
|
||||
}
|
||||
self.help = """\
|
||||
</prefix>fractale info <fractale>
|
||||
=> Affiche les informations relatives à la fractale spécifiée. (paramètres attendus, paramètres par défaut, type des arguments)
|
||||
|
||||
</prefix>fractale <fractale> <arguments>
|
||||
=> Génère une image fractale à partir des arguments fournis. Pour mettre la valeur par défaut pour un des arguments, le remplacer par le caractère *
|
||||
|
||||
</prefix>fractale <fractale>
|
||||
=> Génère une image fractale avec les paramètres par défaut.
|
||||
|
||||
-> Valeurs possible pour <fractale>
|
||||
```..: Toutes les fractales:
|
||||
%s```""" % '\n'.join(['......: %s' % t for t in self.fractals.keys()])
|
||||
|
||||
async def on_message(self, message):
|
||||
async with message.channel.typing():
|
||||
args = message.content.split(" ")
|
||||
if len(args) == 1:
|
||||
await self.modules['help'][1].send_help(message.channel, self)
|
||||
elif len(args) == 2:
|
||||
if args[1] in self.fractals.keys():
|
||||
await self.send_fractal(message, args[1]+' '+' '.join(['*']*len(self.fractals[args[1]]['ParseData'])))
|
||||
else:
|
||||
await self.modules['help'][1].send_help(message.channel, self)
|
||||
elif len(args) == 3:
|
||||
if args[1] == "info" and args[2] in self.fractals.keys():
|
||||
description = """\
|
||||
La fractale {nom} attend les arguments suivant :
|
||||
`{arguments}`
|
||||
avec le type suivant:
|
||||
`{type}`
|
||||
Les nombres décimaux sont des nombres à virgules là où les nombres entiers n'en ont pas. **(mettre un point à la place de la virgule)**
|
||||
|
||||
Attention, les coordonnées des points doivent être entre parentheses et sont considérées comme un unique argument.
|
||||
Gardez aussi en tête que **l'image a pour résolution 5000*5000.**
|
||||
|
||||
Les arguments valent par défaut :
|
||||
`{defaut}`""".format(nom=args[2], arguments=self.fractals[args[2]]['Indication'], type=' '.join(["point decimal entier hexadecimal".split(" ")["pfix".index(ch)] for ch in self.fractals[args[2]]['ParseData']]), defaut=self.fractals[args[2]]['Default'])
|
||||
await message.channel.send(
|
||||
embed=discord.Embed(title="[%s] - Infos : *%s*" % (self.name, args[2]), description=description, color=self.color))
|
||||
else:
|
||||
await self.modules['help'][1].send_help(message.channel, self)
|
||||
elif args[1] in self.fractals.keys():
|
||||
await self.send_fractal(message, ' '.join(args[1:]))
|
||||
else:
|
||||
await self.modules['help'][1].send_help(message.channel, self)
|
||||
|
||||
async def send_fractal(self, message, command):
|
||||
parsed_data=self.parse(command)
|
||||
if parsed_data["Success"]:
|
||||
res=parsed_data["Result"]
|
||||
tmpstr="/tmp/%s.png"%random.randint(1,10000000)
|
||||
im=Image.new('RGB', (5000,5000), tuple([int(i, 16) for i in map(''.join, zip(*[iter(hex(res[-2])[2:].ljust(6,'0'))]*2))]))
|
||||
fig = eval("fractale.source.main."+self.fractals[command.split(' ')[0]]["Type"]+"(im=im)")
|
||||
if self.fractals[command.split(' ')[0]]["Type"]=="Lsystem":
|
||||
fig.state.x,fig.state.y=res[0]
|
||||
#fig.state.color,fig.state.width=tuple([int(i, 16) for i in map(''.join, zip(*[iter(hex(res[-3])[2:].ljust(6,'0'))]*2))]),res[-1]
|
||||
eval("fig."+command.split(' ')[0])(*res[1:-3],color=tuple([int(i, 16) for i in map(''.join, zip(*[iter(hex(res[-3])[2:].ljust(6,'0'))]*2))]), width=res[-1])
|
||||
else:
|
||||
eval("fig."+command.split(' ')[0])(*res[:-3],color=tuple([int(i, 16) for i in map(''.join, zip(*[iter(hex(res[-3])[2:].ljust(6,'0'))]*2))]), width=res[-1])
|
||||
im.save(tmpstr)
|
||||
await message.channel.send(file=discord.File(tmpstr))
|
||||
else:
|
||||
await message.channel.send(parsed_data["Message"])
|
||||
|
||||
def parse(self, inp):
|
||||
retDic = {"Success": False, "Message": "", "Result": ()}
|
||||
# Parsing the fractal name and storing the corresponding dic into a variable
|
||||
try:
|
||||
fractal = self.fractals[inp.split(' ')[0]]
|
||||
except KeyError:
|
||||
retDic.update({"Success": False, "Message": "La fractale %s n'existe pas." % inp.split(' ')[0]})
|
||||
return (retDic)
|
||||
arg = ' '.join(inp.split(' ')[1:]) # Stuff after the fractal name
|
||||
# checking for incoherent parentheses usage
|
||||
parentheses_count = 0
|
||||
for i, char in enumerate(arg):
|
||||
if char == '(':
|
||||
parentheses_count += 1
|
||||
elif char == ')':
|
||||
parentheses_count -= 1
|
||||
if not (-1 < parentheses_count < 2):
|
||||
retDic.update({"Success": False,
|
||||
"Message": "Usage invalide de parentheses au charactère numéro %s (à partir d'après le nom de la fractale)." % i})
|
||||
return (retDic)
|
||||
|
||||
# Here, we have a coherent parentheses usage
|
||||
if ',' in arg:
|
||||
retDic.update({"Success": False,
|
||||
"Message": "Les virgules n'ont pas leur place dans les paramètres de génération. Il ne doit y avoir que des espaces uniques."})
|
||||
return (retDic)
|
||||
|
||||
# parsing the fractal
|
||||
args = arg.replace(')', '').replace('(', '').split(' ')
|
||||
parsed_args = []
|
||||
i = 0
|
||||
for parse in fractal['ParseData']:
|
||||
if parse == 'p':
|
||||
if args[i] != '*':
|
||||
try:
|
||||
int(args[i])
|
||||
int(args[i + 1])
|
||||
except:
|
||||
retDic.update({"Success": False,
|
||||
"Message": "Les valeurs ne sont pas du bon type. (nombre entiers attendus pour les coordonnées d'un point)"})
|
||||
return (retDic)
|
||||
parsed_args.append((int(args[i]), int(args[i + 1])))
|
||||
i += 2
|
||||
else:
|
||||
parsed_args.append(
|
||||
self.parse(inp.split(' ')[0] + ' ' + fractal['Default'])['Result'][len(parsed_args)])
|
||||
i += 1
|
||||
|
||||
elif parse == 'f':
|
||||
if args[i] != '*':
|
||||
try:
|
||||
float(args[i])
|
||||
except:
|
||||
retDic.update({"Success": False,
|
||||
"Message": "Les valeurs ne sont pas du bon type. (Nombre à virgule flottante attendu (mettre un point pour la virgule))"})
|
||||
return (retDic)
|
||||
parsed_args.append(float(args[i]))
|
||||
i += 1
|
||||
else:
|
||||
parsed_args.append(
|
||||
self.parse(inp.split(' ')[0] + ' ' + fractal['Default'])['Result'][len(parsed_args)])
|
||||
i += 1
|
||||
|
||||
elif parse == 'i':
|
||||
if args[i] != '*':
|
||||
try:
|
||||
int(args[i])
|
||||
except:
|
||||
retDic.update({"Success": False,
|
||||
"Message": "Les valeurs ne sont pas du bon type. (Nombre entier attendu)"})
|
||||
return (retDic)
|
||||
parsed_args.append(int(args[i]))
|
||||
i += 1
|
||||
else:
|
||||
parsed_args.append(
|
||||
self.parse(inp.split(' ')[0] + ' ' + fractal['Default'])['Result'][len(parsed_args)])
|
||||
i += 1
|
||||
|
||||
elif parse == 'x':
|
||||
if args[i] != '*':
|
||||
try:
|
||||
if '#' in args[i]:
|
||||
int(args[i].replace('#', '0x'), 16)
|
||||
else:
|
||||
raise
|
||||
except:
|
||||
retDic.update({"Success": False,
|
||||
"Message": "Les valeurs ne sont pas du bon type. (Valeur hexadécimale attendue)"})
|
||||
return (retDic)
|
||||
parsed_args.append(int(args[i].replace('#', '0x'), 16))
|
||||
i += 1
|
||||
else:
|
||||
parsed_args.append(
|
||||
self.parse(inp.split(' ')[0] + ' ' + fractal['Default'])['Result'][len(parsed_args)])
|
||||
i += 1
|
||||
for i,element in enumerate(parsed_args):
|
||||
if element>fractal['Max'][i]:
|
||||
retDic.update({"Success": False,
|
||||
"Message": "Les valeurs sont trop grandes (%s > %s)"%(str(element),str(fractal['Max'][i]))})
|
||||
return retDic
|
||||
elif element<fractal['Min'][i]:
|
||||
retDic.update({"Success": False,
|
||||
"Message": "Les valeurs sont trop petites (%s < %s)"%(str(element),str(fractal['Min'][i]))})
|
||||
return retDic
|
||||
retDic.update({"Success": True, "Result": parsed_args})
|
||||
return (retDic)
|
@ -1,37 +0,0 @@
|
||||
import os
|
||||
|
||||
|
||||
class MainClass:
|
||||
def __init__(self, client, modules, owners, prefix):
|
||||
self.client = client
|
||||
self.modules = modules
|
||||
self.owners = owners
|
||||
self.prefix = prefix
|
||||
self.events = ['on_message'] # events list
|
||||
self.command = "%sgit" % self.prefix # command prefix (can be empty to catch every single messages)
|
||||
|
||||
self.name = "Git"
|
||||
self.description = "Module de gestion de Git"
|
||||
self.interactive = True
|
||||
self.authlist = []
|
||||
self.color = 0xdc0000
|
||||
self.help = """\
|
||||
</prefix>git update
|
||||
=> Execute les commandes suivantes dans le dossier du bot:
|
||||
```BASH
|
||||
git fetch --all
|
||||
git reset --hard origin/<branch_name>```
|
||||
"""
|
||||
|
||||
async def on_message(self, message):
|
||||
args = message.content.split(' ')
|
||||
if len(args) == 2 and args[1] == 'update':
|
||||
with os.popen('git fetch --all') as std_out:
|
||||
await message.channel.send(std_out.read())
|
||||
with os.popen('git symbolic-ref HEAD 2>/dev/null') as std_out:
|
||||
branch = std_out.read().replace('refs/heads/', '')
|
||||
with os.popen('git reset --hard origin/%s' % branch) as std_out:
|
||||
await message.channel.send(std_out.read())
|
||||
await message.channel.send(message.author.mention + ", Le dépôt a été mis à jour (fetch + reset --hard).")
|
||||
else:
|
||||
await self.modules['help'][1].send_help(message.channel, self)
|
34
modules/git/__init__.py
Normal file
34
modules/git/__init__.py
Normal file
@ -0,0 +1,34 @@
|
||||
# dummy module
|
||||
|
||||
from modules.base import BaseClass
|
||||
|
||||
|
||||
class MainClass(BaseClass):
|
||||
name = "git"
|
||||
|
||||
super_users = [431043517217898496]
|
||||
|
||||
color = 0x000000
|
||||
|
||||
help_active = True
|
||||
help = {
|
||||
"description": "Module gérant les redémarages du bot",
|
||||
"commands": {
|
||||
"`{prefix}{command} update`": "Execute les commandes suivantes dans le dossier du bot:"
|
||||
"```BASH\n"
|
||||
"git fetch --all "
|
||||
"git reset --hard origin/<branch_name>```",
|
||||
}
|
||||
}
|
||||
|
||||
command_text = "git"
|
||||
|
||||
async def com_update(self, message, args, kwargs):
|
||||
# with os.popen('git fetch --all') as std_in:
|
||||
# await message.channel.send(std_in.read())
|
||||
# with os.popen('git symbolic-ref HEAD 2>/dev/null') as std_in:
|
||||
# branch = std_in.read().replace('refs/heads/', '')
|
||||
# with os.popen('git reset --hard origin/%s' % branch) as std_in:
|
||||
# await message.channel.send(std_in.read())
|
||||
# await message.channel.send(message.author.mention + ", Le dépôt a été mis à jour (fetch + reset --hard).")
|
||||
print("git update")
|
13
modules/git/version.json
Normal file
13
modules/git/version.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"base": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
import discord
|
||||
|
||||
|
||||
class MainClass:
|
||||
def auth(self, user, moduleName):
|
||||
if user.id in self.owners:
|
||||
return True
|
||||
try:
|
||||
self.modules[moduleName][1].authlist
|
||||
except:
|
||||
return True
|
||||
for guild in self.client.guilds:
|
||||
if guild.get_member(user.id):
|
||||
for role_id in self.modules[moduleName][1].authlist:
|
||||
if role_id in [r.id for r in guild.get_member(user.id).roles]:
|
||||
return True
|
||||
|
||||
def __init__(self, client, modules, owners, prefix):
|
||||
self.client = client
|
||||
self.modules = modules
|
||||
self.owners = owners
|
||||
self.prefix = prefix
|
||||
self.events = ['on_message'] # events list
|
||||
self.command = "%shelp" % self.prefix # command prefix (can be empty to catch every single messages)
|
||||
|
||||
self.name = "Aide"
|
||||
self.description = "Module d'aide"
|
||||
self.interactive = True
|
||||
self.color = 0x3c9653
|
||||
self.help = """\
|
||||
</prefix>help list
|
||||
=> Affiche une liste des modules ainsi qu'une description
|
||||
|
||||
</prefix>help <nom du module>
|
||||
=> Affiche l'aide spécifique d'un module.
|
||||
|
||||
</prefix>help all
|
||||
=> Affiche les aides de tous les modules
|
||||
"""
|
||||
|
||||
async def on_message(self, message):
|
||||
args = message.content.lower().split(' ')
|
||||
if len(args) == 2 and args[1] == 'list':
|
||||
embed = discord.Embed(title="[Aide] - Liste des modules", color=self.color)
|
||||
for moduleName in list(self.modules.keys()):
|
||||
if self.modules[moduleName][1].interactive and \
|
||||
self.auth(message.author, moduleName):
|
||||
embed.add_field(name=moduleName.capitalize(), value=self.modules[moduleName][1].description)
|
||||
await message.channel.send(embed=embed)
|
||||
elif len(args) == 2 and args[1] in list(self.modules.keys()) and \
|
||||
self.modules[args[1]][1].interactive and \
|
||||
self.auth(message.author, args[1]):
|
||||
await message.channel.send(embed=discord.Embed(title="[{0}] - Aide".format(args[1].capitalize()),
|
||||
description=self.modules[args[1]][1].help.replace(
|
||||
"</prefix>", self.prefix),
|
||||
color=self.modules[args[1]][1].color)
|
||||
)
|
||||
elif len(args) == 2 and args[1] == 'all':
|
||||
async with message.channel.typing():
|
||||
for moduleName in list(self.modules.keys()):
|
||||
if self.modules[moduleName][1].interactive and \
|
||||
self.auth(message.author, moduleName):
|
||||
await message.channel.send(
|
||||
embed=discord.Embed(title="[{0}] - Aide".format(moduleName.capitalize()),
|
||||
description=self.modules[moduleName][1].help.replace("</prefix>",
|
||||
self.prefix),
|
||||
color=self.modules[moduleName][1].color)
|
||||
)
|
||||
else:
|
||||
await self.modules['help'][1].send_help(message.channel, self)
|
||||
|
||||
async def send_help(self, channel, module):
|
||||
moduleName = None
|
||||
for name, list_module_instance in self.modules.items():
|
||||
if module == list_module_instance[1]:
|
||||
moduleName = name
|
||||
break
|
||||
await channel.send(embed=discord.Embed(title="[{0}] - Aide".format(moduleName.capitalize()),
|
||||
description=self.modules[moduleName][1].help.replace("</prefix>",
|
||||
self.prefix),
|
||||
color=self.modules[moduleName][1].color))
|
38
modules/help/__init__.py
Normal file
38
modules/help/__init__.py
Normal file
@ -0,0 +1,38 @@
|
||||
import discord
|
||||
|
||||
from modules.base import BaseClass
|
||||
|
||||
|
||||
class MainClass(BaseClass):
|
||||
name = "help"
|
||||
help_active = True
|
||||
help = {
|
||||
"description": "Module d'aide.",
|
||||
"commands": {
|
||||
"`{prefix}{command} list`": "Affiche la liste de tous les modules ainsi qu'une description.",
|
||||
"`{prefix}{command} <module>`": "Renvoie l'aide spécifique à un module.",
|
||||
"`{prefix}{command} all`": "Affiche l'aide de tous les modules."
|
||||
}
|
||||
}
|
||||
color = 0x3c9653
|
||||
command_text = "help"
|
||||
|
||||
async def com_list(self, message, args, kwargs):
|
||||
embed = discord.Embed(title=_("[Aide] - Liste des modules"), color=self.color)
|
||||
for moduleName in list(self.client.modules.keys()):
|
||||
if self.client.modules[moduleName]["initialized_class"].help_active and \
|
||||
self.auth(message.author, moduleName):
|
||||
embed.add_field(
|
||||
name=moduleName.capitalize(),
|
||||
value=self.client.modules[moduleName]["initialized_class"].help["description"])
|
||||
await message.channel.send(embed=embed)
|
||||
|
||||
async def com_all(self, message, args, kwargs):
|
||||
for name, module in self.client.modules.items():
|
||||
await module["initialized_class"].send_help(message.channel)
|
||||
|
||||
async def command(self, message, args, kwargs):
|
||||
if len(args) and args[0] in self.client.modules.keys():
|
||||
await self.client.modules[args[0]]["initialized_class"].send_help(message.channel)
|
||||
else:
|
||||
await self.send_help(message.channel)
|
10
modules/help/version.json
Normal file
10
modules/help/version.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"dependencies": {
|
||||
"base":"==0.1.0"
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,183 +0,0 @@
|
||||
import importlib
|
||||
import os
|
||||
import random
|
||||
import shutil
|
||||
import time
|
||||
|
||||
import discord
|
||||
|
||||
temp_dir = "temp_load"
|
||||
moduleFiles = "modules"
|
||||
|
||||
|
||||
class MainClass:
|
||||
def __init__(self, client, modules, owners, prefix):
|
||||
if os.path.exists("storage/" + moduleFiles + "/" + temp_dir):
|
||||
shutil.rmtree("storage/" + moduleFiles + "/" + temp_dir)
|
||||
if not os.path.exists("storage/" + moduleFiles):
|
||||
os.mkdir("storage/" + moduleFiles)
|
||||
os.mkdir("storage/" + moduleFiles + "/" + temp_dir)
|
||||
self.states = {}
|
||||
for moduleName in os.listdir('modules'):
|
||||
if moduleName.endswith(".py"):
|
||||
self.states.update({moduleName[:-3:]: 'not loaded'})
|
||||
for moduleName in list(modules.keys()):
|
||||
if len(modules[moduleName]) == 2:
|
||||
self.states.update({moduleName: 'initialized'})
|
||||
self.client = client
|
||||
self.modules = modules
|
||||
self.owners = owners
|
||||
self.prefix = prefix
|
||||
self.events = ['on_message', 'on_ready'] # events list
|
||||
self.command = "%smodule" % self.prefix # command prefix (can be empty to catch every single messages)
|
||||
|
||||
self.name = "Modules"
|
||||
self.description = "Module de gestion des modules"
|
||||
self.interactive = True
|
||||
self.authlist = [549662392120901633]
|
||||
self.color = 0x8f3196
|
||||
self.help = """\
|
||||
</prefix>modules list
|
||||
=> Liste les modules ainsi que leurs états
|
||||
|
||||
</prefix>modules enable <module/modules>
|
||||
=> Charge et active le / les module(s) spécifié(s)
|
||||
|
||||
</prefix>modules disable <module/modules>
|
||||
=> Désactive et décharge le / les module(s) spécifié(s)
|
||||
|
||||
</prefix>modules reload <module/modules>
|
||||
=> Désactive, décharge, puis recharge et réactive le / les module(s) spécifié(s)
|
||||
|
||||
=> <module/modules>
|
||||
==> Unique module ou liste de module séparés par des virgules
|
||||
"""
|
||||
self.states.update({'modules': 'initialized'})
|
||||
|
||||
async def on_message(self, message):
|
||||
error = None
|
||||
args = message.content.split(" ")
|
||||
if len(args) == 2 and args[1] == 'list':
|
||||
await message.channel.send(embed=discord.Embed(title="[Modules] - Modules list",
|
||||
description="```PYTHON\n{0}```".format(
|
||||
str(self.states).replace(',', '\n,'))
|
||||
)
|
||||
)
|
||||
elif len(args) == 3 and args[1] in ['enable', 'disable', 'reload']:
|
||||
if args[1] == 'enable':
|
||||
for moduleName in args[2].split(','):
|
||||
if moduleName + '.py' in os.listdir('modules'):
|
||||
try:
|
||||
self.enable_module(moduleName)
|
||||
await message.channel.send(
|
||||
message.author.mention + ", le module {0} a été activé".format(moduleName))
|
||||
except Exception as e:
|
||||
error = e
|
||||
await message.channel.send(
|
||||
message.author.mention + ", le module {0} **n'a pas pu être activé**".format(
|
||||
moduleName))
|
||||
else:
|
||||
await message.channel.send(
|
||||
message.author.mention + ", le module {0} n'existe pas.".format(moduleName))
|
||||
elif args[1] == 'disable':
|
||||
for moduleName in args[2].split(','):
|
||||
if moduleName == 'modules':
|
||||
await message.channel.send(
|
||||
message.author.mention + ", le module {0} ne peut pas être désactivé car il est nécéssaire "
|
||||
"pour gérer les modules.".format(moduleName)
|
||||
)
|
||||
else:
|
||||
if moduleName + '.py' in os.listdir('modules'):
|
||||
self.unload_module(moduleName)
|
||||
await message.channel.send(
|
||||
message.author.mention + ", le module {0} a été désactivé.".format(moduleName))
|
||||
else:
|
||||
await message.channel.send(
|
||||
message.author.mention + ", le module {0} n'existe pas.".format(moduleName))
|
||||
elif args[1] == 'reload':
|
||||
for moduleName in args[2].split(','):
|
||||
if moduleName == 'modules':
|
||||
await message.channel.send(
|
||||
message.author.mention + ", le module {0} ne peut pas être rechargé car il est nécéssaire "
|
||||
"pour gérer les modules.".format(moduleName)
|
||||
)
|
||||
else:
|
||||
if moduleName in self.modules.keys():
|
||||
self.unload_module(moduleName)
|
||||
await message.channel.send(
|
||||
message.author.mention + ", le module {0} a été désactivé.".format(moduleName))
|
||||
else:
|
||||
await message.channel.send(
|
||||
message.author.mention + ", le module {0} n'est pas chargé.".format(moduleName))
|
||||
if moduleName + '.py' in os.listdir('modules'):
|
||||
try:
|
||||
self.enable_module(moduleName)
|
||||
await message.channel.send(
|
||||
message.author.mention + ", le module {0} a été activé".format(moduleName))
|
||||
except Exception as e:
|
||||
error = e
|
||||
await message.channel.send(
|
||||
message.author.mention + ", le module {0} **n'a pas pu être activé**".format(
|
||||
moduleName)
|
||||
)
|
||||
else:
|
||||
await message.channel.send(
|
||||
message.author.mention + ", le module {0} n'existe pas.".format(moduleName)
|
||||
)
|
||||
|
||||
else:
|
||||
await self.modules['help'][1].send_help(message.channel, self)
|
||||
if error:
|
||||
raise error
|
||||
|
||||
async def on_ready(self):
|
||||
error = None
|
||||
for fileName in os.listdir('modules'):
|
||||
try:
|
||||
self.load_module(fileName[:-3:])
|
||||
self.init_module(fileName[:-3:])
|
||||
except Exception as e:
|
||||
error = e
|
||||
if error:
|
||||
raise error
|
||||
|
||||
def enable_module(self, moduleName):
|
||||
self.load_module(moduleName)
|
||||
self.init_module(moduleName)
|
||||
|
||||
def load_module(self, moduleName):
|
||||
if moduleName + ".py" in os.listdir('modules'):
|
||||
if moduleName not in list(self.states.keys()) or self.states[moduleName] == 'not loaded':
|
||||
try:
|
||||
new_file_name = str(
|
||||
"storage/" +
|
||||
moduleFiles + "/" +
|
||||
temp_dir + "/" + moduleName + "-%s.py") % random.randint(1, 100000000000000000000000000000000)
|
||||
shutil.copy2("modules/%s.py" % moduleName, new_file_name)
|
||||
time.sleep(0.1)
|
||||
self.modules.update({moduleName: [importlib.import_module(new_file_name.replace('/', '.')[:-3:])]})
|
||||
print("Module {0} chargé.".format(moduleName))
|
||||
self.states[moduleName] = 'loaded'
|
||||
except:
|
||||
print("[ERROR] Le module {0} n'a pas pu être chargé.".format(moduleName))
|
||||
self.unload_module(moduleName)
|
||||
raise
|
||||
|
||||
def init_module(self, moduleName):
|
||||
if moduleName + ".py" in os.listdir('modules'):
|
||||
if self.states[moduleName] == 'loaded':
|
||||
try:
|
||||
self.modules[moduleName].append(
|
||||
self.modules[moduleName][0].MainClass(self.client, self.modules, self.owners, self.prefix))
|
||||
print("Module {0} initialisé.".format(moduleName))
|
||||
self.states[moduleName] = 'initialized'
|
||||
except:
|
||||
print("[ERROR] Le module {0} n'a pas pu être initialisé.".format(moduleName))
|
||||
self.unload_module(moduleName)
|
||||
raise
|
||||
|
||||
def unload_module(self, moduleName):
|
||||
if moduleName + ".py" in os.listdir('modules'):
|
||||
self.states[moduleName] = 'not loaded'
|
||||
self.modules.pop(moduleName, None)
|
||||
print("Module {0} déchargé.".format(moduleName))
|
94
modules/modules/__init__.py
Normal file
94
modules/modules/__init__.py
Normal file
@ -0,0 +1,94 @@
|
||||
import os
|
||||
|
||||
import discord
|
||||
|
||||
from modules.base import BaseClass
|
||||
|
||||
|
||||
# todo: tout réécrire
|
||||
|
||||
class MainClass(BaseClass):
|
||||
name = "modules"
|
||||
|
||||
command_text = "modules"
|
||||
color = 0x000000
|
||||
help = {
|
||||
"description": "Manage bot modules.",
|
||||
"commands": {
|
||||
"`{prefix}{command} list`": "List of available modules.",
|
||||
"`{prefix}{command} enable <module>`": "Enable module `<module>`.",
|
||||
"`{prefix}{command} disable <module>`": "Disable module `<module>`.",
|
||||
"`{prefix}{command} web_list`": "List all available modules from repository",
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, client):
|
||||
super().__init__(client)
|
||||
self.storage.mkdir("modules")
|
||||
|
||||
def get_all_modules(self):
|
||||
all_items = os.listdir("modules")
|
||||
modules = []
|
||||
for item in all_items:
|
||||
if item not in ["__init__.py", "base.py", "__pycache__", "dummy"]:
|
||||
if os.path.isfile(os.path.join("modules", item)):
|
||||
modules.append(item[:-3])
|
||||
else:
|
||||
modules.append(item)
|
||||
return set(modules)
|
||||
|
||||
async def com_enable(self, message, args, kwargs):
|
||||
args = args[1:]
|
||||
if len(args) == 0:
|
||||
await message.channel.send("You must specify at least one module.")
|
||||
return
|
||||
if len(args) == 1 and args[0] == "*":
|
||||
for module in self.get_all_modules():
|
||||
e = self.client.load_module(module)
|
||||
if e:
|
||||
await message.channel.send("An error occurred during the loading of the module {module}."
|
||||
.format(module=module))
|
||||
await self.com_list(message, args, kwargs)
|
||||
return
|
||||
for arg in args:
|
||||
e = self.client.load_module(arg)
|
||||
if e:
|
||||
await message.channel.send("An error occurred during the loading of the module {module}."
|
||||
.format(module=arg))
|
||||
await self.com_list(message, args, kwargs)
|
||||
|
||||
async def com_disable(self, message, args, kwargs):
|
||||
args = args[1:]
|
||||
if len(args) == 0:
|
||||
await message.channel.send("You must specify at least one module.")
|
||||
return
|
||||
if len(args) == 1 and args[0] == "*":
|
||||
for module in self.get_all_modules():
|
||||
e = self.client.unload_module(module)
|
||||
if e:
|
||||
await message.channel.send("An error occurred during the loading of the module {module}."
|
||||
.format(module=module))
|
||||
await self.com_list(message, args, kwargs)
|
||||
return
|
||||
for arg in args:
|
||||
print(arg)
|
||||
e = self.client.unload_module(arg)
|
||||
if e:
|
||||
await message.channel.send("An error occurred during the loading of the module {module}."
|
||||
.format(module=arg))
|
||||
await self.com_list(message, [], [])
|
||||
|
||||
async def com_list(self, message, args, kwargs):
|
||||
list_files = self.get_all_modules()
|
||||
activated = set(self.client.config["modules"])
|
||||
activated_string = "\n+ " + "\n+ ".join(activated)
|
||||
deactivated_string = "- " + "\n- ".join(list_files.difference(activated))
|
||||
embed = discord.Embed(title="[Modules] - Liste des modules",
|
||||
description="```diff\n{activated}\n{deactivated}```".format(
|
||||
activated=activated_string,
|
||||
deactivated=deactivated_string)
|
||||
)
|
||||
await message.channel.send(embed=embed)
|
||||
|
||||
async def com_web_list(self, message, args, kwargs):
|
||||
pass
|
13
modules/modules/version.json
Normal file
13
modules/modules/version.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"base": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
class MainClass:
|
||||
def __init__(self, client, modules, owners, prefix):
|
||||
self.client = client
|
||||
self.modules = modules
|
||||
self.owners = owners
|
||||
self.prefix = prefix
|
||||
self.events = ['on_message'] # events list
|
||||
self.command = "%srestart" % self.prefix # command prefix (can be empty to catch every single messages)
|
||||
|
||||
self.name = "Restart"
|
||||
self.description = "Module gérant les redémarrages du bot"
|
||||
self.interactive = True
|
||||
self.authlist = []
|
||||
self.color = 0x000000
|
||||
self.help = """\
|
||||
</prefix>restart
|
||||
"""
|
||||
|
||||
async def on_message(self, message):
|
||||
args = message.content.split(" ")
|
||||
if args[0] == '%srestart' % self.prefix:
|
||||
if 'py' in args:
|
||||
await message.channel.send(message.author.mention + ", Le bot va redémarrer...")
|
||||
await self.client.logout()
|
||||
else:
|
||||
await self.modules['help'][1].send_help(message.channel, self)
|
26
modules/restart/__init__.py
Normal file
26
modules/restart/__init__.py
Normal file
@ -0,0 +1,26 @@
|
||||
from .base import BaseClass
|
||||
|
||||
|
||||
class MainClass(BaseClass):
|
||||
name = "restart"
|
||||
|
||||
super_users = [431043517217898496]
|
||||
|
||||
color = 0x000000
|
||||
|
||||
help_active = True
|
||||
help = {
|
||||
"description": "Gestion des redémarrages du bot",
|
||||
"commands": {
|
||||
"`{prefix}{command} py`": "Redémarre le bot.",
|
||||
}
|
||||
}
|
||||
|
||||
command_text = "restart"
|
||||
|
||||
async def command(self, message, args, kwargs):
|
||||
await message.channel.send("{mention}, vous devez utiliser `{prefix}restart py` pour redémarer."
|
||||
.format(prefix=self.client.config["prefix"], mention=message.author.mention))
|
||||
|
||||
async def com_py(self, message, args, kwargs):
|
||||
await self.client.logout()
|
10
modules/restart/version.json
Normal file
10
modules/restart/version.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"version":"0.1.0",
|
||||
"dependencies": {
|
||||
"base":"==0.1.0"
|
||||
},
|
||||
"bot_version": {
|
||||
"min": "0.1.0",
|
||||
"max": "0.1.0"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user