perplexa

Greasemonkey script to get Perplexity answers within Google search

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name        perplexa
// @match       https://www.google.com/search*
// @namespace    guebosch
// @version      2024-08-11
// @description  Greasemonkey script to get Perplexity answers within Google search
// @author       guebosch, https://github.com/adtac
// @grant        none
// @license      GPLv3
// ==/UserScript==

// Draft: Fix the errors below

async function getID() {
    const resp = await fetch("https://www.perplexity.ai/socket.io/?EIO=4&transport=polling", {
        credentials: "include",
    });
    const text = await resp.text();
    const json = JSON.parse(text.substr(text.indexOf("{")));
    return json.sid;
}

async function postID(sid) {
    const resp = await fetch("https://www.perplexity.ai/socket.io/?EIO=3&transport=polling&sid=" + sid, {
        method: "POST",
        body: '40{"jwt":"anonymous-ask-user"}',
        credentials: "include",
    });
    return resp.status == 200;
};

async function show(query) {
    const root = document.createElement("div");
    root.style.position = "absolute";
    root.style.right = "32px";
    root.style.top = (document.getElementById("appbar").offsetTop + 16) + "px";
    root.style.borderRadius = "4px";
    root.style.background = "white";
    root.style.width = "24rem";
    root.style.border = "1px solid #e0e0e0";
    root.style.padding = "0";
    root.style.margin = "0";
    
    root.style.overflowY = "hidden";
    root.style.minHeight = "6rem";
    root.style.maxHeight = "6rem";
    
    const main = document.createElement("p");
    main.style.fontSize = "13px";
    main.style.position = "relative";
    main.style.padding = "0";
    main.style.margin = "0";
    main.style.padding = "12px 24px";
    root.appendChild(main);
    
    const more = document.createElement("button");
    more.innerText = "Expand";
    more.style.color = "#808080";
    more.style.position = "absolute";
    more.style.top = "2rem";
    more.style.right = "0px";
    more.style.width = "100%";
    more.style.height = "4rem";
    more.style.background = "linear-gradient(180deg, transparent, white 50%)";
    more.style.border = "none";
    more.style.borderRadius = "4px";
    more.style.cursor = "pointer";
    more.style.paddingTop = "2rem";
    more.style.fontSize = "10px";
    root.appendChild(more);
    more.addEventListener("click", () => {
        root.removeChild(more);
        root.style.overflowY = "unset";
        root.style.minHeight = "unset";
        root.style.maxHeight = "unset";
    })
    document.body.appendChild(root);
    
    const sid = await getID();
    await postID(sid);
    const ws = new WebSocket("wss://www.perplexity.ai/socket.io/?EIO=4&transport=websocket&sid=" + sid);
    ws.addEventListener("open", (e) => {
        ws.send("2probe");
        ws.send("5");
        ws.send("421" + JSON.stringify([
            "perplexity_ask",
            query,
            {
                "version": "2.5",
                "search_focus": "internet",
                "mode": "concise",
                "prompt_source": "user",
                "query_source": "home",
            },
        ]));
    });
    
    ws.addEventListener("message", (e) => {
        if (e.data == "2") {
            ws.send("3");
            return;
        }
        
        let s = e.data;
        while (/^\d/.test(s)) {
            s = s.substr(1);
        }
        if (s == "") {
            return;
        }
        try { s = JSON.parse(s); } catch (e) {}
        if (Object.prototype.toString.call(s) != "[object Array]") {
            return;
        }
        
        let t = null;
        if (s[0] == "query_progress") {
            t = "query_progress"
            s = s.slice(1);
        }
        
        const { status } = s[0];
        let data = null;
        switch (status) {
            case "pending":
                data = JSON.parse(s[0].text);
            case "completed":
                data = JSON.parse(s[0].text);
        }
        
        if (data == null) {
            return;
        }
        
        const p = document.createElement("p");
        main.innerHTML = "";
        main.appendChild(p);
        
        let ans = data.answer;
        while (true) {
            const repl = ans.replace(/(\[\d+\])\./, ".$1"); // "[1]." -> ".[1]" for nicer formatting
            if (repl == ans) {
                break;
            }
            ans = repl;
        }
        
        while (ans.length > 0) {
            const m = ans.match(/\[\d+\]/);
            let end = ans.length;
            if (m) {
                end = m.index;
            }
            if (end == 0) {
                const cite = parseInt(ans.substr(1, ans.indexOf("]")));
                const { name, url } = data.web_results[cite-1];
                const a = document.createElement("a");
                a.style.margin = "4px";
                a.style.float = "right";
                a.style.width = "24px";
                a.style.height = "24px";
                a.style.outline = "1px solid #f0f0f0";
                a.style.borderRadius = "4px";
                a.style.background = "center url(https://www.google.com/s2/favicons?sz=128&domain=" + (new URL(url)).hostname + ")";
                a.style.backgroundSize = "cover";
                a.title = name;
                a.href = url;
                p.appendChild(a);
                ans = ans.substr(cite.toString().length + 2)
            } else {
                const span = document.createElement("span");
                span.style.width = "calc(100% - 5rem)";
                span.style.float = "left";
                span.style.borderRight = "1px solid #f0f0f0";
                span.style.padding = "4px 1rem 4px 0px";
                span.style.marginBottom = "1rem";
                span.innerText = ans.substr(0, end).trim();
                p.appendChild(span);
                ans = ans.substr(end);
            }
        }
        
        if (status == "completed") {
            const a = document.createElement("a");
            a.style.margin = "4px";
            a.style.fontSize = "10px";
            a.style.color = "#1d4ed8";
            a.style.width = "24px";
            a.style.height = "24px";
            a.style.border = "1px solid #f0f0f0";
            a.style.borderRadius = "4px";
            a.style.background = "center url(https://www.google.com/s2/favicons?sz=128&domain=perplexity.ai)";
            a.style.backgroundSize = "cover";
            a.href = "https://perplexity.ai/search?q=" + encodeURIComponent(query);
            const el = document.createElement("p");
            el.style.display = "flex";
            el.style.justifyContent = "flex-end";
            el.style.width = "100%";
            el.style.textAlign = "right";
            el.marginTop = "4px";
            el.appendChild(a);
            p.appendChild(el);
            ws.close();
        }
    });
}

function main() {
    const search = document.location.search;
    const params = new URLSearchParams(search);
    show(params.get("q"));
}

main();