LINUX SB 快照站

【插件】响应咱们站长,写了一个图床插件,不知道该不该发.

原帖: linux.sb/topic/11762 · 共 5 楼 · 标题快照 2026-08-12 15:42:17

楼主
发帖 2026-08-12 15:36:14 · 快照 2026-08-12 15:42:17

看到站长的帖子:
image
正好手头有现成的图床插件.非常的短小简单.
用法:
1.把代码加入油猴.
2.复制图片,在编辑器直接粘贴
提示上传:
image
然后上传会非常慢,因为脚本内置的服务器是示例,非常慢小,建议替换成你的图床.
image
3.注意:脚本把其他途径的撇了,只有简单图床的.然后里面内置的图床是我临时搭建的示例.很慢很慢有时候还不成功,换成自己的图床就好了.还有就是如果图片有外链帖子会被强制审核吧所以这个插件不知道该不该发.
4.肯定有很多错误,后续可能有上传失败的情况,可以自己任意修改和发布,本人放弃所有权限只为linux.sb更好发展

// ==UserScript==
// @name         图床linuxdoissb
// @namespace    https://linux.sb/
// @connect      22.2222222.best
// @version      0.9.0
// @description  为linux.sb专门写的图床插件,提供粘贴/拖放/按钮上传到 EasyImages
// @author       大帅哥
// @match        *://linux.sb/*
// @match        *://hostloc.com/*
// @icon         https://linux.sb/app/assets/index.svg
// @grant        GM_xmlhttpRequest
// @license      MPL-1.0 License
// ==/UserScript==



