fix: gpt-oss:20b ollama, streaming, tableaux JSON, recherche flexible salons/categories
This commit is contained in:
parent
14985f6dbb
commit
189d56026b
21 changed files with 2824 additions and 491 deletions
|
|
@ -4,7 +4,7 @@ from datetime import datetime
|
|||
from .config import config
|
||||
|
||||
class ActionLogger:
|
||||
def __init__(self, log_file: str = 'actions.log'):
|
||||
def __init__(self, log_file: str = 'data/actions.log'):
|
||||
self.log_file = log_file
|
||||
self.logger = logging.getLogger('ActionLogger')
|
||||
self.logger.setLevel(logging.INFO)
|
||||
|
|
|
|||
|
|
@ -1,15 +1,52 @@
|
|||
import discord
|
||||
import re
|
||||
|
||||
def strip_emoji(text):
|
||||
"""Remove emoji characters for fuzzy matching."""
|
||||
emoji_pattern = re.compile("["
|
||||
u"\U0001F600-\U0001F64F\U0001F300-\U0001F5FF\U0001F680-\U0001F6FF"
|
||||
u"\U0001F1E0-\U0001F1FF\U00002702-\U000027B0\U000024C2-\U0001F251"
|
||||
u"\U0000FE00-\U0000FE0F\U0000200D\U0000E0100-\U0000E01EFF"
|
||||
u"\u20E3\u00A9\u00AE\u2122"
|
||||
"]+", flags=re.UNICODE)
|
||||
return emoji_pattern.sub('', text).strip()
|
||||
|
||||
async def find_category(guild, category_name_or_id):
|
||||
"""Find a category by ID or name (exact then fuzzy)."""
|
||||
# 1. Try by ID
|
||||
try:
|
||||
cat_id = int(category_name_or_id)
|
||||
cat = guild.get_channel(cat_id)
|
||||
if cat and isinstance(cat, discord.CategoryChannel):
|
||||
return cat
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
# 2. Exact name
|
||||
for cat in guild.categories:
|
||||
if cat.name.casefold() == category_name_or_id.casefold():
|
||||
return cat
|
||||
|
||||
# 3. Fuzzy (strip emoji)
|
||||
clean_search = strip_emoji(category_name_or_id).lower().strip()
|
||||
for cat in guild.categories:
|
||||
clean_name = strip_emoji(cat.name).lower().strip()
|
||||
if clean_name == clean_search or (clean_search and clean_search in clean_name):
|
||||
return cat
|
||||
|
||||
return None
|
||||
|
||||
async def execute(bot, params, message):
|
||||
categories = params.get('categories', [])
|
||||
if not categories:
|
||||
# Fallback for single category
|
||||
categories = [params]
|
||||
guild = message.guild
|
||||
deleted_count = 0
|
||||
for category_params in categories:
|
||||
category_name = category_params.get('category_name')
|
||||
category = discord.utils.find(lambda cat: cat.name.casefold() == category_name.casefold(), guild.categories)
|
||||
category_name = category_params.get('category_name') or category_params.get('category_id')
|
||||
if not category_name:
|
||||
continue
|
||||
category = await find_category(guild, category_name)
|
||||
if not category:
|
||||
await message.channel.send(f"Catégorie '{category_name}' introuvable.")
|
||||
continue
|
||||
|
|
@ -19,4 +56,4 @@ async def execute(bot, params, message):
|
|||
except discord.Forbidden:
|
||||
await message.channel.send(f"Je n'ai pas les permissions pour supprimer la catégorie '{category_name}'.")
|
||||
if deleted_count > 0:
|
||||
await message.channel.send(f"{deleted_count} catégorie(s) supprimée(s).")
|
||||
await message.channel.send(f"{deleted_count} catégorie(s) supprimée(s).")
|
||||
|
|
@ -1,15 +1,66 @@
|
|||
import discord
|
||||
import re
|
||||
|
||||
def strip_emoji(text):
|
||||
"""Remove emoji characters from channel/role names for fuzzy matching."""
|
||||
emoji_pattern = re.compile("["
|
||||
u"\U0001F600-\U0001F64F" # emoticons
|
||||
u"\U0001F300-\U0001F5FF" # symbols & pictographs
|
||||
u"\U0001F680-\U0001F6FF" # transport & map symbols
|
||||
u"\U0001F1E0-\U0001F1FF" # flags (iOS)
|
||||
u"\U00002702-\U000027B0" # dingbats
|
||||
u"\U000024C2-\U0001F251" # misc
|
||||
u"\U0000FE00-\U0000FE0F" # variation selectors
|
||||
u"\U0000200D" # zero width joiner
|
||||
u"\U0000E0100-\U0000E01EFF" # variation selectors supplement
|
||||
u"\u20E3" # combining enclosing keycap
|
||||
u"\u00A9\u00AE\u2122" # copyright, registered, trademark
|
||||
"]+", flags=re.UNICODE)
|
||||
return emoji_pattern.sub('', text).strip()
|
||||
|
||||
async def find_channel(guild, channel_name_or_id):
|
||||
"""Find a channel by ID first, then by name (exact, then flexible)."""
|
||||
# 1. Try by ID
|
||||
try:
|
||||
ch_id = int(channel_name_or_id)
|
||||
channel = guild.get_channel(ch_id)
|
||||
if channel:
|
||||
return channel
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
# 2. Try exact name match (case-insensitive)
|
||||
for ch in guild.channels:
|
||||
if ch.name.casefold() == channel_name_or_id.casefold():
|
||||
return ch
|
||||
|
||||
# 3. Try fuzzy match (strip emojis from both sides)
|
||||
clean_search = strip_emoji(channel_name_or_id).lower().strip()
|
||||
for ch in guild.channels:
|
||||
clean_name = strip_emoji(ch.name).lower().strip()
|
||||
if clean_name == clean_search:
|
||||
return ch
|
||||
# 4. If the search name is contained in the channel name
|
||||
if clean_search and clean_search in clean_name:
|
||||
return ch
|
||||
# 5. Or the channel name is contained in the search
|
||||
if clean_search and clean_name in clean_search:
|
||||
return ch
|
||||
|
||||
return None
|
||||
|
||||
async def execute(bot, params, message):
|
||||
channels = params.get('channels', [])
|
||||
if not channels:
|
||||
# Fallback for single channel
|
||||
channels = [params]
|
||||
guild = message.guild
|
||||
deleted_count = 0
|
||||
for channel_params in channels:
|
||||
channel_name = channel_params.get('channel_name')
|
||||
channel = discord.utils.find(lambda c: c.name.casefold() == channel_name.casefold(), guild.channels)
|
||||
# Support both channel_id and channel_name
|
||||
channel_name = channel_params.get('channel_name') or channel_params.get('channel_id')
|
||||
if not channel_name:
|
||||
continue # skip if no name or id
|
||||
channel = await find_channel(guild, channel_name)
|
||||
if not channel:
|
||||
await message.channel.send(f"Salon '{channel_name}' introuvable.")
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import json
|
|||
import os
|
||||
from datetime import datetime
|
||||
|
||||
WARNINGS_FILE = 'warnings.json'
|
||||
WARNINGS_FILE = 'data/warnings.json'
|
||||
|
||||
def load_warnings():
|
||||
"""Charge les avertissements depuis le fichier JSON"""
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import discord
|
|||
import json
|
||||
import os
|
||||
|
||||
WARNINGS_FILE = 'warnings.json'
|
||||
WARNINGS_FILE = 'data/warnings.json'
|
||||
|
||||
def load_warnings():
|
||||
if os.path.exists(WARNINGS_FILE):
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import os
|
|||
import aiohttp
|
||||
from datetime import datetime
|
||||
|
||||
WARNINGS_FILE = 'warnings.json'
|
||||
WARNINGS_FILE = 'data/warnings.json'
|
||||
|
||||
def load_warnings():
|
||||
if os.path.exists(WARNINGS_FILE):
|
||||
|
|
@ -123,7 +123,9 @@ async def execute(bot, params, message):
|
|||
target_mention = warn_params.get('target_user_mention')
|
||||
original_reason = warn_params.get('reason', 'Aucune raison spécifiée')
|
||||
# Rephrase the reason using Ollama
|
||||
rephrased_reason = await rephrase_reason(original_reason, bot.ollama_api_url, bot.ollama_api_key, bot.ollama_model, bot.ollama_timeout, bot.ollama_temperature)
|
||||
ollama_url = os.environ.get("OLLAMA_BASE_URL", "http://localhost:11434") + "/api/generate"
|
||||
ollama_model = os.environ.get("OLLAMA_MODEL", "gpt-oss:20b")
|
||||
rephrased_reason = await rephrase_reason(original_reason, ollama_url, "", ollama_model, 30, 0.7)
|
||||
reason = rephrased_reason if rephrased_reason else original_reason
|
||||
# Parse mention or ID
|
||||
if target_mention.startswith('<@') and target_mention.endswith('>'):
|
||||
|
|
@ -168,7 +170,7 @@ async def execute(bot, params, message):
|
|||
)
|
||||
await message.channel.send(embed=embed)
|
||||
# Generate full DM message using AI with original reason for polite reformulation
|
||||
dm_message = await generate_dm_message(original_reason, warning_count, bot.ollama_api_url, bot.ollama_api_key, bot.ollama_model, bot.ollama_timeout, bot.ollama_temperature)
|
||||
dm_message = await generate_dm_message(original_reason, warning_count, ollama_url, "", ollama_model, 30, 0.7)
|
||||
dm_content = dm_message if dm_message else f"Avertissement : {reason}. Nombre total : {warning_count}."
|
||||
# Send DM to user
|
||||
try:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue