Compare commits

...

3 Commits

39 changed files with 1598 additions and 1106 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__/*
.idea
*/__pycache__/*
.directory
storage/*
*.sha512
log
.idea
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 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
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 @@
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
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 +0,0 @@
Subproject commit 013814928c567deb2272581f7a27c2b1e15eec2f

969
main.py
View File

@ -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
try:
modules[module_name][1].authlist
except:
@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
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
async def on_ready():
print("Bienvenue, {0.user}, l'heure est venue de faire des fractales.".format(client))
panic = False
error = None
: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"])
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)
@property
def bot_version(self) -> dict:
"""
returns the min and max version of the bot that is compatible with the module
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:
modules.update({'modules': [importlib.import_module('modules.' + 'modules')]})
print("Module {0} chargé.".format('modules'))
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
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:
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
else:
await panic_load()
return await channel.get_message(id_, *args, **kwargs)
except discord.NotFound:
continue
if msg is None:
raise discord.NotFound(None, "Message not found")
if panic:
for moduleName in list(modules.keys()):
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
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_error(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)
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_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)
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_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.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)
@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'])
client = LBI()
client.run('NTUwMDkxOTAyMDY2ODg0NjA4.XKpsPQ.T5emitHQDrt7SxfUNgY1awzX-OY', max_messages=500000)

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