dsh-foxy-weibo 0.2.1 → 0.2.3

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 CHANGED
@@ -6,7 +6,7 @@ Foxy 的微博子插件:按 foxy3 预设关注微博账号、阅读博文、
6
6
 
7
7
  ## 功能
8
8
 
9
- - **主模式**:检查自己的微博 UID,读取自己新博文下的评论,只回复未回复的他人评论;
9
+ - **博文评论回复**:检查指定的监控账号 UID,读取其新博文下的评论,只回复未处理的他人评论;登录账号是机器人自己,监控账号通常是主人账号;
10
10
  - 可设置每日检查时间(`HH:MM`,最多 6 个),例如 `08:30, 20:00`;
11
11
  - 回复前按 foxy3 预设判断 `skip / comment`,不设最少字数,最多 80 字;
12
12
  - 旧模式保留:关注一个或多个微博 UID,定时读取时间线并评论;
@@ -47,7 +47,7 @@ Cookie 只保存在本机 `~/.dsh/foxy-weibo/settings.json`,不会回传给前
47
47
 
48
48
  ### 自己的 UID / 关注 UID
49
49
 
50
- `自己的微博 UID` 是主模式使用的账号,插件只检查它的新博文和评论。
50
+ `监控账号 UID` 是要检查博文评论的目标账号(通常是主人账号);`当前账号` 是扫码登录的机器人账号,两者可以不同。
51
51
 
52
52
  `关注 UID` 是旧模式使用,多个 UID 用逗号分隔,最多 20 个。UID 是微博用户主页 URL 里的数字 ID,例如 `https://weibo.com/u/1234567890`。
53
53
 
@@ -63,7 +63,7 @@ Cookie 只保存在本机 `~/.dsh/foxy-weibo/settings.json`,不会回传给前
63
63
 
64
64
  ## 建议顺序
65
65
 
66
- 1. 扫码登录,填自己的微博 UID,`enabled: true`、`replyMode: off`;
66
+ 1. 扫码登录,填监控账号 UID,`enabled: true`、`replyMode: off`;
67
67
  2. 设置每日检查时间,或先点「立即运行」,看日志和「调试」里的模型决策;
68
68
  3. 确认判断正常后,再开启 `replyMode: auto`,每日上限先设为 1;
69
69
  4. 「立即检查」只判断不发布,用来预览回复;「立即回复」会真实发布一条,适合测试;
package/cordis.patch.yml CHANGED
@@ -10,7 +10,7 @@
10
10
  config:
11
11
  enabled: false
12
12
  cookie: ''
13
- selfUid: ''
13
+ targetUid: ''
14
14
  targetUids: []
15
15
  replyOwnPosts: true
16
16
  replyMode: 'off'
