Ajout de la gestion des messages de bienvenue et de départ pour les membres. Mise à jour des configurations pour activer ces fonctionnalités dans le panneau d'administration.

This commit is contained in:
Mow
2025-10-16 00:04:39 +02:00
parent fd172e2ea0
commit 6a171e795f
4 changed files with 244 additions and 31 deletions

View File

@@ -6,9 +6,10 @@ import random
from database import db
from database.helpers import ConfigurationHelper
from database.models import Configuration, Humeur, Commande
from discord import Message, TextChannel
from discord import Message, TextChannel, Member
from discordbot.humblebundle import checkHumbleBundleAndNotify
from discordbot.command import handle_warning_command, handle_remove_warning_command, handle_list_warnings_command, handle_ban_command, handle_kick_command, handle_unban_command
from discordbot.welcome import sendWelcomeMessage, sendLeaveMessage, updateInviteCache
from protondb import searhProtonDb
class DiscordBot(discord.Client):
@@ -17,6 +18,9 @@ class DiscordBot(discord.Client):
for c in self.get_all_channels() :
logging.info(f'{c.id} {c.name}')
for guild in self.guilds:
await updateInviteCache(guild)
self.loop.create_task(self.updateStatus())
self.loop.create_task(self.updateHumbleBundle())
@@ -54,6 +58,8 @@ class DiscordBot(discord.Client):
intents = discord.Intents.default()
intents.message_content = True
intents.members = True
intents.invites = True
bot = DiscordBot(intents=intents)
# https://discordpy.readthedocs.io/en/stable/quickstart.html
@@ -122,3 +128,19 @@ async def on_message(message: Message):
except Exception as e:
logging.error(f'Échec de l\'envoi du message ProtonDB : {e}')
@bot.event
async def on_member_join(member: Member):
await sendWelcomeMessage(bot, member)
@bot.event
async def on_member_remove(member: Member):
await sendLeaveMessage(bot, member)
@bot.event
async def on_invite_create(invite):
await updateInviteCache(invite.guild)
@bot.event
async def on_invite_delete(invite):
await updateInviteCache(invite.guild)

147
discordbot/welcome.py Normal file
View File

