Skip to content

Commit ebb4876

Browse files
committed
Boutique fiable
1 parent 14b915d commit ebb4876

File tree

10 files changed

+406
-3
lines changed

10 files changed

+406
-3
lines changed

TODO.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
- [ ] création de la banque centrale avec la gestion des données
2929
- [ ] capacité d'utiliser sont argent + récompense
3030
- [ ] commande de management de l'économie du serveur
31+
- [ ] Système bancaire
32+
- [ ] Systeme écurie et abonnement
3133

3234
## 🧹 Nettoyage & Sécurité
3335

13.6 KB
Binary file not shown.
270 Bytes
Binary file not shown.

app/_app.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from datetime import timedelta
1212
from typing import Literal
1313
import economie as eco
14+
import boutique as shop
1415

1516

1617
@bot.event
@@ -479,6 +480,13 @@ async def dwith(interaction: discord.Interaction, somme_a_verser: int):
479480
await eco.virement_compte_protege_to_courant(interaction, somme_a_verser)
480481
logger.info(f"{interaction.user} a verser **{somme_a_verser}** sur son compte courant.")
481482

483+
# _______________________________________________________________________________________________________________________________
484+
485+
@tree.command(name="boutique", description="Ouvre la boutique")
486+
async def boutic(interaction: discord.Interaction):
487+
await interaction.response.defer()
488+
await shop.boutique(interaction)
489+
logger.info(f"{interaction.user} a ouvert la boutique.")
482490

483491

484492
bot.run(TOKEN)

