Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ npm install -g @jackwener/opencli@latest
| **bilibili** | `hot` `search` `me` `favorite` `history` `feed` `subtitle` `dynamic` `ranking` `following` `user-videos` `download` | 浏览器 |
| **codex** | `status` `send` `read` `new` `dump` `extract-diff` `model` `ask` `screenshot` `history` `export` | 桌面端 |
| **chatwise** | `status` `new` `send` `read` `ask` `model` `history` `export` `screenshot` | 桌面端 |
| **doubao** | `status` `new` `send` `read` `ask` | 浏览器 |
| **doubao** | `status` `new` `send` `read` `ask` `history` `detail` `meeting-summary` `meeting-transcript` | 浏览器 |
| **doubao-app** | `status` `new` `send` `read` `ask` `screenshot` `dump` | 桌面端 |
| **notion** | `status` `search` `read` `new` `write` `sidebar` `favorites` `export` | 桌面端 |
| **discord-app** | `status` `send` `read` `channels` `servers` `search` `members` | 桌面端 |
Expand Down
2 changes: 1 addition & 1 deletion docs/adapters/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Run `opencli list` for the live registry.
| **[linux-do](/adapters/browser/linux-do)** | `feed` `categories` `tags` `search` `topic` `user-topics` `user-posts` | 🔐 Browser |
| **[chaoxing](/adapters/browser/chaoxing)** | `assignments` `exams` | 🔐 Browser |
| **[grok](/adapters/browser/grok)** | `ask` | 🔐 Browser |
| **[doubao](/adapters/browser/doubao)** | `status` `new` `send` `read` `ask` | 🔐 Browser |
| **[doubao](/adapters/browser/doubao)** | `status` `new` `send` `read` `ask` `history` `detail` `meeting-summary` `meeting-transcript` | 🔐 Browser |
| **[weread](/adapters/browser/weread)** | `shelf` `search` `book` `ranking` `notebooks` `highlights` `notes` | 🔐 Browser |
| **[douban](/adapters/browser/douban)** | `search` `top250` `subject` `photos` `download` `marks` `reviews` `movie-hot` `book-hot` | 🔐 Browser |
| **[facebook](/adapters/browser/facebook)** | `feed` `profile` `search` `friends` `groups` `events` `notifications` `memories` `add-friend` `join-group` | 🔐 Browser |
Expand Down
43 changes: 43 additions & 0 deletions src/clis/doubao/detail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { cli, Strategy } from '../../registry.js';
import type { IPage } from '../../types.js';
import { DOUBAO_DOMAIN, getConversationDetail } from './utils.js';

export const detailCommand = cli({
site: 'doubao',
name: 'detail',
description: 'Read a specific Doubao conversation by ID',
domain: DOUBAO_DOMAIN,
strategy: Strategy.COOKIE,
browser: true,
navigateBefore: false,
args: [
{ name: 'id', required: true, positional: true, help: 'Conversation ID (numeric or full URL)' },
],
columns: ['Role', 'Text'],
func: async (page: IPage, kwargs: Record<string, unknown>) => {
const raw = kwargs.id as string;
const match = raw.match(/(\d{10,})/);
const conversationId = match ? match[1] : raw;

const { messages, meeting } = await getConversationDetail(page, conversationId);

if (messages.length === 0) {
return [{ Role: 'System', Text: 'No messages found. Verify the conversation ID.' }];
}

const result: Array<{ Role: string; Text: string }> = [];

if (meeting) {
result.push({
Role: 'Meeting',
Text: `${meeting.title}${meeting.time ? ` (${meeting.time})` : ''}`,
});
}

for (const m of messages) {
result.push({ Role: m.Role, Text: m.Text });
}

return result;
},
});
31 changes: 31 additions & 0 deletions src/clis/doubao/history.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { cli, Strategy } from '../../registry.js';
import type { IPage } from '../../types.js';
import { DOUBAO_DOMAIN, getDoubaoConversationList } from './utils.js';

