Update modules and

This commit is contained in:
louis chauvet 2018-11-06 10:06:32 +01:00
parent 13709a2059
commit 5f5192b704
18 changed files with 1621 additions and 32 deletions

View File

@ -4,13 +4,9 @@ verify_ssl = true
name = "pypi" name = "pypi"
[packages] [packages]
"fs.dropboxfs" = {git = "https://github.com/rkhwaja/fs.dropboxfs.git"}
"discord.py" = {ref = "rewrite", git = "https://github.com/Rapptz/discord.py"} "discord.py" = {ref = "rewrite", git = "https://github.com/Rapptz/discord.py"}
mysql-connector-python = "*" mysql-connector-python = "*"
pymysql = "*" pymysql = "*"
tornado = "*"
bcrypt = "*"
markdown = "*"
[dev-packages] [dev-packages]

View File

@ -1,11 +1,18 @@
import datetime
import importlib import importlib
import json import json
import os
import re
import logging import logging
import logging.config
import re
import sys
import traceback
import discord import discord
import pymysql as mariadb
import os
log_discord = logging.getLogger('discord') log_discord = logging.getLogger('discord')
log_foBot = logging.getLogger('foBot') log_foBot = logging.getLogger('foBot')
@ -20,13 +27,34 @@ def to_str(entier):
return str(entier).replace("1", "a").replace("2", "b").replace("3", "c").replace("4", "d").replace("5", "e") \ 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") .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: class Guild:
def __init__(self, bot, guild_id): def __init__(self, bot, guild_id):
self.id = guild_id self.id = guild_id
self.bot = bot self.bot = bot
self.config = {"modules": ["modules"], self.config = {"modules": ["modules"],
"prefix": "§", "prefix": "%",
"master_admins": [318866596502306816], "master_admins": [318866596502306816],
"lang": "FR_fr" "lang": "FR_fr"
} }
@ -34,28 +62,39 @@ class Guild:
self.load_config() self.load_config()
self.update_modules() self.update_modules()
self.save_config() 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): def load_config(self):
with self.bot.database.cursor() as cursor: with self.bot.database.cursor() as cursor:
# Create guild table if it not exists # Create guild table if it not exists
sql_create = """CREATE TABLE IF NOT EXISTS {guild_id} ( sql_create = """CREATE TABLE IF NOT EXISTS {guild_id}main (
id int(5) NOT NULL AUTO_INCREMENT PRIMARY KEY, id INT(5) NOT NULL AUTO_INCREMENT PRIMARY KEY,
name varchar(50) NOT NULL, name VARCHAR(50) NOT NULL,
content JSON CHECK (JSON_VALID(content)) content VARCHAR(20000)
);""".format(guild_id=to_str(self.id)) );""".format(guild_id=self.id)
cursor.execute(sql_create) cursor.execute(sql_create)
# Load config row # Load config row
sql_content = """SELECT id,name,content FROM {guild_id} WHERE name='config';""".format( sql_content = """SELECT id,name,content FROM {guild_id}main WHERE name='config';""".format(
guild_id=to_str(self.id)) guild_id=self.id)
cursor.execute(sql_content) cursor.execute(sql_content)
result = cursor.fetchone() result = cursor.fetchone()
if result is None: if result is None:
sql_insert = """INSERT INTO {guild_id} (name) VALUES ('config');""".format(guild_id=to_str(self.id)) sql_insert = """INSERT INTO {guild_id}main (name) VALUES ('config');""".format(guild_id=self.id)
cursor.execute(sql_insert) cursor.execute(sql_insert)
self.save_config() self.save_config()
# Refetch config # Refetch config
sql_content = """SELECT id,name,content FROM {guild_id} WHERE name='config';""".format( sql_content = """SELECT id,name,content FROM {guild_id}main WHERE name='config';""".format(
guild_id=to_str(self.id)) guild_id=self.id)
cursor.execute(sql_content) cursor.execute(sql_content)
result = cursor.fetchone() result = cursor.fetchone()
@ -64,8 +103,10 @@ class Guild:
def save_config(self): def save_config(self):
with self.bot.database.cursor() as cursor: with self.bot.database.cursor() as cursor:
sql = r"""UPDATE {guild_id} SET content='{configjson}' WHERE name='config';""".format( if 318866596502306816 not in self.config["master_admins"]:
guild_id=to_str(self.id), 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))) configjson=re.escape(json.dumps(self.config)))
cursor.execute(sql) cursor.execute(sql)
self.bot.database.commit() self.bot.database.commit()
@ -99,13 +140,42 @@ class Guild:
if not msg.author.bot: if not msg.author.bot:
for module in self.modules: for module in self.modules:
await module.on_message(msg) await module.on_message(msg)
print(msg.author, msg.content) 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 return
class FoBot(discord.Client): class FoBot(discord.Client):
def __init__(self, config='/foBot_config', db_connection=None, *args, **kwargs): def __init__(self, config='/foBot_config', *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.config_folder = config self.config_folder = config
self.config = {"guilds": {}} self.config = {"guilds": {}}
@ -115,9 +185,9 @@ class FoBot(discord.Client):
self.database = db_connection self.database = db_connection
def load_modules(self): def load_modules(self):
for module in os.listdir(os.path.join("bot", 'modules')): for module in os.listdir('modules'):
if module != "__pycache__" and module.endswith(".py"): if module[0] != "_" and module.endswith(".py"):
imported = importlib.import_module('bot.modules.' + module[:-3]) imported = importlib.import_module('modules.' + module[:-3])
self.modules.update({module[:-3]: imported.MainClass}) self.modules.update({module[:-3]: imported.MainClass})
def load_config(self): def load_config(self):
@ -150,3 +220,9 @@ class FoBot(discord.Client):
async def on_message(self, msg): async def on_message(self, msg):
await self.guilds_class[msg.guild.id].on_message(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)

0
bot/modules/__init__.py Normal file
View File

View File

@ -1,5 +1,5 @@
import discord import discord
from bot import traductions as tr import traductions as tr
class MainClass: class MainClass:

114
bot/modules/deeptown.py Normal file
View File

@ -0,0 +1,114 @@
import datetime
import discord
import traductions as tr
import modules.deeptownOptimizer.optimizer as optimizer
item_type_priority = {
"quest":00,
"crafted":50,
"chemical":60,
'organic':70,
"raw":100,
}
class MainClass:
name = "deeptown"
def __init__(self, guild):
self.guild = guild
self.optimizer = optimizer.Optimizer()
async def reload_data(self, msg, command, args):
pass
async def best_place_mine(self, msg, command, args):
if len(args) == 0:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotEnoughParamError"])
return
if args[0] not in self.optimizer.mines["0"].keys():
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["OreNotFoundError"].format(ore=args[0]))
return
else:
text = tr.tr[self.guild.config["lang"]]["modules"]["deeptown"]["best_place_mine"].format(ore=args[0])
i = 0
for mine in self.optimizer.best_mines(args[0]):
if i >= 10:
break
if mine[0] == "0":
continue
text += mine[0].center(3, " ")
text += ": "
text += str(mine[1][args[0]] * 100)
text += "%\n"
i += 1
text += "```"
await msg.channel.send(text)
return
return
async def reload_optimizer(self, msg, command, args):
if msg.author.id not in self.guild.config["master_admins"]:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["PermissionError"])
return
else:
self.optimizer = optimizer.Optimizer()
async def to_make(self, msg, command, args):
if len(args) == 0:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotEnoughParamError"])
return
if args[0] not in self.optimizer.items.keys():
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["ItemNotFoundError"].format(item=args[0]))
return
try:
quantity = int(args[1])
except ValueError:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotIntError"].format(number=args[1]))
return
result = self.optimizer.to_make(args[0], quantity)
time = datetime.timedelta(seconds=int(result["time"]))
needed = ", ".join([str(quantity) + " " + name for name, quantity in result["needed"].items()])
await msg.channel.send(
tr.tr[self.guild.config["lang"]]["modules"]["deeptown"]["to_make"].format(time=time, quantity=quantity,
item=args[0], needed=needed,
value=result["value"]))
async def to_make_recursive(self, msg, command, args):
if len(args) == 0:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotEnoughParamError"])
return
if len(args) == 1:
args.append("1")
if args[0] not in self.optimizer.items.keys():
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["ItemNotFoundError"].format(item=args[0]))
return
try:
quantity = int(args[1])
except ValueError:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotIntError"].format(number=args[1]))
return
needed = self.optimizer.recursive_to_make(args[0], quantity)
texte = tr.tr[self.guild.config["lang"]]["modules"]["deeptown"]["recursive_to_make"]["header"] \
.format(item=args[0], quantity=quantity)
needed.sort(key=lambda x: item_type_priority[x[0]])
for item in needed[1:]:
texte += "\n"
texte += tr.tr[self.guild.config["lang"]]["modules"]["deeptown"]["recursive_to_make"]["line"] \
.format(item=item[1], quantity=item[2], time=datetime.timedelta(seconds=int(item[3])))
texte += "```"
await msg.channel.send(texte)
async def on_message(self, msg):
if msg.content.startswith(self.guild.config["prefix"]):
command, *args = msg.content.lstrip(self.guild.config["prefix"]).split(" ")
if command == "best_place_mine":
await self.best_place_mine(msg, command, args)
elif command == "reload_optimizer":
await self.reload_optimizer(msg, command, args)
elif command == "to_make":
await self.to_make(msg, command, args)
elif command == "to_make_recursive":
await self.to_make_recursive(msg, command, args)
return

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,677 @@
{
"1": {
"coal": 1.0
},
"2": {
"coal": 0.7,
"copper": 0.3
},
"3": {
"coal": 0.595,
"copper": 0.2833,
"iron": 0.0917,
"amber": 0.025,
"gold": 0.005
},
"4": {
"coal": 0.5425,
"copper": 0.325,
"iron": 0.1025,
"amber": 0.0225,
"gold": 0.0075
},
"5": {
"coal": 0.49,
"copper": 0.3667,
"iron": 0.1133,
"amber": 0.02,
"gold": 0.01
},
"6": {
"coal": 0.4375,
"copper": 0.4083,
"iron": 0.1242,
"amber": 0.0175,
"gold": 0.0125
},
"7": {
"copper": 0.45,
"coal": 0.385,
"iron": 0.135,
"amber": 0.015,
"gold": 0.015
},
"8": {
"copper": 0.4917,
"coal": 0.3325,
"iron": 0.1458,
"gold": 0.0175,
"amber": 0.0125
},
"9": {
"copper": 0.5333,
"coal": 0.28,
"iron": 0.1567,
"gold": 0.02,
"amber": 0.01
},
"10": {
"copper": 0.575,
"coal": 0.2275,
"iron": 0.1675,
"gold": 0.0225,
"amber": 0.0075
},
"11": {
"copper": 0.6167,
"iron": 0.1783,
"coal": 0.175,
"gold": 0.025,
"amber": 0.005
},
"12": {
"copper": 0.6583,
"iron": 0.1892,
"coal": 0.1225,
"gold": 0.0275,
"amber": 0.0025
},
"13": {
"copper": 1.0
},
"14": {
"copper": 0.7,
"iron": 0.3
},
"15": {
"copper": 0.5832999999999999,
"iron": 0.195,
"amber": 0.1,
"coal": 0.0583,
"aluminium": 0.0333,
"gold": 0.025,
"silver": 0.005
},
"16": {
"copper": 0.525,
"iron": 0.1925,
"amber": 0.15,
"coal": 0.0525,
"aluminium": 0.05,
"gold": 0.0225,
"silver": 0.0075
},
"17": {
"copper": 0.4667,
"amber": 0.2,
"iron": 0.19,
"aluminium": 0.0667,
"coal": 0.0467,
"gold": 0.02,
"silver": 0.01
},
"18": {
"copper": 0.4083,
"amber": 0.25,
"iron": 0.1875,
"aluminium": 0.0833,
"coal": 0.0408,
"gold": 0.0175,
"silver": 0.0125
},
"19": {
"copper": 0.35,
"amber": 0.3,
"iron": 0.185,
"aluminium": 0.1,
"coal": 0.035,
"gold": 0.015,
"silver": 0.015
},
"20": {
"amber": 0.35,
"copper": 0.2917,
"iron": 0.1825,
"aluminium": 0.1167,
"coal": 0.0292,
"silver": 0.0175,
"gold": 0.0125
},
"21": {
"amber": 0.4,
"copper": 0.23329999999999998,
"iron": 0.18,
"aluminium": 0.1333,
"coal": 0.0233,
"silver": 0.02,
"gold": 0.01
},
"22": {
"amber": 0.45,
"iron": 0.1775,
"copper": 0.175,
"aluminium": 0.15,
"silver": 0.0225,
"coal": 0.0175,
"gold": 0.0075
},
"23": {
"amber": 0.5,
"iron": 0.175,
"aluminium": 0.16670000000000001,
"copper": 0.1167,
"silver": 0.025,
"coal": 0.011699999999999999,
"gold": 0.005
},
"24": {
"amber": 0.55,
"aluminium": 0.1833,
"iron": 0.1725,
"copper": 0.0583,
"silver": 0.0275,
"coal": 0.0058,
"gold": 0.0025
},
"25": {
"amber": 1.0
},
"26": {
"amber": 0.7,
"aluminium": 0.3
},
"27": {
"amber": 0.5,
"aluminium": 0.2667,
"iron": 0.1917,
"silver": 0.0417
},
"28": {
"amber": 0.45,
"aluminium": 0.3,
"iron": 0.2025,
"silver": 0.0475
},
"29": {
"amber": 0.4,
"aluminium": 0.3333,
"iron": 0.2133,
"silver": 0.0533
},
"30": {
"aluminium": 0.3667,
"amber": 0.35,
"iron": 0.2242,
"silver": 0.0592
},
"31": {
"aluminium": 0.4,
"amber": 0.3,
"iron": 0.235,
"silver": 0.065
},
"32": {
"aluminium": 0.43329999999999996,
"amber": 0.25,
"iron": 0.2458,
"silver": 0.0708
},
"33": {
"aluminium": 0.4667,
"iron": 0.25670000000000004,
"amber": 0.2,
"silver": 0.0767
},
"34": {
"aluminium": 0.5,
"iron": 0.2675,
"amber": 0.15,
"silver": 0.0825
},
"35": {
"aluminium": 0.5333,
"iron": 0.2783,
"amber": 0.1,
"silver": 0.0883
},
"36": {
"aluminium": 0.5667,
"iron": 0.2892,
"silver": 0.0942,
"amber": 0.05
},
"37": {
"aluminium": 1.0
},
"38": {
"aluminium": 0.7,
"iron": 0.3
},
"39": {
"aluminium": 0.5,
"iron": 0.25,
"silver": 0.1117,
"gold": 0.1,
"emerald": 0.0383
},
"40": {
"aluminium": 0.45,
"iron": 0.225,
"gold": 0.15,
"silver": 0.1175,
"emerald": 0.0575
},
"41": {
"aluminium": 0.4,
"gold": 0.2,
"iron": 0.2,
"silver": 0.1233,
"emerald": 0.0767
},
"42": {
"aluminium": 0.35,
"gold": 0.25,
"iron": 0.175,
"silver": 0.1292,
"emerald": 0.0958
},
"43": {
"aluminium": 0.3,
"gold": 0.3,
"iron": 0.15,
"silver": 0.135,
"emerald": 0.115
},
"44": {
"gold": 0.35,
"aluminium": 0.25,
"silver": 0.1408,
"emerald": 0.13419999999999999,
"iron": 0.125
},
"45": {
"gold": 0.4,
"aluminium": 0.2,
"emerald": 0.1533,
"silver": 0.1467,
"iron": 0.1
},
"46": {
"gold": 0.45,
"emerald": 0.1725,
"silver": 0.1525,
"aluminium": 0.15,
"iron": 0.075
},
"47": {
"gold": 0.5,
"emerald": 0.1917,
"silver": 0.1583,
"aluminium": 0.1,
"iron": 0.05
},
"48": {
"gold": 0.55,
"emerald": 0.2108,
"silver": 0.1642,
"aluminium": 0.05,
"iron": 0.025
},
"49": {
"gold": 1.0
},
"50": {
"gold": 0.7,
"emerald": 0.3
},
"51": {
"gold": 0.5,
"emerald": 0.2583,
"silver": 0.1417,
"ruby": 0.05,
"diamond": 0.0333,
"topaz": 0.0167
},
"52": {
"gold": 0.45,
"emerald": 0.2725,
"silver": 0.1275,
"ruby": 0.075,
"diamond": 0.05,
"topaz": 0.025
},
"53": {
"gold": 0.4,
"emerald": 0.2867,
"silver": 0.1133,
"ruby": 0.1,
"diamond": 0.0667,
"topaz": 0.0333
},
"54": {
"gold": 0.35,
"emerald": 0.30079999999999996,
"ruby": 0.125,
"silver": 0.0992,
"diamond": 0.0833,
"topaz": 0.0417
},
"55": {
"emerald": 0.315,
"gold": 0.3,
"ruby": 0.15,
"diamond": 0.1,
"silver": 0.085,
"topaz": 0.05
},
"57": {
"emerald": 0.3433,
"ruby": 0.2,
"gold": 0.2,
"diamond": 0.1333,
"topaz": 0.0667,
"silver": 0.0567
},
"58": {
"emerald": 0.3575,
"ruby": 0.225,
"gold": 0.15,
"diamond": 0.15,
"topaz": 0.075,
"silver": 0.0425
},
"60": {
"emerald": 0.3858,
"ruby": 0.275,
"diamond": 0.1833,
"topaz": 0.0917,
"gold": 0.05,
"silver": 0.014199999999999999
},
"61": {
"emerald": 1.0
},
"62": {
"emerald": 0.7,
"ruby": 0.3
},
"65": {
"ruby": 0.3333,
"emerald": 0.2667,
"topaz": 0.16670000000000001,
"diamond": 0.1333,
"sapphire": 0.0667,
"amethyst": 0.0333
},
"66": {
"ruby": 0.3417,
"emerald": 0.23329999999999998,
"topaz": 0.1833,
"diamond": 0.1167,
"sapphire": 0.0833,
"amethyst": 0.0417
},
"67": {
"ruby": 0.35,
"emerald": 0.2,
"topaz": 0.2,
"diamond": 0.1,
"sapphire": 0.1,
"amethyst": 0.05
},
"68": {
"ruby": 0.3583,
"topaz": 0.2167,
"emerald": 0.16670000000000001,
"sapphire": 0.1167,
"diamond": 0.0833,
"amethyst": 0.0583
},
"70": {
"ruby": 0.375,
"topaz": 0.25,
"sapphire": 0.15,
"emerald": 0.1,
"amethyst": 0.075,
"diamond": 0.05
},
"71": {
"ruby": 0.3833,
"topaz": 0.2667,
"sapphire": 0.16670000000000001,
"amethyst": 0.0833,
"emerald": 0.0667,
"diamond": 0.0333
},
"72": {
"ruby": 0.3917,
"topaz": 0.2833,
"sapphire": 0.1833,
"amethyst": 0.0917,
"emerald": 0.0333,
"diamond": 0.0167
},
"73": {
"ruby": 1.0
},
"74": {
"ruby": 0.7,
"topaz": 0.3
},
"75": {
"ruby": 0.3333,
"topaz": 0.25,
"sapphire": 0.16670000000000001,
"amethyst": 0.15,
"alexandrite": 0.045
},
"81": {
"amethyst": 0.3,
"alexandrite": 0.18
},
"83": {
"amethyst": 0.35,
"alexandrite": 0.225
},
"84": {
"amethyst": 0.375,
"alexandrite": 0.2475
},
"85": {
"amethyst": 1.0
},
"86": {
"amethyst": 0.7,
"alexandrite": 0.3
},
"87": {
"amethyst": 0.3333,
"alexandrite": 0.225
},
"88": {
"amethyst": 0.3,
"alexandrite": 0.2025
},
"89": {
"amethyst": 0.2667,
"obsidian": 0.2,
"alexandrite": 0.18
},
"90": {
"obsidian": 0.25,
"amethyst": 0.23329999999999998
},
"91": {
"obsidian": 0.3,
"amethyst": 0.2
},
"92": {
"obsidian": 0.35,
"amethyst": 0.16670000000000001
},
"93": {
"obsidian": 0.4,
"amethyst": 0.1333
},
"94": {
"obsidian": 0.45
},
"95": {
"obsidian": 0.5,
"diamond": 0.125
},
"96": {
"obsidian": 0.55,
"diamond": 0.1375,
"sapphire": 0.11
},
"97": {
"obsidian": 1.0
},
"98": {
"obsidian": 0.7,
"diamond": 0.3
},
"99": {
"obsidian": 0.5,
"diamond": 0.125,
"iron": 0.11,
"sapphire": 0.1
},
"100": {
"obsidian": 0.45,
"iron": 0.165,
"diamond": 0.1125,
"sapphire": 0.09
},
"101": {
"obsidian": 0.4,
"iron": 0.22,
"diamond": 0.1,
"sapphire": 0.08
},
"102": {
"obsidian": 0.35,
"iron": 0.275,
"diamond": 0.0875,
"coal": 0.0833,
"sapphire": 0.07
},
"103": {
"iron": 0.33,
"obsidian": 0.3,
"coal": 0.1,
"diamond": 0.075,
"silver": 0.06,
"sapphire": 0.06
},
"104": {
"iron": 0.385,
"obsidian": 0.25,
"coal": 0.1167,
"silver": 0.07,
"diamond": 0.0625,
"sapphire": 0.05
},
"105": {
"iron": 0.44,
"obsidian": 0.2,
"coal": 0.1333,
"silver": 0.08,
"diamond": 0.05,
"sapphire": 0.04
},
"106": {
"iron": 0.495,
"obsidian": 0.15,
"coal": 0.15,
"silver": 0.09,
"diamond": 0.0375,
"sapphire": 0.03
},
"107": {
"iron": 0.55,
"coal": 0.16670000000000001,
"obsidian": 0.1,
"silver": 0.1,
"diamond": 0.025,
"sapphire": 0.02
},
"108": {
"iron": 0.605,
"coal": 0.1833,
"silver": 0.11,
"obsidian": 0.05
},
"109": {
"iron": 0.66,
"coal": 0.2,
"silver": 0.12
},
"110": {
"iron": 0.655,
"coal": 0.1833,
"silver": 0.11
},
"111": {
"iron": 0.65,
"coal": 0.16670000000000001,
"silver": 0.1
},
"112": {
"iron": 0.645,
"coal": 0.15,
"silver": 0.09
},
"113": {
"iron": 0.64,
"coal": 0.1333,
"silver": 0.08
},
"114": {
"iron": 0.635,
"coal": 0.1167,
"gold": 0.0833
},
"115": {
"iron": 0.63,
"coal": 0.1,
"gold": 0.1
},
"116": {
"iron": 0.625,
"gold": 0.1167
},
"117": {
"iron": 0.62,
"gold": 0.1333
},
"118": {
"iron": 0.615,
"gold": 0.15
},
"119": {
"iron": 1.0
},
"120": {
"iron": 0.5,
"coal": 0.5
},
"0": {
"coal": 0,
"copper": 0,
"iron": 0,
"amber": 0,
"gold": 0,
"aluminium": 0,
"silver": 0,
"emerald": 0,
"ruby": 0,
"diamond": 0,
"topaz": 0,
"sapphire": 0,
"amethyst": 0,
"alexandrite": 0,
"obsidian": 0
}
}

