VisionApp/features/welcome.py
Mathis 5d9baee102
All checks were successful
ci / ci (push) Successful in 1m1s
feat: full feature suite — welcome, modmail, security, staff, avis, rules, links, shop, custom
features/shared.py: per-guild JSON config store
features/welcome.py: on_member_join message + invite tracking + /invites
features/modmail.py: DM-based tickets with staff relay
features/security.py: antiraid (join rate detection), ban protection, join/leave log
features/staff.py: staff ranks, promote/demote, /staff_list
features/avis.py: /avis modal -> embed in configured channel
features/rules.py: /rules_setup with accept button + role assignment
features/links.py: /links panel, /links_setup, /links_remove
features/shop.py: /shop_announce new product embeds
features/custom.py: /botstatus, /custom_send, /embed
2026-10-02 19:45:46 +02:00

136 lines
5.2 KiB
Python

from __future__ import annotations
import logging
import discord
from discord import app_commands
from discord.ext import commands
from features import shared
logger = logging.getLogger("vision")
class Welcome(commands.Cog):
def __init__(self, bot: commands.Bot):
self.bot = bot
self._invites: dict[int, dict[str, int]] = {}
@commands.Cog.listener()
async def on_ready(self):
await self._cache_invites()
@commands.Cog.listener()
async def on_guild_join(self, guild: discord.Guild):
await self._cache_invites_for(guild)
@commands.Cog.listener()
async def on_member_join(self, member: discord.Member):
cfg = shared.section(member.guild.id, "welcome")
if not cfg.get("enabled"):
return
channel_id = cfg.get("channel_id")
if not channel_id:
return
channel = member.guild.get_channel(channel_id)
if not channel:
return
inviter = await self._find_inviter(member)
text = cfg.get("message", "**{user}** a rejoint **{guild}** ! ({member_count} membres)")
text = text.replace("{user}", member.mention).replace("{guild}", member.guild.name)
text = text.replace("{member_count}", str(member.guild.member_count))
text = text.replace("{username}", member.name)
embed = discord.Embed(description=text, color=discord.Color.blurple())
embed.set_thumbnail(url=member.display_avatar.url)
if inviter:
inviter_data = cfg.get("invites", {}).get(str(inviter.id), 0)
embed.add_field(name="Invité par", value=f"{inviter.mention} ({inviter_data + 1} invites)")
await channel.send(embed=embed)
if cfg.get("dm_message"):
try:
dm_text = cfg["dm_message"].replace("{guild}", member.guild.name)
await member.send(dm_text)
except discord.HTTPException:
pass
await self._update_invite_count(member.guild, inviter)
async def _find_inviter(self, member: discord.Member) -> discord.Member | None:
try:
invites = await member.guild.invites()
except discord.HTTPException:
return None
cached = self._invites.get(member.guild.id, {})
for inv in invites:
if cached.get(inv.code, 0) < inv.uses:
self._invites.setdefault(member.guild.id, {})[inv.code] = inv.uses
return inv.inviter
self._cache_invites_for(member.guild)
return None
async def _update_invite_count(self, guild: discord.Guild, inviter: discord.Member | None):
if not inviter:
return
cfg = shared.get(guild.id, "welcome")
invites = cfg.get("invites", {})
key = str(inviter.id)
invites[key] = invites.get(key, 0) + 1
shared.set(guild.id, "welcome", {**cfg, "invites": invites})
async def _cache_invites(self):
for guild in self.bot.guilds:
await self._cache_invites_for(guild)
async def _cache_invites_for(self, guild: discord.Guild):
try:
invites = await guild.invites()
self._invites[guild.id] = {inv.code: inv.uses for inv in invites}
except discord.HTTPException:
self._invites[guild.id] = {}
@app_commands.command(name="welcome_setup", description="Configurer le message d'arrivée")
@app_commands.describe(channel="Canal d'arrivée", message="Message (utilise {user}, {guild}, {member_count})")
@app_commands.checks.has_permissions(manage_guild=True)
async def welcome_setup(self, interaction: discord.Interaction, channel: discord.TextChannel, message: str = ""):
cfg = shared.get(interaction.guild_id, "welcome")
cfg["enabled"] = True
cfg["channel_id"] = channel.id
if message:
cfg["message"] = message
shared.set(interaction.guild_id, "welcome", cfg)
await interaction.response.send_message(f"Message d'arrivée configuré dans {channel.mention}", ephemeral=True)
@app_commands.command(name="welcome_disable", description="Désactiver le message d'arrivée")
@app_commands.checks.has_permissions(manage_guild=True)
async def welcome_disable(self, interaction: discord.Interaction):
cfg = shared.get(interaction.guild_id, "welcome")
cfg["enabled"] = False
shared.set(interaction.guild_id, "welcome", cfg)
await interaction.response.send_message("Message d'arrivée désactivé.", ephemeral=True)
@app_commands.command(name="invites", description="Voir le nombre d'invitations d'un membre")
@app_commands.describe(member="Membre à vérifier")
async def invites(self, interaction: discord.Interaction, member: discord.Member):
cfg = shared.get(interaction.guild_id, "welcome")
invites = cfg.get("invites", {})
count = invites.get(str(member.id), 0)
embed = discord.Embed(
title=f"Invitations de {member}",
description=f"**{count}** invitations",
color=discord.Color.blurple(),
)
embed.set_thumbnail(url=member.display_avatar.url)
await interaction.response.send_message(embed=embed)
async def setup(bot: commands.Bot):
await bot.add_cog(Welcome(bot))