无聊糊了一个对话脚本

用了此脚本,你会发现你的linux.do多了一个对话窗口,流式输出

起因是 :看到了这两个佬的话题

就无聊的糊了一个脚本,因为会跨域,所以又要找代理
这个代理好像还要点击认证一下
其实最简单的方法,就是让@0x24a 佬允许linux.do跨域

https://cors-anywhere.herokuapp.com/

下面就是脚本的代码了

// ==UserScript==
// @name         Dynamic Chat Button with SSE Response and Clear History
// @namespace    http://tampermonkey.net/
// @version      1.1
// @description  Create a floating chat button that opens a dialog for streaming responses like WeChat, with a clear history button.
// @author       逆向达人
// @match        https://linux.do/*
// @grant        none
// ==/UserScript==
(function () {
    'use strict';

    // Create a floating button
    const button = document.createElement('div');
    button.style.position = 'fixed';
    button.style.bottom = '30px';
    button.style.right = '30px';
    button.style.width = '60px';
    button.style.height = '60px';
    button.style.borderRadius = '50%';
    button.style.backgroundColor = '#1E90FF';
    button.style.boxShadow = '0px 0px 10px rgba(0, 0, 0, 0.2)';
    button.style.cursor = 'pointer';
    button.style.zIndex = '1000';
    button.style.transition = 'transform 0.3s ease';
    button.style.backgroundImage = 'url(https://img.icons8.com/ios-filled/50/ffffff/chat.png)';
    button.style.backgroundSize = '50%';
    button.style.backgroundPosition = 'center';
    button.style.backgroundRepeat = 'no-repeat';

    // Hover effect
    button.onmouseover = function () {
        button.style.transform = 'scale(1.1)';
    };
    button.onmouseout = function () {
        button.style.transform = 'scale(1)';
    };

    // Append the button to the body
    document.body.appendChild(button);


    const style = document.createElement('style');
    style.textContent = `
        .element {
            overflow: auto; /* 保留滚动功能 */
            -ms-overflow-style: none; /* 适用于 Internet Explorer 和 Edge */
            scrollbar-width: none; /* 适用于 Firefox */
        }

        .element::-webkit-scrollbar {
            display: none; /* 隐藏 Webkit 浏览器中的滚动条 */
        }

        /* 针对 dialog 元素 */
        #custom-dialog {
            overflow-y: auto; /* 保留滚动功能 */
            -ms-overflow-style: none; /* 适用于 Internet Explorer 和 Edge */
            scrollbar-width: none; /* 适用于 Firefox */
        }

        #custom-dialog::-webkit-scrollbar {
            display: none; /* 隐藏 Webkit 浏览器中的滚动条 */
        }
    `;
    document.head.appendChild(style);


    // Create a dialog box
    const dialog = document.createElement('div');
    dialog.id = 'custom-dialog';
    dialog.style.position = 'fixed';
    dialog.style.bottom = '100px';
    dialog.style.right = '30px';
    dialog.style.minWidth = '350px';
    dialog.style.height = '450px';
    dialog.style.backgroundColor = '#ffffff';
    dialog.style.borderRadius = '10px';
    dialog.style.boxShadow = '0px 0px 15px rgba(0, 0, 0, 0.3)';
    dialog.style.padding = '10px';
    dialog.style.display = 'none';
    dialog.style.zIndex = '1001';
    dialog.style.overflowY = 'auto';
    document.body.appendChild(dialog);


    // Remove scrollbar and enable implicit scrolling
    dialog.style.overflowY = 'hidden';
    dialog.onmouseenter = function () {
        dialog.style.overflowY = 'auto';
    };
    dialog.onmouseleave = function () {
        dialog.style.overflowY = 'hidden';
    };


    // 创建并设置 chatContainer
    const chatContainer = document.createElement('div');
    chatContainer.className = 'element'; // 添加类名
    chatContainer.style.height = 'calc(100% - 40px)';
    chatContainer.style.overflowY = 'auto';
    dialog.appendChild(chatContainer);

    // Create input box
    const input = document.createElement('input');
    input.type = 'text';
    input.style.width = '100%';
    input.style.padding = '10px';
    input.style.boxSizing = 'border-box';
    input.style.borderRadius = '5px';
    input.style.border = '1px solid #ccc';
    input.placeholder = 'Type your message...';
    dialog.appendChild(input);

    // Create clear history button
    const clearButton = document.createElement('button');
    clearButton.textContent = 'Clear';
    clearButton.style.position = 'absolute';
    clearButton.style.top = '10px';
    clearButton.style.right = '10px';
    //clearButton.style.right = '10px';
    clearButton.style.width = '50px';
    clearButton.style.height = '30px';
    clearButton.style.backgroundColor = '#FF4500';
    clearButton.style.color = '#ffffff';
    clearButton.style.border = 'none';
    clearButton.style.borderRadius = '5px';
    clearButton.style.cursor = 'pointer';
    clearButton.style.boxShadow = '0px 0px 10px rgba(0, 0, 0, 0.2)';
    clearButton.style.zIndex = '1002';
    dialog.appendChild(clearButton);

    // Clear history on button click
    clearButton.addEventListener('click', () => {
        setCookie('chat_history', '', -1);
        chatContainer.innerHTML = '';
    });

    // Toggle dialog visibility
    button.addEventListener('click', () => {
        dialog.style.display = dialog.style.display === 'none' ? 'block' : 'none';
        input.focus();
    });

    // Adjust dialog width based on input length
    input.addEventListener('input', () => {
        dialog.style.width = Math.max(300, input.value.length * 8 + 40) + 'px';
    });

    // Handle user input
    input.addEventListener('keydown', function (event) {
        if (event.key === 'Enter') {
            event.preventDefault();
            const userMessage = input.value.trim();
            if (userMessage) {
                addUserMessage(userMessage);
                sendMessage(userMessage);
                input.value = '';
            }
        }
    });

    function addUserMessage(message) {
        const messageElem = document.createElement('div');
        messageElem.style.margin = '10px 0';
        // messageElem.style.marginTop = '35px';
        messageElem.style.padding = '10px';
        messageElem.style.borderRadius = '5px';
        messageElem.style.backgroundColor = '#1E90FF';
        messageElem.style.color = '#ffffff';
        messageElem.style.textAlign = 'right';
        messageElem.textContent = message;
        chatContainer.appendChild(messageElem);
        chatContainer.scrollTop = chatContainer.scrollHeight;
    }

    function addBotMessage(message) {
        const messageElem = document.createElement('div');
        messageElem.style.margin = '10px 0';
        messageElem.style.padding = '10px';
        messageElem.style.borderRadius = '5px';
        messageElem.style.backgroundColor = '#f0f0f0';
        messageElem.style.color = '#000000';
        messageElem.style.textAlign = 'left';
        chatContainer.appendChild(messageElem);

        let index = 0;
        function typeNextCharacter() {
            if (index < message.length) {
                messageElem.textContent += message[index];
                index++;
                setTimeout(typeNextCharacter, 1);
            }
        }
        typeNextCharacter();

        chatContainer.scrollTop = chatContainer.scrollHeight;
    }

    function getCookie(name) {
        const value = `; ${document.cookie}`;
        const parts = value.split(`; ${name}=`);
        if (parts.length === 2) return parts.pop().split(';').shift();
    }

    function setCookie(name, value, days) {
        let expires = "";
        if (days) {
            const date = new Date();
            date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
            expires = "; expires=" + date.toUTCString();
        }
        document.cookie = name + "=" + (value || "") + expires + "; path=/";
    }

    function getHistoryMessages() {
        const history = getCookie('chat_history');
        return history ? JSON.parse(history) : [];
    }

    function updateHistoryMessages(messages) {
        const history = getHistoryMessages();
        const newHistory = history.concat(messages).slice(-5);
        setCookie('chat_history', JSON.stringify(newHistory), 7);
    }

    async function sendMessage(message) {
        const apiUrl = 'https://fast.snova.ai/api/completion';
        const proxyUrl = 'https://cors-anywhere.herokuapp.com/';

        const historyMessages = getHistoryMessages();
        const userMessage = {
            "role": "user",
            "content": message,
            "id": generateUUID(),
            "ref": generateUUID(),
            "revision": 0,
            "draft": false,
            "status": "done",
            "enableRealTimeChat": false,
            "meta": null
        };
        const requestBody = {
            "body": {
                "messages": historyMessages.concat([userMessage]),
                "max_tokens": 4000,
                "stop": ["<|eot_id|>"],
                "stream": true,
                "stream_options": { "include_usage": true },
                "model": "llama3-405b"
            },
            "env_type": "tp16405b"
        };

        try {
            const response = await fetch(proxyUrl + apiUrl, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify(requestBody)
            });

            if (!response.ok) {
                throw new Error('Network response was not ok');
            }

            const reader = response.body.getReader();
            const decoder = new TextDecoder('utf-8');
            let receivedMessage = '';

            while (true) {
                const { done, value } = await reader.read();
                if (done) break;

                const chunk = decoder.decode(value, { stream: true });
                const lines = chunk.split(/\r?\n/);

                lines.forEach(line => {
                    if (line.startsWith("data:")) {
                        const jsonData = line.slice(5).trim();
                        if (jsonData !== "[DONE]") {
                            try {
                                const json = JSON.parse(jsonData);
                                // 检查 choices 是否存在并且不为空,以及 delta.content 是否存在且不为空
                                if (json.choices && json.choices.length > 0 && json.choices[0].delta && json.choices[0].delta.content) {
                                    receivedMessage += json.choices[0].delta.content;
                                }
                            } catch (e) {
                                console.error('Error parsing SSE chunk', e);
                            }
                        }
                    }
                });

            }

            const assistantMessage = {
                "role": "assistant",
                "content": receivedMessage,
                "id": generateUUID(),
                "ref": generateUUID(),
                "revision": 0,
                "draft": false,
                "status": "done",
                "enableRealTimeChat": false,
                "meta": null
            };
            updateHistoryMessages([userMessage, assistantMessage]);

            addBotMessage(receivedMessage);

        } catch (error) {
            console.error('Error:', error);
            addBotMessage("Error: Unable to send message.");
        }
    }

    function generateUUID() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    }
})();

如有侵犯,着实抱歉,联系即删贴

23 个赞

大佬好强!感谢~

3 个赞

tieba36

感谢大佬的分享

感谢分享这个

不错不错
L站成了我的脚步试验场

建议嵌入到右上角(搜索:mag:按钮的左边)

太强了大佬!

必须要点一下认证代理才可以 https://cors-anywhere.herokuapp.com/

:tieba_034:感谢分享。

跨域我记得加了啊?我等会再看看

好像CF Tunnel没有转发我的CORS标头
我修改一下
Update: 改好了

可以,佬,nb

这也太行啦 部署脚本成功,但是发送消息没有应答

跨域的那个链接点了没?

原来如此,试试去。

有点意思,看看代码,攒一个玩玩

佬这个是用油猴导入吗? :tieba_004:我用arc导入后没出现

油猴貌似有内置函数GM_xmlxxx名字忘了,支持跨域请求

厉害,非常感谢