(function () {
    'use strict';

    // EasyImages 配置,你要配置的2项
    const imgHost = {
        type: 'EasyImages',
        url: 'https://22.2222222.best', // 图床地址,改为你自己的
        token: '4ed5cc908146b9318849e586dc0280f1', // 你的图床apikey,没有就留空
    };
    const mdImgName = 'image'; // 插入 markdown 时的 alt 文本
    const submitByKey = true; // Ctrl+Enter 触发提交

    const EDITOR_SELECTOR = 'textarea#markdown-textarea-1, textarea[name="body"]';
    const TOOLBAR_SELECTOR = '.markdown-textarea[data-markdown-textarea], .markdown-textarea';
    const IMAGE_BTN_SELECTOR = 'button[data-markdown-action="image"]';

    window.addEventListener('load', initEditorEnhancer, false);

    function getEditor() {
        return document.querySelector(EDITOR_SELECTOR);
    }

    function getToolbar() {
        return document.querySelector(TOOLBAR_SELECTOR);
    }

    function initEditorEnhancer() {
        // 粘贴上传
        document.addEventListener('paste', handlePasteEvt);

 
        bindDropZone();
        const dropTimer = setInterval(() => {
            if (bindDropZone()) clearInterval(dropTimer);
        }, 300);
        setTimeout(() => clearInterval(dropTimer), 15000);


        const btnTimer = setInterval(() => {
            const oldBtn = document.querySelector(IMAGE_BTN_SELECTOR);
            if (!oldBtn) return;
            clearInterval(btnTimer);
            const newBtn = oldBtn.cloneNode(true);
            oldBtn.parentNode.replaceChild(newBtn, oldBtn);
            newBtn.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();
                handleImgBtnClick();
            });
        }, 200);
        setTimeout(() => clearInterval(btnTimer), 15000);


        if (submitByKey) {
            document.addEventListener('keydown', (event) => {
                if (!(event.ctrlKey && event.key === 'Enter')) return;
                const button =
                    document.querySelector('.submit.btn') ||
                    document.querySelector('button[type="submit"]');
                if (button) button.click();
            });
        }
    }

    function bindDropZone() {
        const dropZone = getEditor();
        if (!dropZone || dropZone.dataset.easyimgDropBound === '1') return !!dropZone;
        dropZone.dataset.easyimgDropBound = '1';

        dropZone.addEventListener('dragover', (e) => {
            e.preventDefault();
            e.stopPropagation();
            e.dataTransfer.dropEffect = 'copy';
        });

        dropZone.addEventListener('drop', (e) => {
            e.preventDefault();
            e.stopPropagation();
            log('正在处理拖放内容...');
            const imageFiles = [];
            for (const file of e.dataTransfer.files) {
                if (/^image\//i.test(file.type)) {
                    imageFiles.push(file);
                    log(`拖放的文件名: ${file.name}`);
                }
            }
            log(`拖放的图片数量: ${imageFiles.length}`);
            if (imageFiles.length === 0) {
                log('你拖放的内容好像没有图片哦', 'red');
                return;
            }
            uploadImage(
                imageFiles.map((file) => ({
                    kind: 'file',
                    type: file.type,
                    getAsFile: () => file,
                }))
            );
        });
        return true;
    }

    function handlePasteEvt(event) {
        const items = (event.clipboardData || event.originalEvent.clipboardData).items;
        if (!items || items.length === 0) return;

        let hasImage = false;
        for (const item of items) {
            if (item.kind === 'file' && item.type.indexOf('image/') !== -1) {
                hasImage = true;
                break;
            }
        }
        if (!hasImage) return;

 
        event.preventDefault();
        log('正在处理粘贴内容...');
        uploadImage(items);
    }

    function handleImgBtnClick() {
        const input = document.createElement('input');
        input.type = 'file';
        input.multiple = true;
        input.accept = 'image/*';
        input.onchange = (e) => {
            const files = e.target.files;
            if (!files || !files.length) return;
            const items = [...files].map((file) => ({
                kind: 'file',
                type: file.type,
                getAsFile: () => file,
            }));
            uploadImage(items);
        };
        input.click();
    }

    async function uploadImage(items) {
        const imageFiles = [];
        for (const item of items) {
            if (item.kind === 'file' && item.type.indexOf('image/') !== -1) {
                imageFiles.push(item.getAsFile());
            }
        }
        if (imageFiles.length === 0) {
            log('你粘贴的内容好像没有图片哦', 'red');
            return;
        }

        for (let i = 0; i < imageFiles.length; i++) {
            if (imageFiles.length > 1) log(`上传第 ${i + 1} / ${imageFiles.length} 张图片...`);
            else log('上传图片...');
            await uploadToEasyImages(imageFiles[i]);
        }
    }

    async function uploadToEasyImages(file) {
        return new Promise((resolve, reject) => {
            let url = imgHost.url;
            const formData = new FormData();

            if (imgHost.token) {

                url += '/api/index.php';
                formData.append('token', imgHost.token);
                formData.append('image', file);
            } else {

                url += '/app/upload.php';
                formData.append('file', file);
                formData.append('sign', Math.floor(Date.now() / 1000));
            }

            GM_xmlhttpRequest({
                method: 'POST',
                url: url,
                data: formData,
                onload: (rsp) => {
                    let rspJson;
                    try {
                        rspJson = JSON.parse(rsp.responseText);

                    } catch (err) {
                        
                        log(`图片上传失败: 响应不是 JSON`, 'red');
                        reject(err);
                        return;
                    }

                    if (rsp.status !== 200) {
                        log(`图片上传失败: ${rsp.status} ${rsp.statusText}`, 'red');
                        reject(rspJson.result);
                        return;
                    }

                    if (rspJson.code === 200 && rspJson.url) {
                        log('图片上传成功', 'green');
                        insertToEditor(`![${mdImgName}](${rspJson.url})`);
                    } else if (rspJson.code === 200) {
                        log('图片上传成功, 但接口返回有误', 'red');
                        insertToEditor(`图片上传成功, 但接口返回有误: ${JSON.stringify(rspJson)}`);
                    } else {
                        log(`图片上传失败: ${JSON.stringify(rspJson)}`, 'red');
                    }
                    resolve();
                },
                onerror: (error) => {
                    log(`图片上传失败: ${error.status} ${error.statusText}`, 'red');
                    reject(error);
                },
            });
        });
    }


    function insertToEditor(text) {
        const ta =
            getEditor() ||
            document.querySelector('#e_textarea') ||
            document.querySelector('#fastpostmessage');

        if (!ta) {
            log('出现错误: 未找到编辑栏 (#markdown-textarea-1)', 'red');
            return;
        }

        const start = typeof ta.selectionStart === 'number' ? ta.selectionStart : ta.value.length;
        const end = typeof ta.selectionEnd === 'number' ? ta.selectionEnd : start;
        const before = ta.value.substring(0, start);
        const after = ta.value.substring(end);
        const insert = `\n${text}\n`;

        ta.value = before + insert + after;
        const pos = start + insert.length;
        ta.selectionStart = ta.selectionEnd = pos;
        ta.focus();


        ta.dispatchEvent(new Event('input', { bubbles: true }));
        ta.dispatchEvent(new Event('change', { bubbles: true }));

        if (text.startsWith('![')) log('图片已插入到编辑器~', 'green');
    }

    function log(message, color = '') {
        if (!document.getElementById('editor-enhance-logs')) {
            initEditorLogDiv();
        }
        const logDiv = document.getElementById('editor-enhance-logs');
        if (logDiv) {
            logDiv.innerHTML = `<div${color ? ` style="color:${color};"` : ''}>&nbsp;&nbsp;&nbsp;${message}&nbsp;</div>`;
        }
        console.log(`[Editor-EasyImages] ${message}`);
    }

    function initEditorLogDiv() {
        let logDiv = document.getElementById('editor-enhance-logs');
        if (!logDiv) {
            logDiv = document.createElement('div');
            logDiv.id = 'editor-enhance-logs';
            document.body.appendChild(logDiv);
        }
        const toolbar = getToolbar();
        if (toolbar && !toolbar.contains(logDiv)) {
            toolbar.appendChild(logDiv);
        }
    }
})();

附带一个骂始皇的示例图:

image

#1
发帖 2026-08-12 15:42:06 · 快照 2026-08-12 15:45:41

开源!传 Greasy Fork 👍

#2
发帖 2026-08-12 15:42:36 · 快照 2026-08-12 15:45:41

不错,顶!

#3
发帖 2026-08-12 15:45:16 · 快照 2026-08-12 15:45:41

智齿大帅哥!!!!

#4
发帖 2026-08-12 15:50:47 · 快照 2026-08-12 15:51:19

非常好