WME PLN Core - AI Handler

Módulo de comunicación con la API de IA para WME Place Normalizer. No funciona por sí solo.

Verzia zo dňa 08.09.2025. Pozri najnovšiu verziu.

Tento skript by nemal byť nainštalovaný priamo. Je to knižnica pre ďalšie skripty, ktorú by mali používať cez meta príkaz // @require https://update.greatest.deepsurf.us/scripts/548746/1656813/WME%20PLN%20Core%20-%20AI%20Handler.js

// ==UserScript==
// @name         WME PLN Core - AI Handler
// @namespace    https://greatest.deepsurf.us/en/users/mincho77
// @version      9.0.0
// @description  Módulo de comunicación con la API de IA para WME Place Normalizer. No funciona por sí solo.
// @author       mincho77
// @license      MIT
// @grant        GM_xmlhttpRequest
// @connect      wme-normalizer-ai.cotalvaro.workers.dev
// ==/UserScript==

async function getAiSuggestions(placeName, wazeCategories, placeNameToCompare = '') {
    const WORKER_URL = 'https://wme-normalizer-ai.cotalvaro.workers.dev/';
    return new Promise((resolve) => {
        makeRequest({ // Asume que 'makeRequest' estará disponible en el scope principal
            method: "POST",
            url: WORKER_URL,
            headers: { "Content-Type": "application/json" },
            data: JSON.stringify({
                placeName: placeName,
                wazeCategories: wazeCategories,
                placeNameToCompare: placeNameToCompare
            }),
            timeout: 20000,
            onload: function (response) {
                if (response.status >= 200 && response.status < 300) {
                    try { const suggestions = JSON.parse(response.responseText); resolve(suggestions); }
                    catch (e) { resolve({ error: "La respuesta de la IA no es un JSON válido." }); }
                } else {
                     try { const errorResponse = JSON.parse(response.responseText); resolve(errorResponse); }
                     catch(e) { resolve({ error: `Error HTTP ${response.status} del servidor.` }); }
                }
            },
            onerror: function () { resolve({ error: "Error de red al contactar el Worker." }); },
            ontimeout: function () { resolve({ error: "Timeout al contactar el Worker." }); }
        });
    });
}

function plnGetSuggestedNameForPlaceId(placeId){
    try{
        const tr = document.querySelector(`tr[data-place-id="${placeId}"]`);
        if (!tr) return null;
        const tas = tr.querySelectorAll('textarea');
        const suggested = (tas.length > 1 ? tas[1].value : '').trim();
        if (suggested) return suggested;
        const current = (tas.length ? tas[0].value : '').trim();
        return current || null;
    }catch(_){ return null; }
}

function plnAdjustAiPayloadIfNeeded(options){
    try{
        const url = (options && options.url) || '';
        if (!/wme-normalizer-ai\.cotalvaro\.workers\.dev/i.test(url)) return options;
        if (!('data' in options) || options.data == null) return options;

        let data = options.data;
        const isString = typeof data === 'string';
        let payload = null;
        if (isString){
            try { payload = JSON.parse(data); } catch(_){ return options; }
        } else if (typeof data === 'object') {
            payload = data;
        } else {
            return options;
        }

        const adjustEntry = (entry)=>{
            if (!entry || typeof entry !== 'object') return entry;
            const pid = entry.placeId || entry.place_id || entry.id || entry.venueId || entry.venue_id;
            const suggested = pid ? plnGetSuggestedNameForPlaceId(pid) : null;
            if (suggested){
                if ('name' in entry) entry.name = suggested;
                else if ('text' in entry) entry.text = suggested;
                else if ('placeName' in entry) entry.placeName = suggested;
                else if ('query' in entry) entry.query = suggested;
                else if ('prompt' in entry) entry.prompt = `Normaliza este nombre de lugar: ${suggested}`;
            }
            return entry;
        };

        if (Array.isArray(payload)){
            payload = payload.map(adjustEntry);
        } else if (payload && typeof payload === 'object'){
            if (Array.isArray(payload.places)){
                payload.places = payload.places.map(adjustEntry);
            } else {
                adjustEntry(payload);
            }
        }

        const newData = isString ? JSON.stringify(payload) : payload;
        const cloned = { ...options, data: newData };
        if (isString){
            cloned.headers = { ...(options.headers||{}), 'Content-Type': 'application/json' };
        }
        return cloned;
    }catch(_){ return options; }
}