dsh-foxy-weibo 0.2.3 → 0.2.4

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/lib/client.js CHANGED
@@ -224,7 +224,8 @@ window.__ModuleLoader__.load({
224
224
  ? '正在运行,请稍候'
225
225
  : result.skipped === 'no-target'
226
226
  ? '未开始:请先填写监控账号 UID'
227
- : `运行完成:检查 ${result.checked || 0} 条,回复 ${result.replied || 0} 条` +
227
+ : `运行完成:博文 ${result.posts || 0} 篇、评论 ${result.comments || 0} 条,` +
228
+ `处理 ${result.checked || 0} 条,回复 ${result.replied || 0} 条` +
228
229
  (result.previewed ? `,预览 ${result.previewed} 条` : '') +
229
230
  `,跳过 ${result.skipped || 0} 条`,
230
231
  )
@@ -246,7 +247,8 @@ window.__ModuleLoader__.load({
246
247
  ? '未开始:请先填写监控账号 UID'
247
248
  : result.skipped === 'busy'
248
249
  ? '正在运行,请稍候'
249
- : `检查完成:检查 ${result.checked || 0} 条评论,预览 ${result.previewed || 0} 条,跳过 ${result.skipped || 0} 条` +
250
+ : `检查完成:博文 ${result.posts || 0} 篇、评论 ${result.comments || 0} 条,` +
251
+ `待处理 ${result.checked || 0} 条,预览 ${result.previewed || 0} 条,跳过 ${result.skipped || 0} 条` +
250
252
  (result.errors ? `,失败 ${result.errors} 条` : ''),
251
253
  )
252
254
  } catch (e) { setError(e.message) } finally { setBusy('') }
@@ -268,7 +270,7 @@ window.__ModuleLoader__.load({
268
270
  ? `已立即回复 ${result.replied} 条,可在下方日志查看`
269
271
  : result.skipped === 'no-target'
270
272
  ? '未开始:请先填写监控账号 UID'
271
- : '没有可立即回复的评论(可能已处理或模型选择跳过)',
273
+ : `已浏览 ${result.posts || 0} 篇博文、${result.comments || 0} 条评论,但没有可立即回复的新评论(可能已处理或模型选择跳过)`,
272
274
  )
273
275
  } catch (e) { setError(e.message) } finally { setBusy('') }
274
276
  }
@@ -337,8 +339,8 @@ window.__ModuleLoader__.load({
337
339
  h('option', { value: 'off' }, '仅检查,不回复'),
338
340
  h('option', { value: 'auto' }, '自动回复'),
339
341
  )),
340
- h(Field, { label: '每轮检查博文数', hint: '范围 1–20' },
341
- h('input', { type: 'number', min: 1, max: 20, value: settings.maxPostsPerRun, onChange: (e) => setField('maxPostsPerRun', e.target.value) })),
342
+ h(Field, { label: '浏览范围(博文条数)', hint: '按时间倒序检查;1–100,填 100 即尽量浏览全部' },
343
+ h('input', { type: 'number', min: 1, max: 100, value: settings.maxPostsPerRun, onChange: (e) => setField('maxPostsPerRun', e.target.value) })),
342
344
  h(Field, { label: '每日回复上限', hint: '0–10,同时限制评论' },
343
345
  h('input', { type: 'number', min: 0, max: 10, value: settings.maxCommentsPerDay, onChange: (e) => setField('maxCommentsPerDay', e.target.value) })),
344
346
  ),
package/lib/index.js CHANGED
@@ -171,6 +171,8 @@ async function scanOwnComments(ctx, config, state, log, force = false, options =
171
171
  previewed: 0,
172
172
  skipped: 0,
173
173
  errors: 0,
174
+ posts: 0,
175
+ comments: 0,
174
176
  items: [],
175
177
  }
176
178
  const targetUid = String(config.targetUid || '').trim()
@@ -189,12 +191,16 @@ async function scanOwnComments(ctx, config, state, log, force = false, options =
189
191
  } else {
190
192
  return { ...result, errors: 1, error: '请先扫码登录微博账号' }
191
193
  }
192
- const maxPosts = Math.max(1, Math.min(20, Number(config.maxPostsPerRun) || 5))
194
+ const maxPosts = Math.max(1, Math.min(100, Number(config.maxPostsPerRun) || 5))
193
195
  const maxReplies = Math.max(1, Math.min(8, Number(options.maxReplies ?? config.maxCommentsPerDay) || 1))
194
196
  const ignoreQuota = forceReply
195
197
  let posts = []
196
198
  try {
197
- posts = await fetchTimeline(targetUid, config)
199
+ posts = await fetchTimeline(targetUid, config, {
200
+ maxPosts,
201
+ maxPages: Math.ceil(maxPosts / 10) + 1,
202
+ })
203
+ result.posts = posts.length
198
204
  } catch (error) {
199
205
  return { ...result, errors: 1, error: error?.message || String(error) }
200
206
  }
@@ -209,6 +215,7 @@ async function scanOwnComments(ctx, config, state, log, force = false, options =
209
215
  log(`[foxy-weibo] 读取评论失败 ${post.id}:${error?.message || error}`)
210
216
  continue
211
217
  }
218
+ result.comments += comments.length
212
219
  for (const comment of comments) {
213
220
  if (targetCid && String(comment.cid) !== targetCid) continue
214
221
  if (canReply && result.replied >= maxReplies) break
package/lib/settings.js CHANGED
@@ -97,7 +97,7 @@ function clampNumber(key, value, fallback) {
97
97
  const n = Number(value)
98
98
  if (!Number.isFinite(n)) return fallback
99
99
  if (key === 'intervalMinutes') return Math.max(5, Math.min(24 * 60, Math.round(n)))
100
- if (key === 'maxPostsPerRun') return Math.max(1, Math.min(20, Math.round(n)))
100
+ if (key === 'maxPostsPerRun') return Math.max(1, Math.min(100, Math.round(n)))
101
101
  if (key === 'maxCommentsPerDay') return Math.max(0, Math.min(10, Math.round(n)))
102
102
  return n
103
103
  }
package/lib/weibo.js CHANGED
@@ -100,11 +100,11 @@ export function normalizePost(raw) {
100
100
  }
101
101
  }
