diff --git a/README.md b/README.md index 6c6af8a..22bffa2 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,8 @@ buvid3亦可不填 使用随机生成值 关键词有限 可能会有**漏掉**的或**误报** +可在设置开启AI判断 + ### 中奖推送 > 填写在env.js内 diff --git a/env.example.js b/env.example.js index 9532b96..1701832 100644 --- a/env.example.js +++ b/env.example.js @@ -10,6 +10,7 @@ module.exports = Object.freeze({ * ## 高级功能 * - `ENABLE_CHAT_CAPTCHA_OCR` 开启评论验证码识别 使用方法见README * - `CHAT_CAPTCHA_OCR_URL` 验证码识别接口 POST `url`->`code` + * - `ENABLE_AI_JUDGE` 是否启用AI判断抽奖 * - `ENABLE_AI_COMMENTS` 是否启用AI评论 * * ## 调试相关 @@ -34,6 +35,7 @@ module.exports = Object.freeze({ ENABLE_CHAT_CAPTCHA_OCR: false, CHAT_CAPTCHA_OCR_URL: 'http://127.0.0.1:9898/ocr/url/text', + ENABLE_AI_JUDGE: false, ENABLE_AI_COMMENTS: false, ENABLE_MULTIPLE_ACCOUNT: false, diff --git a/lib/core/monitor.js b/lib/core/monitor.js index ec7ab15..83ad48a 100644 --- a/lib/core/monitor.js +++ b/lib/core/monitor.js @@ -384,7 +384,7 @@ class Monitor extends Searcher { return false; } - const + let [m_uid, ori_uid] = uids, mIsFollowed = !m_uid || (new RegExp(m_uid)).test(attentionList), oriIsFollowed = !ori_uid || (new RegExp(ori_uid)).test(attentionList), @@ -452,8 +452,28 @@ class Monitor extends Searcher { } } - if (!hasOfficialLottery && model[1] === '1' && !has_key_words && description) { - log.warn('筛选动态', `无关键词动态的描述: ${description}\n\n考虑是否修改设置key_words:\n${key_words.join('\n且满足: ')}`); + if (!hasOfficialLottery && model[1] === '1') { + if (hasEnv('ENABLE_AI_JUDGE')) { + let msg = await getAiContent( + config.ai_judge_parm.url, + config.ai_judge_parm.body, + config.ai_judge_parm.prompt, + lottery_info.des + ); + try { + let msg_json = JSON.parse(msg); + has_key_words = msg_json.has_key_words; + needTopic = msg_json.needTopic; + needAt = msg_json.needAt; + isLottery = has_key_words; + log.info('ai判断抽奖', msg_json.more); + } catch (_) { + log.error('ai判断抽奖', '未返回JSON格式'); + } + } + if (!has_key_words && description) { + log.warn('筛选动态', `无关键词动态的描述: ${description}\n\n考虑是否修改设置key_words或ai提示词`); + } } /**若勾选只转已关注 */ @@ -717,4 +737,4 @@ class Monitor extends Searcher { } -module.exports = { Monitor }; \ No newline at end of file +module.exports = { Monitor }; diff --git a/lib/data/config.js b/lib/data/config.js index e92f185..0b55969 100644 --- a/lib/data/config.js +++ b/lib/data/config.js @@ -86,6 +86,12 @@ const config = { '[转关评粉]|参与' ], + ai_judge_parm: { + url: '', + body: {}, + prompt: '' + }, + /** * - '00' 关闭自动抽奖 * - '10' 只转发官方抽奖 @@ -484,4 +490,4 @@ const config = { }; -module.exports = config; \ No newline at end of file +module.exports = config; diff --git a/lib/utils.js b/lib/utils.js index be19fad..a222ba9 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -514,7 +514,7 @@ const utils = { }); }, /** - * 获取ai评论 + * 获取ai * @param {string} url * @param {object} body * @param {string} prompt @@ -530,10 +530,12 @@ const utils = { 'authorization': 'Bearer ' + process.env.AI_API_KEY, 'content-type': 'application/json' }, + config: { + timeout: 120000 + }, contents: { ...body, 'stream': false, - 'enable_thinking': true, 'response_format': { 'type': 'text' }, 'messages': [ { diff --git a/my_config.example.js b/my_config.example.js index 3397af9..5bc280d 100644 --- a/my_config.example.js +++ b/my_config.example.js @@ -90,6 +90,19 @@ module.exports = Object.freeze({ '[转关评粉]|参与' ], + /** + * AI 判断抽奖 + * https://learn.microsoft.com/en-us/azure/ai-foundry/openai/reference#chat-completions + */ + ai_judge_parm: { + /** + * /chat/completions + */ + url: '', + body: {}, + prompt: '' + }, + /** * - '00' 关闭自动抽奖 * - '10' 只转发官方抽奖 @@ -496,23 +509,22 @@ module.exports = Object.freeze({ APIs: [], - /** - * 默认为硅基流动,可以修改为其他AI服务 - */ - ai_comments_parm: { - url: 'https://api.siliconflow.cn/v1/chat/completions', + ai_judge_parm: { + url: 'https://api.deepseek.com/chat/completions', body: { 'model': 'Qwen/Qwen3-32B', - 'max_tokens': 512, - 'thinking_budget': 4096, - 'min_p': 0.05, - 'temperature': 0.7, - 'top_p': 0.7, - 'top_k': 50, - 'frequency_penalty': 0.5, - 'n': 1, + 'enable_thinking': true, }, - prompt: '请根据以下内容直接生成一条简短评论,无需说明信息,且不包含任何敏感词汇。' + prompt: '你是一个B站用户,需要判断动态内容是否是抽奖动态,以及参与条件,以json格式输出,仅需包含key:has_key_words(bool 是否是抽奖动态),needAt(bool 是否需要@),needTopic(bool 是否需要带话题),more(string 总结参与抽奖的条件).回答不要包含markdown标记文本,输出纯json文本' + }, + + ai_comments_parm: { + url: 'https://api.deepseek.com/chat/completions', + body: { + 'model': 'Qwen/Qwen3-32B', + 'enable_thinking': true, + }, + prompt: '你是一个B站用户,请根据以下内容直接生成一条模拟真实用户的评论,不要使用表情,无需说明信息,且不包含任何敏感词汇。' }, save_lottery_info_to_file: true, diff --git a/test/ai.test.js b/test/ai.test.js new file mode 100644 index 0000000..afe34f7 --- /dev/null +++ b/test/ai.test.js @@ -0,0 +1,27 @@ +const util = require('./util'); +const utils = require('../lib/utils.js'); +const config = require('../lib/data/config'); + +(async () => { + await util.par_run([], [ + // 0 + async () => { + let msg = await utils.getAiContent( + config.ai_judge_parm.url, + config.ai_judge_parm.body, + config.ai_judge_parm.prompt, + '#胜利女神新的希望# #nikke# \n【一周年评论盖楼挑战③】拿来吧妮……的表情包!\n\n指挥官的手机里,\n一定存着几张出场率超高的妮姬表情包吧?\n和妮姬们相遇的一周年已至,这些也成了旅途中的一段快乐印记~是时候公开自己的库存了yo!\n\n无论是新收新做的趣味表情,还是珍藏一年的经典老图——\n现在,让它们登场吧!\n\n评论区交出您的表情包库存,看看这一年谁的“收藏”最cool!!\n🎁我们将在6月9日于本条评论区随机抽选:\n▶1位指挥官赠送【周边大礼盒】× 1\n▶10位指挥官赠送【Q版印章小立牌】× 1\n\n💝盖楼目标达成奖励:\n5月20日~22日期间,参与【一周年评论盖楼挑战】系列话题活动! 当全平台评论数累计达成 【2026】 楼时,我们将从本平台参与系列话题活动的用户中额外抽取2位幸运指挥官,每人送出【哈曼卡顿音响】× 1!\n\n————————————\n✦《胜利女神:新的希望》一周年庆典版本「OLD TALES 尘封童话」现已上线!游戏多端互通,前往Bilibili游戏中心搜索《胜利女神:新的希望》即可下载。' + ); + console.log(msg); + msg = await utils.getAiContent( + config.ai_comments_parm.url, + config.ai_comments_parm.body, + config.ai_comments_parm.prompt, + '#胜利女神新的希望# #nikke# \n【一周年评论盖楼挑战③】拿来吧妮……的表情包!\n\n指挥官的手机里,\n一定存着几张出场率超高的妮姬表情包吧?\n和妮姬们相遇的一周年已至,这些也成了旅途中的一段快乐印记~是时候公开自己的库存了yo!\n\n无论是新收新做的趣味表情,还是珍藏一年的经典老图——\n现在,让它们登场吧!\n\n评论区交出您的表情包库存,看看这一年谁的“收藏”最cool!!\n🎁我们将在6月9日于本条评论区随机抽选:\n▶1位指挥官赠送【周边大礼盒】× 1\n▶10位指挥官赠送【Q版印章小立牌】× 1\n\n💝盖楼目标达成奖励:\n5月20日~22日期间,参与【一周年评论盖楼挑战】系列话题活动! 当全平台评论数累计达成 【2026】 楼时,我们将从本平台参与系列话题活动的用户中额外抽取2位幸运指挥官,每人送出【哈曼卡顿音响】× 1!\n\n————————————\n✦《胜利女神:新的希望》一周年庆典版本「OLD TALES 尘封童话」现已上线!游戏多端互通,前往Bilibili游戏中心搜索《胜利女神:新的希望》即可下载。' + ); + console.log(msg); + + }, + ]); + console.log('ai.test ... ok!'); +})();