From d7bb8aa270da20e9aa8e263ba2e7a600c34a426b Mon Sep 17 00:00:00 2001 From: guams Date: Sun, 30 Mar 2025 18:31:18 +0200 Subject: [PATCH] Points management and functional '/bet' command --- README.md | 4 ++ database.py | 65 ++++++++++++++++++++++++++++++- database.sql | 2 +- main.py | 108 +++++++++++++++++++++++++++++++++++---------------- 4 files changed, 143 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index db9cc87..3d11a8a 100644 --- a/README.md +++ b/README.md @@ -24,3 +24,7 @@ Readme in progress... \] +## TODO + +- [ ] corriger le problème de seuil +- [ ] changer la bet_value après un vote perdant (on se retrouve parfois avec un bet_value > points) diff --git a/database.py b/database.py index b372062..80d2a34 100644 --- a/database.py +++ b/database.py @@ -95,6 +95,25 @@ async def get_user_vote(conn: connection, user_id: str, poll_id: str): return result +async def get_user_points(conn: connection, user_id: str, guild_id: str): + cur: cursor = conn.cursor() + + cur.execute("" + "SELECT points " + "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 get_bet_value(conn: connection, user_id: str, guild_id: str): cur: cursor = conn.cursor() @@ -219,5 +238,47 @@ async def insert_user(conn, user_id, guild_id, username, avatar): conn.commit() cur.close() -async def get_users_by_vote_id_and_poll_id(): - pass + +async def get_users_and_bet_value_by_vote_id(conn: connection, vote_id: str): + cur = conn.cursor() + + cur.execute("SELECT user_id, bet_value FROM vote_member " + "WHERE vote_id=%s", + (vote_id,) + ) + result = cur.fetchall() + + cur.close() + + return result + + +async def add_user_points(conn: connection, user_id: str, bet_value: int): + cur = conn.cursor() + + cur.execute( + "UPDATE member " + "SET points = points + %s " + "WHERE user_id=%s", + (bet_value, user_id) + ) + + conn.commit() + cur.close() + + +async def minus_user_points(conn: connection, user_id: str, bet_value: int): + try: + cur = conn.cursor() + + cur.execute( + "UPDATE member " + "SET points = points - %s " + "WHERE user_id=%s", + (bet_value, user_id) + ) + + conn.commit() + cur.close() + except Exception: + conn.rollback() diff --git a/database.sql b/database.sql index 9c858ae..83f0763 100644 --- a/database.sql +++ b/database.sql @@ -35,7 +35,7 @@ CREATE TABLE member user_id VARCHAR(255), guild_id VARCHAR(255), username VARCHAR(255), - points bigint DEFAULT 50, + points bigint DEFAULT 50 NOT NULL CHECK (points >= 50), bet_value bigint DEFAULT 50, avatar TEXT, PRIMARY KEY (user_id), diff --git a/main.py b/main.py index 68d516a..0eba655 100644 --- a/main.py +++ b/main.py @@ -10,7 +10,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, get_bet_value, - get_bet_value_multipliers, get_vote_id_by_vote_option_and_poll_id) + get_bet_value_multipliers, get_vote_id_by_vote_option_and_poll_id, + get_users_and_bet_value_by_vote_id, add_user_points, minus_user_points, get_user_points) from enums import OpCode, EventTitle, InteractionType with open('configuration.json', 'r') as file: @@ -286,7 +287,14 @@ async def create_poll_form(interaction_id: str, interaction_token: str, guild_id async def heartbeat(websocket, interval): while True: await asyncio.sleep(interval / 1000) - await websocket.send(json.dumps({"op": OpCode.HEARTBEAT, "d": None})) + try: + await websocket.send(json.dumps({"op": OpCode.HEARTBEAT, "d": None})) + except websockets.exceptions.ConnectionClosed: + print("WebSocket fermé") + break + except Exception as e: + print(f"Erreur lors du heartbeat : {e}") + break async def vote_confirmation(interaction_id: str, interaction_token: str, custom_id: str, user_id: str, @@ -309,10 +317,11 @@ async def vote_confirmation(interaction_id: str, interaction_token: str, custom_ 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], bet_value=bet_value) + await minus_user_points(conn=conn, user_id=user_id, bet_value=bet_value) body = { "type": 4, "data": { - "content": f"<@{user_id}>, tu as misé 50 sur l'option **{custom_id.split('_')[1]}** ! 🎉", + "content": f"<@{user_id}>, tu as misé {bet_value} sur l'option **{custom_id.split('_')[1]}** ! 🎉", "flags": 64 } } @@ -343,22 +352,24 @@ async def close_poll_cmd(guild_id: str, user_id: str, interaction_id: str, issue values = await get_bet_value_multipliers(conn=conn, poll_id=poll[1]) for value in values: vote_id = value[0] + users_with_bet_values = await get_users_and_bet_value_by_vote_id(conn=conn, vote_id=vote_id) 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 + for user_with_bet_value in users_with_bet_values: + print(int(user_with_bet_value[1] * winner_cote)) + await add_user_points(conn=conn, + user_id=user_with_bet_value[0], + bet_value=int(user_with_bet_value[1] * winner_cote)) await close_poll(conn=conn, poll_id=poll[1]) - + if winner_cote is not None: + out = (round(winner_cote, 2) - 1) * 100 + else: + out = 0.0 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}%' + f'avec une côte de {out}%' } } else: @@ -387,8 +398,7 @@ async def close_poll_cmd(guild_id: str, user_id: str, interaction_id: str, issue async def get_event(response: Any): match response["t"]: - case EventTitle.MESSAGE_CREATE: - print(f'{response["d"]["author"]["username"]}: {response["d"]["content"]}') + # case EventTitle.MESSAGE_CREATE: case EventTitle.INTERACTION_CREATE: if response["d"]["type"] == InteractionType.MODAL_SUBMIT: await create_poll( @@ -428,27 +438,50 @@ async def get_event(response: Any): username=response["d"]["member"]["user"]["username"]) elif (response["d"]["type"] == InteractionType.APPLICATION_COMMAND and response["d"]["data"]["name"] == 'bet'): - try: - command_option: str = response["d"]["data"]["options"][0]["value"] - if await guild_exists(conn=conn, guild_id=response["d"]["guild_id"]): - if command_option.isnumeric() and await user_exists(conn=conn, - user_id=response["d"]["member"]["user"][ - "id"], - guild_id=response["d"]["guild_id"]): - await change_bet(conn=conn, - guild_id=response["d"]["guild_id"], - user_id=response["d"]["member"]["user"]["id"], - new_bet_amount=response["d"]["data"]["name"]) - else: - await bet_error_message("Vous devez rentrer un nombre") - except Exception: - await bet_error_message("Vous avez oublié de donner une somme !") + command_option: int = response["d"]["data"]["options"][0]["value"] + await change_bet_cmd(guild_id=response["d"]["guild_id"], + command_option=command_option, + user_id=response["d"]["member"]["user"]["id"], + username=response["d"]["member"]["user"]["username"], + avatar=response["d"]["member"]["user"]["avatar"], + interaction_id=response["d"]["id"], + interaction_token=response["d"]["token"]) + case _: print(response) -async def bet_error_message(message: str): - print(message) +async def change_bet_cmd(guild_id: str, command_option: int, user_id: str, username: str, avatar: str, + interaction_id: str, interaction_token: str): + if not await guild_exists(conn=conn, guild_id=guild_id): + await insert_user(conn=conn, guild_id=guild_id, user_id=user_id, username=username, avatar=avatar) + if not await user_exists(conn=conn, user_id=user_id, guild_id=guild_id): + await insert_guild(conn=conn, guild_id=guild_id) + + if await get_user_points(conn=conn, user_id=user_id, guild_id=guild_id) >= command_option: + await change_bet(conn=conn, guild_id=guild_id, user_id=user_id, new_bet_amount=command_option) + body = { + "type": 4, + "data": { + "content": f"<@{user_id}>, ton prochain pari vaudra **{command_option}**.", + "flags": 64 + } + } + else: + body = { + "type": 4, + "data": { + "content": f"<@{user_id}>, solde insuffisant !", + "flags": 64 + } + } + + res = requests.post(f"{API_URL}/interactions/{interaction_id}/{interaction_token}/callback", json=body, + headers={"Authorization": f"Bot {TOKEN}"}) + if res.status_code == 400: + print(res.json()) + else: + print(res) async def connect(): @@ -460,13 +493,22 @@ async def connect(): await identify(websocket) - while True: + connected = True + while connected: response = json.loads(await websocket.recv()) match response["op"]: case OpCode.DISPATCH: await get_event(response) + case OpCode.RECONNECT: + print("Reconnexion...") + connected = False case _: - print(response) + pass + + # Reconnexion + await websocket.close() + await asyncio.sleep(5) + await connect() async def main():