// ==UserScript== // @name 链滴新消息提示 // @namespace https://bbs.tampermonkey.net.cn/2 // @version 0.1.7 // @description 链滴新消息提示 // @author Wilsons // @icon https://ld246.com/images/favicon.png // @background // @connect ld246.com // @grant GM_xmlhttpRequest // @grant GM_notification // @grant GM_getValue // @grant GM_setValue // @grant GM_info // @grant GM_log // ==/UserScript== /* ==UserConfig== noticeConfig: delayTime: title: 检查消息时间间隔 description: 多久获取一次消息(单位分钟) type: number default: 10 min: 1 max: 240 password: false unit: 分钟 enableNewDoc: title: 是否开启新帖子提示 description: 开启 type: checkbox default: true maxPoint: title: 当新帖悬赏大于或等于多少时提示 description: 默认0,即所有新帖都提示 type: text default: min: 0 enableNewNotice: title: 是否开启新消息提示 description: 开启 type: checkbox default: true token: title: 链滴Token description: 在链滴 设置 - 账号 中找到 API Token type: text default: min: 3 max: 50 password: false ==/UserConfig== */ /////////////////// 主流程 //////////////////////////// let token = '', articleTitle='', noticeNum = 0, articleType=0; const main = async () => { let hasNewDoc=false, hasNewNotice=false, lastArticleId; const isFirstRun = getUnixTimestamp() > GM_getValue('firstRunEndTime', 0); if(isFirstRun) GM_setValue('firstRunEndTime', getTodayEndTimestamp()); const lastRunTime = GM_getValue('lastRunTime') || 0; const delayTime = GM_getValue('noticeConfig.delayTime') || 0; if(!isFirstRun && getUnixTimestamp() - lastRunTime < delayTime * 60) return; GM_setValue('lastRunTime', getUnixTimestamp()); token = GM_getValue('noticeConfig.token'); if(!token) { if(isFirstRun) shownNotification('请先配置Token,方法:在链滴 设置 - 账号 中找到 API Token', ()=>{ window.open('https://ld246.com/settings/account'); }); return; } lastArticleId = GM_getValue('lastArticleId'); if(!lastArticleId) { lastArticleId = await getLastArticleId(); GM_setValue('lastArticleId', lastArticleId); } let pointStr = '', aid=0; const enableNewDoc = GM_getValue('noticeConfig.enableNewDoc'); if(enableNewDoc) { aid = await getLastArticleId(); if(aid !== lastArticleId) { hasNewDoc = true; GM_setValue('lastArticleId', aid); const maxPoint = (GM_getValue('noticeConfig.maxPoint') || 0) - 0; // 获取帖子悬赏 if(articleType === 5){ // 当是问答时,检测悬赏值 const point = await getArticlePoint(aid); pointStr = `(悬赏:${point})`; // 悬赏低于指定值不提示 if(maxPoint && point < maxPoint) { hasNewDoc = false; } } else if(maxPoint){ hasNewDoc = false; // 非问答,且开启了最大悬赏限制则不提示 } } } const enableNewNotice = GM_getValue('noticeConfig.enableNewNotice'); if(enableNewNotice) { const num = await getNoticeNums(); if(num) hasNewNotice = true; } if(hasNewDoc && hasNewNotice) { shownNotification(`有新帖${articleTitle.replace(/(.+)/, '“$1”')}发布${pointStr}和有${(noticeNum+'').replace(/(.+)/, '$1条')}新消息!`, ()=>{ window.open('https://ld246.com/article/'+aid); //window.open('https://ld246.com/notifications/commented'); }); } else if(hasNewDoc) { shownNotification(`有新帖${articleTitle.replace(/(.+)/, '“$1”')}发布${pointStr}!`, ()=>{ window.open('https://ld246.com/article/'+aid); }); } else if(hasNewNotice) { shownNotification(`您有${(noticeNum+'').replace(/(.+)/, '$1条')}新的消息`, ()=>{ window.open('https://ld246.com/notifications/commented'); }); } }; // 启动主函数 main(); setInterval(()=>{ main(); }, 60000); /////////////////// 辅助函数 //////////////////////////// async function getLastArticleId() { try{ // see https://ld246.com/article/1488603534762 const result = await fetchUrl('https://ld246.com/api/v2/articles/latest?p=1'); const json = JSON.parse(result.text); const articles = json?.data?.articles || []; const topestArticle = articles.filter(a => a.articleStick === -1) // 获取置顶帖子 .sort((a, b) => b.articleCreateTime - a.articleCreateTime)[0]; // 按时间降序,取第一个 let article = articles.find(a=>a.articleStick===0); // 获取非置顶帖子 if(!article) return 0; if(topestArticle && topestArticle.articleCreateTime > article.articleCreateTime) { article = topestArticle; // 当置顶是最新贴取置顶,否则取最新非置顶的帖子 } const aid = article?.oId; articleTitle = article?.articleTitle || ''; articleType = article?.articleType || 0; return aid; }catch(e) { console.log(e); GM_log(`《${GM_info.script.name}》脚本异常:${e}`, "warn"); return 0; } } async function getNoticeNums() { try{ // see https://ld246.com/article/1488603534762 const result = await fetchUrl('https://ld246.com/api/v2/notifications/unread/count'); const json = JSON.parse(result.text); const nums = Object.values(json?.data) || []; const sum = nums.reduce((acc, curr) => acc + curr, 0); noticeNum = sum || 0; return sum; }catch(e) { console.log(e); GM_log(`《${GM_info.script.name}》脚本异常:${e}`, "warn"); return 0; } } async function getArticlePoint(aid) { try{ // see https://ld246.com/article/1488603534762 const result = await fetchUrl('https://ld246.com/api/v2/article/'+aid+'?p=1'); const json = JSON.parse(result.text); const point = json?.data?.article?.articleQnAOfferPoint; return point; }catch(e) { console.log(e); GM_log(`《${GM_info.script.name}》脚本异常:${e}`, "warn"); return 0; } } function shownNotification(msg, callback) { GM_notification({ title: `${GM_info.script.name}`, text: `${msg}`, requireInteraction: true, onclick: callback }); } function fetchUrl(url, method = 'GET', data = null) { return new Promise((resolve, reject) => { GM_xmlhttpRequest({ method: method, url: url, data: data, headers: { 'Content-Type': 'application/x-www-form-urlencoded', 'Authorization': `token ${token}`, }, onload: (response) => { if (response.status >= 200 && response.status < 400) { resolve({text: response.responseText, status: response.status}); } else { reject(new Error(`网络请求状态码异常-${response.status}`)); } }, onerror: (error) => { reject(new Error(`网络请求异常-${error}`)); } }); }); } function getUnixTimestamp() { return Math.floor(Date.now() / 1000); } function getTodayEndTimestamp() { const end = new Date(); end.setHours(23, 59, 59, 999); // 设置为今天 23:59:59.999 return Math.floor(end.getTime() / 1000); }