Greasy Fork

Greasy Fork is available in English.

Perplexity to Local/Notion

修复批量下载Chrome拦截问题、Notion block/title缺type字段导致400失败的问题

当前为 2026-04-12 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Perplexity to Local/Notion
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  修复批量下载Chrome拦截问题、Notion block/title缺type字段导致400失败的问题
// @author       sandleft
// @match        https://www.perplexity.ai/*
// @run-at       document-end
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_addStyle
// @grant        GM_download
// @connect      api.notion.com
// ==/UserScript==

(function () {
    'use strict';


    let isProcessing = false;


    const CONFIG = {
        get token()     { return (GM_getValue('notion_token', '') || '').trim(); },
        get dbId()      { return (GM_getValue('db_id',        '') || '').trim(); },
        get count()     {
            const v = parseInt(GM_getValue('save_count', '12'));
            return (isNaN(v) || v <= 0) ? 12 : v;
        },
        get fileType()  {
            const v = GM_getValue('file_type', 'md');
            return (v === 'md' || v === 'txt') ? v : 'md';
        },
        get propTitle() { return (GM_getValue('prop_title', 'Title') || 'Title').trim(); },
        get propUrl()   { return (GM_getValue('prop_url',   'URL')   || 'URL'  ).trim(); },
        get propTags()  { return (GM_getValue('prop_tags',  'Tags')  || 'Tags' ).trim(); },
        get propTime()  { return (GM_getValue('prop_time',  'Time')  || 'Time' ).trim(); },
        get autoRun()   { return GM_getValue('auto_run', false); },
        get autoRunDelay() {
            const v = parseInt(GM_getValue('auto_run_delay', '5'));
            return (isNaN(v) || v < 0) ? 5 : v;
        },
    };


    GM_addStyle(`
        #hiyori-panel {
            position: fixed; top: 5%; right: 20px; width: 290px;
            max-height: 88vh; overflow-y: auto;
            background: #fff0f5; border: 2px solid #ffb6c1;
            border-radius: 15px; z-index: 10000; padding: 15px;
            font-family: 'Microsoft YaHei', sans-serif;
            box-shadow: 0 4px 15px rgba(255,182,193,0.4);
            scrollbar-width: thin; scrollbar-color: #ffb6c1 #fff0f5;
        }
        .hiyori-title { color: #ff69b4; font-weight: bold; text-align: center; margin-bottom: 10px; font-size: 16px; }
        .hiyori-label { font-size: 12px; color: #ff69b4; font-weight: bold; margin-top: 5px; display: block; }
        .hiyori-input, .hiyori-select {
            width: 100%; padding: 6px; margin: 3px 0 8px 0;
            border: 1px solid #ffb6c1; border-radius: 5px;
            font-size: 12px; box-sizing: border-box;
        }
        .hiyori-btn {
            width: 100%; padding: 8px; margin-top: 5px;
            background: #ffb6c1; color: white; border: none;
            border-radius: 20px; cursor: pointer; font-weight: bold; transition: 0.3s;
        }
        .hiyori-btn:hover { background: #ff69b4; transform: scale(1.02); }
        .hiyori-float-btn {
            position: fixed; bottom: 30px; right: 30px;
            width: 60px; height: 60px; background: #ffb6c1; color: white;
            border-radius: 50%; border: 4px solid #fff; cursor: pointer;
            z-index: 9999; font-size: 30px;
            display: flex; align-items: center; justify-content: center;
            box-shadow: 0 4px 10px rgba(0,0,0,0.2);
        }
        .hiyori-checkbox { margin-right: 10px; transform: scale(1.3); cursor: pointer; accent-color: #ff69b4; }
        #hiyori-single-btn {
            position: fixed; bottom: 100px; right: 30px; width: 120px;
            z-index: 9998; box-shadow: 0 4px 6px rgba(0,0,0,0.1);
        }
        #hiyori-panel details > summary {
            color: #ff69b4; font-size: 12px; cursor: pointer; font-weight: bold;
        }
    `);


    function escHtml(str) {
        return (str || '').replace(/&/g, '&amp;').replace(/"/g, '&quot;');
    }


    function initUI() {
        if (document.getElementById('hiyori-panel')) return;

        const panel = document.createElement('div');
        panel.id = 'hiyori-panel';
        panel.style.display = GM_getValue('panel_show', 'none');
        panel.innerHTML = `
            <div class="hiyori-title">🌸 P2L/N 🌸</div>

            <label class="hiyori-label">Notion 密钥 (Token)</label>
            <input id="h-token" class="hiyori-input" type="password"
                   placeholder="secret_..." value="${escHtml(CONFIG.token)}">

            <label class="hiyori-label">数据库 ID</label>
            <input id="h-dbid" class="hiyori-input"
                   placeholder="输入数据库ID..." value="${escHtml(CONFIG.dbId)}">

            <details style="margin: 6px 0;">
                <summary>▸ Notion 字段名设置(默认通常不用改)</summary>
                <label class="hiyori-label">标题字段名(Notion 默认库为 Name)</label>
                <input id="h-prop-title" class="hiyori-input"
                       placeholder="Title" value="${escHtml(CONFIG.propTitle)}">
                <label class="hiyori-label">链接字段名</label>
                <input id="h-prop-url" class="hiyori-input"
                       placeholder="URL" value="${escHtml(CONFIG.propUrl)}">
                <label class="hiyori-label">标签字段名</label>
                <input id="h-prop-tags" class="hiyori-input"
                       placeholder="Tags" value="${escHtml(CONFIG.propTags)}">
                <label class="hiyori-label">日期字段名</label>
                <input id="h-prop-time" class="hiyori-input"
                       placeholder="Time" value="${escHtml(CONFIG.propTime)}">
            </details>

            <label class="hiyori-label">自动保存数量</label>
            <input id="h-count" class="hiyori-input" type="number"
                   min="1" value="${CONFIG.count}">

            <label class="hiyori-label">本地保存格式</label>
            <select id="h-filetype" class="hiyori-select">
                <option value="md"  ${CONFIG.fileType === 'md'  ? 'selected' : ''}>Markdown (.md)</option>
                <option value="txt" ${CONFIG.fileType === 'txt' ? 'selected' : ''}>纯文本</option>
            </select>

            <label class="hiyori-label">
                <input type="checkbox" id="h-auto-run" ${CONFIG.autoRun ? 'checked' : ''}
                       style="margin-right: 5px; vertical-align: middle; accent-color: #ff69b4;">
                进入 Library 自动开始抓取
            </label>
            <div style="display: flex; align-items: center; gap: 8px; margin-top: 5px;">
                <label class="hiyori-label" style="margin: 0;">延时</label>
                <input id="h-auto-delay" class="hiyori-input" type="number" min="0" max="300"
                       value="${CONFIG.autoRunDelay}" style="width: 70px;">
                <span style="font-size: 12px; color: #ff69b4;">秒后启动</span>
            </div>

            <button id="h-save-config"    class="hiyori-btn">保存设置 ✨</button>
            <hr style="border: 0.5px solid #ffb6c1; margin: 10px 0;">
            <button id="h-batch-auto"     class="hiyori-btn">自动倒序抓取最新文章 🚀</button>
            <button id="h-batch-selected" class="hiyori-btn">保存已勾选的文章 🎯</button>
            <button id="h-stop" class="hiyori-btn"
                    style="background:#ff9999; display:none;">紧急停止 🛑</button>
        `;
        document.body.appendChild(panel);


        const floatBtn = document.createElement('div');
        floatBtn.className = 'hiyori-float-btn';
        floatBtn.innerHTML = '🌸';
        floatBtn.onclick = () => {
            const show = panel.style.display === 'none' ? 'block' : 'none';
            panel.style.display = show;
            GM_setValue('panel_show', show);
        };
        document.body.appendChild(floatBtn);


        document.getElementById('h-save-config').onclick = () => {
            const cnt = parseInt(document.getElementById('h-count').value);
            const delay = parseInt(document.getElementById('h-auto-delay').value);
            GM_setValue('notion_token', document.getElementById('h-token').value.trim());
            GM_setValue('db_id',        document.getElementById('h-dbid').value.trim());
            GM_setValue('prop_title',   document.getElementById('h-prop-title').value.trim() || 'Title');
            GM_setValue('prop_url',     document.getElementById('h-prop-url').value.trim()   || 'URL');
            GM_setValue('prop_tags',    document.getElementById('h-prop-tags').value.trim()  || 'Tags');
            GM_setValue('prop_time',    document.getElementById('h-prop-time').value.trim()  || 'Time');
            GM_setValue('save_count',   String(isNaN(cnt) || cnt <= 0 ? 12 : cnt));
            GM_setValue('file_type',    document.getElementById('h-filetype').value);
            GM_setValue('auto_run',     document.getElementById('h-auto-run').checked);
            GM_setValue('auto_run_delay', String(isNaN(delay) || delay < 0 ? 5 : delay));
            alert('宝宝!设置已经牢牢记住辣!');
        };

        document.getElementById('h-batch-auto').onclick     = () => startBatch(false);
        document.getElementById('h-batch-selected').onclick = () => startBatch(true);
        document.getElementById('h-stop').onclick           = () => stopBatch(true);

        if (GM_getValue('hiyori_running', false)) {
            document.getElementById('h-stop').style.display = 'block';
        }

        injectSingleSaveBtn();
    }


    function injectSingleSaveBtn() {
        if (!window.location.href.includes('/search/')) return;
        if (document.getElementById('hiyori-single-btn'))  return;
        const btn     = document.createElement('button');
        btn.id        = 'hiyori-single-btn';
        btn.className = 'hiyori-btn';
        btn.innerHTML = '🌸 保存当前页';
        btn.onclick   = () => processCurrentPage(true)
            .catch(e => console.error('[妃爱] 单篇保存出错:', e));
        document.body.appendChild(btn);
    }


    let lastHref = location.href;
    new MutationObserver(() => {
        if (location.href === lastHref) return;
        lastHref = location.href;
        const old = document.getElementById('hiyori-single-btn');
        if (old) old.remove();
        injectSingleSaveBtn();

        if (location.href.includes('/search/') && GM_getValue('hiyori_running', false)) {
            processCurrentPage().catch(err => {
                console.error('[妃爱] 自动处理入口出错:', err);
                if (GM_getValue('hiyori_running', false)) processNextInQueue();
            });
        }
    }).observe(document.body, { childList: true, subtree: true });


    function injectCheckboxes() {
        if (!window.location.href.includes('/library')) return;
        const root = document.querySelector('main') || document.body;
        root.querySelectorAll('a[href^="/search/"]').forEach(a => {
            if (!a.parentElement) return;
            if (!a.parentElement.querySelector('.hiyori-checkbox')) {
                const cb     = document.createElement('input');
                cb.type      = 'checkbox';
                cb.className = 'hiyori-checkbox';
                a.parentElement.insertBefore(cb, a);
            }
        });
    }


    function stopBatch(showAlert = false) {
        GM_setValue('hiyori_running', false);
        GM_setValue('hiyori_queue',   []);

        if (showAlert) {
            const failures = GM_getValue('hiyori_failures', []);
            if (Array.isArray(failures) && failures.length > 0) {
                alert(`🛑 已停止!以下文章写入 Notion 失败(本地文件已保存):\n${failures.join('\n')}`);
            } else {
                alert('🛑 已经乖乖停下啦!');
            }
        }

        GM_setValue('hiyori_failures', []);

        const btn = document.getElementById('h-stop');
        if (btn) btn.style.display = 'none';
    }


    function startBatch(useSelected) {
        if (GM_getValue('hiyori_running', false)) {
            if (!confirm('检测到已有批量任务运行中,是否强制覆盖?')) return;
        }

        const root  = document.querySelector('main') || document.body;
        const items = [];
        const seen  = new Set();

        if (useSelected) {
            const allLinks = Array.from(root.querySelectorAll('a[href^="/search/"]'));
            root.querySelectorAll('.hiyori-checkbox:checked').forEach(cb => {
                const a = cb.parentElement.querySelector('a[href^="/search/"]');
                if (!a || !a.href || seen.has(a.href)) return;
                seen.add(a.href);
                const realIdx = allLinks.indexOf(a);
                items.push({ url: a.href, originalIndex: realIdx >= 0 ? realIdx : 999 });
            });
        } else {
            Array.from(root.querySelectorAll('a[href^="/search/"]'))
                .slice(0, CONFIG.count * 2)
                .forEach((a, idx) => {
                    if (!a.href || seen.has(a.href) || items.length >= CONFIG.count) return;
                    seen.add(a.href);
                    items.push({ url: a.href, originalIndex: idx });
                });
        }

        if (items.length === 0) {
            alert('宝宝,没有找到文章呀,请确认是否勾选或者页面加载完毕哦!');
            return;
        }

        items.reverse();
        GM_setValue('hiyori_queue',    items);
        GM_setValue('hiyori_running',  true);
        GM_setValue('hiyori_failures', []);

        const stopBtn = document.getElementById('h-stop');
        if (stopBtn) stopBtn.style.display = 'block';

        processNextInQueue();
    }


    function processNextInQueue() {
        if (!GM_getValue('hiyori_running', false)) return;

        const queue = GM_getValue('hiyori_queue', []);

        if (!Array.isArray(queue)) {
            console.error('[妃爱] 队列数据损坏,已静默重置');
            stopBatch(false);
            alert('⚠️ 队列数据异常,已自动重置,请重新开始。');
            return;
        }

        if (queue.length > 0) {
            const next = queue.shift();
            GM_setValue('hiyori_current_tag_index', next.originalIndex);
            GM_setValue('hiyori_queue',             queue);

            if (next.url === window.location.href) {
                window.location.reload();
            } else {
                window.location.href = next.url;
            }
        } else {
            GM_setValue('hiyori_running', false);
            const failures = GM_getValue('hiyori_failures', []);
            GM_setValue('hiyori_failures', []);

            if (Array.isArray(failures) && failures.length > 0) {
                alert(`🌸 批量完成!但以下文章写入 Notion 失败(本地文件已保存):\n${failures.join('\n')}`);
            } else {
                alert('🌸 宝宝!所有的素材都已经完美归档啦!');
            }
            window.location.href = 'https://www.perplexity.ai/library';
        }
    }


    async function waitForStableContent(timeout = 15000, stableWindow = 1500) {
        const deadline  = Date.now() + timeout;
        let lastLen     = -1;
        let stableSince = 0;

        while (Date.now() < deadline) {
            const main = document.querySelector('main') || document.body;
            const len  = (main.innerText || '').length;
            if (len > 200) {
                if (len === lastLen) {
                    if (stableSince === 0) stableSince = Date.now();
                    if (Date.now() - stableSince >= stableWindow) return main;
                } else {
                    stableSince = 0;
                    lastLen     = len;
                }
            }
            await sleep(400);
        }
        return document.querySelector('main') || document.body;
    }


    async function waitForTitle(timeout = 8000) {
        const generic = new Set([
            '', 'Perplexity', 'Perplexity AI',
            'New Thread', 'Ask anything', 'Untitled',
            '新建对话', '新线程',
            '新しいスレッド', '新しい質問',
            '새 스레드',
        ]);
        const deadline = Date.now() + timeout;
        while (Date.now() < deadline) {
            const t = document.title.replace(/ - Perplexity/g, '').trim();
            if (t && !generic.has(t)) return t;
            await sleep(300);
        }
        return document.title.replace(/ - Perplexity/g, '').trim() || '未命名对话';
    }

    function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }


    function chunkText(text, maxLen) {
        const chars  = Array.from(text);
        const chunks = [];
        for (let i = 0; i < chars.length; i += maxLen) {
            chunks.push(chars.slice(i, i + maxLen).join(''));
        }
        return chunks;
    }


    function safeSubstring(str, maxCodePoints) {
        return Array.from(str).slice(0, maxCodePoints).join('');
    }


    // - 挂载 <a> 到 DOM 后点击(修复 Firefox 不触发未挂载元素下载问题)

    // - safeSubstring 截断(防服务对代理对)
    function downloadLocal(title, content, ext) {
        const safe    = safeSubstring(
            title.replace(/[/\\:*?"<>|]/g, '_'), 40
        );
        const mime    = ext === 'md' ? 'text/markdown' : 'text/plain';
        const blobUrl = URL.createObjectURL(
            new Blob([content], { type: mime + ';charset=utf-8' })
        );
        const a       = document.createElement('a');
        a.href        = blobUrl;
        a.download    = `${safe}.${ext}`;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        setTimeout(() => URL.revokeObjectURL(blobUrl), 10000);
    }


    function recordFailure(title) {
        const f = GM_getValue('hiyori_failures', []);
        if (!Array.isArray(f)) return;
        f.push(title);
        GM_setValue('hiyori_failures', f);
    }


    async function processCurrentPage(isSingle = false) {
        if (isProcessing) {
            console.warn('[妃爱] 已在处理中,跳过重复调用');
            return;
        }
        isProcessing = true;

        try {
            const main = await waitForStableContent();

            if (!isSingle && !GM_getValue('hiyori_running', false)) return;

            const title = await waitForTitle();

            if (!isSingle && !GM_getValue('hiyori_running', false)) return;

            const url = window.location.href;

            const uniqueSources = new Set();
            main.querySelectorAll('a[href^="http"]').forEach(a => {
                if (a.href && !a.href.includes('perplexity.ai')) {
                    uniqueSources.add(a.href);
                }
            });

            const images = Array.from(main.querySelectorAll('img')).filter(img => {
                const src = img.currentSrc || img.dataset.src || img.src || '';
                return src.startsWith('http') &&
                       (img.naturalWidth > 50 || !!img.dataset.src);
            });

            const bodyText = (main.innerText || '').trim();
            if (!bodyText) {
                console.warn('[妃爱] 页面内容为空,跳过:', title);
                if (isSingle) alert('宝宝,页面内容还没加载出来哦,请稍后再试!');
                if (!isSingle) processNextInQueue();
                return;
            }

            const sourcesSection = uniqueSources.size > 0
                ? '\n\n---\n### 🌸 引用来源 (Sources)\n' +
                  Array.from(uniqueSources).map((l, i) => `[${i + 1}] ${l}`).join('\n')
                : '';

            const imagesSection = images.length > 0
                ? '\n\n---\n### 🌸 提取的附图资源\n' +
                  images.map(img => {
                      const src = img.currentSrc || img.dataset.src || img.src;
                      return CONFIG.fileType === 'md'
                          ? `![图片](<${src}>)`
                          : `[图片链接]: ${src}`;
                  }).join('\n\n')
                : '';

            const fullContent = bodyText + sourcesSection + imagesSection;

            let tag = '综合';
            if (!isSingle) {
                const idx = GM_getValue('hiyori_current_tag_index', 0);
                tag = idx < 4 ? '经济' : idx < 10 ? '政治军事' : '综合';
            }

            downloadLocal(title, fullContent, CONFIG.fileType);

            if (CONFIG.token && CONFIG.dbId) {
                await saveToNotion(title, url, fullContent, tag, isSingle);
            } else {
                if (isSingle) {
                    alert('宝宝,只帮您保存了本地文件哦!Notion 配置未填写。');
                } else {
                    processNextInQueue();
                }
            }

        } catch (err) {
            console.error('[妃爱] processCurrentPage 出错:', err);
            if (!isSingle && GM_getValue('hiyori_running', false)) {
                processNextInQueue();
            }
        } finally {
            isProcessing = false;
        }
    }


    function saveToNotion(title, url, content, tag, isSingle, retry = 0) {
        return new Promise(resolve => {
            const allChars    = Array.from(content);
            const isTruncated = allChars.length > 99 * 1900;


            //   block 顶层需要 type: 'paragraph'

            const blocks = chunkText(content, 1900).slice(0, 99).map(chunk => ({
                object:    'block',
                type:      'paragraph',
                paragraph: { rich_text: [{ type: 'text', text: { content: chunk } }] }
            }));

            const now       = new Date();
            const localDate = [
                now.getFullYear(),
                String(now.getMonth() + 1).padStart(2, '0'),
                String(now.getDate()).padStart(2, '0')
            ].join('-');

            const props = {};

            props[CONFIG.propTitle] = { title:        [{ type: 'text', text: { content: safeSubstring(title, 100) } }] };
            props[CONFIG.propUrl]   = { url };
            props[CONFIG.propTags]  = { multi_select: [{ name: tag }] };
            props[CONFIG.propTime]  = { date:         { start: localDate } };

            GM_xmlhttpRequest({
                method:  'POST',
                url:     'https://api.notion.com/v1/pages',
                timeout: 20000,
                headers: {
                    'Authorization':  `Bearer ${CONFIG.token}`,
                    'Content-Type':   'application/json',
                    'Notion-Version': '2022-06-28'
                },
                data: JSON.stringify({
                    parent:     { database_id: CONFIG.dbId },
                    properties: props,
                    children:   blocks
                }),

                onload(res) {
                    if (res.status === 429 && retry < 3) {
                        const wait = (retry + 1) * 8000;
                        console.warn(`[妃爱] Notion 限流,${wait / 1000}s 后重试(第 ${retry + 1} 次)`);
                        setTimeout(
                            () => saveToNotion(title, url, content, tag, isSingle, retry + 1)
                                    .then(resolve),
                            wait
                        );
                        return;
                    }

                    if (res.status < 200 || res.status >= 300) {
                        console.error('[妃爱] Notion 写入失败:', res.status, res.responseText);
                        recordFailure(title);
                    } else if (isTruncated) {
                        console.warn('[妃爱] 内容超限,已截断至约 18.6 万字符:', title);
                    }

                    resolve();
                    if (!isSingle && GM_getValue('hiyori_running', false)) {
                        processNextInQueue();
                    }
                },

                onerror(err) {
                    console.error('[妃爱] Notion 网络错误:', err);
                    recordFailure(title);
                    resolve();
                    if (!isSingle && GM_getValue('hiyori_running', false)) {
                        processNextInQueue();
                    }
                },

                ontimeout() {
                    console.error('[妃爱] Notion 请求超时,已跳过:', title);
                    recordFailure(title);
                    resolve();
                    if (!isSingle && GM_getValue('hiyori_running', false)) {
                        processNextInQueue();
                    }
                }
            });
        });
    }


    if (document.readyState === 'complete') {
        initUI();
    } else {
        window.addEventListener('load', initUI);
    }

    setInterval(injectCheckboxes, 2000);

    // Library 页面自动运行检测
    if (window.location.href.includes('/library') && CONFIG.autoRun && !GM_getValue('hiyori_running', false)) {
        const delay = CONFIG.autoRunDelay;
        console.log(`[妃爱] 已开启自动抓取,${delay} 秒后启动...`);

        let countdown = delay;
        const countdownInterval = setInterval(() => {
            countdown--;
            console.log(`[妃爱] 自动抓取倒计时: ${countdown} 秒`);
            if (countdown <= 0) clearInterval(countdownInterval);
        }, 1000);

        setTimeout(() => {
            if (GM_getValue('hiyori_running', false)) return; // 防止重复触发
            console.log('[妃爱] 自动抓取已启动!');
            startBatch(false);
        }, delay * 1000);
    }

    if (window.location.href.includes('/search/') &&
        GM_getValue('hiyori_running', false)) {
        processCurrentPage().catch(err => {
            console.error('[妃爱] 自动处理入口出错:', err);
            if (GM_getValue('hiyori_running', false)) processNextInQueue();
        });
    }

})();