@the-convocation/twitter-scraper 0.15.1 → 0.16.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.
- package/dist/default/cjs/index.js +172 -83
- package/dist/default/cjs/index.js.map +1 -1
- package/dist/default/esm/index.mjs +172 -84
- package/dist/default/esm/index.mjs.map +1 -1
- package/dist/node/cjs/index.cjs +172 -83
- package/dist/node/cjs/index.cjs.map +1 -1
- package/dist/node/esm/index.mjs +172 -84
- package/dist/node/esm/index.mjs.map +1 -1
- package/dist/types/index.d.ts +189 -8
- package/package.json +1 -1
|
@@ -47,6 +47,12 @@ class ApiError extends Error {
|
|
|
47
47
|
return new ApiError(response, data, `Response status: ${response.status}`);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
|
+
class AuthenticationError extends Error {
|
|
51
|
+
constructor(message) {
|
|
52
|
+
super(message || "Authentication failed");
|
|
53
|
+
this.name = "AuthenticationError";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
50
56
|
|
|
51
57
|
class WaitingRateLimitStrategy {
|
|
52
58
|
async onRateLimit({ response: res }) {
|
|
@@ -275,7 +281,9 @@ class TwitterGuestAuth {
|
|
|
275
281
|
}
|
|
276
282
|
const token = this.guestToken;
|
|
277
283
|
if (token == null) {
|
|
278
|
-
throw new
|
|
284
|
+
throw new AuthenticationError(
|
|
285
|
+
"Authentication token is null or undefined."
|
|
286
|
+
);
|
|
279
287
|
}
|
|
280
288
|
headers.set("authorization", `Bearer ${this.bearerToken}`);
|
|
281
289
|
headers.set("x-guest-token", token);
|
|
@@ -322,15 +330,15 @@ class TwitterGuestAuth {
|
|
|
322
330
|
});
|
|
323
331
|
await updateCookieJar(this.jar, res.headers);
|
|
324
332
|
if (!res.ok) {
|
|
325
|
-
throw new
|
|
333
|
+
throw new AuthenticationError(await res.text());
|
|
326
334
|
}
|
|
327
335
|
const o = await res.json();
|
|
328
336
|
if (o == null || o["guest_token"] == null) {
|
|
329
|
-
throw new
|
|
337
|
+
throw new AuthenticationError("guest_token not found.");
|
|
330
338
|
}
|
|
331
339
|
const newGuestToken = o["guest_token"];
|
|
332
340
|
if (typeof newGuestToken !== "string") {
|
|
333
|
-
throw new
|
|
341
|
+
throw new AuthenticationError("guest_token was not a string.");
|
|
334
342
|
}
|
|
335
343
|
this.guestToken = newGuestToken;
|
|
336
344
|
this.guestCreatedAt = /* @__PURE__ */ new Date();
|
|
@@ -351,6 +359,47 @@ const TwitterUserAuthSubtask = typebox.Type.Object({
|
|
|
351
359
|
class TwitterUserAuth extends TwitterGuestAuth {
|
|
352
360
|
constructor(bearerToken, options) {
|
|
353
361
|
super(bearerToken, options);
|
|
362
|
+
this.subtaskHandlers = /* @__PURE__ */ new Map();
|
|
363
|
+
this.initializeDefaultHandlers();
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Register a custom subtask handler or override an existing one
|
|
367
|
+
* @param subtaskId The ID of the subtask to handle
|
|
368
|
+
* @param handler The handler function that processes the subtask
|
|
369
|
+
*/
|
|
370
|
+
registerSubtaskHandler(subtaskId, handler) {
|
|
371
|
+
this.subtaskHandlers.set(subtaskId, handler);
|
|
372
|
+
}
|
|
373
|
+
initializeDefaultHandlers() {
|
|
374
|
+
this.subtaskHandlers.set(
|
|
375
|
+
"LoginJsInstrumentationSubtask",
|
|
376
|
+
this.handleJsInstrumentationSubtask.bind(this)
|
|
377
|
+
);
|
|
378
|
+
this.subtaskHandlers.set(
|
|
379
|
+
"LoginEnterUserIdentifierSSO",
|
|
380
|
+
this.handleEnterUserIdentifierSSO.bind(this)
|
|
381
|
+
);
|
|
382
|
+
this.subtaskHandlers.set(
|
|
383
|
+
"LoginEnterAlternateIdentifierSubtask",
|
|
384
|
+
this.handleEnterAlternateIdentifierSubtask.bind(this)
|
|
385
|
+
);
|
|
386
|
+
this.subtaskHandlers.set(
|
|
387
|
+
"LoginEnterPassword",
|
|
388
|
+
this.handleEnterPassword.bind(this)
|
|
389
|
+
);
|
|
390
|
+
this.subtaskHandlers.set(
|
|
391
|
+
"AccountDuplicationCheck",
|
|
392
|
+
this.handleAccountDuplicationCheck.bind(this)
|
|
393
|
+
);
|
|
394
|
+
this.subtaskHandlers.set(
|
|
395
|
+
"LoginTwoFactorAuthChallenge",
|
|
396
|
+
this.handleTwoFactorAuthChallenge.bind(this)
|
|
397
|
+
);
|
|
398
|
+
this.subtaskHandlers.set("LoginAcid", this.handleAcid.bind(this));
|
|
399
|
+
this.subtaskHandlers.set(
|
|
400
|
+
"LoginSuccessSubtask",
|
|
401
|
+
this.handleSuccessSubtask.bind(this)
|
|
402
|
+
);
|
|
354
403
|
}
|
|
355
404
|
async isLoggedIn() {
|
|
356
405
|
const res = await requestApi(
|
|
@@ -365,52 +414,49 @@ class TwitterUserAuth extends TwitterGuestAuth {
|
|
|
365
414
|
}
|
|
366
415
|
async login(username, password, email, twoFactorSecret) {
|
|
367
416
|
await this.updateGuestToken();
|
|
417
|
+
const credentials = {
|
|
418
|
+
username,
|
|
419
|
+
password,
|
|
420
|
+
email,
|
|
421
|
+
twoFactorSecret
|
|
422
|
+
};
|
|
368
423
|
let next = await this.initLogin();
|
|
369
|
-
while ("
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
} else if (next.subtask.subtask_id === "AccountDuplicationCheck") {
|
|
382
|
-
next = await this.handleAccountDuplicationCheck(next);
|
|
383
|
-
} else if (next.subtask.subtask_id === "LoginTwoFactorAuthChallenge") {
|
|
384
|
-
if (twoFactorSecret) {
|
|
385
|
-
next = await this.handleTwoFactorAuthChallenge(next, twoFactorSecret);
|
|
386
|
-
} else {
|
|
387
|
-
throw new Error(
|
|
388
|
-
"Requested two factor authentication code but no secret provided"
|
|
389
|
-
);
|
|
390
|
-
}
|
|
391
|
-
} else if (next.subtask.subtask_id === "LoginAcid") {
|
|
392
|
-
next = await this.handleAcid(next, email);
|
|
393
|
-
} else if (next.subtask.subtask_id === "LoginSuccessSubtask") {
|
|
394
|
-
next = await this.handleSuccessSubtask(next);
|
|
424
|
+
while (next.status === "success" && next.response.subtasks?.length) {
|
|
425
|
+
const flowToken = next.response.flow_token;
|
|
426
|
+
if (flowToken == null) {
|
|
427
|
+
throw new Error("flow_token not found.");
|
|
428
|
+
}
|
|
429
|
+
const subtaskId = next.response.subtasks[0].subtask_id;
|
|
430
|
+
const handler = this.subtaskHandlers.get(subtaskId);
|
|
431
|
+
if (handler) {
|
|
432
|
+
next = await handler(subtaskId, next.response, credentials, {
|
|
433
|
+
sendFlowRequest: this.executeFlowTask.bind(this),
|
|
434
|
+
getFlowToken: () => flowToken
|
|
435
|
+
});
|
|
395
436
|
} else {
|
|
396
|
-
throw new Error(`Unknown subtask ${
|
|
437
|
+
throw new Error(`Unknown subtask ${subtaskId}`);
|
|
397
438
|
}
|
|
398
439
|
}
|
|
399
|
-
if ("
|
|
440
|
+
if (next.status === "error") {
|
|
400
441
|
throw next.err;
|
|
401
442
|
}
|
|
402
443
|
}
|
|
403
444
|
async logout() {
|
|
404
|
-
if (!this.
|
|
445
|
+
if (!this.hasToken()) {
|
|
405
446
|
return;
|
|
406
447
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
448
|
+
try {
|
|
449
|
+
await requestApi(
|
|
450
|
+
"https://api.twitter.com/1.1/account/logout.json",
|
|
451
|
+
this,
|
|
452
|
+
"POST"
|
|
453
|
+
);
|
|
454
|
+
} catch (error) {
|
|
455
|
+
console.warn("Error during logout:", error);
|
|
456
|
+
} finally {
|
|
457
|
+
this.deleteToken();
|
|
458
|
+
this.jar = new toughCookie.CookieJar();
|
|
459
|
+
}
|
|
414
460
|
}
|
|
415
461
|
async installCsrfToken(headers) {
|
|
416
462
|
const cookies = await this.getCookies();
|
|
@@ -449,12 +495,12 @@ class TwitterUserAuth extends TwitterGuestAuth {
|
|
|
449
495
|
}
|
|
450
496
|
});
|
|
451
497
|
}
|
|
452
|
-
async handleJsInstrumentationSubtask(
|
|
453
|
-
return await
|
|
454
|
-
flow_token:
|
|
498
|
+
async handleJsInstrumentationSubtask(subtaskId, _prev, _credentials, api) {
|
|
499
|
+
return await api.sendFlowRequest({
|
|
500
|
+
flow_token: api.getFlowToken(),
|
|
455
501
|
subtask_inputs: [
|
|
456
502
|
{
|
|
457
|
-
subtask_id:
|
|
503
|
+
subtask_id: subtaskId,
|
|
458
504
|
js_instrumentation: {
|
|
459
505
|
response: "{}",
|
|
460
506
|
link: "next_link"
|
|
@@ -463,32 +509,32 @@ class TwitterUserAuth extends TwitterGuestAuth {
|
|
|
463
509
|
]
|
|
464
510
|
});
|
|
465
511
|
}
|
|
466
|
-
async handleEnterAlternateIdentifierSubtask(
|
|
512
|
+
async handleEnterAlternateIdentifierSubtask(subtaskId, _prev, credentials, api) {
|
|
467
513
|
return await this.executeFlowTask({
|
|
468
|
-
flow_token:
|
|
514
|
+
flow_token: api.getFlowToken(),
|
|
469
515
|
subtask_inputs: [
|
|
470
516
|
{
|
|
471
|
-
subtask_id:
|
|
517
|
+
subtask_id: subtaskId,
|
|
472
518
|
enter_text: {
|
|
473
|
-
text: email,
|
|
519
|
+
text: credentials.email,
|
|
474
520
|
link: "next_link"
|
|
475
521
|
}
|
|
476
522
|
}
|
|
477
523
|
]
|
|
478
524
|
});
|
|
479
525
|
}
|
|
480
|
-
async handleEnterUserIdentifierSSO(
|
|
526
|
+
async handleEnterUserIdentifierSSO(subtaskId, _prev, credentials, api) {
|
|
481
527
|
return await this.executeFlowTask({
|
|
482
|
-
flow_token:
|
|
528
|
+
flow_token: api.getFlowToken(),
|
|
483
529
|
subtask_inputs: [
|
|
484
530
|
{
|
|
485
|
-
subtask_id:
|
|
531
|
+
subtask_id: subtaskId,
|
|
486
532
|
settings_list: {
|
|
487
533
|
setting_responses: [
|
|
488
534
|
{
|
|
489
535
|
key: "user_identifier",
|
|
490
536
|
response_data: {
|
|
491
|
-
text_data: { result: username }
|
|
537
|
+
text_data: { result: credentials.username }
|
|
492
538
|
}
|
|
493
539
|
}
|
|
494
540
|
],
|
|
@@ -498,26 +544,26 @@ class TwitterUserAuth extends TwitterGuestAuth {
|
|
|
498
544
|
]
|
|
499
545
|
});
|
|
500
546
|
}
|
|
501
|
-
async handleEnterPassword(
|
|
547
|
+
async handleEnterPassword(subtaskId, _prev, credentials, api) {
|
|
502
548
|
return await this.executeFlowTask({
|
|
503
|
-
flow_token:
|
|
549
|
+
flow_token: api.getFlowToken(),
|
|
504
550
|
subtask_inputs: [
|
|
505
551
|
{
|
|
506
|
-
subtask_id:
|
|
552
|
+
subtask_id: subtaskId,
|
|
507
553
|
enter_password: {
|
|
508
|
-
password,
|
|
554
|
+
password: credentials.password,
|
|
509
555
|
link: "next_link"
|
|
510
556
|
}
|
|
511
557
|
}
|
|
512
558
|
]
|
|
513
559
|
});
|
|
514
560
|
}
|
|
515
|
-
async handleAccountDuplicationCheck(
|
|
561
|
+
async handleAccountDuplicationCheck(subtaskId, _prev, _credentials, api) {
|
|
516
562
|
return await this.executeFlowTask({
|
|
517
|
-
flow_token:
|
|
563
|
+
flow_token: api.getFlowToken(),
|
|
518
564
|
subtask_inputs: [
|
|
519
565
|
{
|
|
520
|
-
subtask_id:
|
|
566
|
+
subtask_id: subtaskId,
|
|
521
567
|
check_logged_in_account: {
|
|
522
568
|
link: "AccountDuplicationCheck_false"
|
|
523
569
|
}
|
|
@@ -525,16 +571,24 @@ class TwitterUserAuth extends TwitterGuestAuth {
|
|
|
525
571
|
]
|
|
526
572
|
});
|
|
527
573
|
}
|
|
528
|
-
async handleTwoFactorAuthChallenge(
|
|
529
|
-
|
|
574
|
+
async handleTwoFactorAuthChallenge(subtaskId, _prev, credentials, api) {
|
|
575
|
+
if (!credentials.twoFactorSecret) {
|
|
576
|
+
return {
|
|
577
|
+
status: "error",
|
|
578
|
+
err: new AuthenticationError(
|
|
579
|
+
"Two-factor authentication is required but no secret was provided"
|
|
580
|
+
)
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
const totp = new OTPAuth__namespace.TOTP({ secret: credentials.twoFactorSecret });
|
|
530
584
|
let error;
|
|
531
585
|
for (let attempts = 1; attempts < 4; attempts += 1) {
|
|
532
586
|
try {
|
|
533
|
-
return await
|
|
534
|
-
flow_token:
|
|
587
|
+
return await api.sendFlowRequest({
|
|
588
|
+
flow_token: api.getFlowToken(),
|
|
535
589
|
subtask_inputs: [
|
|
536
590
|
{
|
|
537
|
-
subtask_id:
|
|
591
|
+
subtask_id: subtaskId,
|
|
538
592
|
enter_text: {
|
|
539
593
|
link: "next_link",
|
|
540
594
|
text: totp.generate()
|
|
@@ -549,23 +603,23 @@ class TwitterUserAuth extends TwitterGuestAuth {
|
|
|
549
603
|
}
|
|
550
604
|
throw error;
|
|
551
605
|
}
|
|
552
|
-
async handleAcid(
|
|
606
|
+
async handleAcid(subtaskId, _prev, credentials, api) {
|
|
553
607
|
return await this.executeFlowTask({
|
|
554
|
-
flow_token:
|
|
608
|
+
flow_token: api.getFlowToken(),
|
|
555
609
|
subtask_inputs: [
|
|
556
610
|
{
|
|
557
|
-
subtask_id:
|
|
611
|
+
subtask_id: subtaskId,
|
|
558
612
|
enter_text: {
|
|
559
|
-
text: email,
|
|
613
|
+
text: credentials.email,
|
|
560
614
|
link: "next_link"
|
|
561
615
|
}
|
|
562
616
|
}
|
|
563
617
|
]
|
|
564
618
|
});
|
|
565
619
|
}
|
|
566
|
-
async handleSuccessSubtask(
|
|
620
|
+
async handleSuccessSubtask(_subtaskId, _prev, _credentials, api) {
|
|
567
621
|
return await this.executeFlowTask({
|
|
568
|
-
flow_token:
|
|
622
|
+
flow_token: api.getFlowToken(),
|
|
569
623
|
subtask_inputs: []
|
|
570
624
|
});
|
|
571
625
|
}
|
|
@@ -573,7 +627,9 @@ class TwitterUserAuth extends TwitterGuestAuth {
|
|
|
573
627
|
const onboardingTaskUrl = "https://api.twitter.com/1.1/onboarding/task.json";
|
|
574
628
|
const token = this.guestToken;
|
|
575
629
|
if (token == null) {
|
|
576
|
-
throw new
|
|
630
|
+
throw new AuthenticationError(
|
|
631
|
+
"Authentication token is null or undefined."
|
|
632
|
+
);
|
|
577
633
|
}
|
|
578
634
|
const headers = new headersPolyfill.Headers({
|
|
579
635
|
authorization: `Bearer ${this.bearerToken}`,
|
|
@@ -598,12 +654,15 @@ class TwitterUserAuth extends TwitterGuestAuth {
|
|
|
598
654
|
}
|
|
599
655
|
const flow = await res.json();
|
|
600
656
|
if (flow?.flow_token == null) {
|
|
601
|
-
return {
|
|
657
|
+
return {
|
|
658
|
+
status: "error",
|
|
659
|
+
err: new AuthenticationError("flow_token not found.")
|
|
660
|
+
};
|
|
602
661
|
}
|
|
603
662
|
if (flow.errors?.length) {
|
|
604
663
|
return {
|
|
605
664
|
status: "error",
|
|
606
|
-
err: new
|
|
665
|
+
err: new AuthenticationError(
|
|
607
666
|
`Authentication error (${flow.errors[0].code}): ${flow.errors[0].message}`
|
|
608
667
|
)
|
|
609
668
|
};
|
|
@@ -611,7 +670,7 @@ class TwitterUserAuth extends TwitterGuestAuth {
|
|
|
611
670
|
if (typeof flow.flow_token !== "string") {
|
|
612
671
|
return {
|
|
613
672
|
status: "error",
|
|
614
|
-
err: new
|
|
673
|
+
err: new AuthenticationError("flow_token was not a string.")
|
|
615
674
|
};
|
|
616
675
|
}
|
|
617
676
|
const subtask = flow.subtasks?.length ? flow.subtasks[0] : void 0;
|
|
@@ -619,13 +678,12 @@ class TwitterUserAuth extends TwitterGuestAuth {
|
|
|
619
678
|
if (subtask && subtask.subtask_id === "DenyLoginSubtask") {
|
|
620
679
|
return {
|
|
621
680
|
status: "error",
|
|
622
|
-
err: new
|
|
681
|
+
err: new AuthenticationError("Authentication error: DenyLoginSubtask")
|
|
623
682
|
};
|
|
624
683
|
}
|
|
625
684
|
return {
|
|
626
685
|
status: "success",
|
|
627
|
-
|
|
628
|
-
flowToken: flow.flow_token
|
|
686
|
+
response: flow
|
|
629
687
|
};
|
|
630
688
|
}
|
|
631
689
|
}
|
|
@@ -1288,8 +1346,8 @@ async function fetchSearchProfiles(query, maxProfiles, auth, cursor) {
|
|
|
1288
1346
|
return parseSearchTimelineUsers(timeline);
|
|
1289
1347
|
}
|
|
1290
1348
|
async function getSearchTimeline(query, maxItems, searchMode, auth, cursor) {
|
|
1291
|
-
if (!auth.isLoggedIn()) {
|
|
1292
|
-
throw new
|
|
1349
|
+
if (!await auth.isLoggedIn()) {
|
|
1350
|
+
throw new AuthenticationError("Scraper is not logged-in for search.");
|
|
1293
1351
|
}
|
|
1294
1352
|
if (maxItems > 50) {
|
|
1295
1353
|
maxItems = 50;
|
|
@@ -1396,6 +1454,11 @@ function getFollowers(userId, maxProfiles, auth) {
|
|
|
1396
1454
|
});
|
|
1397
1455
|
}
|
|
1398
1456
|
async function fetchProfileFollowing(userId, maxProfiles, auth, cursor) {
|
|
1457
|
+
if (!await auth.isLoggedIn()) {
|
|
1458
|
+
throw new AuthenticationError(
|
|
1459
|
+
"Scraper is not logged-in for profile following."
|
|
1460
|
+
);
|
|
1461
|
+
}
|
|
1399
1462
|
const timeline = await getFollowingTimeline(
|
|
1400
1463
|
userId,
|
|
1401
1464
|
maxProfiles,
|
|
@@ -1405,6 +1468,11 @@ async function fetchProfileFollowing(userId, maxProfiles, auth, cursor) {
|
|
|
1405
1468
|
return parseRelationshipTimeline(timeline);
|
|
1406
1469
|
}
|
|
1407
1470
|
async function fetchProfileFollowers(userId, maxProfiles, auth, cursor) {
|
|
1471
|
+
if (!await auth.isLoggedIn()) {
|
|
1472
|
+
throw new AuthenticationError(
|
|
1473
|
+
"Scraper is not logged-in for profile followers."
|
|
1474
|
+
);
|
|
1475
|
+
}
|
|
1408
1476
|
const timeline = await getFollowersTimeline(
|
|
1409
1477
|
userId,
|
|
1410
1478
|
maxProfiles,
|
|
@@ -1415,7 +1483,9 @@ async function fetchProfileFollowers(userId, maxProfiles, auth, cursor) {
|
|
|
1415
1483
|
}
|
|
1416
1484
|
async function getFollowingTimeline(userId, maxItems, auth, cursor) {
|
|
1417
1485
|
if (!auth.isLoggedIn()) {
|
|
1418
|
-
throw new
|
|
1486
|
+
throw new AuthenticationError(
|
|
1487
|
+
"Scraper is not logged-in for profile following."
|
|
1488
|
+
);
|
|
1419
1489
|
}
|
|
1420
1490
|
if (maxItems > 50) {
|
|
1421
1491
|
maxItems = 50;
|
|
@@ -1448,7 +1518,9 @@ async function getFollowingTimeline(userId, maxItems, auth, cursor) {
|
|
|
1448
1518
|
}
|
|
1449
1519
|
async function getFollowersTimeline(userId, maxItems, auth, cursor) {
|
|
1450
1520
|
if (!auth.isLoggedIn()) {
|
|
1451
|
-
throw new
|
|
1521
|
+
throw new AuthenticationError(
|
|
1522
|
+
"Scraper is not logged-in for profile followers."
|
|
1523
|
+
);
|
|
1452
1524
|
}
|
|
1453
1525
|
if (maxItems > 50) {
|
|
1454
1526
|
maxItems = 50;
|
|
@@ -1705,8 +1777,10 @@ function getTweetsAndRepliesByUserId(userId, maxTweets, auth) {
|
|
|
1705
1777
|
});
|
|
1706
1778
|
}
|
|
1707
1779
|
async function fetchLikedTweets(userId, maxTweets, cursor, auth) {
|
|
1708
|
-
if (!auth.isLoggedIn()) {
|
|
1709
|
-
throw new
|
|
1780
|
+
if (!await auth.isLoggedIn()) {
|
|
1781
|
+
throw new AuthenticationError(
|
|
1782
|
+
"Scraper is not logged-in for fetching liked tweets."
|
|
1783
|
+
);
|
|
1710
1784
|
}
|
|
1711
1785
|
if (maxTweets > 200) {
|
|
1712
1786
|
maxTweets = 200;
|
|
@@ -1811,6 +1885,20 @@ class Scraper {
|
|
|
1811
1885
|
this.token = bearerToken;
|
|
1812
1886
|
this.useGuestAuth();
|
|
1813
1887
|
}
|
|
1888
|
+
/**
|
|
1889
|
+
* Registers a subtask handler for the given subtask ID. This
|
|
1890
|
+
* will override any existing handler for the same subtask.
|
|
1891
|
+
* @param subtaskId The ID of the subtask to register the handler for.
|
|
1892
|
+
* @param subtaskHandler The handler function to register.
|
|
1893
|
+
*/
|
|
1894
|
+
registerAuthSubtaskHandler(subtaskId, subtaskHandler) {
|
|
1895
|
+
if (this.auth instanceof TwitterUserAuth) {
|
|
1896
|
+
this.auth.registerSubtaskHandler(subtaskId, subtaskHandler);
|
|
1897
|
+
}
|
|
1898
|
+
if (this.authTrends instanceof TwitterUserAuth) {
|
|
1899
|
+
this.authTrends.registerSubtaskHandler(subtaskId, subtaskHandler);
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1814
1902
|
/**
|
|
1815
1903
|
* Initializes auth properties using a guest token.
|
|
1816
1904
|
* Used when creating a new instance of this class, and when logging out.
|
|
@@ -2145,6 +2233,7 @@ class Scraper {
|
|
|
2145
2233
|
}
|
|
2146
2234
|
|
|
2147
2235
|
exports.ApiError = ApiError;
|
|
2236
|
+
exports.AuthenticationError = AuthenticationError;
|
|
2148
2237
|
exports.ErrorRateLimitStrategy = ErrorRateLimitStrategy;
|
|
2149
2238
|
exports.Scraper = Scraper;
|
|
2150
2239
|
exports.SearchMode = SearchMode;
|