22import os
33import json
44import 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
78DATA_FILE = 'data/eco/central_account.json'
89
910# --------- Gestion des données ---------
11+
12+
1013def 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+
1721def sauvegarder_donnees (donnees ):
1822 with open (DATA_FILE , "w" , encoding = "utf-8" ) as f :
1923 json .dump (donnees , f , indent = 4 )
2024
25+
2126def 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+
3541def 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+
5562async 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+
6876async 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 ---------
87143def 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+
109179class 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+
162257class 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+
180289class 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+
194305async 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 )
0 commit comments