multi-publisher 1.0.0

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.
Files changed (100) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +370 -0
  3. package/dist/adapters/baijiahao.d.ts +14 -0
  4. package/dist/adapters/baijiahao.js +77 -0
  5. package/dist/adapters/base-adapter.d.ts +41 -0
  6. package/dist/adapters/base-adapter.js +84 -0
  7. package/dist/adapters/bilibili.d.ts +14 -0
  8. package/dist/adapters/bilibili.js +86 -0
  9. package/dist/adapters/cnblogs.d.ts +14 -0
  10. package/dist/adapters/cnblogs.js +90 -0
  11. package/dist/adapters/csdn.d.ts +29 -0
  12. package/dist/adapters/csdn.js +174 -0
  13. package/dist/adapters/cto51.d.ts +14 -0
  14. package/dist/adapters/cto51.js +72 -0
  15. package/dist/adapters/douban.d.ts +14 -0
  16. package/dist/adapters/douban.js +49 -0
  17. package/dist/adapters/eastmoney.d.ts +14 -0
  18. package/dist/adapters/eastmoney.js +47 -0
  19. package/dist/adapters/imooc.d.ts +14 -0
  20. package/dist/adapters/imooc.js +78 -0
  21. package/dist/adapters/index.d.ts +26 -0
  22. package/dist/adapters/index.js +22 -0
  23. package/dist/adapters/interface.d.ts +44 -0
  24. package/dist/adapters/interface.js +1 -0
  25. package/dist/adapters/juejin.d.ts +23 -0
  26. package/dist/adapters/juejin.js +166 -0
  27. package/dist/adapters/oschina.d.ts +14 -0
  28. package/dist/adapters/oschina.js +77 -0
  29. package/dist/adapters/registry.d.ts +61 -0
  30. package/dist/adapters/registry.js +146 -0
  31. package/dist/adapters/segmentfault.d.ts +14 -0
  32. package/dist/adapters/segmentfault.js +73 -0
  33. package/dist/adapters/sohu.d.ts +14 -0
  34. package/dist/adapters/sohu.js +72 -0
  35. package/dist/adapters/toutiao.d.ts +9 -0
  36. package/dist/adapters/toutiao.js +199 -0
  37. package/dist/adapters/wechat-publisher.d.ts +55 -0
  38. package/dist/adapters/wechat-publisher.js +210 -0
  39. package/dist/adapters/weibo.d.ts +14 -0
  40. package/dist/adapters/weibo.js +99 -0
  41. package/dist/adapters/weixin.d.ts +14 -0
  42. package/dist/adapters/weixin.js +63 -0
  43. package/dist/adapters/woshipm.d.ts +14 -0
  44. package/dist/adapters/woshipm.js +72 -0
  45. package/dist/adapters/xiaohongshu.d.ts +9 -0
  46. package/dist/adapters/xiaohongshu.js +199 -0
  47. package/dist/adapters/xueqiu.d.ts +14 -0
  48. package/dist/adapters/xueqiu.js +74 -0
  49. package/dist/adapters/yuque.d.ts +13 -0
  50. package/dist/adapters/yuque.js +25 -0
  51. package/dist/adapters/zhihu.d.ts +23 -0
  52. package/dist/adapters/zhihu.js +142 -0
  53. package/dist/cli/cookie.d.ts +5 -0
  54. package/dist/cli/cookie.js +80 -0
  55. package/dist/cli/credential.d.ts +6 -0
  56. package/dist/cli/credential.js +32 -0
  57. package/dist/cli/index.d.ts +5 -0
  58. package/dist/cli/index.js +72 -0
  59. package/dist/cli/login.d.ts +3 -0
  60. package/dist/cli/login.js +101 -0
  61. package/dist/cli/platforms.d.ts +1 -0
  62. package/dist/cli/platforms.js +35 -0
  63. package/dist/cli/publish-all.d.ts +6 -0
  64. package/dist/cli/publish-all.js +101 -0
  65. package/dist/cli/publish.d.ts +7 -0
  66. package/dist/cli/publish.js +78 -0
  67. package/dist/cli/render.d.ts +9 -0
  68. package/dist/cli/render.js +40 -0
  69. package/dist/commands/login.d.ts +43 -0
  70. package/dist/commands/login.js +248 -0
  71. package/dist/commands/publish.d.ts +19 -0
  72. package/dist/commands/publish.js +275 -0
  73. package/dist/config.d.ts +177 -0
  74. package/dist/config.js +333 -0
  75. package/dist/core/mathjax.d.ts +4 -0
  76. package/dist/core/mathjax.js +33 -0
  77. package/dist/core/parser.d.ts +17 -0
  78. package/dist/core/parser.js +42 -0
  79. package/dist/core/renderer.d.ts +18 -0
  80. package/dist/core/renderer.js +30 -0
  81. package/dist/core/styler.d.ts +8 -0
  82. package/dist/core/styler.js +11 -0
  83. package/dist/core/theme.d.ts +9 -0
  84. package/dist/core/theme.js +420 -0
  85. package/dist/index.d.ts +8 -0
  86. package/dist/index.js +7 -0
  87. package/dist/runtime/browser-runtime.d.ts +79 -0
  88. package/dist/runtime/browser-runtime.js +501 -0
  89. package/dist/runtime/index.d.ts +12 -0
  90. package/dist/runtime/index.js +1 -0
  91. package/dist/runtime/node-runtime.d.ts +5 -0
  92. package/dist/runtime/node-runtime.js +25 -0
  93. package/dist/tools/xiaohongshu-sniffer.d.ts +1 -0
  94. package/dist/tools/xiaohongshu-sniffer.js +54 -0
  95. package/package.json +62 -0
  96. package/themes/all-themes-preview.html +522 -0
  97. package/themes/previews/cyberpunk.png +0 -0
  98. package/themes/previews/minimal.png +0 -0
  99. package/themes/previews/nord.png +0 -0
  100. package/themes/previews/wechat.png +0 -0
