84 lines
3.0 KiB
Python
84 lines
3.0 KiB
Python
"""Illustrations contextuelles Wikipédia déclenchées APRÈS une réponse Hermes.
|
|
|
|
Pas de modification des prompts Hermes ou de /api/render. Échec silencieux.
|
|
"""
|
|
from __future__ import annotations
|
|
import re
|
|
import threading
|
|
import time
|
|
import unicodedata
|
|
from urllib.parse import urlencode, quote, urlparse
|
|
import requests
|
|
|
|
CACHE = {}
|
|
LOCK = threading.Lock()
|
|
|
|
|
|
def _norm(s):
|
|
s = unicodedata.normalize('NFKD', str(s)).casefold()
|
|
return re.sub(r'[^a-z0-9 ]+', '', ''.join(c for c in s if not unicodedata.combining(c)))
|
|
|
|
|
|
def entity_question(s):
|
|
text = ' '.join(str(s or '').split())
|
|
m = re.match(r'^(?:alfred[ ,.:!?]*)?(?:qui\s+(?:est|etait|était|sont)\s+|c.?est\s+qui\s+)([^?!.]{3,95})', text, re.I)
|
|
if not m:
|
|
return None
|
|
entity = m.group(1).strip(' «»\'".,?!')
|
|
entity = re.sub(r'\s+(?:au juste|exactement|s.il te plait)$', '', entity, flags=re.I)
|
|
if len(entity.split()) > 7 or len(entity) < 3:
|
|
return None
|
|
return entity
|
|
|
|
|
|
def _query_wikipedia(name):
|
|
# PageImages retourne une illustration publique et l'URL de la page source.
|
|
params = {'action':'query','generator':'search','gsrsearch':name,'gsrlimit':1,
|
|
'prop':'pageimages','piprop':'thumbnail','pithumbsize':500,
|
|
'format':'json','formatversion':'2','redirects':'1'}
|
|
headers = {'User-Agent':'AlfredAssistant/1.0 (personal home assistant; image source: Wikipedia)'}
|
|
response = requests.get('https://fr.wikipedia.org/w/api.php', params=params,
|
|
headers=headers, timeout=4, allow_redirects=False)
|
|
response.raise_for_status()
|
|
pages = response.json().get('query', {}).get('pages', [])
|
|
if not pages:
|
|
return None
|
|
page = pages[0]
|
|
title = str(page.get('title',''))
|
|
if not title or _norm(name) not in _norm(title) and _norm(title) not in _norm(name):
|
|
return None
|
|
url = str(page.get('thumbnail', {}).get('source', ''))
|
|
parsed = urlparse(url)
|
|
if parsed.scheme != 'https' or parsed.hostname not in ('upload.wikimedia.org','commons.wikimedia.org'):
|
|
return None
|
|
return {'title': title, 'image_url':url,
|
|
'source_url':'https://fr.wikipedia.org/wiki/'+quote(title.replace(' ','_')),
|
|
'credit':'Image : Wikipédia / Wikimedia Commons'}
|
|
|
|
|
|
def image_for_question(name):
|
|
key = _norm(name)
|
|
with LOCK:
|
|
old = CACHE.get(key)
|
|
if old and time.monotonic()-old[0] < 4*3600:
|
|
return old[1]
|
|
try:
|
|
result = _query_wikipedia(name)
|
|
except (requests.RequestException, ValueError, KeyError):
|
|
result = None
|
|
with LOCK:
|
|
CACHE[key] = (time.monotonic(), result)
|
|
return result
|
|
|
|
|
|
def schedule_context_image(question, answer, socketio, still_current):
|
|
name = entity_question(question)
|
|
if not name:
|
|
return
|
|
def worker():
|
|
info = image_for_question(name)
|
|
if info and still_current():
|
|
info['text'] = str(answer or '')[:500]
|
|
socketio.emit('alfred_context_image', info)
|
|
threading.Thread(target=worker, name='alfred-context-image', daemon=True).start()
|