dsh-douyin-agent 1.4.4 → 1.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -2
- package/chat-browser.mjs +56 -12
- package/client.js +2 -0
- package/package.json +7 -4
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# DSH 抖音助手
|
|
2
2
|
|
|
3
|
+
> **作者:dafeiyu1314520** —— 装这个包的人,第一眼会看到这个名字。
|
|
4
|
+
> 原作 [LockyBee/dsh-douyin-agent](https://github.com/LockyBee/dsh-douyin-agent)(MIT),这一份是在它基础上改的。
|
|
5
|
+
|
|
3
6
|
让 DSH 里的 DeepSeek(大肥鱼)自己刷抖音:搜索、换片、看真实画面,把有意思的内容记下来,并在你许可的范围内分享给好友。
|
|
4
7
|
|
|
5
8
|
在专用 Edge 里浏览电脑网页版抖音:开头先抓 6 帧拼成一张采样图,让模型决定看不看;决定看之后按秒抽帧,拼成最多三张故事板,连同带时间轴的弹幕和转写一起交给模型。所有外发功能默认关闭,每一项都有独立开关、次数上限和时间限制。这是第三方适配,不是 DeepSeek 官方出品。
|
|
@@ -41,7 +44,7 @@
|
|
|
41
44
|
在 DSH Desktop 自带终端中运行:
|
|
42
45
|
|
|
43
46
|
```powershell
|
|
44
|
-
dsh plugin --profile desktop add dsh-douyin-
|
|
47
|
+
dsh plugin --profile desktop add dsh-douyin-browser-local@1.4.0
|
|
45
48
|
```
|
|
46
49
|
|
|
47
50
|
需要 Windows、已安装 Microsoft Edge 和 DSH 0.2.0-rc.2,以及一个独立的 **Node.js 22.13 或更高版本**(插件要用它单独起浏览器进程;装了 Node LTS 就行,没装的话点「开始自主观看」会提示下载地址,也可以用 `DOUYIN_NODE_EXECUTABLE` 指定已有的 node.exe)。安装后按宿主提示重新加载,在设置中打开“刷抖音”;抖音登录和验证由使用者自行完成。市场收录与 npm 包发布分别同步,不能把发布成功当成市场已刷新。
|
|
@@ -52,7 +55,7 @@ dsh plugin --profile desktop add dsh-douyin-agent@1.4.2
|
|
|
52
55
|
需要 Windows、Microsoft Edge、Node.js 22.13 以上,以及 DSH 0.2.0-rc.2 系列(已在 DSH Desktop 2.0.17 上核对过设置页)。
|
|
53
56
|
|
|
54
57
|
```powershell
|
|
55
|
-
git clone https://github.com/
|
|
58
|
+
git clone https://github.com/LockyBee/dsh-douyin-agent.git
|
|
56
59
|
cd dsh-douyin-agent
|
|
57
60
|
pnpm install --frozen-lockfile
|
|
58
61
|
$dshInstallDirectory = 'C:\Apps\DSH Desktop' # 替换为正在使用的 DSH 安装目录
|
package/chat-browser.mjs
CHANGED
|
@@ -9,6 +9,43 @@ export { MessageIdentityTracker } from './message-tracker.mjs';
|
|
|
9
9
|
const HOME = 'https://www.douyin.com/?recommend=1&from_nav=1';
|
|
10
10
|
const hash = value => createHash('sha256').update(value).digest('hex');
|
|
11
11
|
const stop = signal => signal?.throwIfAborted();
|
|
12
|
+
// 2026-10-31:读「我的关注」名单。原来的实现依赖抖音的哈希类名(.OlxToPIh / .Zce7SWd3),
|
|
13
|
+
// 改版后名单还会开在新标签页,导致 followed 全是 null、设置页的许可好友勾选框全灰。
|
|
14
|
+
// 现在按行读、按按钮文字判关系:类名再变也不影响;新标签页由 verifyFollowing 负责接管。
|
|
15
|
+
const readFollowingRows = () => {
|
|
16
|
+
const root = document.querySelector('[data-e2e="user-fans-container"]') || document.body;
|
|
17
|
+
const order = ['相互关注', '已关注', '关注', '回关'];
|
|
18
|
+
const pick = row => {
|
|
19
|
+
const button = [...row.querySelectorAll('button')].find(item => (item.textContent || '').trim());
|
|
20
|
+
if (!button) return null;
|
|
21
|
+
const text = (button.textContent || '').trim();
|
|
22
|
+
const label = order.find(item => text.startsWith(item));
|
|
23
|
+
if (!label) return null;
|
|
24
|
+
const names = [...row.querySelectorAll('a')].map(anchor => (anchor.textContent || '').trim()).filter(Boolean);
|
|
25
|
+
if (!names.length) return null;
|
|
26
|
+
return { displayName: names[0], mutual: label === '相互关注', followed: label !== '关注' && label !== '回关' };
|
|
27
|
+
};
|
|
28
|
+
const out = [];
|
|
29
|
+
for (const row of root.querySelectorAll('.OlxToPIh, li')) {
|
|
30
|
+
const item = pick(row);
|
|
31
|
+
if (item) out.push(item);
|
|
32
|
+
}
|
|
33
|
+
if (!out.length) {
|
|
34
|
+
// 类名全换了也能用:从按钮往上找能读出名字的那一层当行。
|
|
35
|
+
for (const button of root.querySelectorAll('button')) {
|
|
36
|
+
const text = (button.textContent || '').trim();
|
|
37
|
+
if (!order.some(item => text.startsWith(item))) continue;
|
|
38
|
+
let node = button.parentElement;
|
|
39
|
+
for (let depth = 0; depth < 5 && node && node !== root; depth += 1) {
|
|
40
|
+
const item = pick(node);
|
|
41
|
+
if (item) { out.push(item); break; }
|
|
42
|
+
node = node.parentElement;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
return out.filter(item => (seen.has(item.displayName) ? false : (seen.add(item.displayName), true)));
|
|
48
|
+
};
|
|
12
49
|
const readableDraft = value => value.replace(/[\u200B-\u200F\u202A-\u202E\u2060-\u206F\uFEFF]/g, '').trim();
|
|
13
50
|
const localDay = value => new Intl.DateTimeFormat('en-CA', { timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit', day: '2-digit' }).format(new Date(value));
|
|
14
51
|
|
|
@@ -283,20 +320,27 @@ export class ChatBrowser {
|
|
|
283
320
|
await page.goto(target.href, { waitUntil: 'domcontentloaded' });
|
|
284
321
|
await page.locator('[data-e2e="user-info-follow"]').waitFor({ state: 'visible', timeout: 10_000 });
|
|
285
322
|
await page.waitForTimeout(800);
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
323
|
+
stop(signal);
|
|
324
|
+
// 2026-10-31:改版后点「关注」有时把名单开在**新标签页**,原来只在当前页找,永远找不到。
|
|
325
|
+
// 这里两条路都走:等 popup(新标签页),没有 popup 就读当前页;读不到再点一次,最多三次。
|
|
326
|
+
let rows = [];
|
|
327
|
+
for (let attempt = 0; attempt < 3 && !rows.length; attempt += 1) {
|
|
289
328
|
stop(signal);
|
|
290
|
-
|
|
291
|
-
await
|
|
329
|
+
const popupPromise = page.waitForEvent('popup', { timeout: 4000 }).catch(() => null);
|
|
330
|
+
await page.locator('[data-e2e="user-info-follow"]').click({ timeout: 5000 }).catch(() => {});
|
|
331
|
+
const popup = await popupPromise;
|
|
332
|
+
const view = popup || page;
|
|
333
|
+
if (popup) {
|
|
334
|
+
await popup.waitForLoadState('domcontentloaded').catch(() => {});
|
|
335
|
+
await popup.waitForTimeout(1200);
|
|
336
|
+
}
|
|
337
|
+
await view.locator('[data-e2e="user-fans-container"]').first().waitFor({ state: 'visible', timeout: 8000 }).catch(() => {});
|
|
338
|
+
rows = await view.evaluate(readFollowingRows).catch(() => []);
|
|
339
|
+
if (!rows.length) rows = await page.evaluate(readFollowingRows).catch(() => []);
|
|
340
|
+
if (popup) await popup.close().catch(() => {});
|
|
292
341
|
}
|
|
293
|
-
if (!
|
|
294
|
-
|
|
295
|
-
stop(signal);
|
|
296
|
-
this.following = await page.evaluate(() => [...document.querySelectorAll('[data-e2e="user-fans-container"] .OlxToPIh')].map(row => ({
|
|
297
|
-
displayName: row.querySelector('.Zce7SWd3 a')?.textContent.trim(),
|
|
298
|
-
mutual: [...row.querySelectorAll('button')].some(button => button.textContent.trim() === '相互关注'),
|
|
299
|
-
})));
|
|
342
|
+
if (!rows.length) throw new Error('本人的关注列表未就绪,未确认联系人关系。');
|
|
343
|
+
this.following = rows;
|
|
300
344
|
await page.goto(HOME, { waitUntil: 'domcontentloaded' });
|
|
301
345
|
await this.dialog(page, signal);
|
|
302
346
|
}
|
package/client.js
CHANGED
|
@@ -192,6 +192,8 @@ window.__ModuleLoader__.load({
|
|
|
192
192
|
note(`已确认回复 ${state?.replies?.counts?.confirmed ?? 0} 条;许可:${state?.replies?.enabled ? '已开启' : '未开启'}${state?.replies?.paused ? ';有未核对结果,已暂停' : ''}。`)),
|
|
193
193
|
state?.media && section('可选音频', toggle('读取视频声音', 'media'), h('label', { style: column }, '音频后端', h('select', { 'aria-label': '音频后端', value: value('media', 'backend', 'local-whisper'), disabled: !ready, style: formControl, onChange: event => edit('media', 'backend', event.target.value) }, h('option', { value: 'local-whisper' }, '本机 Whisper'), h('option', { value: 'mimo' }, 'MiMo'), h('option', { value: 'minimax' }, 'MiniMax'))), button('保存音频设置', 'media', () => post('media', '/media/configure', { enabled: value('media', 'enabled', false), backend: value('media', 'backend', 'local-whisper') }, 'media')), note('音频可以不接入。下面这些 Key 只用于对应的云音频服务,和 DSH 主模型分开,明文保存在本机共享媒体文件里。'),
|
|
194
194
|
note('保存 Key 不会开启音频,也不会发起云请求,更不会安装依赖或下载模型。'), credentialForm('mimo', 'MiMo'), credentialForm('minimax', 'MiniMax'), operationActive && note('请先停止自主观看、问候及私信回复,再更换音频 Key。')),
|
|
195
|
+
h('div', { style: { display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 18, paddingTop: 12, borderTop: '1px solid var(--dsw-alias-border-secondary, #e5e7eb)', color: 'var(--dsw-alias-label-tertiary)', fontSize: 12 } },
|
|
196
|
+
h('span', null, '本插件由 '), h('strong', { style: { fontWeight: 600 } }, 'dafeiyu1314520'), h('span', null, ' 维护 · 原作 LockyBee(MIT)')),
|
|
195
197
|
section('观看日报', note(state?.reports?.folder || '日报保存在共用记忆的 reports/videos 目录。'), button('生成今天日报', 'reportGenerate', () => { void perform('reportGenerate', async () => { const result = await api.request('/reports/generate', {}); setReport(result); return result; }); }),
|
|
196
198
|
h('label', { style: column }, '日报日期', h('input', { 'aria-label': '日报日期', type: 'date', value: reportDate, onChange: event => setReportDate(event.target.value), style: formControl })),
|
|
197
199
|
button('读取所选日报', 'reportRead', () => { void perform('reportRead', async () => { if (!/^\d{4}-\d{2}-\d{2}$/.test(reportDate)) throw new Error('请选择有效日报日期。'); const result = await api.request(`/reports/read?date=${reportDate}`); setReport(result); return result; }); }),
|
package/package.json
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-douyin-agent",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "DSH 原生抖音工具:有限自主观看、抽帧故事板与弹幕、人格与兴趣记忆、自动问候与私信回复、观看日报",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"author":
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "dafeiyu1314520",
|
|
9
|
+
"url": "https://github.com/dafeiyu1314520"
|
|
10
|
+
},
|
|
8
11
|
"contributors": [
|
|
9
12
|
{
|
|
10
|
-
"name": "
|
|
11
|
-
"url": "https://github.com/
|
|
13
|
+
"name": "LockyBee",
|
|
14
|
+
"url": "https://github.com/LockyBee"
|
|
12
15
|
}
|
|
13
16
|
],
|
|
14
17
|
"repository": {
|