export const historyCommand = cli({
site: 'doubao',
name: 'history',
description: 'List conversation history from Doubao sidebar',
domain: DOUBAO_DOMAIN,
strategy: Strategy.COOKIE,
browser: true,
navigateBefore: false,
args: [
{ name: 'limit', required: false, help: 'Max number of conversations to show', default: '50' },
],
columns: ['Index', 'Title', 'Url'],
func: async (page: IPage, kwargs: Record<string, unknown>) => {
const limit = parseInt(kwargs.limit as string, 10) || 50;
const conversations = await getDoubaoConversationList(page);

if (conversations.length === 0) {
return [{ Index: 0, Title: 'No conversation history found. Make sure you are logged in.', Url: '' }];
}

return conversations.slice(0, limit).map((conv, i) => ({
Index: i + 1,
Title: conv.Title,
Url: conv.Url,
}));
},
});
54 changes: 54 additions & 0 deletions src/clis/doubao/meeting-summary.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { cli, Strategy } from '../../registry.js';
import type { IPage } from '../../types.js';
import {
DOUBAO_DOMAIN,
openMeetingPanel,
getMeetingSummary,
getMeetingChapters,
} from './utils.js';

export const meetingSummaryCommand = cli({
site: 'doubao',
name: 'meeting-summary',
description: 'Get meeting summary and chapters from a Doubao conversation',
domain: DOUBAO_DOMAIN,
strategy: Strategy.COOKIE,
browser: true,
navigateBefore: false,
args: [
{ name: 'id', required: true, positional: true, help: 'Conversation ID (numeric or full URL)' },
{ name: 'chapters', required: false, help: 'Also include AI chapters', default: 'false' },
],
columns: ['Section', 'Content'],
func: async (page: IPage, kwargs: Record<string, unknown>) => {
const raw = kwargs.id as string;
const match = raw.match(/(\d{10,})/);
const conversationId = match ? match[1] : raw;
const includeChapters = kwargs.chapters === 'true' || kwargs.chapters === true;

const opened = await openMeetingPanel(page, conversationId);
if (!opened) {
return [{ Section: 'Error', Content: 'No meeting card found in this conversation.' }];
}

const summary = await getMeetingSummary(page);
const result: Array<{ Section: string; Content: string }> = [];

if (summary) {
result.push({ Section: 'Summary', Content: summary });
}

if (includeChapters) {
const chapters = await getMeetingChapters(page);
if (chapters) {
result.push({ Section: 'Chapters', Content: chapters });
}
}

if (result.length === 0) {
return [{ Section: 'Info', Content: 'Meeting panel opened but no content found yet. Try again.' }];
}

return result;
},
});
49 changes: 49 additions & 0 deletions src/clis/doubao/meeting-transcript.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { cli, Strategy } from '../../registry.js';
import type { IPage } from '../../types.js';
import {
DOUBAO_DOMAIN,
openMeetingPanel,
getMeetingTranscript,
triggerTranscriptDownload,
} from './utils.js';

export const meetingTranscriptCommand = cli({
site: 'doubao',
name: 'meeting-transcript',
description: 'Get or download the meeting transcript from a Doubao conversation',
domain: DOUBAO_DOMAIN,
strategy: Strategy.COOKIE,
browser: true,
navigateBefore: false,
args: [
{ name: 'id', required: true, positional: true, help: 'Conversation ID (numeric or full URL)' },
{ name: 'download', required: false, help: 'Trigger browser file download instead of reading text', default: 'false' },
],
columns: ['Section', 'Content'],
func: async (page: IPage, kwargs: Record<string, unknown>) => {
const raw = kwargs.id as string;
const match = raw.match(/(\d{10,})/);
const conversationId = match ? match[1] : raw;
const shouldDownload = kwargs.download === 'true' || kwargs.download === true;

const opened = await openMeetingPanel(page, conversationId);
if (!opened) {
return [{ Section: 'Error', Content: 'No meeting card found in this conversation.' }];
}

if (shouldDownload) {
const ok = await triggerTranscriptDownload(page);
if (!ok) {
return [{ Section: 'Error', Content: 'Failed to trigger transcript download.' }];
}
return [{ Section: 'Download', Content: 'Transcript download triggered in browser. Check your Downloads folder.' }];
}

const transcript = await getMeetingTranscript(page);
if (!transcript) {
return [{ Section: 'Info', Content: 'No transcript content found. The meeting may not have a text record.' }];
}

return [{ Section: 'Transcript', Content: transcript }];
},
});
Loading