app/boutique.py

Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
import discord
2+
import os
3+
import json
4+
import asyncio
5+
from config import logger, EMBED_BOUTIQUE_OFFICEL, EMBED_COLOR_RED, EMBED_THUMBNAIL, EMBED_FOOTER_TEXT, JOUR
6+
7+
DATA_FILE = 'data/eco/central_account.json'
8+
9+
# --------- Gestion des données ---------
10+
def charger_donnees():
11+
if not os.path.exists(DATA_FILE):
12+
with open(DATA_FILE, "w", encoding="utf-8") as f:
13+
json.dump({}, f)
14+
with open(DATA_FILE, "r", encoding="utf-8") as f:
15+
return json.load(f)
16+
17+
def sauvegarder_donnees(donnees):
18+
with open(DATA_FILE, "w", encoding="utf-8") as f:
19+
json.dump(donnees, f, indent=4)
20+
21+
def ajouter_argent(user_id: int, montant: int):
22+
donnees = charger_donnees()
23+
user_id = str(user_id)
24+
25+
if user_id not in donnees:
26+
donnees[user_id] = {
27+
"Pseudo": "Inconnu",
28+
"Solde": 0,
29+
"last_claim": ""
30+
}
31+
32+
donnees[user_id]["Solde"] += montant
33+
sauvegarder_donnees(donnees)
34+
35+
def retirer_argent(user_id: int, montant: int) -> bool:
36+
donnees = charger_donnees()
37+
user_id = str(user_id)
38+
39+
if user_id not in donnees:
40+
donnees[user_id] = {
41+
"Pseudo": "Inconnu",
42+
"Solde": 0,
43+
"last_claim": ""
44+
}
45+
46+
solde_actuel = donnees[user_id]["Solde"]
47+
if solde_actuel < montant:
48+
return True # Pas assez d'argent
49+
else:
50+
donnees[user_id]["Solde"] -= montant
51+
donnees["F1FACCOUNT"]["Solde"] += montant
52+
sauvegarder_donnees(donnees)
53+
return False
54+
55+
async def ajouter_role(member: discord.Member, role_id: int):
56+
role = member.guild.get_role(role_id)
57+
if role is None:
58+
logger.warning(f"❌ Rôle introuvable : {role_id}")
59+
return
60+
try:
61+
await member.add_roles(role)
62+
logger.info(f"✅ Rôle '{role.name}' ajouté à {member.display_name}")
63+
except discord.Forbidden:
64+
logger.warning("❌ Le bot n'a pas la permission d'ajouter ce rôle.")
65+
except discord.HTTPException as e:
66+
logger.error(f"❌ Une erreur est survenue : {e}")
67+
68+
async def ajouter_role_temporaire(membre: discord.Member, role_id: int, duree_sec: int):
69+
role = membre.guild.get_role(role_id)
70+
if not role:
71+
return
72+
73+
await membre.add_roles(role, reason="Rôle temporaire ajouté via la boutique")
74+
logger.info(f"Rôle {role.name} ajouté à {membre.display_name} pour {duree_sec} secondes.")
75+
76+
await asyncio.sleep(duree_sec)
77+
78+
# Vérification que le rôle n'a pas été retiré entre-temps
79+
if role in membre.roles:
80+
await membre.remove_roles(role, reason="Durée du rôle temporaire expirée")
81+
logger.info(f"Rôle {role.name} retiré de {membre.display_name} après expiration.")
82+
83+
84+
85+
86+
# --------- Messages personnalisés ---------
87+
def get_item_message(valeur):
88+
messages = {
89+
"role_GdG": {
90+
"confirmation":"Veux tu obtenir le ROLE ULTIME le GOAT des GOATs pour **200 000 pièces** ?",
91+
"success": "Te voici maintenant **le GOAT des GOATs**"
92+
},
93+
"boost24h": {
94+
"confirmation":"Veux tu vraiment acheter un boost de 24h pour **35 000 pièces** ?",
95+
"success": "Tu as obtenue ton boost xp fois 2 pendant 24h !"
96+
},
97+
"MS": {
98+
"confirmation":"Veux tu obtenir le ROLE Mention Suprême parle trop pour **150 000 pièces**",
99+
"success":"Tu as obtenue le ROLE Mention Suprême parle trop félicitation !"
100+
}
101+
102+
}
103+
return messages.get(valeur, {
104+
"confirmation": "❓ Article inconnu. Tu veux quand même continuer ?",
105+
"success": "❌ Article inconnu."
106+
})
107+
108+
# --------- Vue de confirmation ---------
109+
class ConfirmView(discord.ui.View):
110+
def __init__(self, interaction: discord.Interaction, valeur: str):
111+
super().__init__(timeout=30)
112+
self.interaction = interaction
113+
self.valeur = valeur
114+
115+
@discord.ui.button(label="✅ Confirmer", style=discord.ButtonStyle.success)
116+
async def confirm(self, interaction: discord.Interaction, button: discord.ui.Button):
117+
if interaction.user != self.interaction.user:
118+
await interaction.response.send_message("❌ Tu ne peux pas confirmer cette action.", ephemeral=True)
119+
return
120+
121+
# Valeur par défaut
122+
success_text = get_item_message(self.valeur)["success"]
123+
erreur = None
124+
125+
if self.valeur == "role_GdG":
126+
if retirer_argent(interaction.user.id, 200000):
127+
erreur = "❌ Tu n'as pas assez d'argent sur ton compte."
128+
else:
129+
await ajouter_role(interaction.user, 1339978184594034802)
130+
131+
if self.valeur == "MS":
132+
if retirer_argent(interaction.user.id, 150000):
133+
erreur = "❌ Tu n'as pas assez d'argent sur ton compte."
134+
else:
135+
await ajouter_role(interaction.user, 1339978383244656753)
136+
137+
elif self.valeur == "boost24h":
138+
if retirer_argent(interaction.user.id, 35000):
139+
erreur = "❌ Tu n'as pas assez d'argent sur ton compte."
140+
else:
141+
# On lance la coroutine de façon "background" (non bloquante)
142+
asyncio.create_task(ajouter_role_temporaire(interaction.user, 1400473431782330528, JOUR)) #24
143+
144+
# Une seule réponse à l'interaction
145+
if erreur:
146+
await interaction.response.send_message(erreur, ephemeral=True)
147+
else:
148+
await interaction.response.edit_message(content=success_text, view=None)
149+
150+
self.stop()
151+
152+
@discord.ui.button(label="❌ Annuler", style=discord.ButtonStyle.danger)
153+
async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button):
154+
if interaction.user != self.interaction.user:
155+
await interaction.response.send_message("❌ Tu ne peux pas annuler cette action.", ephemeral=True)
156+
return
157+
158+
await interaction.response.edit_message(content="❌ Achat annulé.", view=None)
159+
self.stop()
160+
161+
# --------- Menu de la boutique ---------
162+
class BoutiqueMenu(discord.ui.Select):
163+
def __init__(self):
164+
options = [
165+
discord.SelectOption(label="=== Rôles ==="),
166+
discord.SelectOption(label="🟥 Role GOAT des GOATs", description="Reçois le rôle GOAT des GOATs", value="role_GdG"),
167+
discord.SelectOption(label="🟧 Mention Suprême parle trop", description="Tu parles tellement que tu as besoin d'un role", value="MS"),
168+
discord.SelectOption(label="=== Boost ==="),
169+
discord.SelectOption(label="🔮 Boost 24H", description="Reçois un rôle qui boost ton xp pendant 24h", value="boost24h")
170+
]
171+
super().__init__(placeholder="Choisis un article...", options=options, min_values=1, max_values=1)
172+
173+
async def callback(self, interaction: discord.Interaction):
174+
valeur = self.values[0]
175+
message = get_item_message(valeur)["confirmation"]
176+
view = ConfirmView(interaction, valeur)
177+
await interaction.response.send_message(message, view=view, ephemeral=True)
178+
179+
# --------- Vue principale ---------
180+
class BoutiqueView(discord.ui.View):
181+
def __init__(self):
182+
super().__init__(timeout=60)
183+
self.add_item(BoutiqueMenu())
184+
185+
@discord.ui.button(label="Annuler", style=discord.ButtonStyle.danger)
186+
async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button):
187+
if interaction.response.is_done():
188+
await interaction.followup.send("❌ Commande annulée.", ephemeral=True)
189+
else:
190+
await interaction.response.send_message("❌ Commande annulée.", ephemeral=True)
191+
self.stop()
192+
193+
# --------- Commande principale ---------
194+
async def boutique(interaction: discord.Interaction):
195+
embed = discord.Embed(
196+
title="🛒 Bienvenue dans la Boutique Officielle F1F",
197+
description="La boutique te permet de dépenser les pièces que tu as gagnées sur le serveur pour obtenir toutes sortes d’avantages !\n"
198+
"Utilise le menu déroulant ci-dessous pour parcourir les options disponibles.\n"
199+
"Une fois ton choix fait, une double validation te sera proposée pour éviter toute erreur.\n\n"
200+
"🎯 Gagne des récompenses, fais évoluer ton profil, et distingue-toi des autres membres !\n"
201+
"💡 Des nouveautés seront ajoutées régulièrement, alors n'hésite pas à revenir souvent.",
202+
color=EMBED_COLOR_RED
203+
)
204+
embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL)
205+
embed.set_thumbnail(url=interaction.user.display_avatar.url)
206+
embed.set_image(url=EMBED_BOUTIQUE_OFFICEL)
207+
208+
await interaction.followup.send(embed=embed, view=BoutiqueView(), ephemeral=False)

