1061 lines
43 KiB
Python
1061 lines
43 KiB
Python
import os
|
|
from pathlib import Path
|
|
import re
|
|
import sys
|
|
import time
|
|
import traceback
|
|
import uuid
|
|
|
|
import telepot
|
|
from telepot.loop import MessageLoop
|
|
from telepot.namedtuple import InlineKeyboardMarkup, InlineKeyboardButton
|
|
|
|
import keyboards
|
|
import ticketToday
|
|
import insertCompetence
|
|
|
|
|
|
ROOT_DIR = Path(__file__).resolve().parents[1]
|
|
for path in (ROOT_DIR / "conn", ROOT_DIR / "api"):
|
|
if str(path) not in sys.path:
|
|
sys.path.insert(0, str(path))
|
|
|
|
import user
|
|
import config
|
|
import filter as filter_ui
|
|
import filters as filter_store
|
|
import mysql5
|
|
import phrase_similarity
|
|
import ai_retriever
|
|
|
|
|
|
PAGE_SIZE = 3
|
|
MAX_TELEGRAM_TEXT = 3800
|
|
SESSION_TTL_SECONDS = 60 * 60
|
|
DELETE_FILTER_RE = re.compile(r"^/(Competen|Prodotto|Area|Sottoarea)_id_cf_(\d+)")
|
|
SEARCH_SESSIONS = {}
|
|
|
|
|
|
def _ticket_link(ticket_id):
|
|
return f"https://tsnew.sanmarcoweb.com/it/ticket/index/index/operation/view/id/{ticket_id}"
|
|
|
|
|
|
def _short(value, limit=900):
|
|
text = str(value or "").strip()
|
|
if len(text) <= limit:
|
|
return text
|
|
return text[:limit].rsplit(" ", 1)[0] + "..."
|
|
|
|
|
|
def _send_chunks(chat_id, text, reply_markup=None):
|
|
text = str(text or "")
|
|
if len(text) <= MAX_TELEGRAM_TEXT:
|
|
bot.sendMessage(chat_id, text, reply_markup=reply_markup)
|
|
return
|
|
|
|
chunks = [text[i:i + MAX_TELEGRAM_TEXT] for i in range(0, len(text), MAX_TELEGRAM_TEXT)]
|
|
for index, chunk in enumerate(chunks):
|
|
bot.sendMessage(chat_id, chunk, reply_markup=reply_markup if index == len(chunks) - 1 else None)
|
|
|
|
|
|
def _is_admin(user_id):
|
|
if int(user_id) == int(config.TELEGRAM_ADMIN_ID):
|
|
return True
|
|
try:
|
|
rows = user.getUserByTid(user_id)
|
|
return bool(rows and len(rows[0]) > 4 and rows[0][4])
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def _is_pending_user(user_id):
|
|
rows = user.getUserByTid(user_id)
|
|
return bool(rows and rows[0][3] == "P" and not _is_admin(user_id))
|
|
|
|
|
|
def _parse_int_token(value):
|
|
if value is None:
|
|
return None
|
|
value = str(value).strip()
|
|
if value.startswith("_"):
|
|
value = "-" + value[1:]
|
|
try:
|
|
return int(value)
|
|
except ValueError:
|
|
return None
|
|
|
|
|
|
def _looks_like_int(value):
|
|
return _parse_int_token(value) is not None
|
|
|
|
|
|
def _add_filter_value(search_filters, key, value):
|
|
parsed = _parse_int_token(value)
|
|
if parsed is None:
|
|
return False
|
|
values = search_filters.setdefault(key, [])
|
|
if parsed not in values:
|
|
values.append(parsed)
|
|
return True
|
|
|
|
|
|
def _add_label(labels, label):
|
|
if label and label not in labels:
|
|
labels.append(label)
|
|
|
|
|
|
def _has_metadata_filter_values(search_filters):
|
|
return any(
|
|
search_filters.get(key)
|
|
for key in ("competence_ids", "product_ids", "area_ids", "subarea_ids", "client_ids")
|
|
)
|
|
|
|
|
|
def _copy_filters(search_filters):
|
|
copied = {}
|
|
for key, value in (search_filters or {}).items():
|
|
if isinstance(value, list):
|
|
copied[key] = list(value)
|
|
else:
|
|
copied[key] = value
|
|
return copied
|
|
|
|
|
|
def _merge_saved_filters(search_filters, user_id, labels):
|
|
available = 0
|
|
filter_map = {
|
|
"C": ("competence_ids", "competenza"),
|
|
"P": ("product_ids", "prodotto"),
|
|
"A": ("area_ids", "area"),
|
|
"S": ("subarea_ids", "sottoarea"),
|
|
}
|
|
for row in filter_store.getFilters(user_id):
|
|
key_label = filter_map.get(str(row[2]))
|
|
if not key_label:
|
|
continue
|
|
key, label = key_label
|
|
if _parse_int_token(row[3]) is None:
|
|
continue
|
|
available += 1
|
|
if _add_filter_value(search_filters, key, row[3]):
|
|
_add_label(labels, f"mio filtro {label}")
|
|
else:
|
|
_add_label(labels, f"mio filtro {label}")
|
|
return available
|
|
|
|
|
|
def _apply_default_saved_filters(search_filters, user_id, labels):
|
|
if _has_metadata_filter_values(search_filters):
|
|
return 0
|
|
return _merge_saved_filters(search_filters, user_id, labels)
|
|
|
|
|
|
def _metadata_filter(search_filters, labels, query_ticket, metadata_key, filter_key, label):
|
|
value = (query_ticket or {}).get(metadata_key)
|
|
if _add_filter_value(search_filters, filter_key, value):
|
|
_add_label(labels, label)
|
|
return True
|
|
return False
|
|
|
|
|
|
def _parse_filter_tokens(tokens, user_id, query_ticket=None):
|
|
search_filters = {}
|
|
labels = []
|
|
i = 0
|
|
while i < len(tokens):
|
|
token = tokens[i].strip().lower().replace("-", "_")
|
|
next_token = tokens[i + 1] if i + 1 < len(tokens) else None
|
|
|
|
if token in ("area", "stessa_area"):
|
|
if _looks_like_int(next_token):
|
|
_add_filter_value(search_filters, "area_ids", next_token)
|
|
_add_label(labels, f"area {next_token}")
|
|
i += 2
|
|
else:
|
|
_metadata_filter(search_filters, labels, query_ticket, "area_id", "area_ids", "stessa area")
|
|
i += 1
|
|
elif token in ("sottoarea", "subarea", "sott_area", "stessa_sottoarea"):
|
|
if _looks_like_int(next_token):
|
|
_add_filter_value(search_filters, "subarea_ids", next_token)
|
|
_add_label(labels, f"sottoarea {next_token}")
|
|
i += 2
|
|
else:
|
|
_metadata_filter(search_filters, labels, query_ticket, "subarea_id", "subarea_ids", "stessa sottoarea")
|
|
i += 1
|
|
elif token in ("prodotto", "product", "stesso_prodotto"):
|
|
if _looks_like_int(next_token):
|
|
_add_filter_value(search_filters, "product_ids", next_token)
|
|
_add_label(labels, f"prodotto {next_token}")
|
|
i += 2
|
|
else:
|
|
_metadata_filter(search_filters, labels, query_ticket, "product_id", "product_ids", "stesso prodotto")
|
|
i += 1
|
|
elif token in ("competenza", "competence", "stessa_competenza"):
|
|
if _looks_like_int(next_token):
|
|
_add_filter_value(search_filters, "competence_ids", next_token)
|
|
_add_label(labels, f"competenza {next_token}")
|
|
i += 2
|
|
else:
|
|
_metadata_filter(search_filters, labels, query_ticket, "competence_id", "competence_ids", "stessa competenza")
|
|
i += 1
|
|
elif token in ("cliente", "client", "stesso_cliente"):
|
|
if _looks_like_int(next_token):
|
|
_add_filter_value(search_filters, "client_ids", next_token)
|
|
_add_label(labels, f"cliente {next_token}")
|
|
i += 2
|
|
else:
|
|
_metadata_filter(search_filters, labels, query_ticket, "client_id", "client_ids", "stesso cliente")
|
|
i += 1
|
|
elif token in ("no_cliente", "diverso_cliente", "escludi_cliente"):
|
|
if _looks_like_int(next_token):
|
|
_add_filter_value(search_filters, "exclude_client_ids", next_token)
|
|
_add_label(labels, f"escludi cliente {next_token}")
|
|
i += 2
|
|
else:
|
|
_metadata_filter(search_filters, labels, query_ticket, "client_id", "exclude_client_ids", "escludi stesso cliente")
|
|
i += 1
|
|
elif token in ("filtri", "miei_filtri"):
|
|
if _merge_saved_filters(search_filters, user_id, labels) == 0:
|
|
_add_label(labels, "miei filtri vuoti")
|
|
i += 1
|
|
elif token in ("codice", "code", "errore"):
|
|
search_filters["require_code_match"] = True
|
|
_add_label(labels, "stesso codice")
|
|
i += 1
|
|
elif token in ("recenti", "ultimi", "ultimi_mesi"):
|
|
months = _parse_int_token(next_token) or 24
|
|
search_filters["recent_months"] = max(1, months)
|
|
_add_label(labels, f"ultimi {max(1, months)} mesi")
|
|
i += 2 if _looks_like_int(next_token) else 1
|
|
elif token in ("media", "confidenza_media"):
|
|
search_filters["min_confidence"] = "media"
|
|
_add_label(labels, "confidenza media")
|
|
i += 1
|
|
elif token in ("alta", "confidenza_alta"):
|
|
search_filters["min_confidence"] = "alta"
|
|
_add_label(labels, "confidenza alta")
|
|
i += 1
|
|
else:
|
|
i += 1
|
|
return search_filters, labels
|
|
|
|
|
|
def _parse_ticket_search(text, user_id):
|
|
parts = text.split()
|
|
if len(parts) < 2 or not parts[1].isdigit():
|
|
return None, None, None, "Usa: /tic <numero_ticket> [filtri]"
|
|
|
|
ticket_id = int(parts[1])
|
|
query_ticket = mysql5.get_ticket_context(ticket_id)
|
|
if not query_ticket:
|
|
return None, None, None, f"Ticket {ticket_id} non trovato."
|
|
|
|
search_filters, labels = _parse_filter_tokens(parts[2:], user_id, query_ticket=query_ticket)
|
|
_apply_default_saved_filters(search_filters, user_id, labels)
|
|
return ticket_id, query_ticket, {"filters": search_filters, "labels": labels}, None
|
|
|
|
|
|
def _parse_text_search(text, user_id):
|
|
raw = text[len("/cerca "):].strip()
|
|
if not raw:
|
|
return None, None, "Usa: /cerca <testo> [--area 12] [--filtri]"
|
|
|
|
tokens = raw.split()
|
|
query_parts = []
|
|
search_filters = {}
|
|
labels = []
|
|
i = 0
|
|
while i < len(tokens):
|
|
token = tokens[i].lower()
|
|
next_token = tokens[i + 1] if i + 1 < len(tokens) else None
|
|
if token in ("--area", "--sottoarea", "--subarea", "--prodotto", "--competenza", "--cliente"):
|
|
key_map = {
|
|
"--area": ("area_ids", "area"),
|
|
"--sottoarea": ("subarea_ids", "sottoarea"),
|
|
"--subarea": ("subarea_ids", "sottoarea"),
|
|
"--prodotto": ("product_ids", "prodotto"),
|
|
"--competenza": ("competence_ids", "competenza"),
|
|
"--cliente": ("client_ids", "cliente"),
|
|
}
|
|
key, label = key_map[token]
|
|
if _add_filter_value(search_filters, key, next_token):
|
|
_add_label(labels, f"{label} {next_token}")
|
|
i += 2
|
|
else:
|
|
i += 1
|
|
elif token == "--filtri":
|
|
if _merge_saved_filters(search_filters, user_id, labels) == 0:
|
|
_add_label(labels, "miei filtri vuoti")
|
|
i += 1
|
|
elif token == "--codice":
|
|
search_filters["require_code_match"] = True
|
|
_add_label(labels, "stesso codice")
|
|
i += 1
|
|
elif token == "--ultimi-mesi":
|
|
months = _parse_int_token(next_token) or 24
|
|
search_filters["recent_months"] = max(1, months)
|
|
_add_label(labels, f"ultimi {max(1, months)} mesi")
|
|
i += 2 if _looks_like_int(next_token) else 1
|
|
elif token == "--media":
|
|
search_filters["min_confidence"] = "media"
|
|
_add_label(labels, "confidenza media")
|
|
i += 1
|
|
elif token == "--alta":
|
|
search_filters["min_confidence"] = "alta"
|
|
_add_label(labels, "confidenza alta")
|
|
i += 1
|
|
else:
|
|
query_parts.append(tokens[i])
|
|
i += 1
|
|
|
|
query_text = " ".join(query_parts).strip()
|
|
if not query_text:
|
|
return None, None, "Manca il testo da cercare."
|
|
_apply_default_saved_filters(search_filters, user_id, labels)
|
|
return query_text, {"filters": search_filters, "labels": labels}, None
|
|
|
|
|
|
def _cleanup_sessions():
|
|
now = time.time()
|
|
for session_id in list(SEARCH_SESSIONS):
|
|
if now - SEARCH_SESSIONS[session_id].get("created_at", now) > SESSION_TTL_SECONDS:
|
|
del SEARCH_SESSIONS[session_id]
|
|
|
|
|
|
def _store_session(session):
|
|
_cleanup_sessions()
|
|
session_id = uuid.uuid4().hex[:10]
|
|
session["created_at"] = time.time()
|
|
SEARCH_SESSIONS[session_id] = session
|
|
return session_id
|
|
|
|
|
|
def _session_filter_text(session):
|
|
labels = session.get("labels") or []
|
|
return ", ".join(labels) if labels else "nessun filtro stretto"
|
|
|
|
|
|
def _format_suggestion_message(suggestion, rank):
|
|
ticket_id = suggestion["ticket_id"]
|
|
product = suggestion.get("product") or "-"
|
|
competence = suggestion.get("competence") or "-"
|
|
area = suggestion.get("area") or "-"
|
|
subarea = suggestion.get("subarea") or "-"
|
|
client = suggestion.get("client") or "-"
|
|
reasons = ", ".join(suggestion.get("reasons") or [])
|
|
opened = suggestion.get("opened_at")
|
|
opened_text = opened.strftime("%Y-%m-%d") if hasattr(opened, "strftime") else str(opened or "-")
|
|
duplicate_note = ""
|
|
if suggestion.get("duplicates_skipped_before"):
|
|
duplicate_note = f"Alternative simili saltate: {suggestion['duplicates_skipped_before']}\n"
|
|
|
|
return (
|
|
f"{rank}. Confidenza {suggestion['confidence']}\n"
|
|
f"Ticket: {ticket_id}\n"
|
|
f"{_ticket_link(ticket_id)}\n"
|
|
f"Cliente: {client}\n"
|
|
f"Prodotto: {product} | Competenza: {competence}\n"
|
|
f"Area: {area} | Sottoarea: {subarea}\n"
|
|
f"Apertura: {opened_text}\n"
|
|
f"Perche: {reasons}\n"
|
|
f"{duplicate_note}"
|
|
f"Score: {suggestion['total_score']:.3f}\n"
|
|
f"SOLUZIONE: {_short(suggestion.get('solution_preview') or 'Soluzione non disponibile')}"
|
|
)
|
|
|
|
|
|
def _suggestion_keyboard(session, suggestion):
|
|
suggested_ticket = suggestion["ticket_id"]
|
|
if session["kind"] == "ticket":
|
|
query_ticket = session["query_ticket_id"]
|
|
return InlineKeyboardMarkup(inline_keyboard=[
|
|
[InlineKeyboardButton(text="Dettaglio", callback_data=f"d:{query_ticket}:{suggested_ticket}")],
|
|
[
|
|
InlineKeyboardButton(text="Utile", callback_data=f"fb:u:{query_ticket}:{suggested_ticket}"),
|
|
InlineKeyboardButton(text="Non utile", callback_data=f"fb:n:{query_ticket}:{suggested_ticket}"),
|
|
],
|
|
[InlineKeyboardButton(text="Preferito", callback_data=f"fav:{query_ticket}:{suggested_ticket}")],
|
|
])
|
|
return InlineKeyboardMarkup(inline_keyboard=[
|
|
[InlineKeyboardButton(text="Dettaglio", callback_data=f"D:{suggested_ticket}")],
|
|
[InlineKeyboardButton(text="Preferito", callback_data=f"F:{suggested_ticket}")],
|
|
])
|
|
|
|
|
|
def _search_controls_keyboard(session_id, session, offset, has_more):
|
|
rows = []
|
|
if has_more:
|
|
rows.append([InlineKeyboardButton(text="Altri risultati", callback_data=f"more:{session_id}:{offset + PAGE_SIZE}")])
|
|
|
|
if session["kind"] == "ticket":
|
|
query_ticket = session.get("query_ticket") or {}
|
|
first_row = []
|
|
if query_ticket.get("area_id"):
|
|
first_row.append(InlineKeyboardButton(text="Solo area", callback_data=f"mode:{session_id}:area"))
|
|
if query_ticket.get("subarea_id"):
|
|
first_row.append(InlineKeyboardButton(text="Solo sottoarea", callback_data=f"mode:{session_id}:subarea"))
|
|
if first_row:
|
|
rows.append(first_row)
|
|
|
|
second_row = []
|
|
if query_ticket.get("product_id"):
|
|
second_row.append(InlineKeyboardButton(text="Solo prodotto", callback_data=f"mode:{session_id}:product"))
|
|
if query_ticket.get("competence_id"):
|
|
second_row.append(InlineKeyboardButton(text="Solo competenza", callback_data=f"mode:{session_id}:competence"))
|
|
if second_row:
|
|
rows.append(second_row)
|
|
|
|
third_row = []
|
|
if query_ticket.get("client_id"):
|
|
third_row.append(InlineKeyboardButton(text="Stesso cliente", callback_data=f"mode:{session_id}:client"))
|
|
third_row.append(InlineKeyboardButton(text="Escludi cliente", callback_data=f"mode:{session_id}:exclude_client"))
|
|
if third_row:
|
|
rows.append(third_row)
|
|
|
|
rows.append([
|
|
InlineKeyboardButton(text="Recenti 24m", callback_data=f"mode:{session_id}:recent"),
|
|
InlineKeyboardButton(text="Stesso codice", callback_data=f"mode:{session_id}:code"),
|
|
])
|
|
rows.append([
|
|
InlineKeyboardButton(text="Min media", callback_data=f"mode:{session_id}:media"),
|
|
InlineKeyboardButton(text="Miei filtri", callback_data=f"mode:{session_id}:saved"),
|
|
InlineKeyboardButton(text="Reset", callback_data=f"mode:{session_id}:reset"),
|
|
])
|
|
return InlineKeyboardMarkup(inline_keyboard=rows)
|
|
|
|
|
|
def _send_ai_results(chat_id, session_id, offset=0, intro=None):
|
|
session = SEARCH_SESSIONS.get(session_id)
|
|
if not session:
|
|
bot.sendMessage(chat_id, "Ricerca scaduta. Rilancia /tic oppure /cerca.")
|
|
return
|
|
|
|
offset = max(0, int(offset or 0))
|
|
search_filters = session.get("filters") or {}
|
|
if session["kind"] == "ticket":
|
|
suggestions = ai_retriever.search_similar_tickets(
|
|
session["query_ticket_id"],
|
|
user_id=session["user_id"],
|
|
limit=PAGE_SIZE,
|
|
offset=offset,
|
|
filters=search_filters,
|
|
)
|
|
title = f"Ticket principale: {_ticket_link(session['query_ticket_id'])}"
|
|
else:
|
|
suggestions = ai_retriever.search_similar_text(
|
|
session["query_text"],
|
|
user_id=session["user_id"],
|
|
limit=PAGE_SIZE,
|
|
offset=offset,
|
|
filters=search_filters,
|
|
)
|
|
title = f"Ricerca testo: {_short(session['query_text'], 120)}"
|
|
|
|
if intro or offset == 0:
|
|
_send_chunks(chat_id, f"{intro or 'Risultati AI'}\n{title}\nFiltro: {_session_filter_text(session)}")
|
|
|
|
if not suggestions:
|
|
bot.sendMessage(
|
|
chat_id,
|
|
"Non ho trovato risultati con questi parametri.",
|
|
reply_markup=_search_controls_keyboard(session_id, session, offset, False),
|
|
)
|
|
return
|
|
|
|
for index, suggestion in enumerate(suggestions, start=1):
|
|
rank = offset + index
|
|
bot.sendMessage(
|
|
chat_id,
|
|
_format_suggestion_message(suggestion, rank),
|
|
reply_markup=_suggestion_keyboard(session, suggestion),
|
|
)
|
|
|
|
bot.sendMessage(
|
|
chat_id,
|
|
"Azioni ricerca",
|
|
reply_markup=_search_controls_keyboard(session_id, session, offset, len(suggestions) == PAGE_SIZE),
|
|
)
|
|
|
|
|
|
def _mode_session(session, mode):
|
|
new_session = dict(session)
|
|
new_session["filters"] = _copy_filters(session.get("filters"))
|
|
new_session["labels"] = list(session.get("labels") or [])
|
|
query_ticket = session.get("query_ticket") or {}
|
|
|
|
if mode == "reset":
|
|
new_session["filters"] = {}
|
|
new_session["labels"] = []
|
|
return new_session, "Filtri rimossi."
|
|
if mode == "area":
|
|
ok = _metadata_filter(new_session["filters"], new_session["labels"], query_ticket, "area_id", "area_ids", "stessa area")
|
|
elif mode == "subarea":
|
|
ok = _metadata_filter(new_session["filters"], new_session["labels"], query_ticket, "subarea_id", "subarea_ids", "stessa sottoarea")
|
|
elif mode == "product":
|
|
ok = _metadata_filter(new_session["filters"], new_session["labels"], query_ticket, "product_id", "product_ids", "stesso prodotto")
|
|
elif mode == "competence":
|
|
ok = _metadata_filter(new_session["filters"], new_session["labels"], query_ticket, "competence_id", "competence_ids", "stessa competenza")
|
|
elif mode == "client":
|
|
ok = _metadata_filter(new_session["filters"], new_session["labels"], query_ticket, "client_id", "client_ids", "stesso cliente")
|
|
elif mode == "exclude_client":
|
|
ok = _metadata_filter(new_session["filters"], new_session["labels"], query_ticket, "client_id", "exclude_client_ids", "escludi stesso cliente")
|
|
elif mode == "recent":
|
|
new_session["filters"]["recent_months"] = 24
|
|
_add_label(new_session["labels"], "ultimi 24 mesi")
|
|
ok = True
|
|
elif mode == "code":
|
|
new_session["filters"]["require_code_match"] = True
|
|
_add_label(new_session["labels"], "stesso codice")
|
|
ok = True
|
|
elif mode == "media":
|
|
new_session["filters"]["min_confidence"] = "media"
|
|
_add_label(new_session["labels"], "confidenza media")
|
|
ok = True
|
|
elif mode == "saved":
|
|
ok = _merge_saved_filters(new_session["filters"], session["user_id"], new_session["labels"]) > 0
|
|
else:
|
|
return None, "Filtro non riconosciuto."
|
|
|
|
if not ok:
|
|
return None, "Filtro non disponibile per questa ricerca."
|
|
return new_session, "Filtro applicato."
|
|
|
|
|
|
def _send_ticket_detail(chat_id, ticket_id, actor_id=None, first_name="", last_name="", query_ticket_id=None):
|
|
bot.sendMessage(chat_id, f"Attendere per favore, sto caricando i dettagli per il ticket <b>{ticket_id}</b> ....", parse_mode="HTML")
|
|
|
|
if query_ticket_id is not None:
|
|
try:
|
|
ai_retriever.record_feedback(actor_id or chat_id, query_ticket_id, ticket_id, "opened")
|
|
except Exception:
|
|
traceback.print_exc()
|
|
|
|
if actor_id is not None and not _is_admin(actor_id):
|
|
bot.sendMessage(
|
|
config.TELEGRAM_ADMIN_ID,
|
|
"Aperto Dettaglio Ticket nr: "
|
|
+ str(ticket_id)
|
|
+ " [ "
|
|
+ str(first_name)
|
|
+ " "
|
|
+ str(last_name)
|
|
+ " "
|
|
+ str(actor_id)
|
|
+ " ]",
|
|
)
|
|
|
|
ready_file = ticketToday.getDetailTicket(ticket_id)
|
|
html_file = "json/" + str(ticket_id) + "_details.html"
|
|
if ready_file and os.path.exists(html_file):
|
|
with open(html_file, "rb") as document:
|
|
bot.sendDocument(chat_id=chat_id, document=document, parse_mode="HTML")
|
|
os.remove(html_file)
|
|
else:
|
|
bot.sendMessage(chat_id, "Non è stato possibile scaricare i dettagli")
|
|
|
|
|
|
def _handle_ticket_search(chat_id, user_id, text, first_name, last_name):
|
|
ticket_id, query_ticket, parsed, error = _parse_ticket_search(text, user_id)
|
|
if error:
|
|
bot.sendMessage(chat_id, error)
|
|
return
|
|
|
|
bot.sendMessage(chat_id, "Attendere per favore sto cercando soluzioni...")
|
|
session_id = _store_session({
|
|
"kind": "ticket",
|
|
"user_id": user_id,
|
|
"query_ticket_id": ticket_id,
|
|
"query_ticket": query_ticket,
|
|
"filters": parsed["filters"],
|
|
"labels": parsed["labels"],
|
|
})
|
|
_send_ai_results(chat_id, session_id, offset=0, intro="Risultati AI")
|
|
|
|
if int(chat_id) != int(config.TELEGRAM_ADMIN_ID):
|
|
bot.sendMessage(config.TELEGRAM_ADMIN_ID, "Manuale Da " + str(ticket_id) + " " + str(first_name) + " " + str(last_name))
|
|
|
|
|
|
def _handle_text_search(chat_id, user_id, text):
|
|
query_text, parsed, error = _parse_text_search(text, user_id)
|
|
if error:
|
|
bot.sendMessage(chat_id, error)
|
|
return
|
|
|
|
bot.sendMessage(chat_id, "Attendere per favore sto cercando soluzioni...")
|
|
session_id = _store_session({
|
|
"kind": "text",
|
|
"user_id": user_id,
|
|
"query_text": query_text,
|
|
"filters": parsed["filters"],
|
|
"labels": parsed["labels"],
|
|
})
|
|
_send_ai_results(chat_id, session_id, offset=0, intro="Risultati AI")
|
|
|
|
|
|
def _handle_feedback_command(chat_id, text, feedback):
|
|
prefix = "/utile_" if feedback == "useful" else "/non_utile_"
|
|
payload = str(text)[len(prefix):]
|
|
parts = payload.split("_", 1)
|
|
if len(parts) == 2 and parts[0].isdigit() and parts[1].isdigit():
|
|
ai_retriever.record_feedback(chat_id, parts[0], parts[1], feedback)
|
|
bot.sendMessage(chat_id, "Feedback registrato: soluzione utile." if feedback == "useful" else "Feedback registrato: soluzione non utile.")
|
|
else:
|
|
bot.sendMessage(chat_id, "Feedback non valido.")
|
|
|
|
|
|
def _handle_favorite_command(chat_id, user_id, text):
|
|
ticket_id = str(text)[len("/preferito_"):].strip()
|
|
if not ticket_id.isdigit():
|
|
bot.sendMessage(chat_id, "Preferito non valido.")
|
|
return
|
|
|
|
try:
|
|
inserted = ai_retriever.record_favorite(user_id or chat_id, ticket_id)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
bot.sendMessage(chat_id, "Non e' stato possibile salvare il preferito.")
|
|
return
|
|
|
|
bot.sendMessage(chat_id, "Ticket aggiunto ai preferiti." if inserted else "Ticket gia' nei preferiti.")
|
|
if int(chat_id) != int(config.TELEGRAM_ADMIN_ID):
|
|
bot.sendMessage(config.TELEGRAM_ADMIN_ID, "Preferito " + str(ticket_id) + " Da " + str(chat_id))
|
|
|
|
|
|
def _format_settings(settings):
|
|
enabled = "attive" if settings.get("notifications_enabled", True) else "in pausa"
|
|
quiet = "nessuno"
|
|
if settings.get("quiet_start_hour") is not None and settings.get("quiet_end_hour") is not None:
|
|
quiet = f"{settings['quiet_start_hour']:02d}:00-{settings['quiet_end_hour']:02d}:00"
|
|
digest = "immediato"
|
|
if int(settings.get("digest_minutes") or 0) > 0:
|
|
digest = f"ogni {settings['digest_minutes']} minuti"
|
|
gravity = "solo alta" if settings.get("high_gravity_only") else "tutte"
|
|
return (
|
|
"Stato notifiche\n"
|
|
f"Notifiche: {enabled}\n"
|
|
f"Silenzio: {quiet}\n"
|
|
f"Digest: {digest}\n"
|
|
f"Gravita: {gravity}"
|
|
)
|
|
|
|
|
|
def _handle_notification_command(chat_id, user_id, text):
|
|
if text == "/notifiche_on":
|
|
settings = ai_retriever.set_notifications_enabled(user_id, True)
|
|
bot.sendMessage(chat_id, _format_settings(settings))
|
|
return True
|
|
if text == "/notifiche_off":
|
|
settings = ai_retriever.set_notifications_enabled(user_id, False)
|
|
bot.sendMessage(chat_id, _format_settings(settings))
|
|
return True
|
|
if text == "/notifiche_status":
|
|
bot.sendMessage(chat_id, _format_settings(ai_retriever.get_user_bot_settings(user_id)))
|
|
return True
|
|
if text == "/silenzio_off":
|
|
bot.sendMessage(chat_id, _format_settings(ai_retriever.clear_quiet_hours(user_id)))
|
|
return True
|
|
if text.startswith("/silenzio "):
|
|
parts = text.split()
|
|
if len(parts) == 3 and _looks_like_int(parts[1]) and _looks_like_int(parts[2]):
|
|
settings = ai_retriever.set_quiet_hours(user_id, int(parts[1]), int(parts[2]))
|
|
bot.sendMessage(chat_id, _format_settings(settings))
|
|
else:
|
|
bot.sendMessage(chat_id, "Usa: /silenzio <ora_inizio> <ora_fine>, esempio /silenzio 22 7")
|
|
return True
|
|
if text == "/digest_off":
|
|
bot.sendMessage(chat_id, _format_settings(ai_retriever.set_digest_minutes(user_id, 0)))
|
|
return True
|
|
if text.startswith("/digest "):
|
|
parts = text.split()
|
|
if len(parts) == 2 and _looks_like_int(parts[1]):
|
|
settings = ai_retriever.set_digest_minutes(user_id, int(parts[1]))
|
|
bot.sendMessage(chat_id, _format_settings(settings))
|
|
else:
|
|
bot.sendMessage(chat_id, "Usa: /digest <minuti>, esempio /digest 30")
|
|
return True
|
|
if text == "/gravita_alta_on":
|
|
bot.sendMessage(chat_id, _format_settings(ai_retriever.set_high_gravity_only(user_id, True)))
|
|
return True
|
|
if text == "/gravita_alta_off":
|
|
bot.sendMessage(chat_id, _format_settings(ai_retriever.set_high_gravity_only(user_id, False)))
|
|
return True
|
|
return False
|
|
|
|
|
|
def _format_dt(value):
|
|
if not value:
|
|
return "-"
|
|
if hasattr(value, "strftime"):
|
|
return value.strftime("%Y-%m-%d %H:%M")
|
|
return str(value)
|
|
|
|
|
|
def _format_favorite(row):
|
|
ticket_id = row.get("ticket_id")
|
|
lines = [
|
|
f"🎫 /Ticket_dettaglio_{ticket_id}",
|
|
_ticket_link(ticket_id),
|
|
]
|
|
metadata = []
|
|
if row.get("product"):
|
|
metadata.append(str(row.get("product")))
|
|
if row.get("area"):
|
|
metadata.append(str(row.get("area")))
|
|
if metadata:
|
|
lines.append(" | ".join(metadata))
|
|
if row.get("subject_text"):
|
|
lines.append("Oggetto: " + _short(row.get("subject_text"), 180))
|
|
lines.append("Salvato: " + _format_dt(row.get("created_at")))
|
|
return "\n".join(lines)
|
|
|
|
|
|
def _send_user_favorites(chat_id, user_id):
|
|
try:
|
|
rows = ai_retriever.get_user_favorites(user_id or chat_id)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
bot.sendMessage(chat_id, "Non e' stato possibile caricare i preferiti.")
|
|
return
|
|
|
|
if not rows:
|
|
bot.sendMessage(chat_id, "Non hai ancora ticket preferiti.")
|
|
return
|
|
|
|
lines = ["I tuoi preferiti"]
|
|
lines.extend(_format_favorite(row) for row in rows)
|
|
_send_chunks(chat_id, "\n\n".join(lines))
|
|
|
|
|
|
def _send_admin_report(chat_id, command):
|
|
if command == "/ai_stats":
|
|
stats = ai_retriever.get_ai_stats()
|
|
feedback = stats.get("feedback_counts") or {}
|
|
message = (
|
|
"AI stats\n"
|
|
f"Ticket indicizzati: {stats.get('indexed_count', 0)}\n"
|
|
f"Ultimo indice: {_format_dt(stats.get('last_indexed_at'))}\n"
|
|
f"Ricerche: {stats.get('search_count', 0)}\n"
|
|
f"Senza risultati: {stats.get('no_result_count', 0)}\n"
|
|
f"Tempo medio: {stats.get('avg_elapsed_ms', 0)} ms\n"
|
|
f"Feedback utile: {feedback.get('useful', 0)}\n"
|
|
f"Feedback non utile: {feedback.get('not_useful', 0)}\n"
|
|
f"Preferiti: {feedback.get('favorite', 0)}\n"
|
|
f"Dettagli aperti: {feedback.get('opened', 0)}"
|
|
)
|
|
bot.sendMessage(chat_id, message)
|
|
return
|
|
|
|
if command == "/ai_no_result":
|
|
rows = ai_retriever.get_no_result_searches()
|
|
if not rows:
|
|
bot.sendMessage(chat_id, "Nessuna ricerca senza risultati.")
|
|
return
|
|
lines = ["Ultime ricerche senza risultati"]
|
|
for row in rows:
|
|
ref = row.get("query_ticket_id") or row.get("query_kind")
|
|
lines.append(f"{_format_dt(row.get('created_at'))} | {ref} | {_short(row.get('query_text'), 120)}")
|
|
_send_chunks(chat_id, "\n".join(lines))
|
|
return
|
|
|
|
if command == "/ai_negative":
|
|
rows = ai_retriever.get_negative_feedback()
|
|
if not rows:
|
|
bot.sendMessage(chat_id, "Nessun feedback negativo.")
|
|
return
|
|
lines = ["Ultimi feedback negativi"]
|
|
for row in rows:
|
|
lines.append(
|
|
f"{_format_dt(row.get('created_at'))} | query {row.get('query_ticket_id')} -> {row.get('suggested_ticket_id')} | "
|
|
f"{row.get('product') or '-'} | {row.get('area') or '-'} | {_short(row.get('subject_text'), 80)}"
|
|
)
|
|
_send_chunks(chat_id, "\n".join(lines))
|
|
return
|
|
|
|
if command == "/ai_top_useful":
|
|
rows = ai_retriever.get_top_useful()
|
|
if not rows:
|
|
bot.sendMessage(chat_id, "Nessun feedback utile registrato.")
|
|
return
|
|
lines = ["Ticket piu utili"]
|
|
for row in rows:
|
|
lines.append(
|
|
f"{row.get('ticket_id')} | +{row.get('positive_count')} -{row.get('negative_count')} | "
|
|
f"{row.get('product') or '-'} | {row.get('area') or '-'} | {_short(row.get('subject_text'), 80)}"
|
|
)
|
|
_send_chunks(chat_id, "\n".join(lines))
|
|
|
|
|
|
def _send_help(chat_id):
|
|
_send_chunks(
|
|
chat_id,
|
|
"Help bot ticket\n"
|
|
"\n"
|
|
"Importante: i nuovi ticket automatici arrivano solo in base ai filtri salvati. Anche /tic e /cerca usano i filtri salvati se non indichi un filtro diverso.\n"
|
|
"Filtri diversi si sommano: Area + Competenza + Prodotto devono combaciare tutti. Piu valori dello stesso tipo sono alternative.\n"
|
|
"\n"
|
|
"Avvio\n"
|
|
"/start - registra o riapre il menu\n"
|
|
"/help - mostra questo aiuto\n"
|
|
"\n"
|
|
"Filtri\n"
|
|
"Usa i pulsanti del menu per creare filtri per Competenza, Prodotto, Area e Sott.Area.\n"
|
|
"/_id_c_<id> - salva filtro competenza\n"
|
|
"/_id_p_<id> - salva filtro prodotto\n"
|
|
"/_id_area_<id> - salva filtro area\n"
|
|
"/_id_suba_<id> - salva filtro sottoarea\n"
|
|
"📌 I mie filtri - mostra i filtri salvati\n"
|
|
"✖ Cancella filtro - mostra i comandi per eliminarli\n"
|
|
"/Competen_id_cf_<id>, /Prodotto_id_cf_<id>, /Area_id_cf_<id>, /Sottoarea_id_cf_<id> - elimina filtro\n"
|
|
"\n"
|
|
"Ricerca soluzioni\n"
|
|
"/tic <ticket> - cerca ticket simili usando i tuoi filtri salvati\n"
|
|
"/tic <ticket> area - solo stessa area del ticket\n"
|
|
"/tic <ticket> sottoarea - solo stessa sottoarea\n"
|
|
"/tic <ticket> prodotto - solo stesso prodotto\n"
|
|
"/tic <ticket> competenza - solo stessa competenza\n"
|
|
"/tic <ticket> cliente - solo stesso cliente\n"
|
|
"/tic <ticket> no_cliente - esclude stesso cliente\n"
|
|
"/tic <ticket> area <id> - usa area specifica\n"
|
|
"/tic <ticket> prodotto <id> - usa prodotto specifico\n"
|
|
"/tic <ticket> competenza <id> - usa competenza specifica\n"
|
|
"/tic <ticket> recenti <mesi> - limita ai ticket recenti\n"
|
|
"/tic <ticket> codice - richiede stesso codice errore\n"
|
|
"/tic <ticket> media oppure alta - alza la confidenza minima\n"
|
|
"/cerca <testo> - cerca da testo libero usando i filtri salvati\n"
|
|
"/cerca --area <id> --prodotto <id> --competenza <id> <testo> - cerca con filtri specifici\n"
|
|
"/cerca --filtri <testo> - forza i filtri salvati\n"
|
|
"/Ticket_dettaglio_<ticket> - scarica il dettaglio ticket\n"
|
|
"\n"
|
|
"Feedback e preferiti\n"
|
|
"/utile_<ticket_origine>_<ticket_suggerito> - segna soluzione utile\n"
|
|
"/non_utile_<ticket_origine>_<ticket_suggerito> - segna soluzione non utile\n"
|
|
"/preferito_<ticket> - salva un ticket nei preferiti\n"
|
|
"/preferiti - mostra i tuoi preferiti\n"
|
|
"\n"
|
|
"Notifiche\n"
|
|
"/notifiche_status - mostra stato notifiche\n"
|
|
"/notifiche_on - attiva notifiche\n"
|
|
"/notifiche_off - mette in pausa notifiche\n"
|
|
"/silenzio <ora_inizio> <ora_fine> - pausa in una fascia oraria, esempio /silenzio 22 7\n"
|
|
"/silenzio_off - rimuove la fascia silenzio\n"
|
|
"/digest <minuti> - raggruppa notifiche, esempio /digest 30\n"
|
|
"/digest_off - notifiche immediate\n"
|
|
"/gravita_alta_on - solo ticket con gravita alta\n"
|
|
"/gravita_alta_off - tutte le gravita",
|
|
)
|
|
|
|
|
|
def on_chat_message(msg):
|
|
try:
|
|
first_name = msg.get("from", {}).get("first_name", "")
|
|
last_name = msg.get("from", {}).get("last_name", "noname")
|
|
user_id = msg.get("from", {}).get("id")
|
|
content_type, chat_type, chat_id = telepot.glance(msg)
|
|
print("Chat Message:", last_name, first_name, chat_id)
|
|
print(content_type)
|
|
|
|
if _is_pending_user(user_id):
|
|
bot.sendMessage(user_id, "In attesa che un amministratore accetti la sua richiesta \n Per favore attendere a breve sarà approvata")
|
|
bot.sendMessage(config.TELEGRAM_ADMIN_ID, "Utente nuovo: [ " + str(user_id) + " " + first_name + " " + last_name + " ] Approva qui: \n" + "/Approval_" + str(chat_id))
|
|
return
|
|
|
|
if content_type != "text":
|
|
return
|
|
|
|
text = str(msg.get("text", "")).strip()
|
|
|
|
if text == "/start":
|
|
keyboard = keyboards.getKeyboard()
|
|
if user.insertUser(user_id, first_name, last_name):
|
|
bot.sendMessage(chat_id, "Registrazione effettuata correttamente. \n Per favore attendere che un amministratore approvi la sua richiesta")
|
|
bot.sendDocument(chat_id=chat_id, document=open("gifs/welcome.gif", "rb"))
|
|
bot.sendMessage(config.TELEGRAM_ADMIN_ID, "Utente nuovo: [ " + str(user_id) + " " + first_name + " " + last_name + " ] Approva qui: \n" + "/Approval_" + str(chat_id))
|
|
return
|
|
bot.sendMessage(chat_id, "Sei nel menu principale", reply_markup=keyboard)
|
|
|
|
elif text in ("/help", "Aiuto"):
|
|
_send_help(chat_id)
|
|
|
|
elif text.startswith("/Approval_"):
|
|
if not _is_admin(user_id):
|
|
bot.sendMessage(chat_id, "Comando riservato agli amministratori.")
|
|
return
|
|
telegram_id = text[len("/Approval_"):]
|
|
is_user_approved, already_approved = user.approveUser(telegram_id)
|
|
if already_approved and already_approved[0][3] == "A":
|
|
bot.sendMessage(chat_id, "Utente è stato approvato già precedentemente!")
|
|
return
|
|
if is_user_approved:
|
|
keyboard = keyboards.getKeyboard()
|
|
bot.sendMessage(int(telegram_id), "L'approvazione avvenuta con successo. \n Adesso può usare il menu principale", reply_markup=keyboard)
|
|
bot.sendMessage(chat_id, "Utente approvato correttamente")
|
|
|
|
elif text == "🖍 Crea filtro x Competenze":
|
|
comp_first, comp_second = filter_ui.get_competences()
|
|
bot.sendMessage(chat_id, comp_first)
|
|
bot.sendMessage(chat_id, comp_second)
|
|
|
|
elif text == "📦 Crea filtro x Prodotto":
|
|
bot.sendMessage(chat_id, filter_ui.get_products())
|
|
|
|
elif text == "👥 Crea filtro x Area":
|
|
bot.sendMessage(chat_id, filter_ui.get_areas())
|
|
|
|
elif text == "👥 Crea filtro x Sott.Area":
|
|
bot.sendMessage(chat_id, filter_ui.get_sub_areas(chat_id))
|
|
|
|
elif text in ("💜 Preferiti", "/preferiti"):
|
|
_send_user_favorites(chat_id, user_id)
|
|
|
|
elif text.startswith("/tic "):
|
|
_handle_ticket_search(chat_id, user_id, text, first_name, last_name)
|
|
|
|
elif text.startswith("/cerca "):
|
|
_handle_text_search(chat_id, user_id, text)
|
|
|
|
elif text.startswith("/utile_"):
|
|
_handle_feedback_command(chat_id, text, "useful")
|
|
|
|
elif text.startswith("/non_utile_"):
|
|
_handle_feedback_command(chat_id, text, "not_useful")
|
|
|
|
elif text.startswith("/preferito_"):
|
|
_handle_favorite_command(chat_id, user_id, text)
|
|
|
|
elif text.startswith("/_id_c_"):
|
|
bot.sendMessage(chat_id, insertCompetence.manageCompetence(chat_id, text[7:], "C"))
|
|
|
|
elif text.startswith("/_id_p_"):
|
|
bot.sendMessage(chat_id, insertCompetence.manageCompetence(chat_id, text[7:], "P"))
|
|
|
|
elif text.startswith("/_id_area_"):
|
|
bot.sendMessage(chat_id, insertCompetence.manageCompetence(chat_id, text[10:], "A"))
|
|
|
|
elif text.startswith("/_id_suba_"):
|
|
bot.sendMessage(chat_id, insertCompetence.manageCompetence(chat_id, text[10:], "S"))
|
|
|
|
elif text == "📌 I mie filtri":
|
|
filters_list = filter_ui.getMyFilters(chat_id)
|
|
bot.sendMessage(chat_id, filters_list if len(filters_list) > 0 else "Non hai creato ancora un filtro!")
|
|
|
|
elif text == "✖ Cancella filtro":
|
|
filters_to_delete = filter_ui.showFilterToBeDelete(chat_id)
|
|
bot.sendMessage(chat_id, filters_to_delete if len(filters_to_delete) > 0 else "Non esistono filtri da cancellare")
|
|
|
|
elif DELETE_FILTER_RE.match(text):
|
|
match = DELETE_FILTER_RE.match(text)
|
|
bot.sendMessage(chat_id, filter_ui.deleteFilter(chat_id, match.group(2)))
|
|
|
|
elif text.startswith("/Ticket_dettaglio_"):
|
|
ticket_id = text[len("/Ticket_dettaglio_"):]
|
|
_send_ticket_detail(chat_id, ticket_id, actor_id=user_id, first_name=first_name, last_name=last_name)
|
|
|
|
elif text in ("/ai_stats", "/ai_no_result", "/ai_negative", "/ai_top_useful"):
|
|
if not _is_admin(user_id):
|
|
bot.sendMessage(chat_id, "Comando riservato agli amministratori.")
|
|
return
|
|
_send_admin_report(chat_id, text)
|
|
|
|
elif _handle_notification_command(chat_id, user_id, text):
|
|
return
|
|
|
|
else:
|
|
bot.sendMessage(chat_id, "Commando non riconosciuto! Premere /start per iniziare.")
|
|
except Exception:
|
|
traceback.print_exc()
|
|
print("Error in on_chat_message")
|
|
|
|
|
|
def on_callback_query(msg):
|
|
try:
|
|
query_id, from_id, query_data = telepot.glance(msg, flavor="callback_query")
|
|
bot.answerCallbackQuery(query_id, text="Ricevuto")
|
|
print(query_data)
|
|
|
|
if query_data.startswith("d:"):
|
|
_, query_ticket, suggested_ticket = query_data.split(":", 2)
|
|
_send_ticket_detail(from_id, suggested_ticket, actor_id=from_id, query_ticket_id=query_ticket)
|
|
elif query_data.startswith("D:"):
|
|
_send_ticket_detail(from_id, query_data[2:], actor_id=from_id)
|
|
elif query_data.startswith("fb:"):
|
|
_, feedback_value, query_ticket, suggested_ticket = query_data.split(":", 3)
|
|
feedback = "useful" if feedback_value == "u" else "not_useful"
|
|
ai_retriever.record_feedback(from_id, query_ticket, suggested_ticket, feedback)
|
|
bot.sendMessage(from_id, "Feedback registrato: soluzione utile." if feedback == "useful" else "Feedback registrato: soluzione non utile.")
|
|
elif query_data.startswith("fav:"):
|
|
_, query_ticket, suggested_ticket = query_data.split(":", 2)
|
|
inserted = ai_retriever.record_favorite(from_id, suggested_ticket, query_ticket_id=query_ticket)
|
|
bot.sendMessage(from_id, "Ticket aggiunto ai preferiti." if inserted else "Ticket gia' nei preferiti.")
|
|
elif query_data.startswith("F:"):
|
|
inserted = ai_retriever.record_favorite(from_id, query_data[2:])
|
|
bot.sendMessage(from_id, "Ticket aggiunto ai preferiti." if inserted else "Ticket gia' nei preferiti.")
|
|
elif query_data.startswith("more:"):
|
|
_, session_id, offset = query_data.split(":", 2)
|
|
_send_ai_results(from_id, session_id, offset=int(offset), intro="Altri risultati AI")
|
|
elif query_data.startswith("mode:"):
|
|
_, session_id, mode = query_data.split(":", 2)
|
|
session = SEARCH_SESSIONS.get(session_id)
|
|
if not session:
|
|
bot.sendMessage(from_id, "Ricerca scaduta. Rilancia /tic oppure /cerca.")
|
|
return
|
|
new_session, message = _mode_session(session, mode)
|
|
if not new_session:
|
|
bot.sendMessage(from_id, message)
|
|
return
|
|
new_session_id = _store_session(new_session)
|
|
_send_ai_results(from_id, new_session_id, offset=0, intro=message)
|
|
except Exception:
|
|
traceback.print_exc()
|
|
print("Error in on_callback_query")
|
|
|
|
|
|
bot = telepot.Bot(config.TELEGRAM_BOT_TOKEN)
|
|
|
|
|
|
try:
|
|
MessageLoop(bot, {"chat": on_chat_message, "callback_query": on_callback_query}).run_as_thread()
|
|
print("Listening ...")
|
|
|
|
while 1:
|
|
if config.ENABLE_NOTIFICATIONS:
|
|
print("Passati 30 Secondi")
|
|
tids = user.getUsers()
|
|
for u in tids:
|
|
if u is None or u[0] is None:
|
|
continue
|
|
target_user_id = int(u[0])
|
|
if not ai_retriever.should_send_notifications(target_user_id):
|
|
continue
|
|
settings = ai_retriever.get_user_bot_settings(target_user_id)
|
|
new_ticket_list = ticketToday.getTicketTodayForNotification(
|
|
target_user_id,
|
|
high_gravity_only=bool(settings.get("high_gravity_only")),
|
|
)
|
|
sent_any = False
|
|
for ticket in new_ticket_list:
|
|
message = ticket
|
|
prefix = "🎫 /Ticket_dettaglio_"
|
|
ticket_value = ticket.split(prefix, 1)[-1].split("\n", 1)[0].strip()
|
|
saved_filters = {}
|
|
saved_labels = []
|
|
_merge_saved_filters(saved_filters, target_user_id, saved_labels)
|
|
best_matches_indices = phrase_similarity.start_guessing_manual(
|
|
ticket_value,
|
|
user_id=target_user_id,
|
|
filters=saved_filters,
|
|
limit=3,
|
|
)
|
|
for ticket_info in best_matches_indices:
|
|
message += str(ticket_info)
|
|
|
|
bot.sendMessage(target_user_id, str(message))
|
|
sent_any = True
|
|
if sent_any:
|
|
ai_retriever.mark_notification_sent(target_user_id)
|
|
else:
|
|
print("Notification scan disabled")
|
|
|
|
time.sleep(30)
|
|
except Exception:
|
|
traceback.print_exc()
|