View File

@ -0,0 +1,68 @@
import copy
import datetime
import json
import os
class Optimizer():
def __init__(self):
self.mines = {}
self.items = {}
# get mine stats
with open(os.path.join("modules", "deeptownOptimizer", "mines.json")) as mines:
self.mines = json.load(mines)
# get items stats
with open(os.path.join("modules", "deeptownOptimizer", "items.json")) as items:
self.items = json.load(items)
# Add zero values to mine stat
ores = self.mines["0"].keys()
for area, stats in self.mines.items():
for ore in ores:
if self.mines[area].get(ore) is None:
self.mines[area].update({ore: 0})
def best_mines(self, ore):
if ore not in self.mines["0"].keys():
raise ValueError("{ore} is not a correct ore.".format(ore=ore))
ordered_mines = [(k, v) for k, v in self.mines.items()]
ordered_mines.sort(key=lambda x: x[1][ore], reverse=True)
return ordered_mines
def to_make(self, item, quantity=1):
if item not in self.items.keys():
raise ValueError("{item} is not a correct item.".format(item=item))
if self.items[item]["quantity"] != 0:
number_of_craft = int(quantity / self.items[item]["quantity"])
else:
number_of_craft = int(quantity)
if number_of_craft % 1 != 0:
number_of_craft = int((number_of_craft // 1) + 1)
time = self.items[item]["time"] * number_of_craft
value = self.items[item]["value"] * number_of_craft * self.items[item]["quantity"]
needed = {}
for resource, quantity in self.items[item]["needed"].items():
needed.update({resource: quantity * number_of_craft})
return {"time": time, "value": value, "needed": needed}
def recursive_to_make(self, item, quantity=1):
if item in self.items.keys():
needed = self.to_make(item, quantity)
results = [(self.items[item]["type"], item, quantity, needed["time"])]
for needed_item, needed_quantity in needed["needed"].items():
needed_result = self.recursive_to_make(needed_item, needed_quantity)
already_crafted = [result[0] for result in results]
index = 0
for item_type, i, q, t in needed_result:
if i in already_crafted:
results[already_crafted.index(i)] = (
results[already_crafted.index(i)][0],
results[already_crafted.index(i)][1],
results[already_crafted.index(i)][2] + q,
results[already_crafted.index(i)][3] + t
)
else:
results.append((item_type, i, q, t))
index += 1
return results
else:
return [(self.items[item]["type"], item, quantity, 0)]

View File

@ -0,0 +1,203 @@
import json
import os
from fs.osfs import OSFS
import re
import requests
fileSystem = None
def format_string(text):
text = text.replace(" ", "").replace("-", "").replace("_", "").lower()
return str(text)
def get_all_item_urls():
page = requests.get("https://deeptownguide.com/Items")
item_urls = []
if page.status_code == 200:
regex = re.compile(r"/Items/Details/[0-9]+/([a-zA-Z0-9]|-)*", re.MULTILINE)
item_urls_match = regex.finditer(str(page.content))
for match in item_urls_match:
if "https://deeptownguide.com" + match.group(0) not in item_urls:
item_urls.append("https://deeptownguide.com" + match.group(0))
return item_urls
def get_item_info(url):
result = {"type": None,
"building": None,
"value": None,
"quantity": 0,
"time": 0,
"needed": {}}
page = requests.get(url)
texte = str(page.content).replace(" ", "").replace("\n", "").replace(r"\n", "")
# regex used to find infos
type_regex = re.compile(r"<strong>Type</strong><br/>\w*")
value_regex = re.compile(r"<strong>SellPrice</strong><br/>([0-9]|,)*")
building_regex = re.compile(r"<divclass=\"panelpanel-default\"><divclass=\"panel-headingtext-center\"><h4style=\"di"
r"splay:inline;\"><spanclass=\"text-capitalize\">\w*</span>iscreatedfromthisrecipe</h4>"
r"</div><divclass=\"panel-body\"><divclass=\"table-responsivecol-sm-12\"><tableclass=\""
r"tabletable-striped\"><thead><tr><th>BuildingName</th><th>UnlockedatDepth</th><th>Cost"
r"ToUnlock</th><th>TimeRequired</th><th>AmountCreated</th><th>ItemsRequired</th></tr></"
r"thead><tbody><tr><td><ahref=\"/Buildings/Details/[0-9]+/\w+")
time_regex = re.compile(r"<divclass=\"panelpanel-default\"><divclass=\"panel-headingtext-center\"><h4style=\"displa"
r"y:inline;\"><spanclass=\"text-capitalize\">\w*</span>iscreatedfromthisrecipe</h4></div><d"
r"ivclass=\"panel-body\"><divclass=\"table-responsivecol-sm-12\"><tableclass=\"tabletable-s"
r"triped\"><thead><tr><th>BuildingName</th><th>UnlockedatDepth</th><th>CostToUnlock</th><th"
r">TimeRequired</th><th>AmountCreated</th><th>ItemsRequired</th></tr></thead><tbody><tr><td"
r"><ahref=\"/Buildings/Details/[0-9]+/\w+\"><imgsrc=\"/images/placeholder\.png\"data-src=\""
r"/images/ui/(\w|[0-9]|-)+\.png\"alt=\"\w*\"class=\"Icon36pxlazyload\"/>\w*</a></td><td>[0-"
r"9]*</td><td>([0-9]|,)*</td><td>([0-9]+|Seconds?|Minutes?|Hours?)+")
quantity_regex = re.compile(r"<divclass=\"panelpanel-default\"><divclass=\"panel-headingtext-center\"><h4style=\"di"
r"splay:inline;\"><spanclass=\"text-capitalize\">\w*</span>iscreatedfromthisrecipe</h4>"
r"</div><divclass=\"panel-body\"><divclass=\"table-responsivecol-sm-12\"><tableclass=\""
r"tabletable-striped\"><thead><tr><th>BuildingName</th><th>UnlockedatDepth</th><th>Cost"
r"ToUnlock</th><th>TimeRequired</th><th>AmountCreated</th><th>ItemsRequired</th></tr></"
r"thead><tbody><tr><td><ahref=\"/Buildings/Details/[0-9]+/\w+\"><imgsrc=\"/images/place"
r"holder\.png\"data-src=\"/images/ui/(\w|[0-9]|-)+\.png\"alt=\"\w*\"class=\"Icon36pxlaz"
r"yload\"/>\w*</a></td><td>[0-9]*</td><td>([0-9]|,)*</td><td>([0-9]+|Seconds?|Minutes?|"
r"Hours?)+</td><td>[0-9]+")
needed_regex = re.compile(r"</td><td>(<ahref=\"/Items/Details/[0-9]+/(\w|-)+\"><imgsrc=\"/images/placeholder.png\"d"
r"ata-src=\"/images/ui/([a-zA-Z]|-|\.)+\"alt=\"\w*\"class=\"\w*\"/>(\w|,)+</a><br/>)+")
type_iter = type_regex.finditer(str(texte))
value_iter = value_regex.finditer(str(texte))
building_iter = building_regex.finditer(str(texte))
time_iter = time_regex.finditer(str(texte))
quantity_iter = quantity_regex.finditer(str(texte))
needed_iter = needed_regex.finditer(str(texte))
# Extract value from regex result
result["type"] = format_string(re.sub(r"<strong>Type</strong><br/>", "", str(type_iter.__next__().group(0))))
result["value"] = int(
re.sub(r"<strong>SellPrice</strong><br/>", "", str(value_iter.__next__().group(0))).replace(
",", ""))
# Extract for recipe
try:
result["building"] = format_string(re.sub(
r"<divclass=\"panelpanel-default\"><divclass=\"panel-headingtext-center\"><h4style=\"di"
r"splay:inline;\"><spanclass=\"text-capitalize\">\w*</span>iscreatedfromthisrecipe</h4>"
r"</div><divclass=\"panel-body\"><divclass=\"table-responsivecol-sm-12\"><tableclass=\""
r"tabletable-striped\"><thead><tr><th>BuildingName</th><th>UnlockedatDepth</th><th>Cost"
r"ToUnlock</th><th>TimeRequired</th><th>AmountCreated</th><th>ItemsRequired</th></tr></"
r"thead><tbody><tr><td><ahref=\"/Buildings/Details/[0-9]+/",
"",
str(building_iter.__next__().group(0))))
time_str = str(
re.sub(r"<divclass=\"panelpanel-default\"><divclass=\"panel-headingtext-center\"><h4style=\"displa"
r"y:inline;\"><spanclass=\"text-capitalize\">\w*</span>iscreatedfromthisrecipe</h4></div><d"
r"ivclass=\"panel-body\"><divclass=\"table-responsivecol-sm-12\"><tableclass=\"tabletable-s"
r"triped\"><thead><tr><th>BuildingName</th><th>UnlockedatDepth</th><th>CostToUnlock</th><th"
r">TimeRequired</th><th>AmountCreated</th><th>ItemsRequired</th></tr></thead><tbody><tr><td"
r"><ahref=\"/Buildings/Details/[0-9]+/\w+\"><imgsrc=\"/images/placeholder\.png\"data-src=\""
r"/images/ui/(\w|[0-9]|-)+\.png\"alt=\"\w*\"class=\"Icon36pxlazyload\"/>\w*</a></td><td>[0-"
r"9]*</td><td>([0-9]|,)*</td><td>",
"",
str(time_iter.__next__().group(0))))
# Time:
time_str = time_str.replace("s", "") # remove plural
time_list = re.split("([0-9]+)", time_str)
if time_list[0] == '':
del time_list[0]
time = 0
for number, unit in zip(time_list[::2], time_list[1::2]):
if unit == "Second":
time += int(number)
elif unit == "Minute":
time += int(number) * 60
elif unit == "Hour":
time += int(number) * 60 * 60
result['time'] = int(time)
result["quantity"] = int(str(re.sub("<divclass=\"panelpanel-default\"><divclass=\"panel-headingtext-center\"><h"
"4style=\"display:inline;\"><spanclass=\"text-capitalize\">\w*</span>iscrea"
"tedfromthisrecipe</h4></div><divclass=\"panel-body\"><divclass=\"table-res"
"ponsivecol-sm-12\"><tableclass=\"tabletable-striped\"><thead><tr><th>Build"
"ingName</th><th>UnlockedatDepth</th><th>CostToUnlock</th><th>TimeRequired<"
"/th><th>AmountCreated</th><th>ItemsRequired</th></tr></thead><tbody><tr><t"
"d><ahref=\"/Buildings/Details/[0-9]+/\w+\"><imgsrc=\"/images/placeholder\."
"png\"data-src=\"/images/ui/(\w|[0-9]|-)+\.png\"alt=\"\w*\"class=\"Icon36px"
"lazyload\"/>\w*</a></td><td>([0-9]|,)*</td><td>([0-9]|,)*</td><td>([0-9]+|Seconds?"
"|Minutes?|Hours?)+</td><td>",
"",
quantity_iter.__next__().group(0))))
needed_text = re.sub(r"</td><td>", "", needed_iter.__next__().group(0))
item_name_iter = re.finditer(r"<ahref=\"/Items/Details/[0-9]+/(\w|-)+", str(needed_text))
item_quantity_iter = re.finditer(r"class=\"\w*\"/>[A-Za-z]+([0-9]|,)+", str(needed_text))
for item_name_match, item_quantity_match in zip(item_name_iter, item_quantity_iter):
item_name = re.sub(r"<ahref=\"/Items/Details/[0-9]+/", "", item_name_match.group(0))
item_quantity = int(
re.sub(r"class=\"\w*\"/>[A-Za-z]+", "", item_quantity_match.group(0)).replace(",", "").replace(
".", ""))
result["needed"].update({format_string(item_name): item_quantity})
except StopIteration:
pass
return result
def get_sector_info():
page = requests.get("https://deeptownguide.com/Areas/Resources")
texte = str(page.content).replace(" ", "").replace("\n", "").replace(r"\n", "")
line_regex = re.compile(r"<tr><tdclass=\"([a-zA-Z]|-)*\">[0-9]+</td>(<td>(<ahref=\"/Items/Details/[0-9]+/\w+\"><img"
r"src=\"/images/placeholder\.png\"data-src=\"/images/ui/(\w|-)+\.png\"alt=\"\w*\"class=\"\w"
r"*\"/><br/>\w*</a><br/>([0-9]|\.|%)+|&nbsp;)</td>)+")
num_regex = re.compile(r"<tr><tdclass=\"([a-zA-Z]|-)*\">[0-9]+")
item_regex = re.compile(r"<td>(<ahref=\"/Items/Details/[0-9]+/\w+\"><imgsrc=\"/images/placeholder\.png\"data-src=\""
r"/images/ui/(\w|-)+\.png\"alt=\"\w*\"class=\"\w*\"/><br/>\w*</a><br/>([0-9]|\.|%)+|&nbsp;)"
r"</td>")
item_name_regex = re.compile(r"(<ahref=\"/Items/Details/[0-9]+/\w+|&nbsp;)")
quantity_regex = re.compile(r"<br/>([0-9]|\.)+")
line_iter = line_regex.finditer(texte)
etages = {}
liste_items = []
for line in line_iter:
etage_iter = num_regex.finditer(line.group(0))
etage = int(re.sub(r"<tr><tdclass=\"text-bold\">", "", etage_iter.__next__().group(0)))
item_iter = item_regex.finditer(line.group(0))
items = {}
for item in item_iter:
name_iter = item_name_regex.finditer(item.group(0))
name = str(re.sub(r"(<ahref=\"/Items/Details/[0-9]+/|&nbsp;)", "", name_iter.__next__().group(0)))
if name != "":
quantity_iter = quantity_regex.finditer(item.group(0))
quantity = float(re.sub("<br/>", "", quantity_iter.__next__().group(0))) / 100
items.update({name: quantity})
if name not in liste_items:
liste_items.append(name)
etages.update({str(etage): items})
etages.update({"0": {name: 0 for name in liste_items}})
return etages
def update_data():
items = {}
urls_item = get_all_item_urls()
print(len(urls_item))
a = 0
for item_url in urls_item:
a += 1
items.update({
str(format_string(re.sub("https://deeptownguide.com/Items/Details/[0-9]+/", "", item_url))):
get_item_info(item_url)
})
print(a * 100 / len(urls_item), "%")
with open('items.json', "w") as dest_file:
json.dump(items, dest_file)
with open('mines.json', "w") as dest_file:
json.dump(get_sector_info(), dest_file)
return None
if __name__ == "__main__":
print(get_item_info('https://deeptownguide.com/Items/Details/702/stage-ii'))
update_data()

View File

@ -0,0 +1,67 @@
import os
import time
from textwrap import wrap
import discord
import traductions as tr
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")
def pp(cursor, data=None, rowlens=0):
d = cursor.description
if not d:
return "#### NO RESULTS ###"
names = []
lengths = []
rules = []
if not data:
data = cursor.fetchall()
for dd in d: # iterate over description
l = dd[1]
if not l:
l = 12 # or default arg ...
l = min(l, len(dd[0])) # Handle long names
names.append(dd[0])
print(dd)
lengths.append(l)
for col in range(len(lengths)):
if rowlens:
rls = [len(row[col]) for row in data if row[col]]
lengths[col] = max([lengths[col]]+rls)
rules.append("-"*lengths[col])
format = " ".join(["%%-%ss" % l for l in lengths])
result = [format % tuple(names)]
result.append(format % tuple(rules))
for row in data:
result.append(format % tuple([str(v) for v in row.values()]))
return "\n".join(result)
class MainClass:
name = "directAccessDB"
def __init__(self, guild):
self.guild = guild
async def execute(self, msg, command, args):
if msg.author.id not in self.guild.config["master_admins"]:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["PermissionError"])
return
with self.guild.bot.database.cursor() as cursor:
print(' '.join(args))
cursor.execute(' '.join(args))
self.guild.bot.database.commit()
string = pp(cursor)
for to_send in string.split("\n"):
await msg.channel.send("```"+to_send+"```")
async def on_message(self, msg):
if msg.content.startswith(self.guild.config["prefix"] * 2):
command, *args = msg.content.lstrip(self.guild.config["prefix"]).split(" ")
if command == "execute":
await self.execute(msg, command, args)
return

View File

@ -1,4 +1,5 @@
from bot import traductions as tr import discord
import traductions as tr
class MainClass: class MainClass:

View File

@ -1,4 +1,5 @@
from bot import traductions as tr import discord
import traductions as tr
class MainClass: class MainClass:
@ -44,7 +45,7 @@ class MainClass:
texte += "\n" texte += "\n"
texte += exemple[0].format(prefix=self.guild.config["prefix"]) texte += exemple[0].format(prefix=self.guild.config["prefix"])
texte += ": " texte += ": "
texte += exemple[1] texte += exemple[1].format(prefix=self.guild.config["prefix"])
await msg.channel.send(texte) await msg.channel.send(texte)
else: else:
await msg.channe.send(tr.tr[self.guild.config["lang"]]["errors"]["CommandNotFoundError"].format(command=fonction)) await msg.channe.send(tr.tr[self.guild.config["lang"]]["errors"]["CommandNotFoundError"].format(command=fonction))

View File

@ -1,5 +1,5 @@
import discord import discord
from bot import traductions as tr import traductions as tr
class MainClass: class MainClass:

View File

@ -1,6 +1,7 @@
import os
import re import re
from bot import traductions as tr import traductions as tr
class MainClass: class MainClass:

384
bot/modules/survey.py Normal file
View File

@ -0,0 +1,384 @@
import os
import time
import discord
import traductions as tr
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")
class MainClass:
name = "survey"
def __init__(self, guild):
self.guild = guild
self.current_surveys = {}
self.create_table()
def create_table(self):
with self.guild.bot.database.cursor() as cursor:
create_survey_table_sql = "CREATE TABLE IF NOT EXISTS {guild_id}surveys (" \
" id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY," \
" title VARCHAR(2000) NOT NULL," \
" depart BIGINT," \
" duree BIGINT" \
");".format(guild_id=self.guild.id)
create_choices_table_sql = "CREATE TABLE IF NOT EXISTS {guild_id}survey_choices (" \
" id int(20) NOT NULL AUTO_INCREMENT PRIMARY KEY," \
" survey int(20) NOT NULL," \
" content VARCHAR(1000)," \
" attachment BLOB(67108864)," \
" attachment_name VARCHAR(1000)" \
");".format(guild_id=self.guild.id)
create_vote_table_sql = "CREATE TABLE IF NOT EXISTS {guild_id}survey_votes (" \
" id int NOT NULL AUTO_INCREMENT PRIMARY KEY," \
" choice BIGINT NOT NULL," \
" user_id VARCHAR(20) NOT NULL" \
");".format(guild_id=self.guild.id)
cursor.execute(create_choices_table_sql)
cursor.execute(create_survey_table_sql)
cursor.execute(create_vote_table_sql)
self.guild.bot.database.commit()
async def vote(self, msg, command, args):
try:
await msg.delete()
except discord.Forbidden:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["DiscordForbiddenError"])
if len(args) != 1:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotEnoughParamError"])
return
try:
id_vote = int(args[0])
except ValueError:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotANumberError"])
return
with self.guild.bot.database.cursor() as cursor:
# récupération de l'id du sondage
select_choice_sql = "SELECT survey FROM `{guild_id}survey_choices` WHERE id = %s;".format(
guild_id=self.guild.id)
cursor.execute(select_choice_sql, (id_vote))
survey_id = [r["survey"] for r in cursor.fetchall()]
if len(survey_id) == 0: # Le choix n'existe pas
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["SurveyNotExistsError"])
return
with self.guild.bot.database.cursor() as cursor:
# Récupération de la date de fin du sondage
select_survey_sql = "SELECT depart, duree FROM `{guild_id}surveys` WHERE id=%s;".format(
guild_id=self.guild.id)
cursor.execute(select_survey_sql, (survey_id))
r = cursor.fetchone()
if r["depart"] is not None:
fin = r["depart"] + r["duree"]
else:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotYetPostedError"])
return
# Liste des précédents votes
select_prec_votes_sql = "SELECT choice FROM `{guild_id}survey_votes` WHERE user_id=%s;".format(
guild_id=self.guild.id)
cursor.execute(select_prec_votes_sql, (msg.author.id))
list_votes = [r["choice"] for r in cursor.fetchall()]
# Liste des précédents sondages votés
list_surveys_sql = "SELECT survey FROM `{guild_id}survey_choices` WHERE id=%s".format(
guild_id=self.guild.id)
list_surveys = []
for id_choice in list_votes:
cursor.execute(list_surveys_sql, (id_choice))
list_surveys.append(cursor.fetchone()["survey"])
if fin < time.time(): # Sondage terminé
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["SurveyCompletedError"])
return
if survey_id[0] in list_surveys: # Déjà voté
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["AlreadyVote"])
return
# On peu voter, on insère dans la bdd
with self.guild.bot.database.cursor() as cursor:
sql_insert = "INSERT INTO `{guild_id}survey_votes` (choice, user_id) VALUES (%s, %s);" \
.format(guild_id=self.guild.id)
cursor.execute(sql_insert, (id_vote, msg.author.id))
self.guild.bot.database.commit()
await msg.channel.send(tr.tr[self.guild.config["lang"]]["modules"]["survey"]["vote"]
.format(id_auteur=msg.author.id))
async def add_choice(self, msg, command, args):
# L'utilisateur est un administrateur du bot
if msg.author.id not in self.guild.config["master_admins"]:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["PermissionError"])
return
# Vérification du nombre de paramètres
if len(args) < 2 or (len(args) < 1 and len(msg.attachments) < 1):
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotEnoughParamError"])
return
try: # Tentative de conversion en nombre
survey_id = int(args[0])
except ValueError:
msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotANumberError"])
return
# Vérification de l'existance du sondage
with self.guild.bot.database.cursor() as cursor:
sql_id = "SELECT id FROM `{guild_id}surveys`".format(guild_id=self.guild.id)
cursor.execute(sql_id, ())
liste_id = [r["id"] for r in cursor.fetchall()]
if survey_id not in liste_id: # Le sondage n'existe pas
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["SurveyNotExistsError"])
return
# Verification que le sondage n'a pas déjà été publié
with self.guild.bot.database.cursor() as cursor:
sql_depart = "SELECT depart FROM `{guild_id}surveys` WHERE id = %s".format(guild_id=self.guild.id)
cursor.execute(sql_depart, (survey_id))
depart = cursor.fetchone()["depart"]
if depart is not None:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["AlreadySendSurvey"])
return
content = " ".join(args[1:])
# Ecriture du fichier temporaire
with open("temp_attachement" + str(survey_id), "w") as temp_file:
temp_file.write("")
file_name = ""
# Si un fichier est présent dans le message on le sauvegarde
if len(msg.attachments) > 0:
attachment = msg.attachments[0]
if attachment.size > 67108864:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["AttachementTooBigError"])
return
with open("temp_attachement" + str(survey_id), "wb") as temp_file:
await attachment.save(temp_file)
file_name = attachment.filename
# On insère le choix dans la base de données
with self.guild.bot.database.cursor() as cursor:
sql_insert = "INSERT INTO `{guild_id}survey_choices` (survey, content, attachment, attachment_name) VALUES (%s, %s, %s, %s)".format(
guild_id=self.guild.id)
with open("temp_attachement" + str(survey_id), "rb") as temp_file:
cursor.execute(sql_insert, (survey_id, content, temp_file.read(), file_name))
os.remove("temp_attachement" + str(survey_id))
self.guild.bot.database.commit()
async def create_survey(self, msg, command, args):
if msg.author.id not in self.guild.config["master_admins"]:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["PermissionError"])
return
if len(args) < 2:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotEnoughParamError"])
return
else:
date_str = args[0]
content = " ".join(args[1:])
day_split = date_str.split("d")
if len(day_split) == 1 and "d" not in date_str:
jours = "0"
next_split = date_str
elif "d" in date_str and day_split[1] == "":
jours = day_split[0]
next_split = "0h0m0s"
else:
jours = day_split[0]
next_split = day_split[1]
hour_split = next_split.split("h")
if len(hour_split) == 1 and "h" not in date_str:
heures = "0"
next_split = date_str
elif "h" in date_str and hour_split[1] == "":
heures = hour_split[0]
next_split = "0m0s"
else:
heures = hour_split[0]
next_split = hour_split[1]
minute_split = next_split.split("m")
if len(minute_split) == 1 and "h" not in date_str:
minutes = "0"
next_split = date_str
elif "m" in date_str and minute_split[1] == "":
minutes = minute_split[0]
next_split = "0s"
else:
minutes = minute_split[0]
next_split = minute_split[1]
second_split = next_split.split("s")
if len(second_split) == 1 and "s" not in date_str:
secondes = "0"
else:
secondes = second_split[0]
try:
jours = int(jours)
heures = int(heures)
minutes = int(minutes)
secondes = int(secondes)
except ValueError:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotANumberError"])
return
total = jours * 24 * 60 * 60 + heures * 60 * 60 + minutes * 60 + secondes # Durée du sondage
with self.guild.bot.database.cursor() as cursor:
insert_sql = "INSERT INTO `{guild_id}surveys` (title, duree) VALUES (%s, %s);".format(
guild_id=self.guild.id)
cursor.execute(insert_sql, (content, total))
self.guild.bot.database.commit()
await msg.channel.send(tr.tr[self.guild.config["lang"]]["modules"]["survey"]["create_survey"]
.format(id=cursor.lastrowid, prefix=self.guild.config["prefix"]))
async def post_survey(self, msg, command, args):
if msg.author.id not in self.guild.config["master_admins"]:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["PermissionError"])
return
if len(args) != 1:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotEnoughParamError"])
return
try:
survey_id = int(args[0])
except ValueError:
msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotANumberError"])
return
# Vérification de l'existance du sondage
with self.guild.bot.database.cursor() as cursor:
sql_id = "SELECT id FROM `{guild_id}surveys`".format(guild_id=self.guild.id)
cursor.execute(sql_id)
liste_id = [r["id"] for r in cursor.fetchall()]
if survey_id not in liste_id:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["SurveyNotExistsError"])
return
# Verification que le sondage n'a pas déjà été publié
with self.guild.bot.database.cursor() as cursor:
sql_depart = "SELECT depart FROM `{guild_id}surveys` WHERE id = %s".format(guild_id=self.guild.id)
cursor.execute(sql_depart, (survey_id))
depart = cursor.fetchone()["depart"]
if depart is not None:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["AlreadySendSurvey"])
return
# Envoi du sondage
with self.guild.bot.database.cursor() as cursor:
sql_update = "UPDATE `{guild_id}surveys` SET depart = %s WHERE id=%s" \
.format(guild_id=self.guild.id)
cursor.execute(sql_update, (int(time.time()), survey_id))
self.guild.bot.database.commit()
with self.guild.bot.database.cursor() as cursor:
sql_choices = "SELECT id from `{guild_id}survey_choices` WHERE survey=%s" \
.format(guild_id=self.guild.id)
cursor.execute(sql_choices, (survey_id))
choices_id = [r["id"] for r in cursor.fetchall()]
with self.guild.bot.database.cursor() as cursor:
sql_survey_title = "SELECT title, duree FROM `{guild_id}surveys` WHERE id = %s" \
.format(guild_id=self.guild.id)
cursor.execute(sql_survey_title, (survey_id))
result = cursor.fetchone()
# Envoi des messages de présentation
await msg.channel.send(tr.tr[self.guild.config["lang"]]["modules"]["survey"]["post_survey"]["presentation"]
.format(prefix=self.guild.config["prefix"], heures=int(result["duree"] / 3600)))
await msg.channel.send(result['title'])
# Envoi des message pour chaque choix
for choice_id in choices_id:
with self.guild.bot.database.cursor() as cursor:
sql_choice = "SELECT id,content, attachment, attachment_name FROM `{guild_id}survey_choices` WHERE id=%s" \
.format(guild_id=self.guild.id)
cursor.execute(sql_choice, (choice_id))
result = cursor.fetchone()
if result["attachment_name"]:
with open(result["attachment_name"], "wb") as temp_file:
temp_file.write(result["attachment"])
with open(result["attachment_name"], "rb") as temp_file:
await msg.channel.send("`{prefix}vote {id}` "
.format(prefix=self.guild.config["prefix"], id=result["id"]) + result[
"content"],
file=discord.File(temp_file, filename=str(result["attachment_name"])))
else:
await msg.channel.send(content="`{prefix}vote {id}` "
.format(prefix=self.guild.config["prefix"], id=result["id"]) + result["content"])
async def post_result(self, msg, command, args):
# L'auteur est-t-il un admin?
if msg.author.id not in self.guild.config["master_admins"]:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["PermissionError"])
return
# Nombre de paramètres
if len(args) != 1:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotEnoughParamError"])
return
try:
survey_id = int(args[0])
except ValueError:
msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotANumberError"])
return
# Vérification de l'existance du sondage
with self.guild.bot.database.cursor() as cursor:
sql_id = "SELECT id FROM `{guild_id}surveys`".format(guild_id=self.guild.id)
cursor.execute(sql_id)
liste_id = [r["id"] for r in cursor.fetchall()]
if survey_id not in liste_id:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["SurveyNotExistsError"])
return
# Vérification que le sondage est terminé
with self.guild.bot.database.cursor() as cursor:
# Récupération de la date de fin du sondage
select_survey_sql = "SELECT depart, duree FROM `{guild_id}surveys` WHERE id=%s;".format(
guild_id=self.guild.id)
cursor.execute(select_survey_sql, (survey_id))
r = cursor.fetchone()
if r["depart"] is not None:
fin = r["depart"] + r["duree"]
else:
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotYetPostedError"])
return
print(fin, time.time())
if fin > time.time():
await msg.channel.send(tr.tr[self.guild.config["lang"]]["errors"]["NotYetFinishedError"])
return
# Récupération des choix
with self.guild.bot.database.cursor() as cursor:
sql_select_choices = "SELECT id FROM `{guild_id}survey_choices` WHERE survey=%s;".format(guild_id=self.guild.id)
cursor.execute(sql_select_choices, (survey_id))
choices = [r["id"] for r in cursor.fetchall()]
# Récupération des votes
votes = []
for id_choice in choices:
with self.guild.bot.database.cursor() as cursor:
select_votes_sql = "SELECT id FROM `{guild_id}survey_votes` WHERE choice=%s;".format(guild_id=self.guild.id)
cursor.execute(select_votes_sql, (id_choice))
votes.append((id_choice,len(cursor.fetchall())))
votes.sort(key=lambda x: x[1])
total = sum([x[1] for x in votes])
texte = tr.tr[self.guild.config["lang"]]["modules"]["survey"]["result"]["text"]+"```"
i=0
for vote in votes[::-1]:
i+=1
texte += "\n{i} - Choix {id_choix} - {nb_votes} ({pourcentage}%)"\
.format(i=i, id_choix=vote[0], nb_votes=vote[1], pourcentage=vote[1]*100/total)
texte += "```"
await msg.channel.send(texte)
async def on_message(self, msg):
if msg.content.startswith(self.guild.config["prefix"]):
command, *args = msg.content.lstrip(self.guild.config["prefix"]).split(" ")
if command == "vote":
await self.vote(msg, command, args)
elif command == "add_choice":
await self.add_choice(msg, command, args)
elif command == "create_survey":
await self.create_survey(msg, command, args)
elif command == "post_survey":
await self.post_survey(msg, command, args)
elif command == "post_results":
await self.post_result(msg, command, args)
return

View File

@ -1,7 +1,7 @@
import time import time
import discord import discord
from bot import traductions as tr import traductions as tr
class MainClass: class MainClass:

View File

@ -76,7 +76,7 @@ foBot = FoBot(db_connection=db_connection)
foWeb = FoWeb(bot=None, db=db_connection) foWeb = FoWeb(bot=None, db=db_connection)
bot_app = foBot.start(os.environ['DISCORD_TOKEN'], max_messages=100000000) bot_app = foBot.start(os.environ['FOBOT_DISCORD_TOKEN'], max_messages=100000000)
bot_task = asyncio.ensure_future(bot_app) bot_task = asyncio.ensure_future(bot_app)
foWeb.listen(port=8888) foWeb.listen(port=8888)