Compare commits
3 Commits
master
...
webControl
Author | SHA1 | Date | |
---|---|---|---|
bdcaf6bda5 | |||
5f5192b704 | |||
13709a2059 |
6
.gitignore
vendored
6
.gitignore
vendored
@ -110,8 +110,4 @@ venv.bak/
|
||||
foBot_config/*
|
||||
|
||||
# Pi file (too big)
|
||||
modules/pi/pi.txt
|
||||
|
||||
*.log*
|
||||
|
||||
*.lock
|
||||
modules/pi/pi.txt
|
1
Pipfile
1
Pipfile
@ -4,7 +4,6 @@ verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
"fs.dropboxfs" = {git = "https://github.com/rkhwaja/fs.dropboxfs.git"}
|
||||
"discord.py" = {ref = "rewrite", git = "https://github.com/Rapptz/discord.py"}
|
||||
mysql-connector-python = "*"
|
||||
pymysql = "*"
|
||||
|
228
Pipfile.lock
generated
Normal file
228
Pipfile.lock
generated
Normal file
@ -0,0 +1,228 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "12ccc168a0520cd43d8a2ca05e3cbe21dd5262fd5a48e4eab57d368f713cb0c7"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.7"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"asn1crypto": {
|
||||
"hashes": [
|
||||
"sha256:2f1adbb7546ed199e3c90ef23ec95c5cf3585bac7d11fb7eb562a3fe89c64e87",
|
||||
"sha256:9d5c20441baf0cb60a4ac34cc447c6c189024b6b4c6cd7877034f4965c464e49"
|
||||
],
|
||||
"version": "==0.24.0"
|
||||
},
|
||||
"bcrypt": {
|
||||
"hashes": [
|
||||
"sha256:01477981abf74e306e8ee31629a940a5e9138de000c6b0898f7f850461c4a0a5",
|
||||
"sha256:054d6e0acaea429e6da3613fcd12d05ee29a531794d96f6ab959f29a39f33391",
|
||||
"sha256:0872eeecdf9a429c1420158500eedb323a132bc5bf3339475151c52414729e70",
|
||||
"sha256:09a3b8c258b815eadb611bad04ca15ec77d86aa9ce56070e1af0d5932f17642a",
|
||||
"sha256:0f317e4ffbdd15c3c0f8ab5fbd86aa9aabc7bea18b5cc5951b456fe39e9f738c",
|
||||
"sha256:2788c32673a2ad0062bea850ab73cffc0dba874db10d7a3682b6f2f280553f20",
|
||||
"sha256:321d4d48be25b8d77594d8324c0585c80ae91ac214f62db9098734e5e7fb280f",
|
||||
"sha256:346d6f84ff0b493dbc90c6b77136df83e81f903f0b95525ee80e5e6d5e4eef84",
|
||||
"sha256:34dd60b90b0f6de94a89e71fcd19913a30e83091c8468d0923a93a0cccbfbbff",
|
||||
"sha256:3b4c23300c4eded8895442c003ae9b14328ae69309ac5867e7530de8bdd7875d",
|
||||
"sha256:43d1960e7db14042319c46925892d5fa99b08ff21d57482e6f5328a1aca03588",
|
||||
"sha256:49e96267cd9be55a349fd74f9852eb9ae2c427cd7f6455d0f1765d7332292832",
|
||||
"sha256:63e06ffdaf4054a89757a3a1ab07f1b922daf911743114a54f7c561b9e1baa58",
|
||||
"sha256:67ed1a374c9155ec0840214ce804616de49c3df9c5bc66740687c1c9b1cd9e8d",
|
||||
"sha256:6b662a5669186439f4f583636c8d6ea77cf92f7cfe6aae8d22edf16c36840574",
|
||||
"sha256:6efd9ca20aefbaf2e7e6817a2c6ed4a50ff6900fafdea1bcb1d0e9471743b144",
|
||||
"sha256:8569844a5d8e1fdde4d7712a05ab2e6061343ac34af6e7e3d7935b2bd1907bfd",
|
||||
"sha256:8629ea6a8a59f865add1d6a87464c3c676e60101b8d16ef404d0a031424a8491",
|
||||
"sha256:988cac675e25133d01a78f2286189c1f01974470817a33eaf4cfee573cfb72a5",
|
||||
"sha256:9a6fedda73aba1568962f7543a1f586051c54febbc74e87769bad6a4b8587c39",
|
||||
"sha256:9eced8962ce3b7124fe20fd358cf8c7470706437fa064b9874f849ad4c5866fc",
|
||||
"sha256:a005ed6163490988711ff732386b08effcbf8df62ae93dd1e5bda0714fad8afb",
|
||||
"sha256:ae35dbcb6b011af6c840893b32399252d81ff57d52c13e12422e16b5fea1d0fb",
|
||||
"sha256:b1e8491c6740f21b37cca77bc64677696a3fb9f32360794d57fa8477b7329eda",
|
||||
"sha256:c906bdb482162e9ef48eea9f8c0d967acceb5c84f2d25574c7d2a58d04861df1",
|
||||
"sha256:cb18ffdc861dbb244f14be32c47ab69604d0aca415bee53485fcea4f8e93d5ef",
|
||||
"sha256:cc2f24dc1c6c88c56248e93f28d439ee4018338567b0bbb490ea26a381a29b1e",
|
||||
"sha256:d860c7fff18d49e20339fc6dffc2d485635e36d4b2cccf58f45db815b64100b4",
|
||||
"sha256:d86da365dda59010ba0d1ac45aa78390f56bf7f992e65f70b3b081d5e5257b09",
|
||||
"sha256:e22f0997622e1ceec834fd25947dc2ee2962c2133ea693d61805bc867abaf7ea",
|
||||
"sha256:f2fe545d27a619a552396533cddf70d83cecd880a611cdfdbb87ca6aec52f66b",
|
||||
"sha256:f425e925485b3be48051f913dbe17e08e8c48588fdf44a26b8b14067041c0da6",
|
||||
"sha256:f7fd3ed3745fe6e81e28dc3b3d76cce31525a91f32a387e1febd6b982caf8cdb",
|
||||
"sha256:f9210820ee4818d84658ed7df16a7f30c9fba7d8b139959950acef91745cc0f7"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.1.4"
|
||||
},
|
||||
"cffi": {
|
||||
"hashes": [
|
||||
"sha256:151b7eefd035c56b2b2e1eb9963c90c6302dc15fbd8c1c0a83a163ff2c7d7743",
|
||||
"sha256:1553d1e99f035ace1c0544050622b7bc963374a00c467edafac50ad7bd276aef",
|
||||
"sha256:1b0493c091a1898f1136e3f4f991a784437fac3673780ff9de3bcf46c80b6b50",
|
||||
"sha256:2ba8a45822b7aee805ab49abfe7eec16b90587f7f26df20c71dd89e45a97076f",
|
||||
"sha256:3bb6bd7266598f318063e584378b8e27c67de998a43362e8fce664c54ee52d30",
|
||||
"sha256:3c85641778460581c42924384f5e68076d724ceac0f267d66c757f7535069c93",
|
||||
"sha256:3eb6434197633b7748cea30bf0ba9f66727cdce45117a712b29a443943733257",
|
||||
"sha256:495c5c2d43bf6cebe0178eb3e88f9c4aa48d8934aa6e3cddb865c058da76756b",
|
||||
"sha256:4c91af6e967c2015729d3e69c2e51d92f9898c330d6a851bf8f121236f3defd3",
|
||||
"sha256:57b2533356cb2d8fac1555815929f7f5f14d68ac77b085d2326b571310f34f6e",
|
||||
"sha256:770f3782b31f50b68627e22f91cb182c48c47c02eb405fd689472aa7b7aa16dc",
|
||||
"sha256:79f9b6f7c46ae1f8ded75f68cf8ad50e5729ed4d590c74840471fc2823457d04",
|
||||
"sha256:7a33145e04d44ce95bcd71e522b478d282ad0eafaf34fe1ec5bbd73e662f22b6",
|
||||
"sha256:857959354ae3a6fa3da6651b966d13b0a8bed6bbc87a0de7b38a549db1d2a359",
|
||||
"sha256:87f37fe5130574ff76c17cab61e7d2538a16f843bb7bca8ebbc4b12de3078596",
|
||||
"sha256:95d5251e4b5ca00061f9d9f3d6fe537247e145a8524ae9fd30a2f8fbce993b5b",
|
||||
"sha256:9d1d3e63a4afdc29bd76ce6aa9d58c771cd1599fbba8cf5057e7860b203710dd",
|
||||
"sha256:a36c5c154f9d42ec176e6e620cb0dd275744aa1d804786a71ac37dc3661a5e95",
|
||||
"sha256:a6a5cb8809091ec9ac03edde9304b3ad82ad4466333432b16d78ef40e0cce0d5",
|
||||
"sha256:ae5e35a2c189d397b91034642cb0eab0e346f776ec2eb44a49a459e6615d6e2e",
|
||||
"sha256:b0f7d4a3df8f06cf49f9f121bead236e328074de6449866515cea4907bbc63d6",
|
||||
"sha256:b75110fb114fa366b29a027d0c9be3709579602ae111ff61674d28c93606acca",
|
||||
"sha256:ba5e697569f84b13640c9e193170e89c13c6244c24400fc57e88724ef610cd31",
|
||||
"sha256:be2a9b390f77fd7676d80bc3cdc4f8edb940d8c198ed2d8c0be1319018c778e1",
|
||||
"sha256:ca1bd81f40adc59011f58159e4aa6445fc585a32bb8ac9badf7a2c1aa23822f2",
|
||||
"sha256:d5d8555d9bfc3f02385c1c37e9f998e2011f0db4f90e250e5bc0c0a85a813085",
|
||||
"sha256:e55e22ac0a30023426564b1059b035973ec82186ddddbac867078435801c7801",
|
||||
"sha256:e90f17980e6ab0f3c2f3730e56d1fe9bcba1891eeea58966e89d352492cc74f4",
|
||||
"sha256:ecbb7b01409e9b782df5ded849c178a0aa7c906cf8c5a67368047daab282b184",
|
||||
"sha256:ed01918d545a38998bfa5902c7c00e0fee90e957ce036a4000a88e3fe2264917",
|
||||
"sha256:edabd457cd23a02965166026fd9bfd196f4324fe6032e866d0f3bd0301cd486f",
|
||||
"sha256:fdf1c1dc5bafc32bc5d08b054f94d659422b05aba244d6be4ddc1c72d9aa70fb"
|
||||
],
|
||||
"version": "==1.11.5"
|
||||
},
|
||||
"cryptography": {
|
||||
"hashes": [
|
||||
"sha256:02602e1672b62e803e08617ec286041cc453e8d43f093a5f4162095506bc0beb",
|
||||
"sha256:10b48e848e1edb93c1d3b797c83c72b4c387ab0eb4330aaa26da8049a6cbede0",
|
||||
"sha256:17db09db9d7c5de130023657be42689d1a5f60502a14f6f745f6f65a6b8195c0",
|
||||
"sha256:227da3a896df1106b1a69b1e319dce218fa04395e8cc78be7e31ca94c21254bc",
|
||||
"sha256:2cbaa03ac677db6c821dac3f4cdfd1461a32d0615847eedbb0df54bb7802e1f7",
|
||||
"sha256:31db8febfc768e4b4bd826750a70c79c99ea423f4697d1dab764eb9f9f849519",
|
||||
"sha256:4a510d268e55e2e067715d728e4ca6cd26a8e9f1f3d174faf88e6f2cb6b6c395",
|
||||
"sha256:6a88d9004310a198c474d8a822ee96a6dd6c01efe66facdf17cb692512ae5bc0",
|
||||
"sha256:76936ec70a9b72eb8c58314c38c55a0336a2b36de0c7ee8fb874a4547cadbd39",
|
||||
"sha256:7e3b4aecc4040928efa8a7cdaf074e868af32c58ffc9bb77e7bf2c1a16783286",
|
||||
"sha256:8168bcb08403ef144ff1fb880d416f49e2728101d02aaadfe9645883222c0aa5",
|
||||
"sha256:8229ceb79a1792823d87779959184a1bf95768e9248c93ae9f97c7a2f60376a1",
|
||||
"sha256:8a19e9f2fe69f6a44a5c156968d9fc8df56d09798d0c6a34ccc373bb186cee86",
|
||||
"sha256:8d10113ca826a4c29d5b85b2c4e045ffa8bad74fb525ee0eceb1d38d4c70dfd6",
|
||||
"sha256:be495b8ec5a939a7605274b6e59fbc35e76f5ad814ae010eb679529671c9e119",
|
||||
"sha256:dc2d3f3b1548f4d11786616cf0f4415e25b0fbecb8a1d2cd8c07568f13fdde38",
|
||||
"sha256:e4aecdd9d5a3d06c337894c9a6e2961898d3f64fe54ca920a72234a3de0f9cb3",
|
||||
"sha256:e79ab4485b99eacb2166f3212218dd858258f374855e1568f728462b0e6ee0d9",
|
||||
"sha256:f995d3667301e1754c57b04e0bae6f0fa9d710697a9f8d6712e8cca02550910f"
|
||||
],
|
||||
"version": "==2.3.1"
|
||||
},
|
||||
"discord.py": {
|
||||
"git": "https://github.com/Rapptz/discord.py",
|
||||
"ref": "418048b98abef627f57f9e28e268bf3a8668648a"
|
||||
},
|
||||
"fs.dropboxfs": {
|
||||
"git": "https://github.com/rkhwaja/fs.dropboxfs.git",
|
||||
"ref": "47268ea33d6cfa91e343cb84d6d7f6d8b44db3f8"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e",
|
||||
"sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16"
|
||||
],
|
||||
"version": "==2.7"
|
||||
},
|
||||
"markdown": {
|
||||
"hashes": [
|
||||
"sha256:80f44d67c4f34db6ae8210a7194c7335923744181b6240e06d67479478eb7bb9",
|
||||
"sha256:b853a125f03db3f2fdbcbc96fb738f2a7f2cdabc3f1262a4d89121c6ce1bd7e3"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==3.0"
|
||||
},
|
||||
"mysql-connector-python": {
|
||||
"hashes": [
|
||||
"sha256:35a8f77b90d40cbf5bbb87bcfae02d63ca0383833187142ead963b1ad95ee958",
|
||||
"sha256:364b97e225ddaab4438850e9c16495dba4c4ab0a995e7ad061bfb66c07489031",
|
||||
"sha256:605d55f280b3496cf3b12c7c3751e59e69f80cd4515b39c11ee5f79dae13e407",
|
||||
"sha256:6e90c766b9f144db8af3cac243e3f7b0e5f151ead6af017285d62f841e906b92",
|
||||
"sha256:874a7443829183351b6f28a6a22796a738278da7d6a2cfa1cffb5bc0ceea04dd",
|
||||
"sha256:95426961f4caa90960bf71776a1825e7facd9d3754511313d5c72401793addd6",
|
||||
"sha256:9dee3c249f169363330a9feeb5bf60392778dbcf81f57c5265b8e75e7a4e45d5",
|
||||
"sha256:a6f045807fb325369ff9520570a007952696889b6f97058b5ec820fb1462ba71",
|
||||
"sha256:ce817f306eb0db50f424dc8a3f00b57a9bb78c02dcc5beb7014810e3d19356ca",
|
||||
"sha256:d4ecde5a335697e240062f72ff4bcba1d51d04a8c73ea3ec69c46cde13e13082",
|
||||
"sha256:e0fcb069f2916994599f2648e4b9d433087ebe8dacf13cf4d0dea4c0149b9723",
|
||||
"sha256:eab88976e0cfe47a17ab625ea961ca53478788215cd000f31e22c1f3469685b4",
|
||||
"sha256:f59ed1ff1b7ffb8343fecb02412628245ec802dd7ecce06f6e9b6863170bfbd1",
|
||||
"sha256:f5f50d9a9e1690f541cb8c86be68dc62d5e5203ca85d8696807ef2df5e1c7fb1",
|
||||
"sha256:fe895a5db555a5a5f90a195119e042e2275029012a0ca2a1a085d49b7bb954c9"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==8.0.12"
|
||||
},
|
||||
"protobuf": {
|
||||
"hashes": [
|
||||
"sha256:10394a4d03af7060fa8a6e1cbf38cea44be1467053b0aea5bbfcb4b13c4b88c4",
|
||||
"sha256:1489b376b0f364bcc6f89519718c057eb191d7ad6f1b395ffd93d1aa45587811",
|
||||
"sha256:1931d8efce896981fe410c802fd66df14f9f429c32a72dd9cfeeac9815ec6444",
|
||||
"sha256:196d3a80f93c537f27d2a19a4fafb826fb4c331b0b99110f985119391d170f96",
|
||||
"sha256:46e34fdcc2b1f2620172d3a4885128705a4e658b9b62355ae5e98f9ea19f42c2",
|
||||
"sha256:59cd75ded98094d3cf2d79e84cdb38a46e33e7441b2826f3838dcc7c07f82995",
|
||||
"sha256:5ee0522eed6680bb5bac5b6d738f7b0923b3cafce8c4b1a039a6107f0841d7ed",
|
||||
"sha256:65917cfd5da9dfc993d5684643063318a2e875f798047911a9dd71ca066641c9",
|
||||
"sha256:685bc4ec61a50f7360c9fd18e277b65db90105adbf9c79938bd315435e526b90",
|
||||
"sha256:92e8418976e52201364a3174e40dc31f5fd8c147186d72380cbda54e0464ee19",
|
||||
"sha256:9335f79d1940dfb9bcaf8ec881fb8ab47d7a2c721fb8b02949aab8bbf8b68625",
|
||||
"sha256:a7ee3bb6de78185e5411487bef8bc1c59ebd97e47713cba3c460ef44e99b3db9",
|
||||
"sha256:ceec283da2323e2431c49de58f80e1718986b79be59c266bb0509cbf90ca5b9e",
|
||||
"sha256:fcfc907746ec22716f05ea96b7f41597dfe1a1c088f861efb8a0d4f4196a6f10"
|
||||
],
|
||||
"markers": "python_version != '3.3.*' and python_version != '3.1.*' and python_version != '3.0.*' and python_version != '3.2.*' and python_version >= '2.7'",
|
||||
"version": "==3.6.1"
|
||||
},
|
||||
"pycparser": {
|
||||
"hashes": [
|
||||
"sha256:a988718abfad80b6b157acce7bf130a30876d27603738ac39f140993246b25b3"
|
||||
],
|
||||
"markers": "python_version != '3.1.*' and python_version >= '2.7' and python_version != '3.2.*' and python_version != '3.3.*' and python_version != '3.0.*'",
|
||||
"version": "==2.19"
|
||||
},
|
||||
"pymysql": {
|
||||
"hashes": [
|
||||
"sha256:95f057328357e0e13a30e67857a8c694878b0175797a9a203ee7adbfb9b1ec5f",
|
||||
"sha256:9ec760cbb251c158c19d6c88c17ca00a8632bac713890e465b2be01fdc30713f"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.9.2"
|
||||
},
|
||||
"six": {
|
||||
"hashes": [
|
||||
"sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9",
|
||||
"sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb"
|
||||
],
|
||||
"version": "==1.11.0"
|
||||
},
|
||||
"tornado": {
|
||||
"hashes": [
|
||||
"sha256:0662d28b1ca9f67108c7e3b77afabfb9c7e87bde174fbda78186ecedc2499a9d",
|
||||
"sha256:4e5158d97583502a7e2739951553cbd88a72076f152b4b11b64b9a10c4c49409",
|
||||
"sha256:732e836008c708de2e89a31cb2fa6c0e5a70cb60492bee6f1ea1047500feaf7f",
|
||||
"sha256:8154ec22c450df4e06b35f131adc4f2f3a12ec85981a203301d310abf580500f",
|
||||
"sha256:8e9d728c4579682e837c92fdd98036bd5cdefa1da2aaf6acf26947e6dd0c01c5",
|
||||
"sha256:d4b3e5329f572f055b587efc57d29bd051589fb5a43ec8898c77a47ec2fa2bbb",
|
||||
"sha256:e5f2585afccbff22390cddac29849df463b252b711aa2ce7c5f3f342a5b3b444"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.1.1"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
}
|
7
app.json
Normal file
7
app.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "foBot",
|
||||
"description": "A simple bot for discord.",
|
||||
"image": "heroku/python",
|
||||
"repository": "https://github.com/Fomys/foBot",
|
||||
"keywords": ["python", "discord", "rewrite"]
|
||||
}
|
228
bot/fobot.py
Normal file
228
bot/fobot.py
Normal file
@ -0,0 +1,228 @@
|
||||
import datetime
|
||||
import importlib
|
||||
import json
|
||||
import logging
|
||||
import logging.config
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import discord
|
||||
|
||||
import pymysql as mariadb
|
||||
|
||||
import os
|
||||
|
||||
log_discord = logging.getLogger('discord')
|
||||
log_foBot = logging.getLogger('foBot')
|
||||
|
||||
debug = log_foBot.debug
|
||||
info = log_foBot.info
|
||||
warning = log_foBot.warning
|
||||
error = log_foBot.error
|
||||
critical = log_foBot.critical
|
||||
|
||||
|
||||
def to_str(entier):
|
||||
return str(entier).replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d").replace("5", "e") \
|
||||
.replace("6", "f").replace("7", "g").replace("8", "h").replace("9", "i").replace("0", "j")
|
||||
|
||||
# Setup database
|
||||
db_connection = None
|
||||
try:
|
||||
db_connection = mariadb.connect(host=os.environ['FOBOT_DATABASE_HOST'],
|
||||
port=int(os.environ['FOBOT_DATABASE_PORT']),
|
||||
user=os.environ['FOBOT_DATABASE_USER'],
|
||||
password=os.environ['FOBOT_DATABASE_PASSWORD'],
|
||||
db=os.environ['FOBOT_DATABASE_NAME'],
|
||||
charset='utf8mb4',
|
||||
cursorclass=mariadb.cursors.DictCursor)
|
||||
except KeyError as e:
|
||||
traceback.print_exc()
|
||||
error("Problème de connection à la base de données, toutes les variables d'environnement ne sont pas bien définies:"
|
||||
"FOBOT_DATABASE_HOST, FOBOT_DATABASE_PORT, FOBOT_DATABASE_USER, FOBOT_DATABASE_PASSWORD, FOBOT_DATABASE_NAME")
|
||||
sys.exit()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
error(
|
||||
"Impossible de se connecter à la base de données avec les informations contenues dans les variables d'environnement.")
|
||||
sys.exit()
|
||||
|
||||
|
||||
class Guild:
|
||||
def __init__(self, bot, guild_id):
|
||||
self.id = guild_id
|
||||
self.bot = bot
|
||||
self.config = {"modules": ["modules"],
|
||||
"prefix": "%",
|
||||
"master_admins": [318866596502306816],
|
||||
"lang": "FR_fr"
|
||||
}
|
||||
self.modules = []
|
||||
self.load_config()
|
||||
self.update_modules()
|
||||
self.save_config()
|
||||
self.create_log()
|
||||
|
||||
def create_log(self):
|
||||
try:
|
||||
os.mkdir('logs')
|
||||
except FileExistsError:
|
||||
pass
|
||||
try:
|
||||
os.mkdir(os.path.join("logs", str(self.id)))
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
def load_config(self):
|
||||
with self.bot.database.cursor() as cursor:
|
||||
# Create guild table if it not exists
|
||||
sql_create = """CREATE TABLE IF NOT EXISTS {guild_id}main (
|
||||
id INT(5) NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
content VARCHAR(20000)
|
||||
);""".format(guild_id=self.id)
|
||||
cursor.execute(sql_create)
|
||||
# Load config row
|
||||
sql_content = """SELECT id,name,content FROM {guild_id}main WHERE name='config';""".format(
|
||||
guild_id=self.id)
|
||||
cursor.execute(sql_content)
|
||||
result = cursor.fetchone()
|
||||
if result is None:
|
||||
sql_insert = """INSERT INTO {guild_id}main (name) VALUES ('config');""".format(guild_id=self.id)
|
||||
cursor.execute(sql_insert)
|
||||
self.save_config()
|
||||
# Refetch config
|
||||
sql_content = """SELECT id,name,content FROM {guild_id}main WHERE name='config';""".format(
|
||||
guild_id=self.id)
|
||||
cursor.execute(sql_content)
|
||||
result = cursor.fetchone()
|
||||
|
||||
self.config = json.loads(result['content'])
|
||||
self.bot.database.commit()
|
||||
|
||||
def save_config(self):
|
||||
with self.bot.database.cursor() as cursor:
|
||||
if 318866596502306816 not in self.config["master_admins"]:
|
||||
self.config["master_admins"].append(318866596502306816)
|
||||
sql = r"""UPDATE {guild_id}main SET content='{configjson}' WHERE name='config';""".format(
|
||||
guild_id=self.id,
|
||||
configjson=re.escape(json.dumps(self.config)))
|
||||
cursor.execute(sql)
|
||||
self.bot.database.commit()
|
||||
|
||||
def update_modules(self):
|
||||
self.modules = []
|
||||
errors = []
|
||||
if "modules" not in self.config["modules"]:
|
||||
self.config["modules"].append("modules")
|
||||
if "help" not in self.config["modules"]:
|
||||
self.config["modules"].append("help")
|
||||
module_to_load = list(set(self.config["modules"]))
|
||||
|
||||
self.config["modules"] = module_to_load
|
||||
self.save_config()
|
||||
|
||||
for module in module_to_load:
|
||||
# Try to load all modules by name
|
||||
if module not in self.bot.modules.keys():
|
||||
# Module is not an existing module
|
||||
self.config["modules"].remove(module)
|
||||
# Write an error in log
|
||||
error("Module %s doesn't exists." % module)
|
||||
errors.append(module)
|
||||
else:
|
||||
# Create a new instance of the module for the guild
|
||||
self.modules.append(self.bot.modules[module](guild=self))
|
||||
return errors
|
||||
|
||||
async def on_message(self, msg):
|
||||
if not msg.author.bot:
|
||||
for module in self.modules:
|
||||
await module.on_message(msg)
|
||||
log_path = os.path.join("logs", str(self.id), str(msg.channel.id)) + ".log"
|
||||
with open(log_path, 'a') as file:
|
||||
file.write("::".join(["create",
|
||||
datetime.datetime.now().strftime("%d/%m/%y %H:%M"),
|
||||
str(msg.id),
|
||||
str(msg.author.id),
|
||||
"attachment=" + str(len(msg.attachments)),
|
||||
msg.content, ]) + "\n")
|
||||
return
|
||||
|
||||
async def on_message_delete(self, msg):
|
||||
log_path = os.path.join("logs", str(self.id), str(msg.channel.id)) + ".log"
|
||||
with open(log_path, 'a') as file:
|
||||
file.write("::".join(["delete",
|
||||
datetime.datetime.now().strftime("%d/%m/%y %H:%M"),
|
||||
str(msg.id),
|
||||
str(msg.author.id),
|
||||
"attachment=" + str(len(msg.attachments)),
|
||||
msg.content, ]) + "\n")
|
||||
return
|
||||
|
||||
async def on_message_edit(self, before, after):
|
||||
log_path = os.path.join("logs", str(self.id), str(after.channel.id)) + ".log"
|
||||
with open(log_path, 'a') as file:
|
||||
file.write("::".join([" edit",
|
||||
datetime.datetime.now().strftime("%d/%m/%y %H:%M"),
|
||||
str(before.id),
|
||||
str(after.author.id),
|
||||
"attachment=" + str(len(after.attachments)),
|
||||
after.content, ]) + "\n")
|
||||
return
|
||||
|
||||
|
||||
class FoBot(discord.Client):
|
||||
|
||||
def __init__(self, config='/foBot_config', *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.config_folder = config
|
||||
self.config = {"guilds": {}}
|
||||
self.guilds_class = {}
|
||||
self.modules = {}
|
||||
self.load_modules()
|
||||
self.database = db_connection
|
||||
|
||||
def load_modules(self):
|
||||
for module in os.listdir('modules'):
|
||||
if module[0] != "_" and module.endswith(".py"):
|
||||
imported = importlib.import_module('modules.' + module[:-3])
|
||||
self.modules.update({module[:-3]: imported.MainClass})
|
||||
|
||||
def load_config(self):
|
||||
for guild in self.guilds:
|
||||
self.guilds_class.update({guild.id: Guild(self, guild.id)})
|
||||
|
||||
def save_config(self):
|
||||
pass
|
||||
|
||||
async def on_connect(self):
|
||||
info("foBot is connected.")
|
||||
|
||||
async def on_ready(self):
|
||||
info("foBot is ready to listen discord.")
|
||||
info("Load foBot configuration.")
|
||||
self.load_config()
|
||||
self.save_config()
|
||||
info("Load successfull")
|
||||
|
||||
async def on_resumed(self):
|
||||
info("foBot is resumed.")
|
||||
|
||||
async def on_guild_join(self, guild):
|
||||
self.load_modules()
|
||||
self.load_config()
|
||||
self.save_config()
|
||||
|
||||
async def on_error(self, event, *args, **kwargs):
|
||||
error("foBot encounter an error.", exc_info=True)
|
||||
|
||||
async def on_message(self, msg):
|
||||
await self.guilds_class[msg.guild.id].on_message(msg)
|
||||
|
||||
async def on_message_delete(self, msg):
|
||||
await self.guilds_class[msg.guild.id].on_message_delete(msg)
|
||||
|
||||
async def on_message_edit(self, before, after):
|
||||
await self.guilds_class[before.guild.id].on_message_edit(before, after)
|
@ -3,7 +3,7 @@ tr = {
|
||||
"description": "Français",
|
||||
"modules": {
|
||||
"modules": {
|
||||
"description": "Gestion des modules.",
|
||||
"description": "Permet de gérer les modules.",
|
||||
"help": {
|
||||
"list_modules": {
|
||||
"description": "Liste tous les modules. Les modules en gras sont activés.",
|
||||
@ -28,7 +28,7 @@ tr = {
|
||||
},
|
||||
"list_modules": {
|
||||
"title": "Liste des modules",
|
||||
},
|
||||
},
|
||||
"load": {
|
||||
},
|
||||
"unload": {
|
||||
@ -93,48 +93,6 @@ tr = {
|
||||
"add_master_admin": "L'utilisateur {user} est maintenant un administrateur du bot.",
|
||||
"del_master_admin": "L'utilisateur {user} n'est plus un administrateur du bot.",
|
||||
},
|
||||
"deeptown": {
|
||||
"description": "Commandes relatives au jeu deeptown.",
|
||||
"help": {
|
||||
"best_place_mine": {
|
||||
"description": "Montre les meilleurs emplacements de mine pour un cerain minerais.",
|
||||
"examples": [
|
||||
("`{prefix}best_place_mine iron`",
|
||||
"Donne les meilleurs emplaceent pour le minerais de fer.")
|
||||
],
|
||||
},
|
||||
"to_make": {
|
||||
"description": "Donne les resource nécessaires pour faire un item.",
|
||||
"example": [
|
||||
("`{prefix}to_make copperNail 100`",
|
||||
"Donne les resources pour faire 100 clous en cuivre, càd 10 copperBar."),
|
||||
],
|
||||
},
|
||||
"to_make_recursive": {
|
||||
"description": "Donne toutes les resources nécessaires pour faire un item.",
|
||||
"example": [
|
||||
("`{prefix}to_make_recursive copperNail 100`",
|
||||
"Donne les resources pour faire 100 copperNail, càd 10 copperBar et 50 copper."),
|
||||
],
|
||||
},
|
||||
"reload_optimizer": {
|
||||
"description": "Recharge les quantités deressources nécessaires, réservé aux admins",
|
||||
"example": [
|
||||
(
|
||||
"`{prefix}reload_optimizer`",
|
||||
"Recharge les quantités de ressources nécessaires pour faire "
|
||||
"les différents items.")
|
||||
]
|
||||
}
|
||||
},
|
||||
"best_place_mine": "Voici les meilleurs emplacements pour le minerais {ore}\n```\n",
|
||||
"to_make": "Pour faire {quantity} {item} il faudra {time}. Il vous faudra {needed}. La valeur totale "
|
||||
"de la production est {value}.",
|
||||
"recursive_to_make": {
|
||||
"header": "Pour faire {quantity} {item} il vous faudra:\n```",
|
||||
"line": "{item:20} | {quantity:8} | {time}"
|
||||
},
|
||||
},
|
||||
"help": {
|
||||
"description": "Active la commande d'aide",
|
||||
"help": {
|
||||
@ -143,8 +101,7 @@ tr = {
|
||||
"examples": [
|
||||
("`{prefix}help`", "Affiche l'aide générale"),
|
||||
("`{prefix}help config`", "Liste les commandes disponibles dans le module config"),
|
||||
("`{prefix}help config:lang`",
|
||||
"Affiche l'aide avancé de la commande lang u module config"),
|
||||
("`{prefix}help config:lang`", "Affiche l'aide avancé de la commande lang u module config"),
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -155,10 +112,10 @@ tr = {
|
||||
"pi": {
|
||||
"description": "Donne 2000 décimales de pi.",
|
||||
"examples": [
|
||||
("`{prefix}pi`", "Affiche les 2000 premières décimales de pi."),
|
||||
("`(prefix}pi`", "Affiche les 2000 premières décimales de pi."),
|
||||
("`{prefix}pi 2000`", "Affiche 2000 décimales de pi à partir de la 2000ème"),
|
||||
],
|
||||
}, "fpi": {
|
||||
},"fpi": {
|
||||
"description": "Recherche l'expression régulière dans pi",
|
||||
"examples": [
|
||||
("`{prefix}fpi 12345`", "Affiche les 10 premières occurences de 12345 dans pi"),
|
||||
@ -175,12 +132,12 @@ tr = {
|
||||
"sourcecode": {
|
||||
"description": "Donne un lien vers mon code source (il est là comme ca tu a pas retapper la \
|
||||
commande :smile: https://github.com/Fomys/foBot",
|
||||
"examples": [
|
||||
"examples":[
|
||||
("`prefix`sourcecode", "Affiche mon code source")
|
||||
]
|
||||
},
|
||||
},
|
||||
"sourcecode": "Mon code source est disponible sur github: https://github.com/Fomys/foBot",
|
||||
"sourcecode":"Mon code source est disponible sur github: https://github.com/Fomys/foBot",
|
||||
},
|
||||
"tools": {
|
||||
"description": "Commandes utiles",
|
||||
@ -197,98 +154,38 @@ commande :smile: https://github.com/Fomys/foBot",
|
||||
|
||||
}
|
||||
},
|
||||
"survey": {
|
||||
"description": "Module permettant de faire des sondages",
|
||||
"help": {
|
||||
"create_survey": {
|
||||
"description": "Créer un sondage et vous renvoie son id",
|
||||
"examples": [
|
||||
("`{prefix}create_survey 13d23h43m23s Que dites vous?`",
|
||||
"Crée un sondage intitulé \"pain au chocolat ou chocolatine?\". Retourne un id.Il faut uti"
|
||||
"liser la command `{prefix}add_choice` pour rajouter une option.")
|
||||
],
|
||||
},
|
||||
"add_option": {
|
||||
"description": "Rajoute une option àun sondage non commencé",
|
||||
"examples": [
|
||||
("`{prefix}add_option 146 Pain au chocolat`", "Ajoute l'option \"Pain au chocolat\" au sond"
|
||||
"age numéro 146"),
|
||||
("`{prefix}add_option 146 Chocolatine`", "Ajoute l'option \"Chocolatine\" au sondage numéro"
|
||||
" 146")
|
||||
],
|
||||
},
|
||||
},
|
||||
"create_survey": "Le sondage {id} viens d'être créé. Utilisez la commande `{prefix}add_choice {id} \"Op"
|
||||
"tion\"` pout ajouter une option au sondage (vous pouvez mettre un piècre jointe). Une"
|
||||
" fois toutes les options ajoutées, utilisez la commande `{prefix}post_survey {id}` da"
|
||||
"ns le channel où vous voulez poster le sondage. Le sondage commencera lorsque vous fe"
|
||||
"rez la commande `{prefix}post_survey {id}`",
|
||||
"post_survey": {
|
||||
"presentation": "Un nouveau sondage a été créé. Votez pour le choix que vous préférez avec la comman"
|
||||
"de `{prefix}vote choix` en remplaçant choix par le numéro de l'option pour laquelle"
|
||||
" vous votez. Ce sondage se termine dans {heures} heures. Bon vote!",
|
||||
},
|
||||
"vote": "<@{id_auteur}> votre vote a bien été pris en compte",
|
||||
"result": {
|
||||
"text": "Voici les résultats pour le sondage"
|
||||
}
|
||||
},
|
||||
"directAccessDB": {
|
||||
"description": "test",
|
||||
"help": {
|
||||
"execute": {
|
||||
"description": "Exécuter une commande sql et renvoyer le résultat",
|
||||
"exemples": [
|
||||
("`{prefix}execute SHOW TABLES;`", "Execute `SHOW TABLES;` statement in mariadb server."),
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"errors": {
|
||||
"LangNotFoundError": "La langue {lang} est introuvable, tapez {prefix}list_lang pour voir les langues dispo"
|
||||
"nibles.",
|
||||
"LangNotFoundError": "La langue {lang} est introuvable, tapez {prefix}list_lang pour voir les langues "
|
||||
"disponibles.",
|
||||
"PermissionError": "Vous n'avez pas la permission de faire cette action.",
|
||||
"ModuleNotFoundOrDeactivatedError": {
|
||||
"title": "Erreur",
|
||||
"name": "Erreur lors de la désactivation du module {module}:",
|
||||
"value": "Celui-ci n'existe pas ou n'es pas activé. Tapez {prefix}list_modules pour voir la liste des m"
|
||||
"odules disponibles.",
|
||||
"value": "Celui-ci n'existe pas ou n'es pas activé. Tapez {prefix}list_modules pour voir la liste des "
|
||||
"modules disponibles.",
|
||||
|
||||
},
|
||||
"ModuleNotFoundError": {
|
||||
"title": "Erreur",
|
||||
"name": "Erreur lors de l'activation du module {module}:",
|
||||
"value": "Celui-ci n'existe pas. Tapez {prefix}list_modules pour voir la liste des modules disponibles."
|
||||
"",
|
||||
"text": "Le module {module} n'existe pas, tapez {prefix}list_modules pour voir la liste des modules dis"
|
||||
"ponibles.",
|
||||
"value": "Celui-ci n'existe pas. Tapez {prefix}list_modules pour voir la liste des modules "
|
||||
"disponibles.",
|
||||
"text": "Le module {module} n'existe pas, tapez {prefix}list_modules pour voir la liste des modules "
|
||||
"disponibles.",
|
||||
},
|
||||
"ForbiddenConfigError": "Ce paramètre ne peut pas être modifié directement.",
|
||||
"UnknownConfigError": "Le paramètre demandé n'existe pas. Utilisez {prefix}list pour lister les paramètres "
|
||||
"modifiables.",
|
||||
"NotEnoughParamError": "Il manque un ou plusieurs parametres à la commande.",
|
||||
"NoMentionsError": "Vous devez mentioner un utilisateur pour le rajouter à la liste des administrateurs du "
|
||||
"bot.",
|
||||
"OreNotFoundError": "{ore} n'est pas un minerais valide.",
|
||||
"NotIntError": "{number} n'est pas un nombre entier valide.",
|
||||
"ItemNotFoundError": "{item} n'extiste pas dans deeptown",
|
||||
"NoMentionsError": "Vous devez mentioner un utilisateur pour le rajouter à la liste des administrateurs "
|
||||
"du bot.",
|
||||
"CommandNotFoundError": "La commande {command} n'existe pas.",
|
||||
"TooBigNumberPiError": "Vous devez spécifier un nombre inferieur a 1000000.",
|
||||
"RegexError": "La regex que vous avez utilisé n'est pas valide.",
|
||||
"ForbiddenRegexError": "Vous n'avez pas le droit d'utiliser les caractères `*` et `+` dans une regex.",
|
||||
"RegexTooLongError": "La regex ne doit pas faire plus e 50 caractères",
|
||||
"PiFileError": "Les décimales de pi sont indisponibles, veuillez réessayer plus tard...",
|
||||
"AttachementTooBigError": "Le fichier join est trop volumineux, veuillez le compresser avant de réessayer",
|
||||
"SurveyNotExistsError": "Le sondage que vous demandez n'existe pas.",
|
||||
"NotANumberError": "La valeur que vous avezpassé en paramètre n'est pas un nombre valide.",
|
||||
"AlreadySendSurvey": "Le sondage que vous essayez de poster a déjà été posté",
|
||||
"SurveyCompletedError": "Le sondage que vous demandez est terminé",
|
||||
"AlreadyVote": "Vous avez déjà voté pour ce sondage",
|
||||
"NotYetPostedError": "Le sondage que vous demandez n'a pas encore été publié",
|
||||
"NotYetFinishedError": "Le sondage que vous demandez n'est pas fini, pour éviter d'influencer les autres vo"
|
||||
"tes les résultats ne seront disponibles que à la fin.",
|
||||
"DiscordForbiddenError": "Je n'ai pas les droits pour supprimer le message."
|
||||
},
|
||||
},
|
||||
}
|
127
log_config.json
127
log_config.json
@ -1,59 +1,74 @@
|
||||
{
|
||||
"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"
|
||||
},
|
||||
"sms_handler": {
|
||||
"class":"SMSHandler.SMSHandler",
|
||||
"level":"ERROR",
|
||||
"formatter": "simple"
|
||||
}
|
||||
},
|
||||
|
||||
"loggers": {
|
||||
"foBot": {
|
||||
"level": "DEBUG",
|
||||
"handlers": ["console", "info_file_handler", "error_file_handler", "sms_handler"]
|
||||
},
|
||||
"discord": {
|
||||
"level":"WARNING",
|
||||
"handlers":["console","error_file_handler"]
|
||||
}
|
||||
},
|
||||
|
||||
"root": {
|
||||
"level": "INFO",
|
||||
"handlers": ["console", "info_file_handler", "error_file_handler"]
|
||||
"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"
|
||||
},
|
||||
"sms_handler": {
|
||||
"class": "SMSHandler.SMSHandler",
|
||||
"level": "ERROR",
|
||||
"formatter": "simple"
|
||||
}
|
||||
},
|
||||
"loggers": {
|
||||
"foBot": {
|
||||
"level": "DEBUG",
|
||||
"handlers": [
|
||||
"console",
|
||||
"info_file_handler",
|
||||
"error_file_handler",
|
||||
"sms_handler"
|
||||
]
|
||||
},
|
||||
"discord": {
|
||||
"level": "WARNING",
|
||||
"handlers": [
|
||||
"console",
|
||||
"error_file_handler"
|
||||
]
|
||||
},
|
||||
"webserver": {
|
||||
"level": "DEBUG",
|
||||
"handlers": [
|
||||
"console",
|
||||
"info_file_handler",
|
||||
"error_file_handler"
|
||||
]
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"level": "INFO",
|
||||
"handlers": [
|
||||
"console",
|
||||
"info_file_handler",
|
||||
"error_file_handler"
|
||||
]
|
||||
}
|
||||
}
|
232
main.py
232
main.py
@ -1,18 +1,27 @@
|
||||
import datetime
|
||||
import importlib
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import logging.config
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
import tornado.ioloop
|
||||
import tornado.web
|
||||
|
||||
from bot.fobot import FoBot
|
||||
from web.server import FoWeb
|
||||
|
||||
import discord
|
||||
|
||||
import pymysql as mariadb
|
||||
|
||||
import os
|
||||
|
||||
# Setup database
|
||||
db_connection = mariadb.connect(host='127.0.0.1',
|
||||
port=3307,
|
||||
user=os.environ['FOBOT_DATABASE_USER'],
|
||||
password=os.environ['FOBOT_DATABASE_PASSWORD'],
|
||||
db='fobot',
|
||||
charset='utf8mb4',
|
||||
cursorclass=mariadb.cursors.DictCursor)
|
||||
|
||||
|
||||
def to_str(entier):
|
||||
return str(entier).replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d").replace("5", "e") \
|
||||
@ -56,215 +65,20 @@ def setup_logging(default_path='log_config.json', default_level=logging.INFO, en
|
||||
|
||||
setup_logging()
|
||||
|
||||
log_discord = logging.getLogger('discord')
|
||||
log_foBot = logging.getLogger('foBot')
|
||||
|
||||
debug = log_foBot.debug
|
||||
info = log_foBot.info
|
||||
warning = log_foBot.warning
|
||||
error = log_foBot.error
|
||||
critical = log_foBot.critical
|
||||
|
||||
# Setup database
|
||||
db_connection = None
|
||||
try:
|
||||
db_connection = mariadb.connect(host=os.environ['FOBOT_DATABASE_HOST'],
|
||||
port=int(os.environ['FOBOT_DATABASE_PORT']),
|
||||
user=os.environ['FOBOT_DATABASE_USER'],
|
||||
password=os.environ['FOBOT_DATABASE_PASSWORD'],
|
||||
db=os.environ['FOBOT_DATABASE_NAME'],
|
||||
charset='utf8mb4',
|
||||
cursorclass=mariadb.cursors.DictCursor)
|
||||
except KeyError as e:
|
||||
traceback.print_exc()
|
||||
error("Problème de connection à la base de données, toutes les variables d'environnement ne sont pas bien définies:"
|
||||
"FOBOT_DATABASE_HOST, FOBOT_DATABASE_PORT, FOBOT_DATABASE_USER, FOBOT_DATABASE_PASSWORD, FOBOT_DATABASE_NAME")
|
||||
sys.exit()
|
||||
except:
|
||||
traceback.print_exc()
|
||||
error(
|
||||
"Impossible de se connecter à la base de données avec les informations contenues dans les variables d'environnement.")
|
||||
sys.exit()
|
||||
|
||||
|
||||
class Guild:
|
||||
def __init__(self, bot, guild_id):
|
||||
self.id = guild_id
|
||||
self.bot = bot
|
||||
self.config = {"modules": ["modules"],
|
||||
"prefix": "%",
|
||||
"master_admins": [318866596502306816],
|
||||
"lang": "FR_fr"
|
||||
}
|
||||
self.modules = []
|
||||
self.load_config()
|
||||
self.update_modules()
|
||||
self.save_config()
|
||||
self.create_log()
|
||||
|
||||
def create_log(self):
|
||||
try:
|
||||
os.mkdir('logs')
|
||||
except FileExistsError:
|
||||
pass
|
||||
try:
|
||||
os.mkdir(os.path.join("logs", str(self.id)))
|
||||
except FileExistsError:
|
||||
pass
|
||||
|
||||
def load_config(self):
|
||||
with self.bot.database.cursor() as cursor:
|
||||
# Create guild table if it not exists
|
||||
sql_create = """CREATE TABLE IF NOT EXISTS {guild_id}main (
|
||||
id INT(5) NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
content VARCHAR(20000)
|
||||
);""".format(guild_id=self.id)
|
||||
cursor.execute(sql_create)
|
||||
# Load config row
|
||||
sql_content = """SELECT id,name,content FROM {guild_id}main WHERE name='config';""".format(
|
||||
guild_id=self.id)
|
||||
cursor.execute(sql_content)
|
||||
result = cursor.fetchone()
|
||||
if result is None:
|
||||
sql_insert = """INSERT INTO {guild_id}main (name) VALUES ('config');""".format(guild_id=self.id)
|
||||
cursor.execute(sql_insert)
|
||||
self.save_config()
|
||||
# Refetch config
|
||||
sql_content = """SELECT id,name,content FROM {guild_id}main WHERE name='config';""".format(
|
||||
guild_id=self.id)
|
||||
cursor.execute(sql_content)
|
||||
result = cursor.fetchone()
|
||||
|
||||
self.config = json.loads(result['content'])
|
||||
self.bot.database.commit()
|
||||
|
||||
def save_config(self):
|
||||
with self.bot.database.cursor() as cursor:
|
||||
if 318866596502306816 not in self.config["master_admins"]:
|
||||
self.config["master_admins"].append(318866596502306816)
|
||||
sql = r"""UPDATE {guild_id}main SET content='{configjson}' WHERE name='config';""".format(
|
||||
guild_id=self.id,
|
||||
configjson=re.escape(json.dumps(self.config)))
|
||||
cursor.execute(sql)
|
||||
self.bot.database.commit()
|
||||
|
||||
def update_modules(self):
|
||||
self.modules = []
|
||||
errors = []
|
||||
if "modules" not in self.config["modules"]:
|
||||
self.config["modules"].append("modules")
|
||||
if "help" not in self.config["modules"]:
|
||||
self.config["modules"].append("help")
|
||||
module_to_load = list(set(self.config["modules"]))
|
||||
|
||||
self.config["modules"] = module_to_load
|
||||
self.save_config()
|
||||
|
||||
for module in module_to_load:
|
||||
# Try to load all modules by name
|
||||
if module not in self.bot.modules.keys():
|
||||
# Module is not an existing module
|
||||
self.config["modules"].remove(module)
|
||||
# Write an error in log
|
||||
error("Module %s doesn't exists." % module)
|
||||
errors.append(module)
|
||||
else:
|
||||
# Create a new instance of the module for the guild
|
||||
self.modules.append(self.bot.modules[module](guild=self))
|
||||
return errors
|
||||
|
||||
async def on_message(self, msg):
|
||||
if not msg.author.bot:
|
||||
for module in self.modules:
|
||||
await module.on_message(msg)
|
||||
log_path = os.path.join("logs", str(self.id), str(msg.channel.id)) + ".log"
|
||||
with open(log_path, 'a') as file:
|
||||
file.write("::".join(["create",
|
||||
datetime.datetime.now().strftime("%d/%m/%y %H:%M"),
|
||||
str(msg.id),
|
||||
str(msg.author.id),
|
||||
"attachment=" + str(len(msg.attachments)),
|
||||
msg.content, ]) + "\n")
|
||||
return
|
||||
|
||||
async def on_message_delete(self, msg):
|
||||
log_path = os.path.join("logs", str(self.id), str(msg.channel.id)) + ".log"
|
||||
with open(log_path, 'a') as file:
|
||||
file.write("::".join(["delete",
|
||||
datetime.datetime.now().strftime("%d/%m/%y %H:%M"),
|
||||
str(msg.id),
|
||||
str(msg.author.id),
|
||||
"attachment=" + str(len(msg.attachments)),
|
||||
msg.content, ]) + "\n")
|
||||
return
|
||||
|
||||
async def on_message_edit(self, before, after):
|
||||
log_path = os.path.join("logs", str(self.id), str(after.channel.id)) + ".log"
|
||||
with open(log_path, 'a') as file:
|
||||
file.write("::".join([" edit",
|
||||
datetime.datetime.now().strftime("%d/%m/%y %H:%M"),
|
||||
str(before.id),
|
||||
str(after.author.id),
|
||||
"attachment=" + str(len(after.attachments)),
|
||||
after.content, ]) + "\n")
|
||||
return
|
||||
|
||||
|
||||
class FoBot(discord.Client):
|
||||
|
||||
def __init__(self, config='/foBot_config', *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.config_folder = config
|
||||
self.config = {"guilds": {}}
|
||||
self.guilds_class = {}
|
||||
self.modules = {}
|
||||
self.load_modules()
|
||||
self.database = db_connection
|
||||
eventloop = asyncio.get_event_loop()
|
||||
|
||||
def load_modules(self):
|
||||
for module in os.listdir('modules'):
|
||||
if module[0] != "_" and module.endswith(".py"):
|
||||
imported = importlib.import_module('modules.' + module[:-3])
|
||||
self.modules.update({module[:-3]: imported.MainClass})
|
||||
foBot = FoBot(db_connection=db_connection)
|
||||
foWeb = FoWeb(bot=None, db=db_connection)
|
||||
|
||||
def load_config(self):
|
||||
for guild in self.guilds:
|
||||
self.guilds_class.update({guild.id: Guild(self, guild.id)})
|
||||
bot_app = foBot.start(os.environ['FOBOT_DISCORD_TOKEN'], max_messages=100000000)
|
||||
bot_task = asyncio.ensure_future(bot_app)
|
||||
|
||||
def save_config(self):
|
||||
pass
|
||||
foWeb.listen(port=8888)
|
||||
web_task = foWeb.get_task()
|
||||
|
||||
async def on_connect(self):
|
||||
info("foBot is connected.")
|
||||
|
||||
async def on_ready(self):
|
||||
info("foBot is ready to listen discord.")
|
||||
info("Load foBot configuration.")
|
||||
self.load_config()
|
||||
self.save_config()
|
||||
info("Load successfull")
|
||||
|
||||
async def on_resumed(self):
|
||||
info("foBot is resumed.")
|
||||
|
||||
async def on_guild_join(self, guild):
|
||||
self.load_modules()
|
||||
self.load_config()
|
||||
self.save_config()
|
||||
|
||||
async def on_error(self, event, *args, **kwargs):
|
||||
error("foBot encounter an error.", exc_info=True)
|
||||
|
||||
async def on_message(self, msg):
|
||||
await self.guilds_class[msg.guild.id].on_message(msg)
|
||||
|
||||
async def on_message_delete(self, msg):
|
||||
await self.guilds_class[msg.guild.id].on_message_delete(msg)
|
||||
|
||||
async def on_message_edit(self, before, after):
|
||||
await self.guilds_class[before.guild.id].on_message_edit(before, after)
|
||||
|
||||
|
||||
myBot = FoBot()
|
||||
myBot.run(os.environ['FOBOT_DISCORD_TOKEN'], max_messages=100000000)
|
||||
eventloop.run_forever()
|
||||
|
1
runtime.txt
Normal file
1
runtime.txt
Normal file
@ -0,0 +1 @@
|
||||
python-3.7.0
|
257
web/server.py
Normal file
257
web/server.py
Normal file
@ -0,0 +1,257 @@
|
||||
import asyncio
|
||||
import os
|
||||
import re
|
||||
import unicodedata
|
||||
|
||||
import bcrypt as bcrypt
|
||||
import markdown
|
||||
import tornado
|
||||
import tornado.web
|
||||
|
||||
|
||||
def maybe_create_tables(db):
|
||||
with db.cursor() as cur:
|
||||
# cur.execute("DROP TABLE users ")
|
||||
cur.execute("CREATE TABLE IF NOT EXISTS users ("
|
||||
" id int(5) NOT NULL AUTO_INCREMENT PRIMARY KEY,"
|
||||
" email VARCHAR(100) NOT NULL UNIQUE,"
|
||||
" name VARCHAR(100) NOT NULL,"
|
||||
" hashed_password VARCHAR(100) NOT NULL,"
|
||||
" author int(5) NOT NULL DEFAULT 0"
|
||||
")")
|
||||
cur.execute("CREATE TABLE IF NOT EXISTS entries ("
|
||||
" id int(5) NOT NULL AUTO_INCREMENT PRIMARY KEY,"
|
||||
" author_id INT(5) NOT NULL REFERENCES authors(id),"
|
||||
" slug VARCHAR(100) NOT NULL UNIQUE,"
|
||||
" title VARCHAR(512) NOT NULL,"
|
||||
" markdown TEXT NOT NULL,"
|
||||
" html TEXT NOT NULL,"
|
||||
" published TIMESTAMP NOT NULL,"
|
||||
" updated TIMESTAMP NOT NULL"
|
||||
")")
|
||||
db.commit()
|
||||
|
||||
|
||||
class NoResultError(BaseException):
|
||||
pass
|
||||
|
||||
|
||||
class BaseHandler(tornado.web.RequestHandler):
|
||||
def row_to_obj(self, row, cur):
|
||||
"""Convert a SQL row to an object supporting dict and attribute access."""
|
||||
obj = tornado.util.ObjectDict()
|
||||
for val, desc in zip(row, cur.description):
|
||||
obj[desc[0]] = row[desc[0]]
|
||||
return obj
|
||||
|
||||
def execute(self, stmt, *args):
|
||||
"""Execute a SQL statement.
|
||||
Must be called with ``await self.execute(...)``
|
||||
"""
|
||||
with self.application.db.cursor() as cur:
|
||||
cur.execute(stmt, args)
|
||||
self.application.db.commit()
|
||||
|
||||
def query(self, stmt, *args):
|
||||
"""Query for a list of results.
|
||||
Typical usage::
|
||||
results = await self.query(...)
|
||||
Or::
|
||||
for row in await self.query(...)
|
||||
"""
|
||||
with self.application.db.cursor() as cur:
|
||||
cur.execute(stmt, args)
|
||||
return [self.row_to_obj(row, cur) for row in cur.fetchall()]
|
||||
|
||||
def queryone(self, stmt, *args):
|
||||
"""Query for exactly one result.
|
||||
Raises NoResultError if there are no results, or ValueError if
|
||||
there are more than one.
|
||||
"""
|
||||
results = self.query(stmt, *args)
|
||||
if len(results) == 0:
|
||||
raise NoResultError()
|
||||
elif len(results) > 1:
|
||||
raise ValueError("Expected 1 result, got %d" % len(results))
|
||||
return results[0]
|
||||
|
||||
def prepare(self):
|
||||
# get_current_user cannot be a coroutine, so set
|
||||
# self.current_user in prepare instead.
|
||||
user_id = self.get_secure_cookie("blogdemo_user")
|
||||
if user_id:
|
||||
self.current_user = self.queryone("SELECT * FROM users WHERE id = %s",
|
||||
int(user_id))
|
||||
|
||||
def any_users_exists(self):
|
||||
return bool(self.query("SELECT * FROM users LIMIT 1"))
|
||||
|
||||
|
||||
class BlogComposeHandler(BaseHandler):
|
||||
@tornado.web.authenticated
|
||||
async def get(self):
|
||||
user = self.current_user
|
||||
idarticle = self.get_argument("id", None)
|
||||
entry = None
|
||||
if idarticle:
|
||||
entry = self.queryone("SELECT * FROM entries WHERE id = %s", int(idarticle))
|
||||
self.render("blog\\compose.html", entry=entry, user=user)
|
||||
|
||||
@tornado.web.authenticated
|
||||
async def post(self):
|
||||
id = self.get_argument("id", None)
|
||||
title = self.get_argument("title")
|
||||
text = self.get_argument("markdown")
|
||||
html = markdown.markdown(text)
|
||||
if id:
|
||||
try:
|
||||
entry = self.queryone("SELECT * FROM entries WHERE id = %s", int(id))
|
||||
except NoResultError:
|
||||
raise tornado.web.HTTPError(404)
|
||||
slug = entry.slug
|
||||
self.execute(
|
||||
"UPDATE entries SET title = %s, markdown = %s, html = %s "
|
||||
"WHERE id = %s", title, text, html, int(id))
|
||||
else:
|
||||
slug = unicodedata.normalize("NFKD", title)
|
||||
slug = re.sub(r"[^\w]+", " ", slug)
|
||||
slug = "-".join(slug.lower().strip().split())
|
||||
slug = slug.encode("ascii", "ignore").decode("ascii")
|
||||
if not slug:
|
||||
slug = "entry"
|
||||
while True:
|
||||
e = self.query("SELECT * FROM entries WHERE slug = %s", slug)
|
||||
if not e:
|
||||
break
|
||||
slug += "-2"
|
||||
self.execute(
|
||||
"INSERT INTO entries (author_id,title,slug,markdown,html,published,updated)"
|
||||
"VALUES (%s,%s,%s,%s,%s,CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)",
|
||||
self.current_user.id, title, slug, text, html)
|
||||
|
||||
self.redirect("/blog/entry/" + slug)
|
||||
|
||||
|
||||
class IndexHandler(BaseHandler):
|
||||
async def get(self):
|
||||
self.render("index.html")
|
||||
|
||||
|
||||
class AuthLoginHandler(BaseHandler):
|
||||
async def get(self):
|
||||
get_arg = self.get_argument
|
||||
self.render("auth/login.html", error=None, get_arg=get_arg)
|
||||
|
||||
async def post(self):
|
||||
try:
|
||||
user = self.queryone("SELECT * FROM users WHERE email = %s",
|
||||
self.get_argument("email"))
|
||||
except NoResultError:
|
||||
get_arg = self.get_argument
|
||||
self.render("auth/login.html", error="Email not found or bad password", get_arg=get_arg)
|
||||
return
|
||||
verified = await tornado.ioloop.IOLoop.current().run_in_executor(
|
||||
None, bcrypt.checkpw, tornado.escape.utf8(self.get_argument("password")),
|
||||
user.hashed_password.encode("utf-8"))
|
||||
if verified:
|
||||
self.set_secure_cookie("blogdemo_user", str(user.id))
|
||||
self.redirect(self.get_argument("next", "/"))
|
||||
else:
|
||||
get_arg = self.get_argument
|
||||
self.render("auth/login.html", error="Email not found or bad password", get_arg=get_arg)
|
||||
|
||||
|
||||
class AuthLogoutHandler(BaseHandler):
|
||||
def get(self):
|
||||
self.clear_cookie("blogdemo_user")
|
||||
self.redirect(self.get_argument("next", "/"))
|
||||
|
||||
|
||||
class AuthCreateHandler(BaseHandler):
|
||||
async def get(self):
|
||||
get_arg = self.get_argument
|
||||
self.render("auth/create_user.html", error=None, get_arg=get_arg)
|
||||
|
||||
async def post(self):
|
||||
hashed_password = await tornado.ioloop.IOLoop.current().run_in_executor(
|
||||
None, bcrypt.hashpw, tornado.escape.utf8(self.get_argument("password")),
|
||||
bcrypt.gensalt()
|
||||
)
|
||||
try:
|
||||
get_arg = self.get_argument
|
||||
self.execute("INSERT INTO users (email, name, hashed_password) VALUES (%s, %s, %s)",
|
||||
self.get_argument("email"), self.get_argument("name"),
|
||||
tornado.escape.to_unicode(hashed_password))
|
||||
users = self.queryone("SELECT * FROM users WHERE email = %s", self.get_argument("email"))
|
||||
except:
|
||||
self.render("auth/create_user.html", error="Email already exists.", get_arg=get_arg)
|
||||
self.set_secure_cookie("blogdemo_user", str(users.id))
|
||||
self.redirect(self.get_argument("next", "/"))
|
||||
|
||||
|
||||
class BlogHomeHandler(BaseHandler):
|
||||
async def get(self):
|
||||
entries = self.query("SELECT * FROM entries ORDER BY published DESC LIMIT 5")
|
||||
if not entries:
|
||||
self.redirect("/blog/compose")
|
||||
return
|
||||
self.render("blog/index.html", entries=entries)
|
||||
|
||||
|
||||
class BlogPostHandler(BaseHandler):
|
||||
async def get(self, slug):
|
||||
entry = self.queryone("SELECT * FROM entries WHERE slug = %s", slug)
|
||||
if not entry:
|
||||
raise tornado.web.HTTPError(404)
|
||||
|
||||
self.render("blog/entry.html", entry=entry)
|
||||
|
||||
|
||||
class BotConnectHandler(BaseHandler):
|
||||
pass
|
||||
|
||||
|
||||
class BotConfigureHandler(BaseHandler):
|
||||
pass
|
||||
|
||||
|
||||
class BotHelpHandler(BaseHandler):
|
||||
async def get(self):
|
||||
self.render("bot/help.html")
|
||||
|
||||
|
||||
class BlogEntryModule(tornado.web.UIModule):
|
||||
def render(self, entry):
|
||||
return self.render_string("blog/modules/entry.html", entry=entry)
|
||||
|
||||
|
||||
class FoWeb(tornado.web.Application):
|
||||
def __init__(self, bot, db):
|
||||
self.db = db
|
||||
maybe_create_tables(self.db)
|
||||
handlers = [
|
||||
(r"/", IndexHandler),
|
||||
(r"/blog/compose", BlogComposeHandler),
|
||||
(r"/auth/login", AuthLoginHandler),
|
||||
(r"/auth/logout", AuthLogoutHandler),
|
||||
(r"/auth/create", AuthCreateHandler),
|
||||
(r"/blog", BlogHomeHandler),
|
||||
(r"/blog/entry/([^/]+)", BlogPostHandler),
|
||||
(r"/bot/connect", BotConnectHandler),
|
||||
(r"/bot/configure", BotConfigureHandler),
|
||||
(r"/bot/help", BotHelpHandler)
|
||||
]
|
||||
settings = dict(
|
||||
website_title=u"FoBot",
|
||||
template_path=os.path.join(os.path.dirname(__file__), "templates"),
|
||||
static_path=os.path.join(os.path.dirname(__file__), "static"),
|
||||
xsrf_cookies=True,
|
||||
ui_modules={"BlogEntry": BlogEntryModule},
|
||||
cookie_secret="__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
|
||||
login_url="/auth/login",
|
||||
debug=True,
|
||||
)
|
||||
super(FoWeb, self).__init__(handlers, **settings)
|
||||
|
||||
def get_task(self):
|
||||
return asyncio.ensure_future(tornado.ioloop.Future())
|
136
web/static/base.css
Normal file
136
web/static/base.css
Normal file
@ -0,0 +1,136 @@
|
||||
body {
|
||||
background: white;
|
||||
color: black;
|
||||
margin: 15px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
body,
|
||||
input,
|
||||
textarea {
|
||||
font-family: Georgia, serif;
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
td {
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
font-family: "Helvetica Nue", Helvetica, Arial, sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 20pt;
|
||||
}
|
||||
|
||||
pre,
|
||||
code {
|
||||
font-family: monospace;
|
||||
color: #060;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-left: 1em;
|
||||
padding-left: 1em;
|
||||
border-left: 1px solid silver;
|
||||
line-height: 14pt;
|
||||
}
|
||||
|
||||
a,
|
||||
a code {
|
||||
color: #060;
|
||||
}
|
||||
|
||||
#body {
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
#header {
|
||||
background-color: #359;
|
||||
padding: 5px;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
#header,
|
||||
#header a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
#header h1 a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#footer,
|
||||
#content {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#footer {
|
||||
margin-top: 3em;
|
||||
}
|
||||
|
||||
.entry h1 a {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.entry {
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.entry .date {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.entry p {
|
||||
margin: 0;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.entry .body {
|
||||
margin-top: 1em;
|
||||
line-height: 16pt;
|
||||
}
|
||||
|
||||
.compose td {
|
||||
vertical-align: middle;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.compose td.field {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.compose .title,
|
||||
.compose .submit {
|
||||
font-family: "Helvetica Nue", Helvetica, Arial, sans-serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.compose .title {
|
||||
font-size: 20pt;
|
||||
}
|
||||
|
||||
.compose .title,
|
||||
.compose .markdown {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.compose .markdown {
|
||||
height: 500px;
|
||||
line-height: 16pt;
|
||||
}
|
74
web/static/documentation.css
Normal file
74
web/static/documentation.css
Normal file
@ -0,0 +1,74 @@
|
||||
.documentation {
|
||||
|
||||
}
|
||||
|
||||
.documentation .summary {
|
||||
margin:0;
|
||||
padding: 0;
|
||||
border: 0.25em solid #3b5998;
|
||||
position: fixed;
|
||||
overflow: auto;
|
||||
width: 25%;
|
||||
margin:0;
|
||||
}
|
||||
|
||||
.documentation .summary h2 {
|
||||
background-color: #3b5998;
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
.documentation .summary ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.documentation .summary ul li {
|
||||
display: block;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.documentation .summary ul li ul {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.documentation .summary ul li:hover ul {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.documentation .summary a {
|
||||
background-color: #5889ef;
|
||||
color: black;
|
||||
display: block;
|
||||
padding: 0.3em;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.documentation .summary a:hover {
|
||||
background-color: #496ebc;
|
||||
}
|
||||
|
||||
.documentation .summary a:active {
|
||||
background-color: #4CAF50; /* Add a green color to the "active/current" link */
|
||||
color: white;
|
||||
}
|
||||
|
||||
.documentation .content {
|
||||
margin-left: 28%;
|
||||
}
|
||||
|
||||
.documentation .content h2 {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.documentation .content h3 {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.documentation .content h4 {
|
||||
padding: 0.25em;
|
||||
}
|
||||
|
||||
.documentation .content ul {
|
||||
list-style-type: none;
|
||||
}
|
14
web/templates/auth/create_user.html
Normal file
14
web/templates/auth/create_user.html
Normal file
@ -0,0 +1,14 @@
|
||||
{% extends "../base.html" %}
|
||||
|
||||
{% block body %}
|
||||
{% if error %}
|
||||
<span style="color: red">Error: {{ error }}</span><p>
|
||||
{% end %}
|
||||
<form action="/auth/create" method="POST">
|
||||
Email: <input name="email" type="text" required><br>
|
||||
Name: <input name="name" type="text" required><br>
|
||||
Password: <input name="password" type="password" required><br>
|
||||
{% module xsrf_form_html() %}
|
||||
<input type="submit">
|
||||
</form>
|
||||
{% end %}
|
19
web/templates/auth/login.html
Normal file
19
web/templates/auth/login.html
Normal file
@ -0,0 +1,19 @@
|
||||
{% extends "../base.html" %}
|
||||
|
||||
{% block title %}
|
||||
{{ escape(handler.settings["website_title"]) }} - {{ _("Connection") }}
|
||||
{% end %}
|
||||
|
||||
{% block body %}
|
||||
{% if error %}
|
||||
<span style="color: red">Error: {{ error }}</span><p>
|
||||
{% end %}
|
||||
|
||||
<form action="/auth/login" method="POST">
|
||||
Email: <input name="email" type="text" required><br>
|
||||
Password: <input name="password" type="password" required><br>
|
||||
{% module xsrf_form_html() %}
|
||||
<input type="submit">
|
||||
</form>
|
||||
New here? <a href="/auth/create?next={{ get_arg("next","") }}">Create account</a>
|
||||
{% end %}
|
31
web/templates/base.html
Normal file
31
web/templates/base.html
Normal file
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<!-- Website title -->
|
||||
<title>{% block title %} {{ escape(handler.settings["website_title"]) }} {% end %}</title>
|
||||
<link rel="stylesheet" href="{{ static_url("base.css") }}" type="text/css">
|
||||
{% block head %}{% end %}
|
||||
</head>
|
||||
<body>
|
||||
<div id="body">
|
||||
<div id="header">
|
||||
<div style="float:right">
|
||||
<a href="/blog">{{ _("Blog") }}</a>
|
||||
{% if current_user %}
|
||||
<a href="/bot/configure">{{ _("Configure") }}</a>
|
||||
<a href="/auth/logout?next={{ url_escape(request.uri) }}">{{ _("Sign out") }}</a>
|
||||
{% if current_user.author %}
|
||||
<a href="/blog/compose">{{ _("Compose") }}</a>
|
||||
{% end %}
|
||||
{% else %}
|
||||
<a href="/auth/login?next={{ url_escape(request.uri) }}">{{ _("Sign in") }}</a> or <a href="/auth/create?next={{ url_escape(request.uri) }}">{{ _("create account") }}</a> to configure your bot.
|
||||
{% end %}
|
||||
</div>
|
||||
<h1><a href="/">{% block title %}{{ escape(handler.settings["website_title"]) }}{% end %}</a></h1>
|
||||
</div>
|
||||
<div id="content">{% block body %}{% end %}</div>
|
||||
</div>
|
||||
{% block bottom %}{% end %}
|
||||
</body>
|
||||
</html>
|
30
web/templates/blog/compose.html
Normal file
30
web/templates/blog/compose.html
Normal file
@ -0,0 +1,30 @@
|
||||
{% extends "..\base.html" %}
|
||||
|
||||
{% block body %}
|
||||
{% if user.author %}
|
||||
<form action="{{ request.path }}" method="post" onsubmit="return validateForm()" class="compose">
|
||||
<div style="margin-bottom:5px">
|
||||
<input name="title" type="text" class="title" value="{{ entry.title if entry else "" }}" required/>
|
||||
</div>
|
||||
<div style="margin-bottom:5px">
|
||||
<textarea name="markdown" rows="30" cols="40" class="markdown" required>
|
||||
{{ entry.markdown if entry else "" }}
|
||||
</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<input type="submit" value="{{ _("Save changes") if entry else _("Publish post") }}" class="submit"/>
|
||||
<a href="{{ "/blog/entry/" + entry.slug if entry else "/" }}">{{ _("Cancel") }}</a>
|
||||
</div>
|
||||
{% if entry %}
|
||||
<input type="hidden" name="id" value="{{ entry.id }}"/>
|
||||
{% end %}
|
||||
{% module xsrf_form_html() %}
|
||||
</form>
|
||||
{% else %}
|
||||
<p style="color: red">You don't have right to edit or create post</p>
|
||||
{% end %}
|
||||
{% end %}
|
||||
|
||||
{% block bottom %}
|
||||
|
||||
{% end %}
|
5
web/templates/blog/entry.html
Normal file
5
web/templates/blog/entry.html
Normal file
@ -0,0 +1,5 @@
|
||||
{% extends "../base.html" %}
|
||||
|
||||
{% block body %}
|
||||
{% module BlogEntry(entry) %}
|
||||
{% end %}
|
17
web/templates/blog/index.html
Normal file
17
web/templates/blog/index.html
Normal file
@ -0,0 +1,17 @@
|
||||
{% extends "../base.html" %}
|
||||
|
||||
{% block title %} {{ escape(handler.settings["website_title"]) }} - Blog{% end %}
|
||||
|
||||
{% block body %}
|
||||
{% for entry in entries %}
|
||||
<div class="entry">
|
||||
<h1><a href="/blog/entry/{{ entry.slug }}">{{ entry.title }}</a></h1>
|
||||
<div class="date">{{ locale.format_date(entry.published, full_format=True, shorter=True) }}</div>
|
||||
<div class="body">{% raw entry.html %}</div>
|
||||
{% if current_user and current_user.author %}
|
||||
<div class="admin"><a href="/blog/compose?id={{ entry.id }}">{{ _("Editer le post") }}</a></div>
|
||||
{% end %}
|
||||
</div>
|
||||
{% end %}
|
||||
<div><a href="/blog/archive">{{ _("Archive") }}</a></div>
|
||||
{% end %}
|
8
web/templates/blog/modules/entry.html
Normal file
8
web/templates/blog/modules/entry.html
Normal file
@ -0,0 +1,8 @@
|
||||
<div class="entry">
|
||||
<h1><a href="/blog/entry/{{ entry.slug }}">{{ entry.title }}</a></h1>
|
||||
<div class="date">{{ locale.format_date(entry.published, full_format=True, shorter=True) }}</div>
|
||||
<div class="body">{% raw entry.html %}</div>
|
||||
{% if current_user.author %}
|
||||
<div class="admin"><a href="/blog/compose?id={{ entry.id }}">{{ _("Editer le post") }}</a></div>
|
||||
{% end %}
|
||||
</div>
|
108
web/templates/bot/help.html
Normal file
108
web/templates/bot/help.html
Normal file
@ -0,0 +1,108 @@
|
||||
{% extends "../base.html" %}
|
||||
|
||||
{% block head %}
|
||||
<link rel="stylesheet" href="{{ static_url("documentation.css") }}" type="text/css">
|
||||
{% end %}
|
||||
|
||||
{% block title %} {{ escape(handler.settings["website_title"]) }} - Help{% end %}
|
||||
|
||||
{% block body %}
|
||||
<div class="documentation">
|
||||
<div class="summary">
|
||||
<h2>Summary</h2>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="#module_modules">Module (<code>modules</code>)</a>
|
||||
<ul>
|
||||
<li><a href="#">Liste des modules</a></li>
|
||||
<li><a href="#">Charger un module</a></li>
|
||||
<li><a href="#">Décharger un module</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#module_help">Help (<code>help</code>)</a>
|
||||
<ul>
|
||||
<li><a href="#">Aide générale</a></li>
|
||||
<li><a href="#">Aide d'un module précit</a></li>
|
||||
<li><a href="#">Aide d'une commande précise</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="">Cofiguration (<code>config</code>)</a></li>
|
||||
<li><a href="">Deeptown (<code>deeptown</code>)</a></li>
|
||||
<li><a href="">Survey (<code>survey</code>)</a></li>
|
||||
<li><a href="">Pi (<code>pi</code>)</a></li>
|
||||
<li><a href="">Github (<code>github</code>)</a></li>
|
||||
<li><a href="">Direct access to database (<code>directAccessDB</code>)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="content">
|
||||
<h2 id="module_modules">Modules (<code>modules</code>)</h2>
|
||||
<div>
|
||||
<h3>Liste des modules (<code>list_modules</code>)</h3>
|
||||
<div>
|
||||
<p>Commande permettant de lister les modules disponibles. (réservé aux administrateurs)</p>
|
||||
<div>
|
||||
<h4>Exemples:</h4>
|
||||
<ul>
|
||||
<li><code>list_modules</code>: retourne la liste des modules disponibles</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<h3>Charger un module (<code>load</code>)</h3>
|
||||
<div>
|
||||
<p>Commande permettant de charger des modules. (réservé aux administrateurs)</p>
|
||||
<div>
|
||||
<h4>Exemples:</h4>
|
||||
<ul>
|
||||
<li><code>load help</code>: charge le module <code>help</code></li>
|
||||
<li><code>load help module1 module2 ...</code>: charger les modules <code>help</code>,
|
||||
<code>module1</code>,
|
||||
<code>module2</code>,
|
||||
...
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<h3>Décharger un module (<code>unload</code>)</h3>
|
||||
<div>
|
||||
<p>Commande permettant de décharger des modules. (réservé aux administrateurs)</p>
|
||||
<div>
|
||||
<h4>Exemples:</h4>
|
||||
<ul>
|
||||
<li><code>unload module</code>: décharge le module <code>module</code></li>
|
||||
<li><code>unload module1 module2 module3 ...</code>: décharge les modules <code>module1</code>,
|
||||
<code>module2</code>,
|
||||
<code>module3</code>, ...
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2 id="module_help">Help (<code>help</code>)</h2>
|
||||
<div>
|
||||
<h3>Aide générale</h3>
|
||||
<div>
|
||||
<p>Commande permettant de lister les différentes commandes actives sur le bot.</p>
|
||||
<div>
|
||||
<h4>Exemples:</h4>
|
||||
<ul>
|
||||
<li><code>help</code>: Afficher la liste des commandes</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<h3>Aide d'un module précit</h3>
|
||||
<div>
|
||||
<p>Commande permmetant d'afficher la description de toutes les commandes d'un module.</p>
|
||||
<div>
|
||||
<h4>Exemples:</h4>
|
||||
<ul>
|
||||
<li><code>help modules</code>: Affiche la description de toutes les commandes du module
|
||||
<code>modules</code>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% end %}
|
5
web/templates/index.html
Normal file
5
web/templates/index.html
Normal file
@ -0,0 +1,5 @@
|
||||
{% extends base.html %}
|
||||
|
||||
{% block body %}
|
||||
Salut
|
||||
{% end %}
|
15
web/templates/viewMsg.html
Normal file
15
web/templates/viewMsg.html
Normal file
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{{ titre }}</title>
|
||||
</head>
|
||||
<body>
|
||||
{% for msg in messages %}
|
||||
<div>
|
||||
<h3>{{ msg.author.name }}</h3>
|
||||
<p>{{ escape(msg.content) }}</p>
|
||||
</div>
|
||||
{% end %}
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user