@@ -0,0 +1,142 @@
1
+ import { ConfigStore } from '../config.js';
2
+ export class ZhihuAdapter {
3
+ meta = {
4
+ id: 'zhihu',
5
+ name: '知乎',
6
+ icon: 'https://static.zhihu.com/favicon.ico',
7
+ homepage: 'https://www.zhihu.com',
8
+ capabilities: ['article', 'draft', 'image_upload'],
9
+ };
10
+ runtime;
11
+ cookieData = null;
12
+ async init(runtime) {
13
+ this.runtime = runtime;
14
+ this.cookieData = await this.loadCookie();
15
+ }
16
+ getCookiePath() {
17
+ return ConfigStore.getDir() + '/cookies/zhihu.json';
18
+ }
19
+ async loadCookie() {
20
+ const cookies = await ConfigStore.getZhihuCookies();
21
+ if (!cookies)
22
+ return null;
23
+ return { cookies };
24
+ }
25
+ buildCookieString(cookieData) {
26
+ if (!cookieData.cookies)
27
+ return '';
28
+ return Object.entries(cookieData.cookies)
29
+ .map(([k, v]) => `${k}=${v}`)
30
+ .join('; ');
31
+ }
32
+ async checkAuth() {
33
+ if (!this.cookieData?.cookies) {
34
+ return { isAuthenticated: false, error: '未配置知乎 Cookie,请运行: mpub cookie --platform zhihu --set' };
35
+ }
36
+ try {
37
+ const res = await this.runtime.fetch('https://www.zhihu.com/api/v4/me', {
38
+ headers: {
39
+ 'Cookie': this.buildCookieString(this.cookieData),
40
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
41
+ 'X-Requested-With': 'fetch',
42
+ },
43
+ });
44
+ if (!res.ok) {
45
+ return { isAuthenticated: false, error: `HTTP ${res.status}` };
46
+ }
47
+ const data = await res.json();
48
+ if (data.id) {
49
+ return { isAuthenticated: true, userId: data.id, username: data.name, avatar: data.avatar_url };
50
+ }
51
+ return { isAuthenticated: false, error: '未获取到用户信息' };
52
+ }
53
+ catch (err) {
54
+ return { isAuthenticated: false, error: err.message };
55
+ }
56
+ }
57
+ async publish(article) {
58
+ const start = Date.now();
59
+ try {
60
+ if (!this.cookieData?.cookies) {
61
+ throw new Error('未配置知乎 Cookie,请运行: mpub cookie --platform zhihu --set');
62
+ }
63
+ const cookieStr = this.buildCookieString(this.cookieData);
64
+ // 1. 创建草稿
65
+ const createRes = await this.runtime.fetch('https://zhuanlan.zhihu.com/api/articles/drafts', {
66
+ method: 'POST',
67
+ headers: {
68
+ 'Content-Type': 'application/json',
69
+ 'Cookie': cookieStr,
70
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
71
+ 'X-Requested-With': 'fetch',
72
+ },
73
+ body: JSON.stringify({
74
+ title: article.title,
75
+ content: '',
76
+ delta_time: 0,
77
+ }),
78
+ });
79
+ if (!createRes.ok) {
80
+ const errBody = await createRes.text();
81
+ throw new Error(`创建草稿失败 (${createRes.status}): ${errBody}`);
82
+ }
83
+ const createData = await createRes.json();
84
+ if (!createData.id) {
85
+ throw new Error('创建草稿失败: 无效响应');
86
+ }
87
+ const draftId = createData.id;
88
+ // 2. 处理内容 - 使用 HTML 格式
89
+ let content = article.html || '';
90
+ // 3. 知乎特殊处理:图片格式、代码块等
91
+ content = this.transformContent(content);
92
+ // 4. 更新草稿内容
93
+ const updateRes = await this.runtime.fetch(`https://zhuanlan.zhihu.com/api/articles/${draftId}/draft`, {
94
+ method: 'PATCH',
95
+ headers: {
96
+ 'Content-Type': 'application/json',
97
+ 'Cookie': cookieStr,
98
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
99
+ 'X-Requested-With': 'fetch',
100
+ },
101
+ body: JSON.stringify({
102
+ title: article.title,
103
+ content: content,
104
+ }),
105
+ });
106
+ if (!updateRes.ok) {
107
+ throw new Error(`更新草稿失败: ${updateRes.status}`);
108
+ }
109
+ const draftUrl = `https://zhuanlan.zhihu.com/p/${draftId}/edit`;
110
+ return {
111
+ platform: this.meta.id,
112
+ success: true,
113
+ postId: draftId,
114
+ postUrl: draftUrl,
115
+ draftOnly: true,
116
+ timestamp: Date.now() - start,
117
+ };
118
+ }
119
+ catch (err) {
120
+ return {
121
+ platform: this.meta.id,
122
+ success: false,
123
+ error: err.message,
124
+ timestamp: Date.now() - start,
125
+ };
126
+ }
127
+ }
128
+ /**
129
+ * 知乎内容转换
130
+ */
131
+ transformContent(content) {
132
+ let result = content;
133
+ // 图片格式 - 知乎需要 figure 包裹
134
+ result = result.replace(/<img([^>]+)src="([^"]+)"([^>]*)>/gi, '<figure><img$1src="$2"$3></figure>');
135
+ // 代码块格式
136
+ result = result.replace(/<pre><code class="language-(\w+)">/gi, '<pre lang="$1"><code>');
137
+ // 移除微信样式属性
138
+ result = result.replace(/\s*data-(?!draft)[a-z-]+="[^"]*"/gi, '');
139
+ result = result.replace(/\s*style="[^"]*"/gi, '');
140
+ return result;
141
+ }
142
+ }
@@ -0,0 +1,5 @@
1
+ export declare function runCookie(options: {
2
+ platform?: string;
3
+ set?: boolean;
4
+ check?: boolean;
5
+ }): Promise<void>;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * cookie 命令 - 管理各平台 Cookie
3
+ */
4
+ import input from '@inquirer/input';
5
+ import { ConfigStore } from '../config.js';
6
+ export async function runCookie(options) {
7
+ const platform = options.platform || 'zhihu';
8
+ try {
9
+ if (options.check) {
10
+ const cookies = await getCookies(platform);
11
+ if (cookies && Object.keys(cookies).length > 0) {
12
+ console.log(`✅ ${platform} Cookie 已配置 (${Object.keys(cookies).length} 条)`);
13
+ }
14
+ else {
15
+ console.log(`⚠️ ${platform} 未配置 Cookie`);
16
+ }
17
+ return;
18
+ }
19
+ if (options.set) {
20
+ const cookieStr = await input({
21
+ message: `请输入 ${platform} 的完整 Cookie (从浏览器开发者工具获取):`,
22
+ });
23
+ if (!cookieStr.trim()) {
24
+ console.error('Cookie 不能为空');
25
+ process.exit(1);
26
+ }
27
+ // 解析 Cookie 字符串为 key-value 对象
28
+ const cookies = {};
29
+ cookieStr.split(';').forEach((part) => {
30
+ const eqIdx = part.indexOf('=');
31
+ if (eqIdx < 0)
32
+ return;
33
+ const key = part.slice(0, eqIdx).trim();
34
+ const valueParts = [part.slice(eqIdx + 1)];
35
+ // 有些 Cookie 值可能包含 =,所以合并回来
36
+ cookies[key] = valueParts.join('=').trim();
37
+ });
38
+ await setCookies(platform, cookies);
39
+ console.log(`✅ ${platform} Cookie 已保存到 config.json!`);
40
+ return;
41
+ }
42
+ // 无参数显示帮助
43
+ console.log(`用法:`);
44
+ console.log(` mpub cookie --platform ${platform} --set 设置 Cookie`);
45
+ console.log(` mpub cookie --platform ${platform} --check 验证 Cookie`);
46
+ }
47
+ catch (err) {
48
+ console.error('[cookie]', err.message);
49
+ process.exit(1);
50
+ }
51
+ }
52
+ async function getCookies(platform) {
53
+ switch (platform) {
54
+ case 'zhihu':
55
+ return ConfigStore.getZhihuCookies();
56
+ case 'juejin':
57
+ return ConfigStore.getJuejinCookies();
58
+ case 'csdn':
59
+ return ConfigStore.getCSDNCookies();
60
+ case 'toutiao':
61
+ return ConfigStore.getToutiaoCookies();
62
+ default:
63
+ return null;
64
+ }
65
+ }
66
+ async function setCookies(platform, cookies) {
67
+ switch (platform) {
68
+ case 'zhihu':
69
+ return ConfigStore.setZhihuCookies(cookies);
70
+ case 'juejin':
71
+ return ConfigStore.setJuejinCookies(cookies);
72
+ case 'csdn':
73
+ return ConfigStore.setCSDNCookies(cookies);
74
+ case 'toutiao':
75
+ return ConfigStore.setToutiaoCookies(cookies);
76
+ default:
77
+ console.error(`暂不支持平台: ${platform}`);
78
+ process.exit(1);
79
+ }
80
+ }
@@ -0,0 +1,6 @@
1
+ export declare function runCredential(options: {
2
+ location?: boolean;
3
+ set?: boolean;
4
+ appId?: string;
5
+ appSecret?: string;
6
+ }): Promise<void>;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * credential 命令 - 管理微信公众号凭据
3
+ */
4
+ import { ConfigStore } from '../config.js';
5
+ export async function runCredential(options) {
6
+ try {
7
+ if (options.location) {
8
+ console.log(ConfigStore.getDir() + '/config.json');
9
+ return;
10
+ }
11
+ if (options.set || options.appId) {
12
+ const appId = options.appId ?? '';
13
+ const appSecret = options.appSecret ?? '';
14
+ if (!appId || !appSecret) {
15
+ console.error('请提供 --app-id 和 --app-secret');
16
+ process.exit(1);
17
+ }
18
+ await ConfigStore.setWeixin({ appId: appId.trim(), appSecret: appSecret.trim() });
19
+ console.log('✅ 微信公众号凭据已保存到 config.json!');
20
+ return;
21
+ }
22
+ // 无参数时显示帮助
23
+ console.log('用法:');
24
+ console.log(' mpub credential --location 显示配置文件路径');
25
+ console.log(' mpub credential --set 交互式设置凭据');
26
+ console.log(' mpub credential --app-id <id> --app-secret <secret> 直接设置凭据');
27
+ }
28
+ catch (err) {
29
+ console.error('[credential]', err.message);
30
+ process.exit(1);
31
+ }
32
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * CLI 入口
3
+ */
4
+ import { Command } from 'commander';
5
+ export declare function createProgram(): Command;
@@ -0,0 +1,72 @@
1
+ /**
2
+ * CLI 入口
3
+ */
4
+ import { Command } from 'commander';
5
+ import pkg from '../../package.json' with { type: 'json' };
6
+ import { runPublish } from './publish.js';
7
+ import { runRender } from './render.js';
8
+ import { runPlatforms } from './platforms.js';
9
+ import { runCredential } from './credential.js';
10
+ import { runCookie } from './cookie.js';
11
+ import { runLogin } from './login.js';
12
+ import { runPublishAll } from './publish-all.js';
13
+ export function createProgram() {
14
+ const program = new Command();
15
+ program
16
+ .name('mpub')
17
+ .description('Markdown → 多平台(微信公众号/知乎/掘金)CLI 发布工具')
18
+ .version(pkg.version, '-v, --version', '输出版本号');
19
+ // publish 命令
20
+ const publishCmd = program.command('publish')
21
+ .description('渲染 Markdown 并发布到平台')
22
+ .requiredOption('-f, --file <path>', 'Markdown 文件路径(支持本地文件和 URL)')
23
+ .option('-p, --platform <platform>', '目标平台 (weixin|zhihu|juejin|csdn)', 'weixin')
24
+ .option('-t, --theme <theme-id>', '主题 ID', 'default')
25
+ .option('--app-id <appId>', '微信公众号 AppID(可省略,从配置文件读取)')
26
+ .option('--no-mac-style', '禁用 Mac 风格代码块')
27
+ .action(runPublish);
28
+ // render 命令
29
+ const renderCmd = program.command('render')
30
+ .description('渲染 Markdown 到 HTML(输出到 stdout)')
31
+ .option('-f, --file <path>', 'Markdown 文件路径(支持本地文件和 URL)')
32
+ .option('-t, --theme <theme-id>', '主题 ID', 'default')
33
+ .option('-h, --highlight <theme>', '代码高亮主题', 'solarized-light')
34
+ .option('-c, --custom-theme <path>', '自定义 CSS 主题文件路径')
35
+ .option('--mac-style', '启用 Mac 风格代码块', true)
36
+ .option('--no-mac-style', '禁用 Mac 风格代码块')
37
+ .action(runRender);
38
+ // platforms 命令
39
+ program.command('platforms').description('列出支持发布的平台').action(runPlatforms);
40
+ // credential 命令
41
+ const credCmd = program.command('credential')
42
+ .description('管理微信公众号凭据(AppID + AppSecret)')
43
+ .option('-l, --location', '显示凭据存储路径')
44
+ .option('-s, --set', '交互式设置凭据')
45
+ .option('--app-id <appId>', '微信公众号 AppID')
46
+ .option('--app-secret <appSecret>', '微信公众号 AppSecret')
47
+ .action(runCredential);
48
+ // cookie 命令
49
+ const cookieCmd = program.command('cookie')
50
+ .description('设置平台 Cookie(手动方式)')
51
+ .requiredOption('-p, --platform <platform>', '平台名称')
52
+ .option('--set', '交互式设置 Cookie(从浏览器复制)')
53
+ .option('--check', '检查 Cookie 是否有效')
54
+ .action(runCookie);
55
+ // login 命令
56
+ const loginCmd = program.command('login')
57
+ .description('通过浏览器自动登录获取 Cookie')
58
+ .option('-p, --platform <platform>', '平台名称', 'zhihu')
59
+ .action(runLogin);
60
+ // publish-all 命令
61
+ const publishAllCmd = program.command('publish-all')
62
+ .description('一键发布到所有已登录平台')
63
+ .requiredOption('-f, --file <path>', 'Markdown 文件路径(支持本地文件和 URL)')
64
+ .option('-t, --theme <theme-id>', '主题 ID', 'default')
65
+ .option('-h, --highlight <theme>', '代码高亮主题', 'solarized-light')
66
+ .option('--mac-style', '启用 Mac 风格代码块', true)
67
+ .option('--no-mac-style', '禁用 Mac 风格代码块')
68
+ .action(runPublishAll);
69
+ return program;
70
+ }
71
+ const program = createProgram();
72
+ program.parse(process.argv);
@@ -0,0 +1,3 @@
1
+ export declare function runLogin(options: {
2
+ platform?: string;
3
+ }): Promise<void>;
@@ -0,0 +1,101 @@
1
+ import { loginPlatform, PLATFORM_LOGIN_CONFIGS } from '../runtime/browser-runtime.js';
2
+ import { ConfigStore } from '../config.js';
3
+ export async function runLogin(options) {
4
+ const platformId = options.platform || 'zhihu';
5
+ // 检查是否支持该平台
6
+ const config = PLATFORM_LOGIN_CONFIGS[platformId];
7
+ if (!config) {
8
+ console.error(`❌ 不支持的平台: ${platformId}`);
9
+ console.log(`\n支持的平台:`);
10
+ for (const [id, cfg] of Object.entries(PLATFORM_LOGIN_CONFIGS)) {
11
+ console.log(` - ${id}: ${cfg.name}`);
12
+ }
13
+ return;
14
+ }
15
+ console.log(`🚀 开始 ${config.name} 登录流程...\n`);
16
+ try {
17
+ // 执行登录
18
+ const result = await loginPlatform(platformId);
19
+ if (result.success && Object.keys(result.cookies).length > 0) {
20
+ // 保存 Cookie 到配置文件
21
+ await saveCookies(platformId, result.cookies);
22
+ console.log(`✅ ${config.name} Cookie 已保存!`);
23
+ console.log(` 共 ${Object.keys(result.cookies).length} 个 cookie`);
24
+ }
25
+ else {
26
+ console.error(`❌ 登录失败: ${result.error || '未知错误'}`);
27
+ process.exit(1);
28
+ }
29
+ }
30
+ catch (err) {
31
+ console.error(`❌ 登录过程出错:`, err.message);
32
+ process.exit(1);
33
+ }
34
+ }
35
+ async function saveCookies(platformId, cookies) {
36
+ switch (platformId) {
37
+ case 'zhihu':
38
+ await ConfigStore.setZhihuCookies(cookies);
39
+ break;
40
+ case 'juejin':
41
+ await ConfigStore.setJuejinCookies(cookies);
42
+ break;
43
+ case 'csdn':
44
+ await ConfigStore.setCSDNCookies(cookies);
45
+ break;
46
+ case 'jianshu':
47
+ await ConfigStore.setJianshuCookies(cookies);
48
+ break;
49
+ case 'weibo':
50
+ await ConfigStore.setWeiboCookies(cookies);
51
+ break;
52
+ case 'xiaohongshu':
53
+ await ConfigStore.setXiaohongshuCookies(cookies);
54
+ break;
55
+ case 'toutiao':
56
+ await ConfigStore.setToutiaoCookies(cookies);
57
+ break;
58
+ case 'baijiahao':
59
+ await ConfigStore.setBaijiahaoCookies(cookies);
60
+ break;
61
+ case 'bilibili':
62
+ await ConfigStore.setBilibiliCookies(cookies);
63
+ break;
64
+ case 'segmentfault':
65
+ await ConfigStore.setSegmentfaultCookies(cookies);
66
+ break;
67
+ case 'cnblogs':
68
+ await ConfigStore.setCnblogsCookies(cookies);
69
+ break;
70
+ case 'oschina':
71
+ await ConfigStore.setOschinaCookies(cookies);
72
+ break;
73
+ case 'imooc':
74
+ await ConfigStore.setImoocCookies(cookies);
75
+ break;
76
+ case 'xueqiu':
77
+ await ConfigStore.setYueqiuCookies(cookies);
78
+ break;
79
+ case 'woshipm':
80
+ await ConfigStore.setWoshipmCookies(cookies);
81
+ break;
82
+ case 'douban':
83
+ await ConfigStore.setDoubanCookies(cookies);
84
+ break;
85
+ case 'sohu':
86
+ await ConfigStore.setSohuCookies(cookies);
87
+ break;
88
+ case 'eastmoney':
89
+ await ConfigStore.setEastmoneyCookies(cookies);
90
+ break;
91
+ case 'yuque':
92
+ // 语雀使用 OAuth,Cookie 方式不支持
93
+ console.warn(`⚠️ 语雀仅支持 OAuth 登录,请使用 wechatsync 浏览器扩展`);
94
+ break;
95
+ case 'cto51':
96
+ await ConfigStore.setCto51Cookies(cookies);
97
+ break;
98
+ default:
99
+ console.warn(`⚠️ 尚未为 ${platformId} 实现 Cookie 保存,请手动保存`);
100
+ }
101
+ }
@@ -0,0 +1 @@
1
+ export declare function runPlatforms(): Promise<void>;
@@ -0,0 +1,35 @@
1
+ const PLATFORM_LIST = [
2
+ { id: 'weixin', name: '微信公众号', auth: 'AppID + AppSecret', capabilities: ['article', 'draft', 'image_upload'], note: '文章保存到草稿箱,需手动发布', status: '✅ 已验证' },
3
+ { id: 'zhihu', name: '知乎', auth: 'Cookie', capabilities: ['article', 'draft'], note: '支持 Markdown 直接发布', status: '✅ 已验证' },
4
+ { id: 'juejin', name: '掘金', auth: 'Cookie', capabilities: ['article', 'draft'], note: '支持 Markdown 直接发布', status: '✅ 已验证' },
5
+ { id: 'csdn', name: 'CSDN', auth: 'Cookie', capabilities: ['article', 'draft', 'image_upload'], note: '支持 Markdown 直接发布', status: '✅ 已验证' },
6
+ { id: 'jianshu', name: '简书', auth: 'Cookie', capabilities: ['article', 'draft'], note: 'Cookie 登录支持', status: '🔄 待测试' },
7
+ { id: 'weibo', name: '微博', auth: 'Cookie', capabilities: ['article', 'draft'], note: 'Cookie 登录支持', status: '🔄 待测试' },
8
+ { id: 'xiaohongshu', name: '小红书', auth: 'Cookie', capabilities: ['article', 'draft'], note: 'Cookie 登录支持', status: '🔄 待测试' },
9
+ { id: 'toutiao', name: '头条号', auth: 'Cookie', capabilities: ['article', 'draft'], note: '有反爬机制', status: '🔄 待测试' },
10
+ { id: 'baijiahao', name: '百家号', auth: 'Cookie', capabilities: ['article', 'draft'], note: 'Cookie 登录支持', status: '🔄 待测试' },
11
+ { id: 'bilibili', name: 'B站', auth: 'Cookie', capabilities: ['article', 'draft'], note: 'Cookie 登录支持', status: '🔄 待测试' },
12
+ { id: 'segmentfault', name: '思否', auth: 'Cookie', capabilities: ['article', 'draft'], note: 'Cookie 登录支持', status: '🔄 待测试' },
13
+ { id: 'cnblogs', name: '博客园', auth: 'Cookie', capabilities: ['article', 'draft'], note: 'Cookie 登录支持', status: '🔄 待测试' },
14
+ { id: 'oschina', name: '开源中国', auth: 'Cookie', capabilities: ['article', 'draft'], note: 'Cookie 登录支持', status: '🔄 待测试' },
15
+ { id: 'imooc', name: '慕课网', auth: 'Cookie', capabilities: ['article', 'draft'], note: 'Cookie 登录支持', status: '🔄 待测试' },
16
+ { id: 'xueqiu', name: '雪球', auth: 'Cookie', capabilities: ['article', 'draft'], note: 'Cookie 登录支持', status: '🔄 待测试' },
17
+ { id: 'woshipm', name: '人人都是产品经理', auth: 'Cookie', capabilities: ['article', 'draft'], note: 'Cookie 登录支持', status: '🔄 待测试' },
18
+ { id: 'douban', name: '豆瓣', auth: 'Cookie', capabilities: ['article', 'draft'], note: 'Cookie 登录支持', status: '🔄 待测试' },
19
+ { id: 'sohu', name: '搜狐号', auth: 'Cookie', capabilities: ['article', 'draft'], note: 'Cookie 登录支持', status: '🔄 待测试' },
20
+ { id: 'eastmoney', name: '东方财富', auth: 'Cookie', capabilities: ['article', 'draft'], note: 'Cookie 登录支持', status: '🔄 待测试' },
21
+ { id: 'cto51', name: '51CTO', auth: 'Cookie', capabilities: ['article', 'draft'], note: 'Cookie 登录支持', status: '🔄 待测试' },
22
+ ];
23
+ export async function runPlatforms() {
24
+ console.log('\n🖥️ 支持的平台(共 20 个):\n');
25
+ console.log('状态说明:');
26
+ console.log(' ✅ 已验证 - 登录和发布功能已测试通过');
27
+ console.log(' 🔄 待测试 - Cookie 登录支持,但发布功能尚未测试\n');
28
+ for (const p of PLATFORM_LIST) {
29
+ console.log(` ${p.name} (${p.id}) ${p.status}`);
30
+ console.log(` 认证方式: ${p.auth}`);
31
+ console.log(` 支持功能: ${p.capabilities.join(', ')}`);
32
+ console.log(` 说明: ${p.note}`);
33
+ console.log();
34
+ }
35
+ }
@@ -0,0 +1,6 @@
1
+ export declare function runPublishAll(options: {
2
+ file: string;
3
+ theme?: string;
4
+ highlight?: string;
5
+ macStyle?: boolean;
6
+ }): Promise<void>;
@@ -0,0 +1,101 @@
1
+ import { initAdapterRegistry, getLoggedInPlatforms, publishToPlatforms } from '../adapters/index.js';
2
+ import { renderMarkdown } from '../core/renderer.js';
3
+ export async function runPublishAll(options) {
4
+ const { file, theme = 'default', highlight = 'solarized-light', macStyle = true } = options;
5
+ console.log('🔍 正在检查已登录的平台...\n');
6
+ // 初始化适配器注册表
7
+ await initAdapterRegistry({
8
+ fetch: globalThis.fetch,
9
+ getCookie: async () => '',
10
+ });
11
+ // 获取已登录的平台
12
+ const loggedInPlatforms = await getLoggedInPlatforms();
13
+ if (loggedInPlatforms.length === 0) {
14
+ console.log('❌ 没有任何平台已登录');
15
+ console.log('\n请先登录平台:');
16
+ console.log(' mpub login -p <平台名>');
17
+ console.log('\n支持的平台: zhihu, juejin, csdn, weibo, bilibili, baijiahao, cnblogs, douban, eastmoney, imooc, oschina, segmentfault, sohu, woshipm, xueqiu');
18
+ return;
19
+ }
20
+ console.log(`✅ 找到 ${loggedInPlatforms.length} 个已登录的平台:`);
21
+ for (const platform of loggedInPlatforms) {
22
+ console.log(` - ${platform.name} (${platform.id})`);
23
+ }
24
+ console.log();
25
+ // 读取并渲染 Markdown
26
+ let markdownContent;
27
+ try {
28
+ if (file.startsWith('http://') || file.startsWith('https://')) {
29
+ const res = await fetch(file);
30
+ markdownContent = await res.text();
31
+ }
32
+ else {
33
+ markdownContent = await import('node:fs/promises').then(fs => fs.readFile(file, 'utf-8'));
34
+ }
35
+ }
36
+ catch (err) {
37
+ console.error(`❌ 读取文件失败: ${err.message}`);
38
+ return;
39
+ }
40
+ // 提取 frontmatter
41
+ const frontmatterMatch = markdownContent.match(/^---\n([\s\S]*?)\n---\n?/);
42
+ let title = '无标题文章';
43
+ let author = '';
44
+ let cover = '';
45
+ if (frontmatterMatch) {
46
+ const frontmatter = frontmatterMatch[1];
47
+ const titleMatch = frontmatter.match(/title:\s*(.+)/);
48
+ const authorMatch = frontmatter.match(/author:\s*(.+)/);
49
+ const coverMatch = frontmatter.match(/cover:\s*(.+)/);
50
+ if (titleMatch)
51
+ title = titleMatch[1].trim();
52
+ if (authorMatch)
53
+ author = authorMatch[1].trim();
54
+ if (coverMatch)
55
+ cover = coverMatch[1].trim();
56
+ markdownContent = markdownContent.slice(frontmatterMatch[0].length);
57
+ }
58
+ // 渲染 HTML
59
+ const renderResult = await renderMarkdown(markdownContent, {
60
+ theme,
61
+ highlight,
62
+ macStyle,
63
+ });
64
+ const article = {
65
+ title: renderResult.title || title,
66
+ markdown: markdownContent,
67
+ html: renderResult.html,
68
+ author: renderResult.author || author,
69
+ cover: renderResult.cover || cover,
70
+ };
71
+ console.log(`📝 开始发布文章: "${title}"`);
72
+ console.log(` 平台数量: ${loggedInPlatforms.length}`);
73
+ console.log();
74
+ // 发布到所有已登录平台
75
+ const results = [];
76
+ for (const platform of loggedInPlatforms) {
77
+ process.stdout.write(`⏳ ${platform.name}... `);
78
+ const result = await publishToPlatforms([platform.id], article);
79
+ results.push({ platform: platform.id, result: result[0] });
80
+ if (result[0].success) {
81
+ console.log(`✅ ${result[0].postUrl || result[0].postId}`);
82
+ }
83
+ else {
84
+ console.log(`❌ ${result[0].error}`);
85
+ }
86
+ }
87
+ console.log('\n========== 发布结果汇总 ==========\n');
88
+ const successCount = results.filter(r => r.result.success).length;
89
+ const failCount = results.filter(r => !r.result.success).length;
90
+ console.log(`成功: ${successCount} | 失败: ${failCount}\n`);
91
+ for (const { platform, result } of results) {
92
+ const status = result.success ? '✅' : '❌';
93
+ const info = result.success
94
+ ? `${result.postUrl || result.postId}${result.draftOnly ? ' (草稿)' : ''}`
95
+ : result.error;
96
+ console.log(`${status} ${platform}: ${info}`);
97
+ }
98
+ if (failCount > 0) {
99
+ console.log('\n⚠️ 部分平台发布失败,可能需要重新登录');
100
+ }
101
+ }
@@ -0,0 +1,7 @@
1
+ export declare function runPublish(options: {
2
+ file?: string;
3
+ platform?: string;
4
+ theme?: string;
5
+ appId?: string;
6
+ macStyle?: boolean;
7
+ }, input?: string): Promise<void>;