Skip to content

Commit e94cc5b

Browse files
committed
maj de la boutique + Doc
1 parent ebb4876 commit e94cc5b

File tree

10 files changed

+344
-117
lines changed

10 files changed

+344
-117
lines changed
5.82 KB
Binary file not shown.
248 Bytes
Binary file not shown.
1.89 KB
Binary file not shown.

app/_app.py

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ async def helping_tools(interaction: discord.Interaction):
4040
# _______________________________________________________________________________________________________________________________
4141

4242

43+
@tree.command(name="admin_help", description="Gives you all the commands you can use with this bot")
44+
async def admin_helping_tools(interaction: discord.Interaction):
45+
if interaction.user.guild_permissions.administrator:
46+
await tool.help_admin(interaction)
47+
else:
48+
await embed.chat_you_dont_have_perm(interaction)
49+
50+
# _______________________________________________________________________________________________________________________________
51+
52+
4353
@tree.command(name="pronos", description="Enregistre tes pronos ou modifie les si tu l'as déjà fait")
4454
@app_commands.describe(
4555
statue="Type de session (Qualif, Course, Sprint, Sprint_Qualif)",
@@ -463,25 +473,32 @@ async def toop(interaction: discord.Interaction):
463473
async def depo(interaction: discord.Interaction, somme_a_verser: int):
464474
await interaction.response.defer()
465475
await eco.virement_compte_courant_to_protect(interaction, somme_a_verser)
466-
logger.info(f"{interaction.user} a verser **{somme_a_verser}** sur son compte sécurisé.")
467-
476+
logger.info(
477+
f"{interaction.user} a verser **{somme_a_verser}** sur son compte sécurisé.")
478+
468479
# _______________________________________________________________________________________________________________________________
469480

481+
470482
@tree.command(name="solde_proteger", description="T'affiche ton solde sur ton compte protéger")
471483
async def show(interaction: discord.Interaction):
472484
await interaction.response.defer()
473-
await eco.voir_solde_proteger(interaction)
485+
await eco.voir_solde_proteger(interaction)
474486
logger.info(f"{interaction.user} a regardé son solde protegé")
475-
487+
488+
# _______________________________________________________________________________________________________________________________
489+
490+
476491
@tree.command(name="withdraw", description="Déplace ton argent de ton compte protegé au compte courant")
477492
@app_commands.describe(somme_a_verser="Argent que tu veux viré")
478493
async def dwith(interaction: discord.Interaction, somme_a_verser: int):
479494
await interaction.response.defer()
480495
await eco.virement_compte_protege_to_courant(interaction, somme_a_verser)
481-
logger.info(f"{interaction.user} a verser **{somme_a_verser}** sur son compte courant.")
482-
496+
logger.info(
497+
f"{interaction.user} a verser **{somme_a_verser}** sur son compte courant.")
498+
483499
# _______________________________________________________________________________________________________________________________
484500

501+
485502
@tree.command(name="boutique", description="Ouvre la boutique")
486503
async def boutic(interaction: discord.Interaction):
487504
await interaction.response.defer()

app/boutique.py

Lines changed: 141 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,27 @@
22
import os
33
import json
44
import asyncio
5-
from config import logger, EMBED_BOUTIQUE_OFFICEL, EMBED_COLOR_RED, EMBED_THUMBNAIL, EMBED_FOOTER_TEXT, JOUR
5+
from config import logger, bot
6+
import config as param
67

78
DATA_FILE = 'data/eco/central_account.json'
89

910
# --------- Gestion des données ---------
11+
12+
1013
def charger_donnees():
1114
if not os.path.exists(DATA_FILE):
1215
with open(DATA_FILE, "w", encoding="utf-8") as f:
1316
json.dump({}, f)
1417
with open(DATA_FILE, "r", encoding="utf-8") as f:
1518
return json.load(f)
1619

20+
1721
def sauvegarder_donnees(donnees):
1822
with open(DATA_FILE, "w", encoding="utf-8") as f:
1923
json.dump(donnees, f, indent=4)
2024

25+
2126
def ajouter_argent(user_id: int, montant: int):
2227
donnees = charger_donnees()
2328
user_id = str(user_id)
@@ -31,7 +36,8 @@ def ajouter_argent(user_id: int, montant: int):
3136

3237
donnees[user_id]["Solde"] += montant
3338
sauvegarder_donnees(donnees)
34-
39+
40+
3541
def retirer_argent(user_id: int, montant: int) -> bool:
3642
donnees = charger_donnees()
3743
user_id = str(user_id)
@@ -51,7 +57,8 @@ def retirer_argent(user_id: int, montant: int) -> bool:
5157
donnees["F1FACCOUNT"]["Solde"] += montant
5258
sauvegarder_donnees(donnees)
5359
return False
54-
60+
61+
5562
async def ajouter_role(member: discord.Member, role_id: int):
5663
role = member.guild.get_role(role_id)
5764
if role is None:
@@ -64,54 +71,117 @@ async def ajouter_role(member: discord.Member, role_id: int):
6471
logger.warning("❌ Le bot n'a pas la permission d'ajouter ce rôle.")
6572
except discord.HTTPException as e:
6673
logger.error(f"❌ Une erreur est survenue : {e}")
67-
74+
75+
6876
async def ajouter_role_temporaire(membre: discord.Member, role_id: int, duree_sec: int):
6977
role = membre.guild.get_role(role_id)
7078
if not role:
7179
return
7280

7381
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.")
82+
logger.info(
83+
f"Rôle {role.name} ajouté à {membre.display_name} pour {duree_sec} secondes.")
7584

7685
await asyncio.sleep(duree_sec)
7786

7887
# Vérification que le rôle n'a pas été retiré entre-temps
7988
if role in membre.roles:
8089
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.")
90+
logger.info(
91+
f"Rôle {role.name} retiré de {membre.display_name} après expiration.")
92+
93+
94+
async def pub(interaction: discord.Interaction, channel_id):
95+
embed = discord.Embed(
96+
title="📰 Publicité F1F — Création de ton message",
97+
description=(
98+
"🎉 Merci d'avoir acheté une **publicité officielle** sur le serveur **Formula 1 France** !\n\n"
99+
"📢 Tu as maintenant l’opportunité d’envoyer un **message promotionnel personnalisé** dans le salon dédié.\n"
100+
"Pour cela, c’est très simple :\n\n"
101+
"✍️ __Réponds directement à **ce message privé** avec le texte que tu souhaites publier__.\n"
102+
"Il sera ensuite transmis automatiquement par le bot dans le salon public prévu à cet effet.\n\n"
103+
"ℹ️ Ton message peut contenir du texte, des liens, des emojis, et même des mentions (dans la limite du raisonnable).\n\n"
104+
"⏳ Tu as **30 minutes** pour répondre à ce message.\n"
105+
"⚠️ Passé ce délai, ton achat sera annulé et aucun message ne sera publié.\n\n"
106+
"Besoin d’aide ? Mentionne un membre du staff dans ta réponse."
107+
),
108+
color=param.EMBED_COLOR_GOLD
109+
)
110+
111+
embed.set_author(name="F1F Boutique — Publicité")
112+
embed.set_thumbnail(url=interaction.user.display_avatar.url)
113+
embed.set_footer(
114+
text="Merci pour ton soutien à la communauté Formula 1 France 🚀")
115+
embed.set_image(url=param.EMBED_BOUTIQUE_OFFICEL)
116+
117+
try:
118+
# 📬 Envoi du MP
119+
await interaction.user.send(embed=embed)
82120

121+
# ⏳ Attente de la réponse en DM (30 minutes max)
122+
msg = await bot.wait_for(
123+
"message",
124+
timeout=1800, # 30 minutes en secondes
125+
check=lambda m: m.author == interaction.user and isinstance(
126+
m.channel, discord.DMChannel)
127+
)
128+
129+
# 📤 Envoi dans le salon de pub
130+
channel = bot.get_channel(channel_id)
131+
if channel:
132+
await channel.send(f"📢 __**Publicité achetée par {interaction.user.mention}**__\n\n{msg.content}")
133+
await interaction.user.send("✅ Ton message publicitaire a bien été publié !")
134+
else:
135+
await interaction.user.send("❌ Salon introuvable, ton message n’a pas pu être publié.")
136+
except discord.Forbidden:
137+
await interaction.channel.send("❌ Je n'ai pas pu envoyer de message privé à cet utilisateur.")
138+
except asyncio.TimeoutError:
139+
await interaction.user.send("⏳ Temps écoulé ! Tu n’as pas envoyé ton message dans les 30 minutes. Achat annulé.")
83140

84-
85141

86142
# --------- Messages personnalisés ---------
87143
def get_item_message(valeur):
88144
messages = {
89145
"role_GdG": {
90-
"confirmation":"Veux tu obtenir le ROLE ULTIME le GOAT des GOATs pour **200 000 pièces** ?",
146+
"confirmation": "Veux tu obtenir le ROLE ULTIME le GOAT des GOATs pour **200 000 pièces** ?",
91147
"success": "Te voici maintenant **le GOAT des GOATs**"
92148
},
93149
"boost24h": {
94-
"confirmation":"Veux tu vraiment acheter un boost de 24h pour **35 000 pièces** ?",
150+
"confirmation": "Veux tu vraiment acheter un boost de 24h pour **35 000 pièces** ?",
95151
"success": "Tu as obtenue ton boost xp fois 2 pendant 24h !"
96152
},
97153
"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 !"
154+
"confirmation": "Veux tu obtenir le ROLE Mention Suprême parle trop pour **150 000 pièces**",
155+
"success": "Tu as obtenue le ROLE Mention Suprême parle trop félicitation !"
156+
},
157+
"MSP": {
158+
"confirmation": "Veux-tu vraiment acheter le ROLE Mythe du sport automobile pour **90 000 pièces**?",
159+
"success": "Félicitation tu as obtenue le ROLE Mythe du sport automobile!"
160+
},
161+
"MF1": {
162+
"confirmation": "Veux-tu vraiment acheter le ROLE Mythe de la F1 pour **75 000 pièces**?",
163+
"success": "Félicitation tu es devenu un Mythe de la F1!"
164+
},
165+
"PUB": {
166+
"confirmation": "Veux tu vraiment acheter un publicité pour **30 000 pièces**?",
167+
"success": "Merci pour ton achat va voir tes **mp** pour pour envoyé ton message !"
100168
}
101-
169+
102170
}
103171
return messages.get(valeur, {
104172
"confirmation": "❓ Article inconnu. Tu veux quand même continuer ?",
105173
"success": "❌ Article inconnu."
106174
})
107175