@@ -0,0 +1,147 @@
import discord
import logging
from database.helpers import ConfigurationHelper
from discord import Member, TextChannel
from datetime import datetime, timezone
invite_cache = {}
async def updateInviteCache(guild):
try:
invites = await guild.invites()
invite_cache[guild.id] = {invite.code: invite.uses for invite in invites}
except:
pass
async def getUsedInvite(guild) -> str:
try:
new_invites = await guild.invites()
for invite in new_invites:
old_uses = invite_cache.get(guild.id, {}).get(invite.code, 0)
if invite.uses > old_uses:
await updateInviteCache(guild)
inviter = f' (créée par {invite.inviter.name})' if invite.inviter else ''
return f'`{invite.code}`{inviter}'
await updateInviteCache(guild)
except:
pass
return 'Inconnue'
async def sendWelcomeMessage(bot: discord.Client, member: Member):
config = ConfigurationHelper()
if not config.getValue('welcome_enable'):
return
channel_id = config.getIntValue('welcome_channel_id')
if not channel_id:
logging.warning('Canal de bienvenue non configuré')
return
channel = bot.get_channel(channel_id)
if not channel or not isinstance(channel, TextChannel):
logging.error(f'Canal de bienvenue {channel_id} introuvable')
return
welcome_message = config.getValue('welcome_message')
if not welcome_message:
welcome_message = 'Bienvenue sur le serveur !'
invite_used = await getUsedInvite(member.guild)
embed = discord.Embed(
title='🎉 Nouveau membre !',
description=welcome_message,
color=discord.Color.green()
)
embed.set_thumbnail(url=member.display_avatar.url)
embed.add_field(name='Membre', value=member.mention, inline=True)
embed.add_field(name='Nombre de membres', value=str(member.guild.member_count), inline=True)
embed.add_field(name='Invitation utilisée', value=invite_used, inline=False)
embed.set_footer(text=f'ID: {member.id}')
try:
await channel.send(embed=embed)
logging.info(f'Message de bienvenue envoyé pour {member.name}')
except Exception as e:
logging.error(f'Échec de l\'envoi du message de bienvenue : {e}')
def formatDuration(seconds: int) -> str:
days = seconds // 86400
hours = (seconds % 86400) // 3600
minutes = (seconds % 3600) // 60
parts = []
if days > 0:
parts.append(f'{days} jour{"s" if days > 1 else ""}')
if hours > 0:
parts.append(f'{hours} heure{"s" if hours > 1 else ""}')
if minutes > 0:
parts.append(f'{minutes} minute{"s" if minutes > 1 else ""}')
if not parts:
return 'moins d\'une minute'
return ' et '.join(parts)
async def sendLeaveMessage(bot: discord.Client, member: Member):
config = ConfigurationHelper()
if not config.getValue('leave_enable'):
return
channel_id = config.getIntValue('leave_channel_id')
if not channel_id:
logging.warning('Canal de départ non configuré')
return
channel = bot.get_channel(channel_id)
if not channel or not isinstance(channel, TextChannel):
logging.error(f'Canal de départ {channel_id} introuvable')
return
leave_message = config.getValue('leave_message')
if not leave_message:
leave_message = 'Un membre a quitté le serveur.'
now = datetime.now(timezone.utc)
duration_seconds = int((now - member.joined_at).total_seconds()) if member.joined_at else 0
duration_text = formatDuration(duration_seconds)
reason = 'Départ volontaire'
try:
async for entry in member.guild.audit_logs(limit=5):
if entry.target and entry.target.id == member.id:
if entry.action == discord.AuditLogAction.kick:
reason = f'Expulsé par {entry.user.mention}'
if entry.reason:
reason += f' - Raison: {entry.reason}'
break
elif entry.action == discord.AuditLogAction.ban:
reason = f'Banni par {entry.user.mention}'
if entry.reason:
reason += f' - Raison: {entry.reason}'
break
except:
pass
embed = discord.Embed(
title='👋 Membre parti',
description=leave_message,
color=discord.Color.red()
)
embed.set_thumbnail(url=member.display_avatar.url)
embed.add_field(name='Membre', value=f'{member.mention} ({member.name})', inline=True)
embed.add_field(name='Nombre de membres', value=str(member.guild.member_count), inline=True)
embed.add_field(name='Temps sur le serveur', value=duration_text, inline=False)
embed.add_field(name='Raison', value=reason, inline=False)
embed.set_footer(text=f'ID: {member.id}')
try:
await channel.send(embed=embed)
logging.info(f'Message de départ envoyé pour {member.name}')
except Exception as e:
logging.error(f'Échec de l\'envoi du message de départ : {e}')

View File

@@ -23,6 +23,10 @@ def updateConfiguration():
ConfigurationHelper().createOrUpdate('moderation_ban_enable', False)
if (request.form.get("moderation_staff_role_id") != None and request.form.get("moderation_kick_enable") == None) :
ConfigurationHelper().createOrUpdate('moderation_kick_enable', False)
if (request.form.get("welcome_channel_id") != None and request.form.get("welcome_enable") == None) :
ConfigurationHelper().createOrUpdate('welcome_enable', False)
if (request.form.get("leave_channel_id") != None and request.form.get("leave_enable") == None) :
ConfigurationHelper().createOrUpdate('leave_enable', False)
db.session.commit()
return redirect(request.referrer)

View File

