fastbrowser_cli 1.0.37 → 1.0.40
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/dist/contribs/_shared/fastbrowser_helper.d.ts +13 -0
- package/dist/contribs/_shared/fastbrowser_helper.d.ts.map +1 -0
- package/dist/contribs/_shared/fastbrowser_helper.js +44 -0
- package/dist/contribs/_shared/fastbrowser_helper.js.map +1 -0
- package/dist/contribs/linkedin_cli/src/cli.d.ts +3 -0
- package/dist/contribs/linkedin_cli/src/cli.d.ts.map +1 -0
- package/dist/contribs/linkedin_cli/src/cli.js +299 -0
- package/dist/contribs/linkedin_cli/src/cli.js.map +1 -0
- package/dist/contribs/linkedin_cli/src/libs/linkedin_profile_helper.d.ts +73 -0
- package/dist/contribs/linkedin_cli/src/libs/linkedin_profile_helper.d.ts.map +1 -0
- package/dist/contribs/linkedin_cli/src/libs/linkedin_profile_helper.js +866 -0
- package/dist/contribs/linkedin_cli/src/libs/linkedin_profile_helper.js.map +1 -0
- package/dist/contribs/linkedin_cli/src/libs/linkedin_recent_posts_helper.d.ts +61 -0
- package/dist/contribs/linkedin_cli/src/libs/linkedin_recent_posts_helper.d.ts.map +1 -0
- package/dist/contribs/linkedin_cli/src/libs/linkedin_recent_posts_helper.js +885 -0
- package/dist/contribs/linkedin_cli/src/libs/linkedin_recent_posts_helper.js.map +1 -0
- package/dist/contribs/linkedin_cli/src/libs/linkedin_thread_helper.d.ts +11 -0
- package/dist/contribs/linkedin_cli/src/libs/linkedin_thread_helper.d.ts.map +1 -0
- package/dist/contribs/linkedin_cli/src/libs/linkedin_thread_helper.js +145 -0
- package/dist/contribs/linkedin_cli/src/libs/linkedin_thread_helper.js.map +1 -0
- package/dist/contribs/twitter_cli/src/cli.d.ts +3 -0
- package/dist/contribs/twitter_cli/src/cli.d.ts.map +1 -0
- package/dist/contribs/twitter_cli/src/cli.js +273 -0
- package/dist/contribs/twitter_cli/src/cli.js.map +1 -0
- package/dist/contribs/twitter_cli/src/libs/twitter_profile_helper.d.ts +28 -0
- package/dist/contribs/twitter_cli/src/libs/twitter_profile_helper.d.ts.map +1 -0
- package/dist/contribs/twitter_cli/src/libs/twitter_profile_helper.js +274 -0
- package/dist/contribs/twitter_cli/src/libs/twitter_profile_helper.js.map +1 -0
- package/dist/contribs/twitter_cli/src/libs/twitter_recent_posts_helper.d.ts +43 -0
- package/dist/contribs/twitter_cli/src/libs/twitter_recent_posts_helper.d.ts.map +1 -0
- package/dist/contribs/twitter_cli/src/libs/twitter_recent_posts_helper.js +519 -0
- package/dist/contribs/twitter_cli/src/libs/twitter_recent_posts_helper.js.map +1 -0
- package/dist/contribs/twitter_cli/src/libs/twitter_thread_helper.d.ts +11 -0
- package/dist/contribs/twitter_cli/src/libs/twitter_thread_helper.d.ts.map +1 -0
- package/dist/contribs/twitter_cli/src/libs/twitter_thread_helper.js +213 -0
- package/dist/contribs/twitter_cli/src/libs/twitter_thread_helper.js.map +1 -0
- package/dist/fastbrowser_cli/fastbrowser_cli.js +43 -0
- package/dist/fastbrowser_cli/fastbrowser_cli.js.map +1 -1
- package/dist/fastbrowser_httpd/libs/tool-schemas.d.ts +4 -0
- package/dist/fastbrowser_httpd/libs/tool-schemas.d.ts.map +1 -1
- package/dist/fastbrowser_httpd/libs/tool-schemas.js +4 -0
- package/dist/fastbrowser_httpd/libs/tool-schemas.js.map +1 -1
- package/dist/fastbrowser_mcp/fastbrowser_mcp.js +36 -2
- package/dist/fastbrowser_mcp/fastbrowser_mcp.js.map +1 -1
- package/dist/fastbrowser_mcp/libs/mcp_target_helper.d.ts +2 -0
- package/dist/fastbrowser_mcp/libs/mcp_target_helper.d.ts.map +1 -1
- package/dist/fastbrowser_mcp/libs/mcp_target_helper.js +12 -0
- package/dist/fastbrowser_mcp/libs/mcp_target_helper.js.map +1 -1
- package/dist/fastbrowser_mcp/libs/response_formatter.d.ts +1 -0
- package/dist/fastbrowser_mcp/libs/response_formatter.d.ts.map +1 -1
- package/dist/fastbrowser_mcp/libs/response_formatter.js +27 -0
- package/dist/fastbrowser_mcp/libs/response_formatter.js.map +1 -1
- package/dist/shared/fastbrowser_helper.d.ts +13 -0
- package/dist/shared/fastbrowser_helper.d.ts.map +1 -0
- package/dist/shared/fastbrowser_helper.js +39 -0
- package/dist/shared/fastbrowser_helper.js.map +1 -0
- package/examples/linkedin_cli_TOREMOVE/README.md +7 -0
- package/examples/{linkedin_cli → linkedin_cli_TOREMOVE}/linkedin_dm.sh +8 -4
- package/examples/linkedin_cli_TOREMOVE/linkedin_dm.ts +326 -0
- package/examples/linkedin_cli_TOREMOVE/linkedin_dm_messages.ts +279 -0
- package/examples/linkedin_cli_TOREMOVE/linkedin_full_cycle.sh +5 -0
- package/examples/{linkedin_cli → linkedin_cli_TOREMOVE}/linkedin_post.sh +3 -0
- package/examples/linkedin_cli_TOREMOVE/message_thread.a11y.txt +252 -0
- package/listitem +4 -0
- package/package.json +7 -3
- package/skills/fastbrowser/SKILL.md +33 -25
- package/src/contribs/_shared/fastbrowser_helper.ts +54 -0
- package/src/contribs/linkedin_cli/README.md +80 -0
- package/src/contribs/linkedin_cli/data/linkedin_posts_jeromeetienne.a11y.txt +2364 -0
- package/src/contribs/linkedin_cli/data/linkedin_posts_jontwigge.a11y.txt +2740 -0
- package/src/contribs/linkedin_cli/data/linkedin_posts_julien_guezennec.a11y.txt +2073 -0
- package/src/contribs/linkedin_cli/data/linkedin_profile_jeromeetienne.a11y.txt +1863 -0
- package/src/contribs/linkedin_cli/data/linkedin_profile_jontwigge.a11y.txt +1738 -0
- package/src/contribs/linkedin_cli/data/linkedin_profile_julien_guezennec.a11y.txt +2182 -0
- package/src/contribs/linkedin_cli/src/cli.ts +345 -0
- package/src/contribs/linkedin_cli/src/libs/linkedin_profile_helper.ts +964 -0
- package/src/contribs/linkedin_cli/src/libs/linkedin_recent_posts_helper.ts +982 -0
- package/src/contribs/linkedin_cli/src/libs/linkedin_thread_helper.ts +171 -0
- package/src/contribs/twitter_cli/README.md +79 -0
- package/src/contribs/twitter_cli/data/twitter_chat.a11y.txt +215 -0
- package/src/contribs/twitter_cli/data/twitter_home.a11y.txt +467 -0
- package/src/contribs/twitter_cli/data/twitter_profile.a11y.txt +418 -0
- package/src/contribs/twitter_cli/data/twitter_profile_jontwigge.a11y.txt +484 -0
- package/src/contribs/twitter_cli/data/twitter_profile_molokoloco.a11y.txt +483 -0
- package/src/contribs/twitter_cli/src/cli.ts +315 -0
- package/src/contribs/twitter_cli/src/libs/twitter_profile_helper.ts +328 -0
- package/src/contribs/twitter_cli/src/libs/twitter_recent_posts_helper.ts +607 -0
- package/src/contribs/twitter_cli/src/libs/twitter_thread_helper.ts +240 -0
- package/src/fastbrowser_cli/fastbrowser_cli.ts +51 -0
- package/src/fastbrowser_httpd/libs/tool-schemas.ts +6 -0
- package/src/fastbrowser_mcp/fastbrowser_mcp.ts +46 -3
- package/src/fastbrowser_mcp/libs/mcp_target_helper.ts +11 -0
- package/src/fastbrowser_mcp/libs/response_formatter.ts +29 -0
- package/src/shared/fastbrowser_helper.ts +49 -0
- package/tsconfig.json +1 -1
- package/examples/mcp_client_playwright.ts +0 -34
- /package/examples/{linkedin_cli → linkedin_cli_TOREMOVE}/linkedin.snapshot.txt +0 -0
- /package/examples/{twitter_cli → twitter_cli_TOREMOVE}/twitter_post.sh +0 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
|
|
3
|
+
// npm imports
|
|
4
|
+
import { Command } from 'commander';
|
|
5
|
+
import { A11yQuery, A11yTree } from 'a11y_parse';
|
|
6
|
+
|
|
7
|
+
// local imports
|
|
8
|
+
import { FastBrowserHelper } from '../../_shared/fastbrowser_helper.js';
|
|
9
|
+
import { TwitterThreadHelper } from './libs/twitter_thread_helper.js';
|
|
10
|
+
import { TwitterProfile, TwitterProfileHelper } from './libs/twitter_profile_helper.js';
|
|
11
|
+
import { TwitterPost, TwitterRecentPostsHelper } from './libs/twitter_recent_posts_helper.js';
|
|
12
|
+
|
|
13
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
14
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
15
|
+
//
|
|
16
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
17
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
18
|
+
|
|
19
|
+
class MainHelper {
|
|
20
|
+
|
|
21
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
22
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
23
|
+
//
|
|
24
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
25
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
26
|
+
|
|
27
|
+
static async gotoPageHome(): Promise<void> {
|
|
28
|
+
await FastBrowserHelper.run('check');
|
|
29
|
+
await FastBrowserHelper.navigatePage('https://x.com/home');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
33
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
34
|
+
//
|
|
35
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
36
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
37
|
+
|
|
38
|
+
static async gotoPageDm(): Promise<void> {
|
|
39
|
+
await FastBrowserHelper.run('check');
|
|
40
|
+
await FastBrowserHelper.navigatePage('https://x.com/i/chat/');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
44
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
45
|
+
//
|
|
46
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
47
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
48
|
+
|
|
49
|
+
static async gotoPageProfile(handle: string): Promise<void> {
|
|
50
|
+
if (handle.length === 0) {
|
|
51
|
+
throw new Error('handle is required');
|
|
52
|
+
}
|
|
53
|
+
await FastBrowserHelper.run('check');
|
|
54
|
+
await FastBrowserHelper.navigatePage(`https://x.com/${handle}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
58
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
59
|
+
//
|
|
60
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
61
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
62
|
+
|
|
63
|
+
static async exportProfile(handle: string): Promise<TwitterProfile> {
|
|
64
|
+
const snapshot = await FastBrowserHelper.takeSnapshot();
|
|
65
|
+
const profile = TwitterProfileHelper.parseProfile(snapshot, handle);
|
|
66
|
+
if (profile.website !== null) {
|
|
67
|
+
profile.website = await TwitterProfileHelper.resolveWebsite(profile.website);
|
|
68
|
+
}
|
|
69
|
+
return profile;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
73
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
74
|
+
//
|
|
75
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
76
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
77
|
+
|
|
78
|
+
static async exportRecentPosts(handle: string, limit: number): Promise<TwitterPost[]> {
|
|
79
|
+
let posts: TwitterPost[] = [];
|
|
80
|
+
const maxAttempts = 8;
|
|
81
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
82
|
+
const snapshot = await FastBrowserHelper.takeSnapshot();
|
|
83
|
+
posts = TwitterRecentPostsHelper.parsePosts(snapshot, handle);
|
|
84
|
+
if (posts.length > 0) {
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
if (attempt < maxAttempts - 1) {
|
|
88
|
+
await new Promise((resolve) => setTimeout(resolve, 1500));
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (limit > 0 && posts.length > limit) {
|
|
92
|
+
return posts.slice(0, limit);
|
|
93
|
+
}
|
|
94
|
+
return posts;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
98
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
99
|
+
//
|
|
100
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
101
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
102
|
+
|
|
103
|
+
static async createPost(content: string): Promise<void> {
|
|
104
|
+
await FastBrowserHelper.click('link[name="Post"]');
|
|
105
|
+
await FastBrowserHelper.fillForm('dialog textbox', content);
|
|
106
|
+
await FastBrowserHelper.click('dialog button[name="Post"]');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
110
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
111
|
+
//
|
|
112
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
113
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
114
|
+
|
|
115
|
+
static async listConversationHandles(): Promise<string[]> {
|
|
116
|
+
const output = await FastBrowserHelper.querySelectorsAllWithChildren(
|
|
117
|
+
'listitem:has(link[name="user avatar"])',
|
|
118
|
+
0,
|
|
119
|
+
);
|
|
120
|
+
const treeText = MainHelper.extractAxTreeText(output);
|
|
121
|
+
if (treeText.length === 0) {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
const root = A11yTree.parse(treeText);
|
|
125
|
+
const items = A11yQuery.querySelectorAll(root, 'listitem:has(link[name="user avatar"])');
|
|
126
|
+
const handles: string[] = [];
|
|
127
|
+
for (const item of items) {
|
|
128
|
+
const link = A11yQuery.querySelector(item, 'link[name="user avatar"]');
|
|
129
|
+
if (link === undefined) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const url = link.attributes['url'];
|
|
133
|
+
if (url === undefined) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const handle = MainHelper.handleFromUrl(url);
|
|
137
|
+
if (handle === null) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
handles.push(handle);
|
|
141
|
+
}
|
|
142
|
+
return handles;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
146
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
147
|
+
//
|
|
148
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
149
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
150
|
+
|
|
151
|
+
static async selectConversation(handle: string): Promise<void> {
|
|
152
|
+
if (handle.length === 0) {
|
|
153
|
+
throw new Error('handle is required');
|
|
154
|
+
}
|
|
155
|
+
const escaped = handle.replace(/"/g, '\\"');
|
|
156
|
+
await FastBrowserHelper.click(`listitem:has(link[url$="/${escaped}"])`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
160
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
161
|
+
//
|
|
162
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
163
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
164
|
+
|
|
165
|
+
static async fillAndSendMessage(message: string): Promise<void> {
|
|
166
|
+
await FastBrowserHelper.fillForm('textbox[name="Unencrypted message"]', message);
|
|
167
|
+
await FastBrowserHelper.click('generic:has(> textbox[name="Unencrypted message"]) > button');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
171
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
172
|
+
//
|
|
173
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
174
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
175
|
+
|
|
176
|
+
static async getMessagesTranscript(handle: string): Promise<string> {
|
|
177
|
+
const output = await FastBrowserHelper.querySelectorsAllWithChildren('main listitem', 0);
|
|
178
|
+
return await TwitterThreadHelper.parseMessagesThread(output, handle);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
182
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
183
|
+
//
|
|
184
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
185
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
186
|
+
|
|
187
|
+
private static extractAxTreeText(rawOutput: string): string {
|
|
188
|
+
const lines: string[] = [];
|
|
189
|
+
for (const line of rawOutput.split('\n')) {
|
|
190
|
+
if (/^\s*uid=/.test(line) === true) {
|
|
191
|
+
lines.push(line);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return lines.join('\n');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
private static handleFromUrl(url: string): string | null {
|
|
198
|
+
const trimmed = url.replace(/\/+$/, '');
|
|
199
|
+
const slash = trimmed.lastIndexOf('/');
|
|
200
|
+
if (slash === -1) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
const handle = trimmed.slice(slash + 1);
|
|
204
|
+
if (handle.length === 0) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
return handle;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
212
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
213
|
+
// Entry point
|
|
214
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
215
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
216
|
+
|
|
217
|
+
async function main(): Promise<void> {
|
|
218
|
+
const program = new Command();
|
|
219
|
+
|
|
220
|
+
program
|
|
221
|
+
.name('twitter_cli')
|
|
222
|
+
.description('Twitter CLI - command line tool to interact with x.com using the FastBrowser CLI');
|
|
223
|
+
|
|
224
|
+
program
|
|
225
|
+
.command('post <content>')
|
|
226
|
+
.description('Create a post on x.com')
|
|
227
|
+
.action(async (content: string) => {
|
|
228
|
+
await MainHelper.gotoPageHome();
|
|
229
|
+
await MainHelper.createPost(content);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
program
|
|
233
|
+
.command('dm_page')
|
|
234
|
+
.description('Navigate to the x.com direct messages page')
|
|
235
|
+
.action(async () => {
|
|
236
|
+
await MainHelper.gotoPageDm();
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
program
|
|
240
|
+
.command('dm_list')
|
|
241
|
+
.description('List the handles of people you have conversations with. (assume you did "dm_page" first)')
|
|
242
|
+
.action(async () => {
|
|
243
|
+
const handles = await MainHelper.listConversationHandles();
|
|
244
|
+
for (const handle of handles) {
|
|
245
|
+
console.log(handle);
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
program
|
|
250
|
+
.command('dm_select <handle>')
|
|
251
|
+
.description('Select an existing conversation by handle. (assume you did "dm_page" first)')
|
|
252
|
+
.action(async (handle: string) => {
|
|
253
|
+
await MainHelper.selectConversation(handle);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
program
|
|
257
|
+
.command('dm_thread <handle>')
|
|
258
|
+
.description('Get the message thread of a conversation. (assume you did "dm_page" first)')
|
|
259
|
+
.action(async (handle: string) => {
|
|
260
|
+
await MainHelper.selectConversation(handle);
|
|
261
|
+
const transcript = await MainHelper.getMessagesTranscript(handle);
|
|
262
|
+
console.log(transcript);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
program
|
|
266
|
+
.command('profile <handle>')
|
|
267
|
+
.description('Export the profile of a twitter handle')
|
|
268
|
+
.option('-f, --format <format>', 'output format: markdown or json', 'markdown')
|
|
269
|
+
.action(async (handle: string, opts: { format: string; }) => {
|
|
270
|
+
if (opts.format !== 'markdown' && opts.format !== 'json') {
|
|
271
|
+
throw new Error(`unknown format '${opts.format}', expected 'markdown' or 'json'`);
|
|
272
|
+
}
|
|
273
|
+
await MainHelper.gotoPageProfile(handle);
|
|
274
|
+
const profile = await MainHelper.exportProfile(handle);
|
|
275
|
+
if (opts.format === 'json') {
|
|
276
|
+
console.log(JSON.stringify(profile));
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
console.log(TwitterProfileHelper.formatMarkdown(profile));
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
program
|
|
283
|
+
.command('recent_posts <handle>')
|
|
284
|
+
.description('Export the recent posts visible on a twitter handle\'s profile')
|
|
285
|
+
.option('-f, --format <format>', 'output format: markdown or json', 'markdown')
|
|
286
|
+
.option('-l, --limit <limit>', 'max number of posts to return (0 = all visible)', '0')
|
|
287
|
+
.action(async (handle: string, opts: { format: string; limit: string; }) => {
|
|
288
|
+
if (opts.format !== 'markdown' && opts.format !== 'json') {
|
|
289
|
+
throw new Error(`unknown format '${opts.format}', expected 'markdown' or 'json'`);
|
|
290
|
+
}
|
|
291
|
+
const limit = parseInt(opts.limit, 10);
|
|
292
|
+
if (Number.isNaN(limit) === true || limit < 0) {
|
|
293
|
+
throw new Error(`invalid limit '${opts.limit}', expected a non-negative integer`);
|
|
294
|
+
}
|
|
295
|
+
await MainHelper.gotoPageProfile(handle);
|
|
296
|
+
const posts = await MainHelper.exportRecentPosts(handle, limit);
|
|
297
|
+
if (opts.format === 'json') {
|
|
298
|
+
console.log(JSON.stringify(posts));
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
console.log(TwitterRecentPostsHelper.formatMarkdown(posts));
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
program
|
|
305
|
+
.command('dm_send <handle> <message>')
|
|
306
|
+
.description('Send a message in an existing conversation. (assume you did \'dm_page\' first)')
|
|
307
|
+
.action(async (handle: string, message: string) => {
|
|
308
|
+
await MainHelper.selectConversation(handle);
|
|
309
|
+
await MainHelper.fillAndSendMessage(message);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
await program.parseAsync();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
void main();
|