From 1354568495115c1f67a86b8a00babf88b4072f03 Mon Sep 17 00:00:00 2001 From: mrtoine Date: Wed, 17 Dec 2025 14:19:21 +0100 Subject: [PATCH 1/7] =?UTF-8?q?Mise=20=C3=A0=20jour=20de=20la=20version=20?= =?UTF-8?q?applicative=20dans=20`VERSION.txt`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index 4654951..e3a8aa8 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.4.0 (536f4e3) \ No newline at end of file +1.4.1 (e9754c2) \ No newline at end of file From 2ec4a5c065f4aefac119a31de2d10876a041a092 Mon Sep 17 00:00:00 2001 From: mrtoine Date: Thu, 18 Dec 2025 10:28:28 +0100 Subject: [PATCH 2/7] =?UTF-8?q?Ajout=20de=20l'application=20`discord=5Fint?= =?UTF-8?q?egration`=20avec=20mod=C3=A8les,=20migrations,=20logique=20d'an?= =?UTF-8?q?nonces=20et=20gestion=20des=20r=C3=B4les=20dans=20Discord.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blog/models.py | 6 +- courses/apps.py | 1 - devart/settings.py | 2 + discord_integration/__init__.py | 0 discord_integration/admin.py | 3 + discord_integration/apps.py | 8 +++ discord_integration/core/__init__.py | 0 discord_integration/core/announces_logic.py | 58 +++++++++++++++++++ discord_integration/core/main_bot.py | 49 ++++++++++++++++ discord_integration/core/role_logic.py | 39 +++++++++++++ .../migrations/0001_initial.py | 26 +++++++++ discord_integration/migrations/__init__.py | 0 discord_integration/models.py | 13 +++++ discord_integration/signals.py | 26 +++++++++ discord_integration/tests.py | 3 + discord_integration/views.py | 3 + templates/partials/_footer.html | 1 + templates/partials/_header.html | 7 +-- 18 files changed, 239 insertions(+), 6 deletions(-) create mode 100644 discord_integration/__init__.py create mode 100644 discord_integration/admin.py create mode 100644 discord_integration/apps.py create mode 100644 discord_integration/core/__init__.py create mode 100644 discord_integration/core/announces_logic.py create mode 100644 discord_integration/core/main_bot.py create mode 100644 discord_integration/core/role_logic.py create mode 100644 discord_integration/migrations/0001_initial.py create mode 100644 discord_integration/migrations/__init__.py create mode 100644 discord_integration/models.py create mode 100644 discord_integration/signals.py create mode 100644 discord_integration/tests.py create mode 100644 discord_integration/views.py diff --git a/blog/models.py b/blog/models.py index 387d847..5d0d68c 100644 --- a/blog/models.py +++ b/blog/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.urls import reverse class Post(models.Model): name = models.CharField(max_length=200) @@ -15,4 +16,7 @@ class Post(models.Model): verbose_name_plural = "Articles" def __str__(self): - return self.name \ No newline at end of file + return self.name + + def get_absolute_url(self): + return reverse('blog:post_detail', kwargs={'slug': self.slug}) \ No newline at end of file diff --git a/courses/apps.py b/courses/apps.py index 89f1ba2..9a0a0cd 100644 --- a/courses/apps.py +++ b/courses/apps.py @@ -1,6 +1,5 @@ from django.apps import AppConfig - class CoursesConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'courses' \ No newline at end of file diff --git a/devart/settings.py b/devart/settings.py index db43f75..f2dab3b 100644 --- a/devart/settings.py +++ b/devart/settings.py @@ -55,6 +55,8 @@ INSTALLED_APPS = [ 'users', 'progression', 'blog', + + 'discord_integration.apps.DiscordIntegrationConfig', ] MIDDLEWARE = [ diff --git a/discord_integration/__init__.py b/discord_integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/discord_integration/admin.py b/discord_integration/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/discord_integration/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/discord_integration/apps.py b/discord_integration/apps.py new file mode 100644 index 0000000..56fe46c --- /dev/null +++ b/discord_integration/apps.py @@ -0,0 +1,8 @@ +from django.apps import AppConfig + + +class DiscordIntegrationConfig(AppConfig): + name = 'discord_integration' + + def ready(self): + import discord_integration.signals diff --git a/discord_integration/core/__init__.py b/discord_integration/core/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/discord_integration/core/announces_logic.py b/discord_integration/core/announces_logic.py new file mode 100644 index 0000000..03e8939 --- /dev/null +++ b/discord_integration/core/announces_logic.py @@ -0,0 +1,58 @@ +from asgiref.sync import sync_to_async +import discord +from discord.ext import tasks +from discord_integration.models import DiscordNotification + +def get_pending_notifications(): + return list(DiscordNotification.objects.filter(is_announced=False)) + +def mark_as_done(notif): + notif.is_announced = True + notif.save() + +def process_notifications(): + # Cette fonction fait tout le travail SQL "interdit" en mode async + notifs = list(DiscordNotification.objects.filter(is_announced=False)) + results = [] + for n in notifs: + # Ici, on peut toucher à content_object car on est en mode "sync" ! + obj = n.content_object + if obj: + # 1. On cherche la description, sinon le contenu, sinon rien + teaser = getattr(obj, 'description', getattr(obj, 'content', "")) + + # 2. Sécurité : On coupe à 3000 caractères pour éviter les erreurs Discord + if len(teaser) > 3000: + teaser = teaser[:2997] + "..." + + results.append({ + 'notif_model': n, + 'title': getattr(obj, 'title', getattr(obj, 'name', str(obj))), + 'url': obj.get_absolute_url() if hasattr(obj, 'get_absolute_url') else "#", + 'summary': teaser + }) + + return results + +@tasks.loop(seconds=5.0) +async def check_announcements(client, channel_id): + print("Checking announcements...") + announcements = await sync_to_async(process_notifications)() + + for data in announcements: + title = data['title'] + link = data['url'] + notif = data['notif_model'] + summary = data['summary'] + + embed = discord.Embed( + title = f"📣 Nouveau contenu : {title}", + url = f"https://partirdezero.com{link}", + description = summary, + color=discord.Color.blue() + ) + + channel_id = client.get_channel(channel_id) + if channel_id: + await channel_id.send(embed=embed) + await sync_to_async(mark_as_done)(notif) diff --git a/discord_integration/core/main_bot.py b/discord_integration/core/main_bot.py new file mode 100644 index 0000000..aae766e --- /dev/null +++ b/discord_integration/core/main_bot.py @@ -0,0 +1,49 @@ +import discord +import django +import os, sys +from discord.ext import commands + +# On import django pour communiquer avec +BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +if BASE_DIR not in sys.path: + sys.path.insert(0, BASE_DIR) +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'devart.settings') +django.setup() + +# Import des fonctions +from role_logic import check_role_reaction +from announces_logic import check_announcements + +# CONFIGURATION +TOKEN = 'MTQ1MDkyNzQ5NzQ3Nzc1MDg1NA.GmkYxN.YHWXYUIav51yriV_9EotmtUO-cQqdVFLkkb6Do' +MESSAGE_ID = 1450928822156263505 # L'ID du message des règles (clic droit > Copier l'identifiant) +ROLE_ID = 1450920002868875435 # L'ID du rôle "Membres" +ANNOUNCEMENT_CHANNEL_ID = 1450912559774306346 +EMOJI_VALIDATION = "✅" + +# LES INTENTS (PERMISSIONS DU BOT) +intents = discord.Intents.default() +intents.members = True # Important pour pouvoir donner des rôles +intents.message_content = True + +client = discord.Client(intents=intents) + +@client.event +async def on_ready(): + print(f'✅ Bot connecté : {client.user}') + + if not check_announcements.is_running(): + check_announcements.start(client, ANNOUNCEMENT_CHANNEL_ID) + +@client.event +async def on_raw_reaction_add(payload): + # On envoie tout le nécessaire à notre fonction dans role_logic.py + await check_role_reaction( + payload, + client, + MESSAGE_ID, + ROLE_ID, + EMOJI_VALIDATION + ) + +client.run(TOKEN) diff --git a/discord_integration/core/role_logic.py b/discord_integration/core/role_logic.py new file mode 100644 index 0000000..8dd2dbc --- /dev/null +++ b/discord_integration/core/role_logic.py @@ -0,0 +1,39 @@ +import discord + + +async def check_role_reaction(payload, client, target_message_id, target_role_id, target_emoji): + # 1. On vérifie si c'est le bon message + if payload.message_id != target_message_id: + return # On ignore si ce n'est pas le bon message + + # 2. On vérifie si c'est le bon emoji + if str(payload.emoji) == target_emoji: + guild = client.get_guild(payload.guild_id) + if guild is None: + print("Erreur: Impossible de trouver le serveur (Guild is None).") + return + + member = guild.get_member(payload.user_id) + if member is None: + print("Erreur: Impossible de trouver le membre (Member is None).") + return + + role = guild.get_role(target_role_id) + if role is None: + print("Erreur : Le role n'existe pas.") + return + + try: + await member.add_roles(role) + print(f"🎉 SUCCÈS : Rôle donné à {member.name} !") + try: + await member.send("Bienvenue ! Tu as accès aux salons.") + except: + print("Note: MP bloqués par l'utilisateur.") + except discord.Forbidden: + print("⛔ ERREUR PERMISSION : Je n'ai pas le droit de donner ce rôle !") + print( + "👉 SOLUTION : Va dans Paramètres Serveur > Rôles. Glisse le rôle 'PartirDeZero Bot' AU-DESSUS du rôle 'Membres'.") + + except Exception as e: + print(f"❌ Erreur inconnue : {e}") \ No newline at end of file diff --git a/discord_integration/migrations/0001_initial.py b/discord_integration/migrations/0001_initial.py new file mode 100644 index 0000000..6c269a9 --- /dev/null +++ b/discord_integration/migrations/0001_initial.py @@ -0,0 +1,26 @@ +# Generated by Django 6.0 on 2025-12-18 08:23 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ] + + operations = [ + migrations.CreateModel( + name='DiscordNotification', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('object_id', models.PositiveIntegerField()), + ('is_announced', models.BooleanField(default=False)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), + ], + ), + ] diff --git a/discord_integration/migrations/__init__.py b/discord_integration/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/discord_integration/models.py b/discord_integration/models.py new file mode 100644 index 0000000..9906f9b --- /dev/null +++ b/discord_integration/models.py @@ -0,0 +1,13 @@ +from django.contrib.contenttypes.fields import GenericForeignKey +from django.db import models +from django.contrib.contenttypes.models import ContentType + +class DiscordNotification(models.Model): + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) + object_id = models.PositiveIntegerField() + content_object = GenericForeignKey('content_type', 'object_id') + is_announced = models.BooleanField(default=False) + created_at = models.DateTimeField(auto_now_add=True) + + def __str__(self): + return f"Annonces pour {self.content_object} ({'✅' if self.is_announced else '⏳'})" \ No newline at end of file diff --git a/discord_integration/signals.py b/discord_integration/signals.py new file mode 100644 index 0000000..c69abe4 --- /dev/null +++ b/discord_integration/signals.py @@ -0,0 +1,26 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver +from .models import DiscordNotification +from blog.models import Post +from courses.models import Lesson, Course + +@receiver(post_save, sender="blog.Post") +def create_discord_notification_blog(sender, instance, created, **kwargs): + print(f"DEBUG : Signal capté ! Nouveau objet : {instance}") + if created: + DiscordNotification.objects.create(content_object=instance) + print("DEBUG : Notification enregistée en base de données") + +@receiver(post_save, sender="courses.Course") +def create_discord_notification_course(sender, instance, created, **kwargs): + print(f"DEBUG : Signal capté ! Nouveau objet : {instance}") + if created: + DiscordNotification.objects.create(content_object=instance) + print("DEBUG : Notification enregistée en base de données") + +@receiver(post_save, sender="courses.Lesson") +def create_discord_notification_lesson(sender, instance, created, **kwargs): + print(f"DEBUG : Signal capté ! Nouveau objet : {instance}") + if created: + DiscordNotification.objects.create(content_object=instance) + print("DEBUG : Notification enregistée en base de données") \ No newline at end of file diff --git a/discord_integration/tests.py b/discord_integration/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/discord_integration/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/discord_integration/views.py b/discord_integration/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/discord_integration/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/templates/partials/_footer.html b/templates/partials/_footer.html index 934a5e3..42cc419 100644 --- a/templates/partials/_footer.html +++ b/templates/partials/_footer.html @@ -29,6 +29,7 @@ {% if settings.contact_email %}
  • Email
  • {% endif %} +
  • Communauté
  • diff --git a/templates/partials/_header.html b/templates/partials/_header.html index a6938aa..b4ffa39 100644 --- a/templates/partials/_header.html +++ b/templates/partials/_header.html @@ -45,11 +45,10 @@ {% endif %} +
  • Discord
  • {% if user.is_authenticated and user.is_staff %} -
  • - Admin - Stats -
  • +
  • Admin
  • +
  • Stats
  • {% endif %}
  • {% if user.is_authenticated %} From afc26145350387ca961ff6715522bf12bf84de33 Mon Sep 17 00:00:00 2001 From: mrtoine Date: Thu, 18 Dec 2025 10:29:14 +0100 Subject: [PATCH 3/7] =?UTF-8?q?Mise=20=C3=A0=20jour=20de=20la=20version=20?= =?UTF-8?q?applicative=20dans=20`VERSION.txt`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index e3a8aa8..0b51dff 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.4.1 (e9754c2) \ No newline at end of file +1.5.0 (2ec4a5c) \ No newline at end of file From ad6600e4f66247a9ef75b2968cd2329af164c920 Mon Sep 17 00:00:00 2001 From: mrtoine Date: Thu, 18 Dec 2025 10:35:56 +0100 Subject: [PATCH 4/7] =?UTF-8?q?Mise=20=C3=A0=20jour=20du=20lien=20Discord?= =?UTF-8?q?=20dans=20`=5Fheader.html`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- templates/partials/_header.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/partials/_header.html b/templates/partials/_header.html index b4ffa39..4c3e694 100644 --- a/templates/partials/_header.html +++ b/templates/partials/_header.html @@ -45,7 +45,7 @@ {% endif %}
  • -
  • Discord
  • +
  • Discord
  • {% if user.is_authenticated and user.is_staff %}
  • Admin
  • Stats
  • From 3f90cfa3396e20f3169349587431de2ecc9a7406 Mon Sep 17 00:00:00 2001 From: mrtoine Date: Thu, 18 Dec 2025 14:38:31 +0100 Subject: [PATCH 5/7] =?UTF-8?q?Ajout=20des=20commandes=20de=20gestion=20du?= =?UTF-8?q?=20niveau,=20d'une=20logique=20XP,=20et=20de=20nouvelles=20fonc?= =?UTF-8?q?tionnalit=C3=A9s=20bot=20dans=20`discord=5Fintegration`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- discord_integration/core/BotClass.py | 14 +++++ discord_integration/core/announces_logic.py | 1 - discord_integration/core/enums.py | 16 +++++ discord_integration/core/level_logic.py | 70 +++++++++++++++++++++ discord_integration/core/main_bot.py | 28 ++++++++- discord_integration/core/random_phrase.py | 32 ++++++++++ discord_integration/models.py | 9 ++- 7 files changed, 166 insertions(+), 4 deletions(-) create mode 100644 discord_integration/core/BotClass.py create mode 100644 discord_integration/core/enums.py create mode 100644 discord_integration/core/level_logic.py create mode 100644 discord_integration/core/random_phrase.py diff --git a/discord_integration/core/BotClass.py b/discord_integration/core/BotClass.py new file mode 100644 index 0000000..ac9da47 --- /dev/null +++ b/discord_integration/core/BotClass.py @@ -0,0 +1,14 @@ +import discord +from discord import app_commands + +intents = discord.Intents.default() +intents.members = True +intents.message_content = True + +class Bot(discord.Client): + def __init__(self, *, intents: discord.Intents): + super().__init__(intents=intents) + self.tree = app_commands.CommandTree(self) + + async def setup_hook(self): + await self.tree.sync() \ No newline at end of file diff --git a/discord_integration/core/announces_logic.py b/discord_integration/core/announces_logic.py index 03e8939..6781202 100644 --- a/discord_integration/core/announces_logic.py +++ b/discord_integration/core/announces_logic.py @@ -36,7 +36,6 @@ def process_notifications(): @tasks.loop(seconds=5.0) async def check_announcements(client, channel_id): - print("Checking announcements...") announcements = await sync_to_async(process_notifications)() for data in announcements: diff --git a/discord_integration/core/enums.py b/discord_integration/core/enums.py new file mode 100644 index 0000000..75c7ced --- /dev/null +++ b/discord_integration/core/enums.py @@ -0,0 +1,16 @@ +XP = { + "MESSAGE": 5 +} + +RANK = { + 1: "Nouveau membre", + 3: "Membre", + 7: "Habitué du comptoir", + 12: "Expert", + 18: "Chevalier du code", + 25: "Baron C#", + 35: "Lord Script", + 50: "Héros des architectures", + 75: "Vétéran", + 100: "Légende" +} \ No newline at end of file diff --git a/discord_integration/core/level_logic.py b/discord_integration/core/level_logic.py new file mode 100644 index 0000000..a3051c6 --- /dev/null +++ b/discord_integration/core/level_logic.py @@ -0,0 +1,70 @@ +from datetime import datetime, timezone + +from asgiref.sync import sync_to_async +import discord +from datetime import datetime +from discord_integration.models import DiscordLevel +from enums import XP, RANK +from discord import app_commands + +def get_user(id_discord): + user, created = DiscordLevel.objects.get_or_create(discord_id=id_discord) + return user, created + +def update_user_xp(user, xp_to_add): + leveled_up = False + + # 1. Mise à jour de l'XP et du temps + user.total_xp += xp_to_add + user.last_message = datetime.now(timezone.utc) + + # 2. Calcul du niveau théorique + calculated_level = int(0.5 + (0.25 + user.total_xp / 50)**0.5) + + # 3. Vérification du Level Up + if calculated_level > user.level: + user.level = calculated_level + leveled_up = True + + new_rank = RANK[1] + + for level_threshold, rank_name in RANK.items(): + if user.level >= level_threshold: + new_rank = rank_name + else: + break + + user.rank = new_rank + + user.save() + return user, leveled_up + +async def check_add_xp(message, client): + id_discord = message.author.id + username = message.author.name + + user_db, created = await sync_to_async(get_user)(id_discord) + + if not created and user_db.last_message: + delta = datetime.now(timezone.utc) - user_db.last_message + if delta.seconds < 6: + return + + user_db, leveled_up = await sync_to_async(update_user_xp)(user_db, XP["MESSAGE"]) + + if leveled_up: + # On crée un petit message sympa + await message.channel.send( + f"🎊 **LEVEL UP** 🎊\nBravo {message.author.mention}, tu passes **niveau {user_db.level}** ! " + f"On applaudit tous bien fort ! Clap Clap !!" + ) + else: + # Juste un petit log console pour toi + print(f"✨ XP ajouté pour {message.author.name} (Total: {user_db.total_xp})") + +# AJOUT DES COMMANDES /xp et /level +@app_commands.command(name="level", description="Permet de connaitre ton xp actuel") +async def get_xp(interaction: discord.Interaction): + user_id = interaction.user.id + user_db, _ = await sync_to_async(get_user)(user_id) + await interaction.response.send_message(f"{interaction.user.mention} : Tu as {user_db.total_xp} XP et tu es niveau {user_db.level} !\nTon rang est {user_db.rank} !") \ No newline at end of file diff --git a/discord_integration/core/main_bot.py b/discord_integration/core/main_bot.py index aae766e..6fa7d4c 100644 --- a/discord_integration/core/main_bot.py +++ b/discord_integration/core/main_bot.py @@ -1,6 +1,8 @@ import discord import django import os, sys + +import dotenv from discord.ext import commands # On import django pour communiquer avec @@ -13,9 +15,12 @@ django.setup() # Import des fonctions from role_logic import check_role_reaction from announces_logic import check_announcements +from random_phrase import get_random_phrase +from level_logic import check_add_xp, get_xp +import BotClass # CONFIGURATION -TOKEN = 'MTQ1MDkyNzQ5NzQ3Nzc1MDg1NA.GmkYxN.YHWXYUIav51yriV_9EotmtUO-cQqdVFLkkb6Do' +TOKEN = dotenv.get_key(BASE_DIR + '/.env', 'D_TOKEN') MESSAGE_ID = 1450928822156263505 # L'ID du message des règles (clic droit > Copier l'identifiant) ROLE_ID = 1450920002868875435 # L'ID du rôle "Membres" ANNOUNCEMENT_CHANNEL_ID = 1450912559774306346 @@ -26,15 +31,34 @@ intents = discord.Intents.default() intents.members = True # Important pour pouvoir donner des rôles intents.message_content = True -client = discord.Client(intents=intents) +client = BotClass.Bot(intents=intents) +client.tree.add_command(get_random_phrase) +client.tree.add_command(get_xp) @client.event async def on_ready(): print(f'✅ Bot connecté : {client.user}') + try: + synced = await client.tree.sync() + print(f"🌍 {len(synced)} commandes slash synchronisées !") + except Exception as e: + print(f"❌ Erreur de synchronisation : {e}") + if not check_announcements.is_running(): check_announcements.start(client, ANNOUNCEMENT_CHANNEL_ID) +@client.event +async def on_message(message): + if message.author == client.user: + return + if message.guild is None: + author = message.author + await message.channel.send("Bonjour !\nJe suis un bot destiné à tester les nouvelles fonctionnalités de Discord. Pour le moment, je suis qu'en lecture seule.") + else: + await check_add_xp(message, client) + + @client.event async def on_raw_reaction_add(payload): # On envoie tout le nécessaire à notre fonction dans role_logic.py diff --git a/discord_integration/core/random_phrase.py b/discord_integration/core/random_phrase.py new file mode 100644 index 0000000..e8cfea2 --- /dev/null +++ b/discord_integration/core/random_phrase.py @@ -0,0 +1,32 @@ +import discord +import random + +from discord import app_commands + +phrase = [ + "Yo !", + "Quoi de neuf ?", + "Je suis occupé à compter mes octets.", + "Vive la Belgique ! 🇧🇪", + "C’est pas un bug, c’est une fonctionnalité non documentée ! 🐛", + "Est-ce qu’on peut dire que mon code est une œuvre d’art ? Non ? Dommage.", + "Je ne plante pas, je fais une pause créative.", + "Quelqu’un a vu mon point-virgule ? Il a disparu depuis le dernier commit.", + "Je mangerais bien une mitraillette sauce andalouse, mais mon système digestif est en 404. 🍟", + "42. Voilà. Maintenant, pose-moi une vraie question.", + "On mange quoi ? Ah non, c'est vrai, je suis un robot... Tristesse infinie. 🤖", + "C'est écrit en Python, donc c'est forcément élégant, non ?", + "Un petit café ? Pour moi, une petite dose d'électricité suffira.", + "Je parie que tu n'as pas encore fait ton `git push` aujourd'hui. Je te surveille ! 👀", + "En Belgique, on n'a peut-être pas toujours du soleil, mais on a les meilleures frites ! 🇧🇪🍟", + "Il y a 10 types de personnes : celles qui comprennent le binaire, et les autres.", + "Mon processeur chauffe... soit je réfléchis trop, soit ton code est trop complexe !", + "Tout va bien, tant que personne ne touche au dossier `migrations` de Django...", + "Sais-tu pourquoi les développeurs détestent la nature ? Parce qu'il y a trop de bugs. 🌳", + "On n'est pas là pour trier des lentilles, une fois ! On code ou quoi ? 🇧🇪" +] + +@app_commands.command(name="random_phrase", description="Envoi une phrase aléatoire !") +async def get_random_phrase(interaction: discord.Interaction): + choice = random.choice(phrase) + await interaction.response.send_message(choice) \ No newline at end of file diff --git a/discord_integration/models.py b/discord_integration/models.py index 9906f9b..614a096 100644 --- a/discord_integration/models.py +++ b/discord_integration/models.py @@ -10,4 +10,11 @@ class DiscordNotification(models.Model): created_at = models.DateTimeField(auto_now_add=True) def __str__(self): - return f"Annonces pour {self.content_object} ({'✅' if self.is_announced else '⏳'})" \ No newline at end of file + return f"Annonces pour {self.content_object} ({'✅' if self.is_announced else '⏳'})" + +class DiscordLevel(models.Model): + discord_id = models.BigIntegerField() + total_xp = models.PositiveIntegerField(default=0) + level = models.PositiveIntegerField(default=1) + rank = models.TextField(default="Nouveau membre") + last_message = models.DateTimeField(auto_now_add=True) \ No newline at end of file From 2a6f670361004b549204135d288277a790769e87 Mon Sep 17 00:00:00 2001 From: mrtoine Date: Thu, 18 Dec 2025 14:41:11 +0100 Subject: [PATCH 6/7] =?UTF-8?q?Ajout=20du=20mod=C3=A8le=20`DiscordLevel`?= =?UTF-8?q?=20avec=20champs=20pour=20la=20gestion=20des=20niveaux=20et=20d?= =?UTF-8?q?e=20l'XP=20dans=20`discord=5Fintegration`.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migrations/0002_discordlevel.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 discord_integration/migrations/0002_discordlevel.py diff --git a/discord_integration/migrations/0002_discordlevel.py b/discord_integration/migrations/0002_discordlevel.py new file mode 100644 index 0000000..b35f4b8 --- /dev/null +++ b/discord_integration/migrations/0002_discordlevel.py @@ -0,0 +1,24 @@ +# Generated by Django 6.0 on 2025-12-18 11:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('discord_integration', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='DiscordLevel', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('discord_id', models.BigIntegerField()), + ('total_xp', models.PositiveIntegerField(default=0)), + ('level', models.PositiveIntegerField(default=1)), + ('rank', models.TextField(default='Nouveau membre')), + ('last_message', models.DateTimeField(auto_now_add=True)), + ], + ), + ] From 40a8c6d10641f5fae3305a556e675a12b6ddbcbe Mon Sep 17 00:00:00 2001 From: mrtoine Date: Thu, 18 Dec 2025 14:50:36 +0100 Subject: [PATCH 7/7] =?UTF-8?q?Suppression=20des=20logs=20de=20d=C3=A9boga?= =?UTF-8?q?ge=20des=20signaux=20et=20am=C3=A9lioration=20du=20formatage=20?= =?UTF-8?q?du=20rang=20dans=20la=20r=C3=A9ponse=20Discord.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- discord_integration/core/level_logic.py | 2 +- discord_integration/signals.py | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/discord_integration/core/level_logic.py b/discord_integration/core/level_logic.py index a3051c6..fe42f8f 100644 --- a/discord_integration/core/level_logic.py +++ b/discord_integration/core/level_logic.py @@ -67,4 +67,4 @@ async def check_add_xp(message, client): async def get_xp(interaction: discord.Interaction): user_id = interaction.user.id user_db, _ = await sync_to_async(get_user)(user_id) - await interaction.response.send_message(f"{interaction.user.mention} : Tu as {user_db.total_xp} XP et tu es niveau {user_db.level} !\nTon rang est {user_db.rank} !") \ No newline at end of file + await interaction.response.send_message(f"{interaction.user.mention} : Tu as {user_db.total_xp} XP et tu es niveau {user_db.level} !\nTon rang est **{user_db.rank}** !") \ No newline at end of file diff --git a/discord_integration/signals.py b/discord_integration/signals.py index c69abe4..cd5897e 100644 --- a/discord_integration/signals.py +++ b/discord_integration/signals.py @@ -1,26 +1,18 @@ from django.db.models.signals import post_save from django.dispatch import receiver from .models import DiscordNotification -from blog.models import Post -from courses.models import Lesson, Course @receiver(post_save, sender="blog.Post") def create_discord_notification_blog(sender, instance, created, **kwargs): - print(f"DEBUG : Signal capté ! Nouveau objet : {instance}") if created: DiscordNotification.objects.create(content_object=instance) - print("DEBUG : Notification enregistée en base de données") @receiver(post_save, sender="courses.Course") def create_discord_notification_course(sender, instance, created, **kwargs): - print(f"DEBUG : Signal capté ! Nouveau objet : {instance}") if created: DiscordNotification.objects.create(content_object=instance) - print("DEBUG : Notification enregistée en base de données") @receiver(post_save, sender="courses.Lesson") def create_discord_notification_lesson(sender, instance, created, **kwargs): - print(f"DEBUG : Signal capté ! Nouveau objet : {instance}") if created: - DiscordNotification.objects.create(content_object=instance) - print("DEBUG : Notification enregistée en base de données") \ No newline at end of file + DiscordNotification.objects.create(content_object=instance) \ No newline at end of file