WME PLN Core - AI Handler

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

Този скрипт не може да бъде инсталиран директно. Това е библиотека за други скриптове и може да бъде използвана с мета-директива // @require https://update.greasyfork.org/scripts/548746/1657857/WME%20PLN%20Core%20-%20AI%20Handler.js

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         WME PLN Core - AI Handler
// @namespace    https://greasyfork.org/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
// ==/UserScript==
// Función para realizar solicitudes HTTP usando GM_xmlhttpRequest
function makeRequest(options)
{
    try
    {
        if (typeof window.plnUiNetRequest === 'function') {
            window.plnUiNetRequest(options); // UI/adapter ejecuta GM_xmlhttpRequest real
        } else {
            throw new Error('No request adapter (plnUiNetRequest) disponible.');
        }
    }
    catch (e)
    {
        plnLog('error', 'AI core: no hay adaptador de red (plnUiNetRequest).', e);
    }
}//makeRequest

// Función para obtener sugerencias de nombres de lugares desde la API de IA
async function getAiSuggestions(placeName, wazeCategories, placeNameToCompare = '', hooks = null)
{
    const WORKER_URL = 'https://wme-normalizer-ai.cotalvaro.workers.dev/';
    return new Promise((resolve) => {
        const req = buildAiRequestOptions(placeName, wazeCategories, placeNameToCompare, WORKER_URL, 20000);
        req.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.` }); }
            }
        };
        req.onerror = function () { resolve({ error: "Error de red al contactar el Worker." }); };
        req.ontimeout = function () { resolve({ error: "Timeout al contactar el Worker." }); };
        const adjusted = plnAdjustAiPayloadIfNeeded(req, hooks);
        makeRequest(adjusted);
    });
}//getAiSuggestions

// Helper pura: arma el objeto de request para el Worker de IA (sin efectos secundarios)
function buildAiRequestOptions(placeName, wazeCategories, placeNameToCompare = '', workerUrl = 'https://wme-normalizer-ai.cotalvaro.workers.dev/', timeoutMs = 20000)
{
    const safeName = String(placeName ?? '');
    const safeCats = Array.isArray(wazeCategories) ? wazeCategories : [];
    const safeCompare = String(placeNameToCompare ?? '');
    return {
        method: "POST",
        url: workerUrl,
        headers: { "Content-Type": "application/json" },
        data: JSON.stringify({ placeName: safeName, wazeCategories: safeCats, placeNameToCompare: safeCompare }),
        timeout: timeoutMs
    };
}

//Permite ajustar la carga útil de las solicitudes a la API de IA para incluir nombres sugeridos
function plnAdjustAiPayloadIfNeeded(options, hooks = null)
{
    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 getSuggested = hooks && typeof hooks.getSuggestedNameByPlaceId === 'function'
            ? hooks.getSuggestedNameByPlaceId
            : null;

        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 && getSuggested ? getSuggested(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;
    }
}