Compare commits

..

3 Commits

40 changed files with 1598 additions and 1107 deletions

53
.gitignore vendored
View File

@ -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 .idea
*/__pycache__/*
Pipfile.lock

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "fractale"]
path = fractale
url = https://moriya.zapto.org/LCI/fractale

View File

@ -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 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 LIBGIT2=$(mainDir)/storage/libs/libgit2/libgit2-0.27.0/installed/;\
export PYTHONPATH=$(mainDir)/storage/libs/python:${PYTHONPATH};\ 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 touch $(mainDir)/storage/libs/python
storage: storage:
cd $(mainDir);\ cd $(mainDir);\

13
Pipfile Normal file
View 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
View 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

Binary file not shown.

BIN
assets/Hack-Bold.ttf Normal file

Binary file not shown.

BIN
assets/ba-dum-tss.mp3 Normal file

Binary file not shown.

Binary file not shown.

BIN
assets/pillar-men-theme.mp3 Normal file

Binary file not shown.

BIN
assets/roundabout-long.mp3 Normal file

Binary file not shown.

BIN
assets/roundabout-short.mp3 Normal file

Binary file not shown.

BIN
assets/see-you-again.mp3 Normal file

Binary file not shown.

1
config/config.json Normal file
View File

@ -0,0 +1 @@
{"modules": ["modules"], "prefix": "%"}

54
config/log_config.json Normal file
View 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": []
}
}

View File

@ -1,2 +1,2 @@
discord.py https://github.com/Rapptz/discord.py/archive/rewrite.zip#egg=discord.py[voice]
Pillow==5.2.0 Pillow==5.2.0

28
errors.py Normal file
View 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
f
View File

@ -1 +0,0 @@
f0

@ -1 +0,0 @@
Subproject commit 013814928c567deb2272581f7a27c2b1e15eec2f

969
main.py
View File

@ -1,398 +1,621 @@
#!/usr/bin/python3 #!/usr/bin/python3
import importlib import importlib
import json
import logging
import logging.config
import os import os
import traceback import traceback
from typing import Dict
import discord import discord
from packaging.version import Version
client = discord.Client() from errors import IncompatibleModule
prefix = ';' from modules.base import BaseClass
modules = {} # format : {'modulename':[module, initializedclass]}
owners = [281166473102098433, 318866596502306816, 436105272310759426] __version__ = "0.1.0"
async def auth(user, module_name): class Module:
if user.id in owners: 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 return True
try:
modules[module_name][1].authlist @property
except: 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 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 True
@property
def version(self) -> Version:
"""
Returns module version
@client.event :return: current module version
async def on_ready(): :rtype: Version
print("Bienvenue, {0.user}, l'heure est venue de faire des fractales.".format(client)) """
panic = False with open(os.path.join("modules", self.name, "version.json")) as file:
error = None versions = json.load(file)
return Version(versions["version"])
async def panic_load(): @property
print("--PANIC LOAD--") def bot_version(self) -> dict:
panic = True """
modules = {} returns the min and max version of the bot that is compatible with the module
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'): :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: try:
modules.update({'modules': [importlib.import_module('modules.' + 'modules')]}) return {"min": Version(versions["bot_version"]["min"]),
print("Module {0} chargé.".format('modules')) "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
MODULES: Dict[str, Module] = {}
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:
logging.basicConfig(level=default_level)
def modules_edit(func):
def wrapper(self, *args, **kwargs):
print(func.__name__, ":", self.reloading)
if self.reloading:
return func(self, *args, **kwargs)
else:
self.reloading = True
a = func(self, *args, **kwargs)
self.reloading = False
return a
return wrapper
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 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)
@event
async def on_webhooks_update(self, channel):
for module in self.modules.values():
await module["initialized_class"].on_webhooks_update(channel)
class ClientById:
client: LBI
def __init__(self, client_):
self.client = client_
async def get_message(self, id_, *args, **kwargs):
"""Find a message by id
:param id_: Id of message to find
:type id_: int
: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: try:
modules['modules'].append(modules['modules'][0].MainClass(client, modules, owners, prefix)) return await channel.get_message(id_, *args, **kwargs)
print("Module {0} initialisé.".format('modules')) except discord.NotFound:
try: continue
await modules['modules'][1].on_ready() if msg is None:
except Exception as e: raise discord.NotFound(None, "Message not found")
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
else:
await panic_load()
if panic: async def edit_message(self, id_, *args, **kwargs):
for moduleName in list(modules.keys()): """Edit message by id_
if 'on_ready' in modules[moduleName][1].events:
await modules[moduleName][1].on_ready()
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
: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 remove_reaction(self, id_message, *args, **kwargs):
async def on_error(event, *args, **kwargs): """Remove reaction from message by id
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)
: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 send_message(self, id_, *args, **kwargs):
async def on_socket_raw_receive(msg): """Send message by channel id
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)
: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_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)
client = LBI()
@client.event client.run('NTUwMDkxOTAyMDY2ODg0NjA4.XKpsPQ.T5emitHQDrt7SxfUNgY1awzX-OY', max_messages=500000)
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)
@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)
@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)
@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)
@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)
@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'])

0
modules/__init__.py Normal file
View File

408
modules/base/__init__.py Normal file
View 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
View File

@ -0,0 +1,10 @@
{
"version":"0.1.0",
"dependencies": {
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -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)

View File

@ -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
View 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')

View File

@ -0,0 +1,10 @@
{
"version":"0.1.0",
"dependencies": {
"base":"0.1.0"
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -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 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)

View File

@ -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
View 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
View 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"
}
}

View File

@ -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
View 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
View File

@ -0,0 +1,10 @@
{
"version":"0.1.0",
"dependencies": {
"base":"==0.1.0"
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}

View File

@ -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))

View 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

View 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"
}
}

View File

@ -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)

View 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()

View File

@ -0,0 +1,10 @@
{
"version":"0.1.0",
"dependencies": {
"base":"==0.1.0"
},
"bot_version": {
"min": "0.1.0",
"max": "0.1.0"
}
}