package/lib/client.js CHANGED
@@ -213,13 +213,17 @@ window.__ModuleLoader__.load({
213
213
  const data = await request('/run', { method: 'POST', body: '{}' })
214
214
  setStatus(data.status)
215
215
  const result = data.result || {}
216
+ if (result.error) {
217
+ setError(`检查失败:${result.error}`)
218
+ return
219
+ }
216
220
  setNotice(
217
221
  result.skipped === 'disabled'
218
222
  ? '未开始:自动浏览已关闭'
219
223
  : result.skipped === 'busy'
220
224
  ? '正在运行,请稍候'
221
- : result.skipped === 'no-self'
222
- ? '未开始:请先填写自己的微博 UID'
225
+ : result.skipped === 'no-target'
226
+ ? '未开始:请先填写监控账号 UID'
223
227
  : `运行完成:检查 ${result.checked || 0} 条,回复 ${result.replied || 0} 条` +
224
228
  (result.previewed ? `,预览 ${result.previewed} 条` : '') +
225
229
  `,跳过 ${result.skipped || 0} 条`,
@@ -233,9 +237,13 @@ window.__ModuleLoader__.load({
233
237
  const data = await request('/check-now', { method: 'POST', body: '{}' })
234
238
  setStatus(data.status)
235
239
  const result = data.result || {}
240
+ if (result.error) {
241
+ setError(`检查失败:${result.error}`)
242
+ return
243
+ }
236
244
  setNotice(
237
- result.skipped === 'no-self'
238
- ? '未开始:请先扫码登录或填写自己的微博 UID'
245
+ result.skipped === 'no-target'
246
+ ? '未开始:请先填写监控账号 UID'
239
247
  : result.skipped === 'busy'
240
248
  ? '正在运行,请稍候'
241
249
  : `检查完成:检查 ${result.checked || 0} 条评论,预览 ${result.previewed || 0} 条,跳过 ${result.skipped || 0} 条` +
@@ -251,11 +259,15 @@ window.__ModuleLoader__.load({
251
259
  const data = await request('/reply-now', { method: 'POST', body: '{}' })
252
260
  setStatus(data.status)
253
261
  const result = data.result || {}
262
+ if (result.error) {
263
+ setError(`回复失败:${result.error}`)
264
+ return
265
+ }
254
266
  setNotice(
255
267
  result.replied > 0
256
268
  ? `已立即回复 ${result.replied} 条,可在下方日志查看`
257
- : result.skipped === 'no-self'
258
- ? '未开始:请先扫码登录或填写自己的微博 UID'
269
+ : result.skipped === 'no-target'
270
+ ? '未开始:请先填写监控账号 UID'
259
271
  : '没有可立即回复的评论(可能已处理或模型选择跳过)',
260
272
  )
261
273
  } catch (e) { setError(e.message) } finally { setBusy('') }
@@ -272,7 +284,7 @@ window.__ModuleLoader__.load({
272
284
  h('div', { className: 'fw-head' },
273
285
  h('div', null,
274
286
  h('p', { className: 'fw-eyebrow' }, 'FOXY EXTENSION'),
275
- h('h1', null, '微博浏览'),
287
+ h('h1', null, '微博评论'),
276
288
  h('p', null, 'Foxy 按 foxy3 预设关注微博账号,阅读、判断、评论并写入记忆。'),
277
289
  ),
278
290
  h('span', { className: 'fw-badge' }, settings.enabled ? '自动浏览已开启' : '自动浏览已关闭'),
@@ -284,8 +296,8 @@ window.__ModuleLoader__.load({
284
296
  h(Field, { label: '当前账号', hint: '扫码成功后自动显示' },
285
297
  h('input', { value: settings.accountName ? `${settings.accountName}(${settings.accountUid || '?'})` : settings.cookieSet ? '已登录' : '未登录', readOnly: true }),
286
298
  ),
287
- h(Field, { label: '自己的微博 UID', hint: '主功能会检查这个账号的新博文评论' },
288
- h('input', { value: settings.selfUid || '', placeholder: '例如 1234567890', onChange: (e) => setField('selfUid', e.target.value) })),
299
+ h(Field, { label: '监控账号 UID', hint: '要检查博文评论的目标账号;不是登录的机器人账号' },
300
+ h('input', { value: settings.targetUid || '', placeholder: '例如 1234567890', onChange: (e) => setField('targetUid', e.target.value) })),
289
301
  ),
290
302
  h('div', { className: 'fw-actions' },
291
303
  h('button', { className: 'fw-btn primary', disabled: !!busy, onClick: startLogin }, busy === 'login' ? '获取二维码…' : qr ? '刷新二维码' : '扫码登录'),
@@ -314,9 +326,9 @@ window.__ModuleLoader__.load({
314
326
  ),
315
327
 
316
328
  h('section', { className: 'fw-card' },
317
- h(SectionHead, { title: '回复自己的博文', description: '主功能:每天按设定时间检查自己的新博文,只回复未回复的他人评论。' }),
329
+ h(SectionHead, { title: '博文评论回复', description: '每日按设定时间检查目标账号的新博文,并回复其评论区中尚未处理的他人评论。' }),
318
330
  h(Toggle, { checked: settings.enabled, onChange: (v) => setField('enabled', v), label: '启用自动检查', hint: '关闭后定时任务不运行,仍可使用「立即运行」。' }),
319
- h(Toggle, { checked: settings.replyOwnPosts, onChange: (v) => setField('replyOwnPosts', v), label: '主模式:只看自己的博文评论', hint: '关闭后回到旧模式,浏览关注账号的时间线并评论博文。' }),
331
+ h(Toggle, { checked: settings.replyOwnPosts, onChange: (v) => setField('replyOwnPosts', v), label: '仅监控目标账号的博文', hint: '关闭后回到旧模式,浏览关注账号的时间线并评论博文。' }),
320
332
  h('div', { className: 'fw-grid' },
321
333
  h(Field, { label: '每日检查时间', hint: 'HH:MM,多个用逗号分隔,最多 6 个,例如 08:30, 20:00' },
322
334
  h('input', { value: scheduleText, placeholder: '20:00', onChange: (e) => setScheduleText(e.target.value) })),
@@ -331,7 +343,7 @@ window.__ModuleLoader__.load({
331
343
  h('input', { type: 'number', min: 0, max: 10, value: settings.maxCommentsPerDay, onChange: (e) => setField('maxCommentsPerDay', e.target.value) })),
332
344
  ),
333
345
  h('div', { className: 'fw-grid' },
334
- h(Field, { label: '时间线轮询(旧模式备用)', hint: '关闭主模式时使用,分钟' },
346
+ h(Field, { label: '时间线轮询(旧模式备用)', hint: '关闭“仅监控目标账号的博文”时使用,分钟' },
335
347
  h('input', { type: 'number', min: 5, max: 1440, value: settings.intervalMinutes, onChange: (e) => setField('intervalMinutes', e.target.value) })),
336
348
  h(Field, { label: '旧模式评论模式', hint: '浏览他人博文时是否发一级评论' },
337
349
  h('select', { value: settings.commentMode, onChange: (e) => setField('commentMode', e.target.value) },
package/lib/index.js CHANGED
@@ -164,11 +164,6 @@ async function scanOwnComments(ctx, config, state, log, force = false, options =
164
164
  const forceReply = options.forceReply === true
165
165
  const canReply = !previewOnly && (forceReply || config.replyMode === 'auto')
166
166
  const targetCid = String(options.cid || '').trim()
167
- const selfUid = String(config.selfUid || config.accountUid || '').trim()
168
- if (!selfUid) return { skipped: 'no-self' }
169
- const maxPosts = Math.max(1, Math.min(20, Number(config.maxPostsPerRun) || 5))
170
- const maxReplies = Math.max(1, Math.min(8, Number(options.maxReplies ?? config.maxCommentsPerDay) || 1))
171
- const ignoreQuota = forceReply
172
167
  const result = {
173
168
  mode: options.mode || 'own-comments',
174
169
  checked: 0,
@@ -178,9 +173,28 @@ async function scanOwnComments(ctx, config, state, log, force = false, options =
178
173
  errors: 0,
179
174
  items: [],
180
175
  }
176
+ const targetUid = String(config.targetUid || '').trim()
177
+ if (!targetUid) return { skipped: 'no-target' }
178
+ let loginUid = String(config.accountUid || '').trim()
179
+ if (config.cookie) {
180
+ const account = await fetchAccount(config.cookie)
181
+ if (!account.uid) {
182
+ return {
183
+ ...result,
184
+ errors: 1,
185
+ error: '微博登录态无效,请重新扫码登录后再检查',
186
+ }
187
+ }
188
+ if (!loginUid) loginUid = account.uid
189
+ } else {
190
+ return { ...result, errors: 1, error: '请先扫码登录微博账号' }
191
+ }
192
+ const maxPosts = Math.max(1, Math.min(20, Number(config.maxPostsPerRun) || 5))
193
+ const maxReplies = Math.max(1, Math.min(8, Number(options.maxReplies ?? config.maxCommentsPerDay) || 1))
194
+ const ignoreQuota = forceReply
181
195
  let posts = []
182
196
  try {
183
- posts = await fetchTimeline(selfUid, config)
197
+ posts = await fetchTimeline(targetUid, config)
184
198
  } catch (error) {
185
199
  return { ...result, errors: 1, error: error?.message || String(error) }
186
200
  }
@@ -199,7 +213,7 @@ async function scanOwnComments(ctx, config, state, log, force = false, options =
199
213
  if (targetCid && String(comment.cid) !== targetCid) continue
200
214
  if (canReply && result.replied >= maxReplies) break
201
215
  if (!comment.cid || state.commentSeen[comment.cid]) continue
202
- if (String(comment.author?.uid || '') === selfUid) {
216
+ if (loginUid && String(comment.author?.uid || '') === loginUid) {
203
217
  state.commentSeen[comment.cid] = { at: Date.now(), status: 'self' }
204
218
  continue
205
219
  }
@@ -529,7 +543,8 @@ export function apply(ctx, rawConfig = {}) {
529
543
  enabled: config.enabled === true,
530
544
  running,
531
545
  mode: config.replyOwnPosts === false ? 'timeline' : 'own-comments',
532
- selfUid: config.selfUid || config.accountUid || '',
546
+ targetUid: config.targetUid || '',
547
+ loginUid: config.accountUid || '',
533
548
  scheduleTimes: config.scheduleTimes,
534
549
  nextRunAt,
535
550
  replyMode: config.replyMode,
@@ -631,7 +646,6 @@ export function apply(ctx, rawConfig = {}) {
631
646
  config.cookie = result.cookie
632
647
  config.accountName = result.account?.screenName || ''
633
648
  config.accountUid = result.account?.uid || ''
634
- if (!config.selfUid && result.account?.uid) config.selfUid = result.account.uid
635
649
  saveSettings(config)
636
650
  }
637
651
  return json({ ok: true, ...result, settings: publicSettings(config), status: status() })
package/lib/settings.js CHANGED
@@ -5,7 +5,7 @@ import { dirname, join } from 'node:path'
5
5
  export const DEFAULTS = {
6
6
  enabled: false,
7
7
  cookie: '',
8
- selfUid: '',
8
+ targetUid: '',
9
9
  targetUids: [],
10
10
  replyOwnPosts: true,
11
11
  replyMode: 'off', // off | auto
@@ -26,7 +26,7 @@ export const DEFAULTS = {
26
26
 
27
27
  export const SETTING_KEYS = [
28
28
  'enabled',
29
- 'selfUid',
29
+ 'targetUid',
30
30
  'targetUids',
31
31
  'replyOwnPosts',
32
32
  'replyMode',
@@ -45,7 +45,7 @@ export const SETTING_KEYS = [
45
45
  ]
46
46
 
47
47
  const TEXT_KEYS = new Set([
48
- 'selfUid',
48
+ 'targetUid',
49
49
  'commentMode',
50
50
  'replyMode',
51
51
  'memoryPresetId',
@@ -137,6 +137,8 @@ export function loadSettings(base = {}) {
137
137
  if (existsSync(settingsPath())) {
138
138
  const parsed = JSON.parse(readFileSync(settingsPath(), 'utf8'))
139
139
  next = normalizePatch(next, parsed)
140
+ // 0.2.x used selfUid for the monitored account; migrate it once.
141
+ if (!next.targetUid && typeof parsed.selfUid === 'string') next.targetUid = parsed.selfUid.trim()
140
142
  if (typeof parsed.cookie === 'string') next.cookie = parsed.cookie
141
143
  if (typeof parsed.llmApiKey === 'string') next.llmApiKey = parsed.llmApiKey
142
144
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-foxy-weibo",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Foxy's Weibo transport for DeepSeek Harness: follow users, read posts, comment, reply, and remember events.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -48,3 +48,4 @@
48
48
  "test": "node test/test-weibo.cjs && node test/test-client.cjs"
49
49
  }
50
50
  }
51
+