dsh-douyin-mac 0.1.10 → 0.1.11
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/CHANGELOG.md +5 -0
- package/chat-browser.mjs +8 -0
- package/client.js +1 -0
- package/core.mjs +13 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
package/chat-browser.mjs
CHANGED
|
@@ -974,6 +974,14 @@ export class ChatBrowser {
|
|
|
974
974
|
listContacts({ signal, verifyFollowing = this.following === null, backgroundOnly = false } = {}) {
|
|
975
975
|
return this.withPage(async page => {
|
|
976
976
|
if (backgroundOnly && await this.isForeground(page)) throw Object.assign(new Error('专用浏览器在前台,暂不切换私信页。'), { code: 'CHAT_FOREGROUND' });
|
|
977
|
+
// 登录后的“是否保存登录信息”弹层会挡住消息列表,先取消它。
|
|
978
|
+
await page.evaluate(() => {
|
|
979
|
+
if (!/是否保存登录信息/.test(document.body.innerText || '')) return false;
|
|
980
|
+
const usable = element => { const box = element.getBoundingClientRect(), style = getComputedStyle(element); return box.width > 0 && box.height > 0 && style.display !== 'none' && style.visibility !== 'hidden'; };
|
|
981
|
+
const cancel = [...document.querySelectorAll('button,div,span')].find(element => usable(element) && (element.innerText || '').trim() === '取消');
|
|
982
|
+
if (!cancel) return false;
|
|
983
|
+
cancel.click(); return true;
|
|
984
|
+
}).catch(() => false);
|
|
977
985
|
const initial = await this.dialog(page, signal, { open: !backgroundOnly });
|
|
978
986
|
if (initial.blocked) return { ...initial, contacts: [] };
|
|
979
987
|
if (verifyFollowing && !backgroundOnly) {
|
package/client.js
CHANGED
|
@@ -232,6 +232,7 @@ window.__ModuleLoader__.load({
|
|
|
232
232
|
state?.interactions?.friend?.waitReason && h('div', { style: row }, h(Button, { type: 'button', size: 'sm', variant: 'outline', disabled: !ready || busy.has('chatPrepare') || state.interactions.friend.canPrepare === false, onClick: () => { void perform('chatPrepare', () => api.request('/chat/prepare', {})); } }, busy.has('chatPrepare') || state.interactions.friend.preparing ? '正在准备…' : '准备回复通道/重新连接并重试')),
|
|
233
233
|
button('读取联系人与群聊', 'chatList', () => { void perform('chatList', async () => { const result = await api.request('/chat/contacts', {}); if (result.blocked || result.loginRequired || result.verificationRequired || !Array.isArray(result.contacts)) throw new Error('请在专用浏览器完成登录或验证。'); setContacts(result.contacts); setDiscovery(result.listDiscovery || null); return result; }); }, chat.status.kind === 'saving'),
|
|
234
234
|
note('读取范围是抖音网页版当前会话,不代表全部关注好友或所有群。要授权群聊,请先让群会话出现在抖音消息列表,再刷新。'), contacts && note(`本次读取:个人 ${contacts.filter(item => item.isGroup === false).length} 个 · 群聊 ${contacts.filter(item => item.isGroup === true).length} 个。${discovery?.scannedToEnd === false ? ` 当前列表尚未完整读取。${discovery.reason || ''}` : ''}`),
|
|
235
|
+
contacts && contacts.length === 0 && note('抖音消息列表目前是空的(页面显示“暂无会话”),所以没有可授权的个人会话或群聊;先在抖音网页版打开“消息”,或让好友先发一条消息,再点“读取联系人与群聊”。'),
|
|
235
236
|
contacts?.some(item => item.relationshipStatus === 'unavailable' || item.isGroup === true || typeof item.isGroup !== 'boolean') && toggle('授权已核对的现有会话', chat.draft.allowConversationOnly, value => { existingAuthorization.current = value; chat.edit({ allowConversationOnly: value, ...(!value ? { recipientIds: chat.draft.recipientIds.filter(id => shownContacts.some(item => item.id === id && item.followed === true && item.isGroup === false)) } : {}) }, value ? { blurOnly: true } : { immediate: true }); }),
|
|
236
237
|
...[false, true, null].flatMap(kind => { const items = shownContacts.filter(item => item.isGroup === kind || kind === null && typeof item.isGroup !== 'boolean'); return items.length ? [note(kind === false ? '个人会话' : kind === true ? '群聊(仅被 @ 或叫到响应昵称时回复)' : '旧记录类型待核对(暂不可选)'), ...items.map(item => h('label', { key: item.id, style: row }, h('input', { type: 'checkbox', 'aria-label': `许可联系人 ${item.name || item.displayName || '联系人'}`, checked: chat.draft.recipientIds.includes(item.id), disabled: !ready || !chat.draft.recipientIds.includes(item.id) && (item.notListed || !(item.followed === true && item.isGroup === false || chat.draft.allowConversationOnly === true && typeof item.isGroup === 'boolean')), onChange: event => chat.edit({ recipientIds: event.target.checked ? [...chat.draft.recipientIds, item.id] : chat.draft.recipientIds.filter(id => id !== item.id) }, { immediate: true }) }), item.name || item.displayName || '许可会话'))] : []; }),
|
|
237
238
|
area('群聊响应昵称(逗号或换行分隔)', chat, 'groupMentionNamesText', 2, true), status(chat), note(`今日回复模型请求 ${state?.chat?.daily?.requests || 0} 次 · 今日分享 ${state?.share?.daily?.attempts || 0} 次。未确认发送会暂停,不自动重发。`), ...(state?.chat?.contacts || []).filter(item => item.identityReviewRequired === true || item.recovery?.requiresUserAction === true || /登录|验证码|未授权|许可.*(?:不足|失效|撤销)|身份.*(?:变化|不一致|未通过|不明|不足)/.test(item.reason || '') || ['paused', 'delivery-uncertain'].includes(item.status) && !(item.recovery?.autoPendingCount > 0 || item.recovery?.autoRetryCount > 0)).map(item => h('div', { key: item.id }, note(item.reason || '该会话暂缓处理,状态会继续更新。'), item.identityReviewRequired === true && note('请重新读取联系人并确认许可名单;身份未通过前不会发送。'))), ...(state?.share?.unavailableRecipients || []).map(item => note(`暂不能自主分享:${item.name || '许可会话'} · ${item.reason}`)), ...reviewList('share'), h('details', null, h('summary', null, '回复处理记录'), h('pre', { style: { whiteSpace: 'pre-wrap' } }, JSON.stringify({ recovery: state?.chat?.recovery, contacts: (state?.chat?.contacts || []).map(item => ({ id: item.id, status: item.status, reason: item.reason, recovery: item.recovery, pending: Boolean(item.pending), unanswered: item.unanswered?.length || 0 })), events: state?.chat?.events || [] }, null, 2))), h('details', null, h('summary', null, '最近分享记录'), ...(state?.share?.recent || []).slice().reverse().map(item => h('div', { key: item.operationId }, note(`作品 ${item.videoId} · ${state?.share?.inProgress && state.share.activity?.operationId === item.operationId ? '正在分享' : ({ confirmed: '已确认送达', 'not-sent': '未发出', uncertain: '送达未确认', reserved: '已预留,待核对', clicking: '处理中,待核对', 'resolved-confirmed': '人工已确认送达', 'resolved-not-sent': '人工已确认未送达' }[item.status] || item.status)}`), shareFailureMessage(item) && note(`原因:${shareFailureMessage(item)}`))))),
|
package/core.mjs
CHANGED
|
@@ -586,7 +586,20 @@ export class BrowserController {
|
|
|
586
586
|
: loggedIn === false ? '未检测到抖音登录态,点“打开登录页”扫码登录。' : '无法确认登录态;可点“打开登录页”刷新后重试。';
|
|
587
587
|
return { connected: true, pageOpen: true, loggedIn, pageShowsLogin, loginRequired, verificationRequired, url: page.url(), title: state.title || '', note, at: Date.now() };
|
|
588
588
|
}
|
|
589
|
+
// 登录成功后抖音会弹“是否保存登录信息”,会挡住消息面板;这里选择“取消”避免保存密码。
|
|
590
|
+
async _dismissLoginPrompt(page) {
|
|
591
|
+
try {
|
|
592
|
+
return await page.evaluate(() => {
|
|
593
|
+
if (!/是否保存登录信息/.test(document.body.innerText || '')) return false;
|
|
594
|
+
const usable = element => { const box = element.getBoundingClientRect(), style = getComputedStyle(element); return box.width > 0 && box.height > 0 && style.display !== 'none' && style.visibility !== 'hidden'; };
|
|
595
|
+
const cancel = [...document.querySelectorAll('button,div,span')].find(element => usable(element) && (element.innerText || '').trim() === '取消');
|
|
596
|
+
if (!cancel) return false;
|
|
597
|
+
cancel.click(); return true;
|
|
598
|
+
}).catch(() => false);
|
|
599
|
+
} catch { return false; }
|
|
600
|
+
}
|
|
589
601
|
async _bringPageToFront(page) {
|
|
602
|
+
await this._dismissLoginPrompt(page);
|
|
590
603
|
try {
|
|
591
604
|
const session = await page.context().newCDPSession(page);
|
|
592
605
|
try {
|