108176
# --------- Vue de confirmation ---------
177+
178+
109179
class ConfirmView(discord.ui.View):
110180
def __init__(self, interaction: discord.Interaction, valeur: str):
111181
super().__init__(timeout=30)
112182
self.interaction = interaction
113183
self.valeur = valeur
114-
184+
115185
@discord.ui.button(label="✅ Confirmer", style=discord.ButtonStyle.success)
116186
async def confirm(self, interaction: discord.Interaction, button: discord.ui.Button):
117187
if interaction.user != self.interaction.user:
@@ -126,26 +196,46 @@ async def confirm(self, interaction: discord.Interaction, button: discord.ui.But
126196
if retirer_argent(interaction.user.id, 200000):
127197
erreur = "❌ Tu n'as pas assez d'argent sur ton compte."
128198
else:
129-
await ajouter_role(interaction.user, 1339978184594034802)
130-
199+
await ajouter_role(interaction.user, param.GOAT_DES_GOATS)
200+
131201
if self.valeur == "MS":
132202
if retirer_argent(interaction.user.id, 150000):
133203
erreur = "❌ Tu n'as pas assez d'argent sur ton compte."
134204
else:
135-
await ajouter_role(interaction.user, 1339978383244656753)
205+
await ajouter_role(interaction.user, param.SUPREME_PARLE_TROP)
206+
207+
if self.valeur == "MF1":
208+
if retirer_argent(interaction.user.id, 75000):
209+
erreur = "❌ Tu n'as pas assez d'argent sur ton compte."
210+
else:
211+
await ajouter_role(interaction.user, param.MYTHE_DE_LA_F1)
136212

137-
elif self.valeur == "boost24h":
213+
if self.valeur == "boost24h":
138214
if retirer_argent(interaction.user.id, 35000):
139215
erreur = "❌ Tu n'as pas assez d'argent sur ton compte."
140216
else:
141217
# On lance la coroutine de façon "background" (non bloquante)
142-
asyncio.create_task(ajouter_role_temporaire(interaction.user, 1400473431782330528, JOUR)) #24
143-
218+
asyncio.create_task(ajouter_role_temporaire(
219+
interaction.user, param.BOOST_JOUR, param.JOUR)) # 24
220+
221+
if self.valeur == "PUB":
222+
if retirer_argent(interaction.user.id, 30000):
223+
erreur = "❌ Tu n'as pas assez d'argent sur ton compte."
224+
else:
225+
# ✅ On lance l’envoi du MP de pub en arrière-plan (non bloquant)
226+
asyncio.create_task(pub(interaction, param.COMMANDE_BOTS))
227+
144228
# Une seule réponse à l'interaction
145229
if erreur:
146-
await interaction.response.send_message(erreur, ephemeral=True)
230+
if interaction.response.is_done():
231+
await interaction.followup.send(erreur, ephemeral=True)
232+
else:
233+
await interaction.response.send_message(erreur, ephemeral=True)
147234
else:
148-
await interaction.response.edit_message(content=success_text, view=None)
235+
if interaction.response.is_done():
236+
await interaction.followup.send(success_text, ephemeral=True)
237+
else:
238+
await interaction.response.edit_message(content=success_text, view=None)
149239

150240
self.stop()
151241

@@ -155,20 +245,37 @@ async def cancel(self, interaction: discord.Interaction, button: discord.ui.Butt
155245
await interaction.response.send_message("❌ Tu ne peux pas annuler cette action.", ephemeral=True)
156246
return
157247

158-
await interaction.response.edit_message(content="❌ Achat annulé.", view=None)
248+
# Réponds d'abord à l'interaction pour éviter l'erreur
249+
await interaction.response.send_message("❌ Achat annulé.", ephemeral=True)
250+
251+
159252
self.stop()
160253

161254
# --------- Menu de la boutique ---------
255+
256+
162257
class BoutiqueMenu(discord.ui.Select):
163258
def __init__(self):
164259
options = [
165260
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"),
261+
discord.SelectOption(label="🟥 Role GOAT des GOATs",
262+
description="Reçois le rôle GOAT des GOATs pour 200 000", value="role_GdG"),
263+
discord.SelectOption(label="🟧 Mention Suprême parle trop",
264+
description="Tu parles tellement que tu as besoin d'un role pour 150 000", value="MS"),
265+
discord.SelectOption(label="🟨 Mythe du sport automobile",
266+
description="Reçois le role qui te reviens de droit! pour 90 000", value="MSP"),
267+
discord.SelectOption(
268+
label="🟩 Mythe de la F1", description="Deviens l'un des plus grand pilote de l'histoire! pour 75 000", value="MF1"),
168269
discord.SelectOption(label="=== Boost ==="),
169-
discord.SelectOption(label="🔮 Boost 24H", description="Reçois un rôle qui boost ton xp pendant 24h", value="boost24h")
270+
discord.SelectOption(
271+
label="🔮 Boost 24H", description="Reçois un rôle qui boost ton xp pendant 24h pour 35 000", value="boost24h"),
272+
discord.SelectOption(label="=== Visibilité ==="),
273+
discord.SelectOption(
274+
label="📰 Publicité", description="Envoi ta pub dans le salon", value="PUB")
275+
170276
]
171-
super().__init__(placeholder="Choisis un article...", options=options, min_values=1, max_values=1)
277+
super().__init__(placeholder="Choisis un article...",
278+
options=options, min_values=1, max_values=1)
172279

173280
async def callback(self, interaction: discord.Interaction):
174281
valeur = self.values[0]
@@ -177,6 +284,8 @@ async def callback(self, interaction: discord.Interaction):
177284
await interaction.response.send_message(message, view=view, ephemeral=True)
178285

179286
# --------- Vue principale ---------
287+
288+
180289
class BoutiqueView(discord.ui.View):
181290
def __init__(self):
182291
super().__init__(timeout=60)
@@ -191,6 +300,8 @@ async def cancel(self, interaction: discord.Interaction, button: discord.ui.Butt
191300
self.stop()
192301

193302
# --------- Commande principale ---------
303+
304+
194305
async def boutique(interaction: discord.Interaction):
195306
embed = discord.Embed(
196307
title="🛒 Bienvenue dans la Boutique Officielle F1F",
@@ -199,10 +310,11 @@ async def boutique(interaction: discord.Interaction):
199310
"Une fois ton choix fait, une double validation te sera proposée pour éviter toute erreur.\n\n"
200311
"🎯 Gagne des récompenses, fais évoluer ton profil, et distingue-toi des autres membres !\n"
201312
"💡 Des nouveautés seront ajoutées régulièrement, alors n'hésite pas à revenir souvent.",
202-
color=EMBED_COLOR_RED
313+
color=param.EMBED_COLOR_RED
203314
)
204-
embed.set_footer(text=EMBED_FOOTER_TEXT, icon_url=EMBED_THUMBNAIL)
315+
embed.set_footer(text=param.EMBED_FOOTER_TEXT,
316+
icon_url=param.EMBED_THUMBNAIL)
205317
embed.set_thumbnail(url=interaction.user.display_avatar.url)
206-
embed.set_image(url=EMBED_BOUTIQUE_OFFICEL)
318+
embed.set_image(url=param.EMBED_BOUTIQUE_OFFICEL)
207319

208320
await interaction.followup.send(embed=embed, view=BoutiqueView(), ephemeral=False)

app/config.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,15 @@
7272

7373
JOUR = 86400
7474

75+
# === 🟨 ROLE ===
76+
77+
GOAT_DES_GOATS = 1339978184594034802
78+
SUPREME_PARLE_TROP = 1339978383244656753
79+
MYTHE_DU_SPORT_AUTO = 1339978320745594961
80+
MYTHE_DE_LA_F1 = 1339978247433224303
81+
BOOST_JOUR = 1400473431782330528
82+
83+
# === 🟩 Salon ===
84+
85+
COMMANDE_BOTS = 928612687263444992
86+

0 commit comments

Comments
 (0)