102
102
 
103
- async function fetchWebTimeline(uid, config = {}) {
103
+ async function fetchWebTimeline(uid, config = {}, page = 1) {
104
104
  const target = String(uid || '').trim()
105
105
  const url =
106
106
  `https://weibo.com/ajax/statuses/mymblog?uid=${encodeURIComponent(target)}` +
107
- `&page=1&feature=0`
107
+ `&page=${Math.max(1, page)}&feature=0`
108
108
  const response = await fetch(url, { headers: headersFor(config, false) })
109
109
  if (!response.ok) throw new WeiboError(`读取微博失败:HTTP ${response.status}`, 'HTTP')
110
110
  const text = await response.text()
@@ -122,39 +122,70 @@ async function fetchWebTimeline(uid, config = {}) {
122
122
  return list.map(normalizePost).filter((post) => post.id && post.text)
123
123
  }
124
124
 
125
- export async function fetchTimeline(uid, config = {}) {
125
+ function uniquePosts(posts) {
126
+ const seen = new Set()
127
+ const out = []
128
+ for (const post of posts) {
129
+ const key = post.id || post.mid
130
+ if (!key || seen.has(key)) continue
131
+ seen.add(key)
132
+ out.push(post)
133
+ }
134
+ return out
135
+ }
136
+
137
+ export async function fetchTimeline(uid, config = {}, options = {}) {
126
138
  const target = String(uid || '').trim()
127
139
  if (!/^\d{5,20}$/.test(target)) throw new WeiboError('微博 UID 格式不正确', 'BAD_UID')
128
- const url =
129
- `https://m.weibo.cn/api/container/getIndex?type=uid&value=${encodeURIComponent(target)}` +
130
- `&containerid=107603${encodeURIComponent(target)}&page=1`
131
- try {
132
- const response = await fetch(url, { headers: headersFor(config, true) })
133
- if (!response.ok) throw new WeiboError(`读取微博失败:HTTP ${response.status}`, 'HTTP')
134
- const json = await readJson(response)
135
- const cards = Array.isArray(json?.data?.cards) ? json.data.cards : []
136
- return cards
137
- .filter((card) => card?.mblog)
138
- .map((card) => normalizePost(card.mblog))
139
- .filter((post) => post.id && post.text)
140
- } catch (error) {
141
- if (config.cookie) {
142
- try {
143
- return await fetchWebTimeline(target, config)
144
- } catch (webError) {
145
- throw new WeiboError(
146
- `${error.message};备用接口也失败:${webError.message}`,
147
- 'FETCH_FAILED',
148
- webError.detail,
149
- )
140
+ const maxPosts = Math.max(1, Math.min(100, Number(options.maxPosts) || 20))
141
+ const maxPages = Math.max(1, Math.min(20, Number(options.maxPages) || Math.ceil(maxPosts / 10) + 1))
142
+ const collected = []
143
+
144
+ for (let page = 1; page <= maxPages && collected.length < maxPosts; page += 1) {
145
+ const url =
146
+ `https://m.weibo.cn/api/container/getIndex?type=uid&value=${encodeURIComponent(target)}` +
147
+ `&containerid=107603${encodeURIComponent(target)}&page=${page}`
148
+ try {
149
+ const response = await fetch(url, { headers: headersFor(config, true) })
150
+ if (!response.ok) throw new WeiboError(`读取微博失败:HTTP ${response.status}`, 'HTTP')
151
+ const json = await readJson(response)
152
+ const cards = Array.isArray(json?.data?.cards) ? json.data.cards : []
153
+ const posts = uniquePosts(
154
+ cards
155
+ .filter((card) => card?.mblog)
156
+ .map((card) => normalizePost(card.mblog))
157
+ .filter((post) => post.id && post.text),
158
+ )
159
+ if (!posts.length) break
160
+ collected.push(...posts)
161
+ if (posts.length < 10) break
162
+ } catch (error) {
163
+ if (config.cookie) {
164
+ try {
165
+ const posts = uniquePosts(await fetchWebTimeline(target, config, page))
166
+ if (!posts.length) break
167
+ collected.push(...posts)
168
+ if (posts.length < 10) break
169
+ continue
170
+ } catch (webError) {
171
+ if (collected.length) break
172
+ throw new WeiboError(
173
+ `${error.message};备用接口也失败:${webError.message}`,
174
+ 'FETCH_FAILED',
175
+ webError.detail,
176
+ )
177
+ }
150
178
  }
179
+ if (collected.length) break
180
+ throw new WeiboError(
181
+ `${error.message};微博接口可能要求登录 Cookie(设置 → Foxy-weibo → 微博 Cookie)`,
182
+ 'LOGIN_REQUIRED',
183
+ error.detail,
184
+ )
151
185
  }
152
- throw new WeiboError(
153
- `${error.message};微博接口可能要求登录 Cookie(设置 → Foxy-weibo → 微博 Cookie)`,
154
- 'LOGIN_REQUIRED',
155
- error.detail,
156
- )
157
186
  }
187
+
188
+ return uniquePosts(collected).slice(0, maxPosts)
158
189
  }
159
190
 
160
191
  function normalizeComment(raw) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-foxy-weibo",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
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",
@@ -49,3 +49,4 @@
49
49
  }
50
50
  }
51
51
 
52
+