@@ -4,7 +4,9 @@
<h1>Configuration de Mamie</h1>
<p>Configurez les tokens Discord, les notifications Humble Bundle et l'API ProtonDB pour la commande !protondb.</p>
<h2>API Discord</h2>
<h2>Discord</h2>
<h3>API Discord</h3>
<form action="{{ url_for('updateConfiguration') }}" method="POST">
<label for="discord_token">API Discord (cachée)</label>
<input name="discord_token" type="password" />
@@ -12,6 +14,72 @@
<p>Nécessite un redémarrage</p>
</form>
<h3>Message de bienvenue Discord</h3>
<form action="{{ url_for('updateConfiguration') }}" method="POST">
<label for="welcome_enable">Activer</label>
<input type="checkbox" name="welcome_enable" {% if configuration.getValue('welcome_enable') %}
checked="checked" {% endif %}>
<label>Activer le message de bienvenue pour les nouveaux membres</label>
<label for="welcome_channel_id">Canal de bienvenue</label>
<select name="welcome_channel_id">
{% for channel in channels %}
<option value="{{channel.id}}" {% if configuration.getIntValue('welcome_channel_id')==channel.id %}
selected="selected" {% endif %}>
{{channel.name}}</option>
{% endfor %}
</select>
<label for="welcome_message">Message personnalisé</label>
<textarea name="welcome_message" rows="4" placeholder="Bienvenue sur le serveur !">{{ configuration.getValue('welcome_message') }}</textarea>
<input type="Submit" value="Définir">
</form>
<h3>Message de départ Discord</h3>
<form action="{{ url_for('updateConfiguration') }}" method="POST">
<label for="leave_enable">Activer</label>
<input type="checkbox" name="leave_enable" {% if configuration.getValue('leave_enable') %}
checked="checked" {% endif %}>
<label>Activer le message de départ quand un membre quitte le serveur</label>
<label for="leave_channel_id">Canal de départ</label>
<select name="leave_channel_id">
{% for channel in channels %}
<option value="{{channel.id}}" {% if configuration.getIntValue('leave_channel_id')==channel.id %}
selected="selected" {% endif %}>
{{channel.name}}</option>
{% endfor %}
</select>
<label for="leave_message">Message personnalisé</label>
<textarea name="leave_message" rows="4" placeholder="Un membre a quitté le serveur.">{{ configuration.getValue('leave_message') }}</textarea>
<input type="Submit" value="Définir">
</form>
<h3>Modération Discord</h3>
<form action="{{ url_for('updateConfiguration') }}" method="POST">
<label for="moderation_enable">Activer les avertissements</label>
<input type="checkbox" name="moderation_enable" {% if configuration.getValue('moderation_enable') %}
checked="checked" {% endif %}>
<label>Activer les commandes d'avertissement (!averto, !delaverto, !listaverto)</label>
<label for="moderation_ban_enable">Activer le ban</label>
<input type="checkbox" name="moderation_ban_enable" {% if configuration.getValue('moderation_ban_enable') %}
checked="checked" {% endif %}>
<label>Activer les commandes de bannissement (!ban, !unban)</label>
<label for="moderation_kick_enable">Activer le kick</label>
<input type="checkbox" name="moderation_kick_enable" {% if configuration.getValue('moderation_kick_enable') %}
checked="checked" {% endif %}>
<label>Activer la commande d'expulsion (!kick)</label>
<label for="moderation_staff_role_id">ID du rôle Staff</label>
<input name="moderation_staff_role_id" type="text" value="{{ configuration.getValue('moderation_staff_role_id') }}"
placeholder="581990740431732738" />
<label for="moderation_embed_delete_delay">Délai de suppression des embeds (en secondes, 0 = ne pas supprimer)</label>
<input name="moderation_embed_delete_delay" type="number" value="{{ configuration.getValue('moderation_embed_delete_delay') or '0' }}"
placeholder="0" min="0" />
<input type="Submit" value="Définir">
</form>
<h2>API Twitch</h2>
<form action="{{ url_for('updateConfiguration') }}" method="POST">
<label for="twitch_client_id">Client ID</label>
@@ -55,32 +123,4 @@
</select>
<input type="Submit" value="Définir">
</form>
<h2>Modération Discord</h2>
<form action="{{ url_for('updateConfiguration') }}" method="POST">
<label for="moderation_enable">Activer les avertissements</label>
<input type="checkbox" name="moderation_enable" {% if configuration.getValue('moderation_enable') %}
checked="checked" {% endif %}>
<label>Activer les commandes d'avertissement (!averto, !delaverto, !listaverto)</label>
<label for="moderation_ban_enable">Activer le ban</label>
<input type="checkbox" name="moderation_ban_enable" {% if configuration.getValue('moderation_ban_enable') %}
checked="checked" {% endif %}>
<label>Activer les commandes de bannissement (!ban, !unban)</label>
<label for="moderation_kick_enable">Activer le kick</label>
<input type="checkbox" name="moderation_kick_enable" {% if configuration.getValue('moderation_kick_enable') %}
checked="checked" {% endif %}>
<label>Activer la commande d'expulsion (!kick)</label>
<label for="moderation_staff_role_id">ID du rôle Staff</label>
<input name="moderation_staff_role_id" type="text" value="{{ configuration.getValue('moderation_staff_role_id') }}"
placeholder="581990740431732738" />
<label for="moderation_embed_delete_delay">Délai de suppression des embeds (en secondes, 0 = ne pas supprimer)</label>
<input name="moderation_embed_delete_delay" type="number" value="{{ configuration.getValue('moderation_embed_delete_delay') or '0' }}"
placeholder="0" min="0" />
<input type="Submit" value="Définir">
</form>
{% endblock %}