app/config.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545

4646
EMBED_THUMBNAIL = "https://cdn.discordapp.com/attachments/1339299411360088226/1367477935392428083/Votre_texte_de_paragraphe_12.png?ex=6871ac52&is=68705ad2&hm=a63fd375a9f30130247df80b936c43e1d93b3a5b16c3415f7a63cac72614058e&"
4747
EMBED_IMAGE = "https://cdn.discordapp.com/attachments/1339299411360088226/1368544420504272987/Design_sans_titre_2.png?ex=68719910&is=68704790&hm=46bd1e4a625f33cc26d5e029888bef5c265732b842de241daa62662206f13885&"
48+
EMBED_BOUTIQUE_OFFICEL = "https://cdn.discordapp.com/attachments/1212852209491116113/1400458515289739434/Boutique_Officielle_-_2_-_Modifie.png?ex=688cb621&is=688b64a1&hm=76ad76a3246b3f9b3b613f112667873316d601e13c9b5d388effeea8957839cb&"
4849

4950
EMBED_FOOTER_TEXT = "Bot créé par F1F Team (BETA)"
5051
EMBED_FOOTER_ICON = EMBED_THUMBNAIL
@@ -66,3 +67,8 @@
6667

6768
# === 🪙 Bot et Tree pour les commandes slash ===
6869
SALAIRE_JOURNALIER = 100
70+
71+
# === Timer ===
72+
73+
JOUR = 86400
74+

data/eco/central_account.json

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
{
2+
"F1FACCOUNT": {
3+
"Pseudo": "F1F",
4+
"Solde": 420000,
5+
"last_claim": "2025-07-31"
6+
},
27
"1200489866165747722": {
38
"Pseudo": "matt_karting",
4-
"Solde": 1000,
9+
"Solde": 9999998502500,
510
"last_claim": "2025-07-31"
611
},
712
"674627381683552256": {
813
"Pseudo": "whisper9741",
9-
"Solde": 15099999999999999999990000,
14+
"Solde": 15099999998999899997606000,
1015
"last_claim": "2025-07-30"
1116
},
1217
"427103316028162048": {
@@ -23,5 +28,15 @@
2328
"Pseudo": "la_bagguette",
2429
"Solde": 100000000000000000,
2530
"last_claim": "2025-07-31"
31+
},
32+
"matt_karting": {
33+
"Pseudo": "Inconnu",
34+
"Solde": -400000,
35+
"last_claim": ""
36+
},
37+
"663321201766039572": {
38+
"Pseudo": "cy16.lmb",
39+
"Solde": 100,
40+
"last_claim": "2025-07-31"
2641
}
2742
}

data/eco/protect_account.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
{
22
"1200489866165747722": {
33
"Pseudo": "matt_karting",
4-
"Solde": 299999999999999999999000,
4+
"Solde": 299999999989999999299000,
5+
"last_claim": "2025-07-31"
6+
},
7+
"674627381683552256": {
8+
"Pseudo": "whisper9741",
9+
"Solde": 1000100001495000,
510
"last_claim": "2025-07-31"
611
}
712
}

0 commit comments

Comments
 (0)