diff --git a/README.md b/README.md index 69d5c71..db9cc87 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,26 @@ # Gambling BOT -Readme in progress... \ No newline at end of file +Readme in progress... + +## Formules de calcul + +### **Formule de la cote** +\[ +1 + \left( 1 - \frac{\sum \text{vote\_member.bet\_value}}{\text{total.sum}} \right) +\] + +--- + +### **Formule du gain pour les gagnants** +\[ +\text{nbr\_points} \times \left( \text{cote\_vote} \right) +\] + +--- + +### **Formule pour les perdants** +\[ +\text{Perte totale, avec un seuil minimal fixé à 50} +\] + + diff --git a/database.py b/database.py index ab7112d..b372062 100644 --- a/database.py +++ b/database.py @@ -12,14 +12,14 @@ async def user_exists(conn, user_id, guild_id): return result is not None -async def insert_vote_options(conn, vote_id, vote_option): +async def insert_vote_options(conn, poll_id: str, vote_id: str, vote_option: str): cur = conn.cursor() cur.execute( "INSERT INTO " - "vote (vote_id, vote_option) " - "VALUES (%s, %s)", - (vote_id, vote_option) + "vote (poll_id, vote_id, vote_option) " + "VALUES (%s, %s, %s)", + (poll_id, vote_id, vote_option) ) conn.commit() @@ -95,20 +95,79 @@ async def get_user_vote(conn: connection, user_id: str, poll_id: str): return result -async def bet(conn, vote_id: str, poll_id: str, user_id: str, guild_id: str): +async def get_bet_value(conn: connection, user_id: str, guild_id: str): + cur: cursor = conn.cursor() + + cur.execute("" + "SELECT bet_value " + "FROM member " + "WHERE user_id=%s " + "AND member.guild_id=%s", + (user_id, guild_id) + ) + + result = cur.fetchone() + cur.close() + + if result is not None: + return result[0] + return + + +async def bet(conn, vote_id: str, poll_id: str, user_id: str, guild_id: str, bet_value: int): cur = conn.cursor() cur.execute( "INSERT INTO " - "vote_member (user_id, poll_id, guild_id, vote_id) " - "VALUES (%s, %s, %s, %s)", - (user_id, poll_id, guild_id, vote_id) + "vote_member (user_id, poll_id, guild_id, vote_id, bet_value) " + "VALUES (%s, %s, %s, %s, %s)", + (user_id, poll_id, guild_id, vote_id, bet_value) ) conn.commit() cur.close() +async def get_bet_value_multipliers(conn: connection, poll_id: str): + cur = conn.cursor() + + cur.execute("SELECT vote_id,(1 + (1 - (SUM(vote_member.bet_value) / total.sum))) " + "FROM vote_member " + "CROSS JOIN (SELECT SUM(bet_value) AS sum FROM vote_member WHERE poll_id=%s) AS total " + "WHERE poll_id=%s " + "GROUP BY vote_id, total.sum", + (poll_id, poll_id)) + result = cur.fetchall() + + return result + + +async def get_vote_id_by_vote_option_and_poll_id(conn: connection, vote_option: str, poll_id: str): + cur = conn.cursor() + + cur.execute("SELECT vote_id FROM vote " + "WHERE vote_option=%s " + "AND poll_id=%s", + (vote_option, poll_id)) + result = cur.fetchone() + + if result is not None: + return result[0] + return + + +async def get_users_that_voted(conn: connection, vote_id: str): + cur = conn.cursor() + + cur.execute("select user_id " + "from vote_member " + "where vote_id=%s", + (vote_id,)) + results = cur.fetchall() + + return results + + async def guild_exists(conn, guild_id): cur = conn.cursor() @@ -160,5 +219,5 @@ async def insert_user(conn, user_id, guild_id, username, avatar): conn.commit() cur.close() -# nombre de vote par vote_id pour calculer l'issue du vote (et potentiellement la côte) -# select vote_id, count(distinct vote_member.user_id) from vote_member group by vote_id \ No newline at end of file +async def get_users_by_vote_id_and_poll_id(): + pass diff --git a/database.sql b/database.sql index aa41432..9c858ae 100644 --- a/database.sql +++ b/database.sql @@ -1,7 +1,7 @@ DROP TABLE IF EXISTS vote_member; DROP TABLE IF EXISTS member; -DROP TABLE IF EXISTS poll; DROP TABLE IF EXISTS vote; +DROP TABLE IF EXISTS poll; DROP TABLE IF EXISTS guild; CREATE TABLE guild @@ -10,13 +10,6 @@ CREATE TABLE guild PRIMARY KEY (guild_id) ); -CREATE TABLE vote -( - vote_id VARCHAR(255), - vote_option VARCHAR(255), - PRIMARY KEY (vote_id) -); - CREATE TABLE poll ( poll_id UUID, @@ -28,24 +21,34 @@ CREATE TABLE poll FOREIGN KEY (guild_id) REFERENCES guild (guild_id) ); +CREATE TABLE vote +( + poll_id UUID, + vote_id VARCHAR(255), + vote_option VARCHAR(255), + PRIMARY KEY (vote_id), + FOREIGN KEY (poll_id) REFERENCES poll (poll_id) +); + CREATE TABLE member ( - user_id VARCHAR(255), - guild_id VARCHAR(255), - username VARCHAR(255), - points bigint DEFAULT 50, - bet_value bigint DEFAULT 50, - avatar TEXT, + user_id VARCHAR(255), + guild_id VARCHAR(255), + username VARCHAR(255), + points bigint DEFAULT 50, + bet_value bigint DEFAULT 50, + avatar TEXT, PRIMARY KEY (user_id), FOREIGN KEY (guild_id) REFERENCES guild (guild_id) ); CREATE TABLE vote_member ( - user_id VARCHAR(255), - guild_id VARCHAR(255), - vote_id VARCHAR(255), - poll_id UUID, + user_id VARCHAR(255), + guild_id VARCHAR(255), + vote_id VARCHAR(255), + poll_id UUID, + bet_value bigint DEFAULT 50, FOREIGN KEY (guild_id) REFERENCES guild (guild_id), FOREIGN KEY (user_id) REFERENCES member (user_id), FOREIGN KEY (vote_id) REFERENCES vote (vote_id), diff --git a/main.py b/main.py index d5a127a..68d516a 100644 --- a/main.py +++ b/main.py @@ -9,7 +9,8 @@ import websockets from database import (user_exists, insert_user, guild_exists, insert_guild, insert_poll, insert_vote_options, bet, get_user_vote, change_bet, - is_last_poll_created_opened, get_poll_id_opened, close_poll) + is_last_poll_created_opened, get_poll_id_opened, close_poll, get_bet_value, + get_bet_value_multipliers, get_vote_id_by_vote_option_and_poll_id) from enums import OpCode, EventTitle, InteractionType with open('configuration.json', 'r') as file: @@ -43,13 +44,13 @@ async def create_command(): "name": "bet", "type": 1, "description": "Pour pouvoir planifier le prochain pari !", - "required": True, - "min_value": 0, "options": [ { "name": "somme", + "required": True, + "min_value": 0, "description": "La somme du prochain pari", - "type": 3 + "type": 4 } ] }, @@ -57,6 +58,14 @@ async def create_command(): "name": "close", "type": 1, "description": "Pour clôturer un sondage", + "options": [ + { + "name": "option", + "description": "L'issue du sondage", + "required": True, + "type": 3 + } + ] }, { # à améliorer en /profil <@user_id> "name": "me", @@ -94,16 +103,19 @@ async def create_poll(guild_id: str, creator_id: str, poll_created_id: str, inte vote_options = "" buttons = [] + vote_text_mem = [] # mémoire pour détecter les doublons for index, comp in enumerate(components): if "vote" in comp["components"][0]["custom_id"] and comp["components"][0]["value"] != "": vote_text = comp["components"][0]["value"] - vote_options += f"Option {index}: {vote_text}\n" - buttons.append({ - "type": 2, - "label": vote_text, - "style": 1, - "custom_id": f"{poll_created_id}_{vote_text}_{index}" - }) + if vote_text not in vote_text_mem: + vote_text_mem.append(vote_text) + vote_options += f"Option {index}: {vote_text}\n" + buttons.append({ + "type": 2, + "label": vote_text, + "style": 1, + "custom_id": f"{poll_created_id}_{vote_text}_{index}" + }) action_rows = [{"type": 1, "components": buttons[i:i + 5]} for i in range(0, len(buttons), 5)] @@ -138,7 +150,8 @@ async def create_poll(guild_id: str, creator_id: str, poll_created_id: str, inte await insert_poll(conn=conn, creator=creator_id, poll_id=poll_created_id, guild_id=guild_id, question=question) for option in buttons: - await insert_vote_options(conn=conn, vote_id=option["custom_id"], vote_option=option["label"]) + await insert_vote_options(conn=conn, poll_id=poll_created_id, vote_id=option["custom_id"], + vote_option=option["label"]) async def identify(websocket): @@ -293,8 +306,9 @@ async def vote_confirmation(interaction_id: str, interaction_token: str, custom_ if not await guild_exists(conn=conn, guild_id=guild_id): await insert_guild(conn=conn, guild_id=guild_id) await insert_user(conn=conn, user_id=user_id, avatar=avatar, guild_id=guild_id, username=username) + bet_value = await get_bet_value(conn=conn, user_id=user_id, guild_id=guild_id) await bet(conn=conn, guild_id=guild_id, user_id=user_id, vote_id=custom_id, - poll_id=custom_id.split("_")[0]) + poll_id=custom_id.split("_")[0], bet_value=bet_value) body = { "type": 4, "data": { @@ -314,7 +328,8 @@ async def vote_confirmation(interaction_id: str, interaction_token: str, custom_ print(res) -async def close_poll_cmd(guild_id: str, user_id: str, interaction_id: str, interaction_token: str, username: str, +async def close_poll_cmd(guild_id: str, user_id: str, interaction_id: str, issue: str, interaction_token: str, + username: str, avatar: str): if not await guild_exists(conn=conn, guild_id=guild_id): await insert_guild(conn=conn, guild_id=guild_id) @@ -322,13 +337,38 @@ async def close_poll_cmd(guild_id: str, user_id: str, interaction_id: str, inter await insert_user(conn=conn, user_id=user_id, guild_id=guild_id, username=username, avatar=avatar) poll: tuple = await get_poll_id_opened(conn=conn, creator=user_id) if poll is not None: - await close_poll(conn=conn, poll_id=poll[1]) - body = { - "type": 4, - "data": { - "content": f'@everyone le sondage "{poll[2]}" est terminé !', + winner_id = await get_vote_id_by_vote_option_and_poll_id(conn=conn, vote_option=issue, poll_id=poll[1]) + if winner_id is not None: + winner_cote = None + values = await get_bet_value_multipliers(conn=conn, poll_id=poll[1]) + for value in values: + vote_id = value[0] + if vote_id == winner_id: + winner_cote = value[1] + # get users where vote_id + # users.points + (value[1] * users.vote.bet_value) + pass + else: + # get users where vote_id + # users.points - users.vote.bet_value + pass + await close_poll(conn=conn, poll_id=poll[1]) + + body = { + "type": 4, + "data": { + "content": f'@everyone le sondage **{poll[2]}** est terminé ! L\'issue gagnante est **{issue}** ' + f'avec une côte de {(round(winner_cote, 2) - 1) * 100}%' + } + } + else: + body = { + "type": 4, + "data": { + "content": f"<@{user_id}>, L'option **{issue}** n'existe pas.", + "flags": 64 + } } - } else: body = { "type": 4, @@ -383,6 +423,7 @@ async def get_event(response: Any): user_id=response["d"]["member"]["user"]["id"], interaction_id=response["d"]["id"], interaction_token=response["d"]["token"], + issue=response["d"]["data"]["options"][0]["value"], avatar=response["d"]["member"]["user"]["avatar"], username=response["d"]["member"]["user"]["username"]) elif (response["d"]["type"] == InteractionType.APPLICATION_COMMAND and