xtrawl 0.1.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/DOCUMENTATION.md +774 -0
- package/README.md +232 -0
- package/dist/auth/bootstrap.d.ts +4 -0
- package/dist/auth/bootstrap.d.ts.map +1 -0
- package/dist/auth/bootstrap.js +29 -0
- package/dist/auth/bootstrap.js.map +1 -0
- package/dist/auth/cookies.d.ts +7 -0
- package/dist/auth/cookies.d.ts.map +1 -0
- package/dist/auth/cookies.js +83 -0
- package/dist/auth/cookies.js.map +1 -0
- package/dist/auth/loaders.d.ts +6 -0
- package/dist/auth/loaders.d.ts.map +1 -0
- package/dist/auth/loaders.js +69 -0
- package/dist/auth/loaders.js.map +1 -0
- package/dist/auth/material.d.ts +3 -0
- package/dist/auth/material.d.ts.map +1 -0
- package/dist/auth/material.js +20 -0
- package/dist/auth/material.js.map +1 -0
- package/dist/auth/records.d.ts +4 -0
- package/dist/auth/records.d.ts.map +1 -0
- package/dist/auth/records.js +38 -0
- package/dist/auth/records.js.map +1 -0
- package/dist/cli/help.d.ts +2 -0
- package/dist/cli/help.d.ts.map +1 -0
- package/dist/cli/help.js +30 -0
- package/dist/cli/help.js.map +1 -0
- package/dist/cli/main.d.ts +3 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +85 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/parser.d.ts +22 -0
- package/dist/cli/parser.d.ts.map +1 -0
- package/dist/cli/parser.js +275 -0
- package/dist/cli/parser.js.map +1 -0
- package/dist/client/client.d.ts +29 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +192 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/collectors.d.ts +15 -0
- package/dist/client/collectors.d.ts.map +1 -0
- package/dist/client/collectors.js +185 -0
- package/dist/client/collectors.js.map +1 -0
- package/dist/client/database.d.ts +56 -0
- package/dist/client/database.d.ts.map +1 -0
- package/dist/client/database.js +146 -0
- package/dist/client/database.js.map +1 -0
- package/dist/client/profiles.d.ts +5 -0
- package/dist/client/profiles.d.ts.map +1 -0
- package/dist/client/profiles.js +36 -0
- package/dist/client/profiles.js.map +1 -0
- package/dist/client/search.d.ts +21 -0
- package/dist/client/search.d.ts.map +1 -0
- package/dist/client/search.js +139 -0
- package/dist/client/search.js.map +1 -0
- package/dist/client/types.d.ts +21 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/client/types.js +2 -0
- package/dist/client/types.js.map +1 -0
- package/dist/config/defaults.d.ts +3 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +38 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/environment.d.ts +5 -0
- package/dist/config/environment.d.ts.map +1 -0
- package/dist/config/environment.js +62 -0
- package/dist/config/environment.js.map +1 -0
- package/dist/config/types.d.ts +47 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config/validation.d.ts +6 -0
- package/dist/config/validation.d.ts.map +1 -0
- package/dist/config/validation.js +147 -0
- package/dist/config/validation.js.map +1 -0
- package/dist/domain/accounts.d.ts +64 -0
- package/dist/domain/accounts.d.ts.map +1 -0
- package/dist/domain/accounts.js +2 -0
- package/dist/domain/accounts.js.map +1 -0
- package/dist/domain/errors.d.ts +61 -0
- package/dist/domain/errors.d.ts.map +1 -0
- package/dist/domain/errors.js +89 -0
- package/dist/domain/errors.js.map +1 -0
- package/dist/domain/http.d.ts +36 -0
- package/dist/domain/http.d.ts.map +1 -0
- package/dist/domain/http.js +2 -0
- package/dist/domain/http.js.map +1 -0
- package/dist/domain/manifest.d.ts +23 -0
- package/dist/domain/manifest.d.ts.map +1 -0
- package/dist/domain/manifest.js +2 -0
- package/dist/domain/manifest.js.map +1 -0
- package/dist/domain/records.d.ts +67 -0
- package/dist/domain/records.d.ts.map +1 -0
- package/dist/domain/records.js +2 -0
- package/dist/domain/records.js.map +1 -0
- package/dist/domain/requests.d.ts +87 -0
- package/dist/domain/requests.d.ts.map +1 -0
- package/dist/domain/requests.js +2 -0
- package/dist/domain/requests.js.map +1 -0
- package/dist/engine/api-engine.d.ts +25 -0
- package/dist/engine/api-engine.d.ts.map +1 -0
- package/dist/engine/api-engine.js +98 -0
- package/dist/engine/api-engine.js.map +1 -0
- package/dist/engine/extractors.d.ts +27 -0
- package/dist/engine/extractors.d.ts.map +1 -0
- package/dist/engine/extractors.js +248 -0
- package/dist/engine/extractors.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest/default-manifest.d.ts +3 -0
- package/dist/manifest/default-manifest.d.ts.map +1 -0
- package/dist/manifest/default-manifest.js +46 -0
- package/dist/manifest/default-manifest.js.map +1 -0
- package/dist/manifest/model.d.ts +3 -0
- package/dist/manifest/model.d.ts.map +1 -0
- package/dist/manifest/model.js +48 -0
- package/dist/manifest/model.js.map +1 -0
- package/dist/manifest/provider.d.ts +19 -0
- package/dist/manifest/provider.d.ts.map +1 -0
- package/dist/manifest/provider.js +94 -0
- package/dist/manifest/provider.js.map +1 -0
- package/dist/manifest/scraper.d.ts +11 -0
- package/dist/manifest/scraper.d.ts.map +1 -0
- package/dist/manifest/scraper.js +130 -0
- package/dist/manifest/scraper.js.map +1 -0
- package/dist/output/csv-writer.d.ts +2 -0
- package/dist/output/csv-writer.d.ts.map +1 -0
- package/dist/output/csv-writer.js +94 -0
- package/dist/output/csv-writer.js.map +1 -0
- package/dist/output/json-writer.d.ts +2 -0
- package/dist/output/json-writer.d.ts.map +1 -0
- package/dist/output/json-writer.js +29 -0
- package/dist/output/json-writer.js.map +1 -0
- package/dist/output/names.d.ts +4 -0
- package/dist/output/names.d.ts.map +1 -0
- package/dist/output/names.js +22 -0
- package/dist/output/names.js.map +1 -0
- package/dist/output/writer.d.ts +7 -0
- package/dist/output/writer.d.ts.map +1 -0
- package/dist/output/writer.js +33 -0
- package/dist/output/writer.js.map +1 -0
- package/dist/pool/account-pool.d.ts +29 -0
- package/dist/pool/account-pool.d.ts.map +1 -0
- package/dist/pool/account-pool.js +145 -0
- package/dist/pool/account-pool.js.map +1 -0
- package/dist/pool/cooldown.d.ts +18 -0
- package/dist/pool/cooldown.d.ts.map +1 -0
- package/dist/pool/cooldown.js +38 -0
- package/dist/pool/cooldown.js.map +1 -0
- package/dist/pool/limiter.d.ts +11 -0
- package/dist/pool/limiter.d.ts.map +1 -0
- package/dist/pool/limiter.js +27 -0
- package/dist/pool/limiter.js.map +1 -0
- package/dist/query/builder.d.ts +19 -0
- package/dist/query/builder.d.ts.map +1 -0
- package/dist/query/builder.js +110 -0
- package/dist/query/builder.js.map +1 -0
- package/dist/query/hash.d.ts +2 -0
- package/dist/query/hash.d.ts.map +1 -0
- package/dist/query/hash.js +6 -0
- package/dist/query/hash.js.map +1 -0
- package/dist/query/normalize.d.ts +39 -0
- package/dist/query/normalize.d.ts.map +1 -0
- package/dist/query/normalize.js +176 -0
- package/dist/query/normalize.js.map +1 -0
- package/dist/query/targets.d.ts +11 -0
- package/dist/query/targets.d.ts.map +1 -0
- package/dist/query/targets.js +96 -0
- package/dist/query/targets.js.map +1 -0
- package/dist/runner/index.d.ts +4 -0
- package/dist/runner/index.d.ts.map +1 -0
- package/dist/runner/index.js +4 -0
- package/dist/runner/index.js.map +1 -0
- package/dist/runner/retry.d.ts +7 -0
- package/dist/runner/retry.d.ts.map +1 -0
- package/dist/runner/retry.js +26 -0
- package/dist/runner/retry.js.map +1 -0
- package/dist/runner/runner.d.ts +20 -0
- package/dist/runner/runner.d.ts.map +1 -0
- package/dist/runner/runner.js +37 -0
- package/dist/runner/runner.js.map +1 -0
- package/dist/runner/task-queue.d.ts +19 -0
- package/dist/runner/task-queue.d.ts.map +1 -0
- package/dist/runner/task-queue.js +58 -0
- package/dist/runner/task-queue.js.map +1 -0
- package/dist/storage/account-repository.d.ts +50 -0
- package/dist/storage/account-repository.d.ts.map +1 -0
- package/dist/storage/account-repository.js +171 -0
- package/dist/storage/account-repository.js.map +1 -0
- package/dist/storage/account-row.d.ts +4 -0
- package/dist/storage/account-row.d.ts.map +1 -0
- package/dist/storage/account-row.js +46 -0
- package/dist/storage/account-row.js.map +1 -0
- package/dist/storage/checkpoint-repository.d.ts +10 -0
- package/dist/storage/checkpoint-repository.d.ts.map +1 -0
- package/dist/storage/checkpoint-repository.js +30 -0
- package/dist/storage/checkpoint-repository.js.map +1 -0
- package/dist/storage/database.d.ts +18 -0
- package/dist/storage/database.d.ts.map +1 -0
- package/dist/storage/database.js +42 -0
- package/dist/storage/database.js.map +1 -0
- package/dist/storage/index.d.ts +15 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +18 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/manifest-repository.d.ts +9 -0
- package/dist/storage/manifest-repository.d.ts.map +1 -0
- package/dist/storage/manifest-repository.js +22 -0
- package/dist/storage/manifest-repository.js.map +1 -0
- package/dist/storage/run-repository.d.ts +24 -0
- package/dist/storage/run-repository.d.ts.map +1 -0
- package/dist/storage/run-repository.js +51 -0
- package/dist/storage/run-repository.js.map +1 -0
- package/dist/storage/schema.d.ts +3 -0
- package/dist/storage/schema.d.ts.map +1 -0
- package/dist/storage/schema.js +53 -0
- package/dist/storage/schema.js.map +1 -0
- package/dist/transport/graphql.d.ts +8 -0
- package/dist/transport/graphql.d.ts.map +1 -0
- package/dist/transport/graphql.js +104 -0
- package/dist/transport/graphql.js.map +1 -0
- package/dist/transport/proxy.d.ts +5 -0
- package/dist/transport/proxy.d.ts.map +1 -0
- package/dist/transport/proxy.js +36 -0
- package/dist/transport/proxy.js.map +1 -0
- package/dist/transport/session.d.ts +25 -0
- package/dist/transport/session.d.ts.map +1 -0
- package/dist/transport/session.js +138 -0
- package/dist/transport/session.js.map +1 -0
- package/dist/transport/transaction-id.d.ts +20 -0
- package/dist/transport/transaction-id.d.ts.map +1 -0
- package/dist/transport/transaction-id.js +74 -0
- package/dist/transport/transaction-id.js.map +1 -0
- package/dist/utils/fingerprint.d.ts +3 -0
- package/dist/utils/fingerprint.d.ts.map +1 -0
- package/dist/utils/fingerprint.js +20 -0
- package/dist/utils/fingerprint.js.map +1 -0
- package/dist/utils/guards.d.ts +8 -0
- package/dist/utils/guards.d.ts.map +1 -0
- package/dist/utils/guards.js +38 -0
- package/dist/utils/guards.js.map +1 -0
- package/docs/architecture/overview.md +59 -0
- package/docs/decisions/0001-runtime-and-state.md +21 -0
- package/docs/decisions/0002-modular-boundaries.md +16 -0
- package/docs/plans/initial-build.md +18 -0
- package/docs/product/specification.md +87 -0
- package/docs/security/data-boundary.md +27 -0
- package/package.json +89 -0
package/dist/cli/main.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { realpathSync } from "node:fs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { XTrawl } from "../client/client.js";
|
|
5
|
+
import { HELP } from "./help.js";
|
|
6
|
+
import { collectionOptionsFromCli, parseArgs, searchRequestFromCli, CliUsageError } from "./parser.js";
|
|
7
|
+
export async function runCli(argv = process.argv.slice(2)) {
|
|
8
|
+
let verbose = false;
|
|
9
|
+
try {
|
|
10
|
+
const args = parseArgs(argv);
|
|
11
|
+
verbose = args.verbose;
|
|
12
|
+
if (!args.command) {
|
|
13
|
+
console.log(HELP);
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
const pretty = args.options.pretty === true;
|
|
17
|
+
const client = await XTrawl.create({
|
|
18
|
+
authToken: args.authToken,
|
|
19
|
+
csrfToken: args.csrfToken,
|
|
20
|
+
cookiesFile: args.cookiesFile,
|
|
21
|
+
envFile: args.envFile,
|
|
22
|
+
dbPath: args.dbPath,
|
|
23
|
+
proxy: args.proxy,
|
|
24
|
+
concurrency: args.concurrency,
|
|
25
|
+
manifestScrapeOnInit: args.manifestScrapeOnInit,
|
|
26
|
+
});
|
|
27
|
+
try {
|
|
28
|
+
if (verbose)
|
|
29
|
+
console.error(`[xtrawl] accounts=${client.poolSummary.total} eligible=${client.poolSummary.eligible} concurrency=${client.config.concurrency}`);
|
|
30
|
+
let result;
|
|
31
|
+
if (args.command === "search")
|
|
32
|
+
result = await client.search(args.values[0] ?? "", searchRequestFromCli(args));
|
|
33
|
+
else if (args.command === "tweet") {
|
|
34
|
+
const tweets = [];
|
|
35
|
+
for (const value of args.values)
|
|
36
|
+
tweets.push(await client.getTweet(value));
|
|
37
|
+
result = tweets;
|
|
38
|
+
}
|
|
39
|
+
else if (args.command === "user-info")
|
|
40
|
+
result = await client.getUserInfo(args.values, collectionOptionsFromCli(args));
|
|
41
|
+
else if (args.command === "profile-tweets")
|
|
42
|
+
result = await client.getProfileTweets(args.values, collectionOptionsFromCli(args));
|
|
43
|
+
else
|
|
44
|
+
result =
|
|
45
|
+
args.command === "followers"
|
|
46
|
+
? await client.getFollowers(args.values, collectionOptionsFromCli(args))
|
|
47
|
+
: args.command === "following"
|
|
48
|
+
? await client.getFollowing(args.values, collectionOptionsFromCli(args))
|
|
49
|
+
: await client.getVerifiedFollowers(args.values, collectionOptionsFromCli(args));
|
|
50
|
+
if (pretty)
|
|
51
|
+
console.log(JSON.stringify(result, null, 2));
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
client.close();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (error instanceof CliUsageError) {
|
|
60
|
+
console.error(error.message);
|
|
61
|
+
console.error(HELP);
|
|
62
|
+
return 2;
|
|
63
|
+
}
|
|
64
|
+
console.error(verbose && error instanceof Error
|
|
65
|
+
? (error.stack ?? error.message)
|
|
66
|
+
: error instanceof Error
|
|
67
|
+
? error.message
|
|
68
|
+
: String(error));
|
|
69
|
+
return 1;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function isDirectExecution() {
|
|
73
|
+
const entryPath = process.argv[1];
|
|
74
|
+
if (!entryPath)
|
|
75
|
+
return false;
|
|
76
|
+
try {
|
|
77
|
+
return realpathSync(entryPath) === realpathSync(fileURLToPath(import.meta.url));
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (isDirectExecution())
|
|
84
|
+
process.exitCode = await runCli();
|
|
85
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { FollowType, FollowsRequest, ProfileTimelineRequest, SearchRequest } from "../domain/requests.js";
|
|
2
|
+
export interface CliArgs {
|
|
3
|
+
readonly authToken?: string;
|
|
4
|
+
readonly csrfToken?: string;
|
|
5
|
+
readonly cookiesFile?: string;
|
|
6
|
+
readonly envFile?: string;
|
|
7
|
+
readonly dbPath: string;
|
|
8
|
+
readonly proxy?: string;
|
|
9
|
+
readonly concurrency: number;
|
|
10
|
+
readonly manifestScrapeOnInit: boolean;
|
|
11
|
+
readonly verbose: boolean;
|
|
12
|
+
readonly command?: "search" | "tweet" | "profile-tweets" | "followers" | "following" | "verified-followers" | "user-info";
|
|
13
|
+
readonly values: readonly string[];
|
|
14
|
+
readonly options: Readonly<Record<string, string | boolean | readonly string[]>>;
|
|
15
|
+
}
|
|
16
|
+
export declare class CliUsageError extends Error {
|
|
17
|
+
}
|
|
18
|
+
export declare function parseArgs(argv: readonly string[]): CliArgs;
|
|
19
|
+
export declare function searchRequestFromCli(args: CliArgs): SearchRequest;
|
|
20
|
+
export declare function collectionOptionsFromCli(args: CliArgs): Omit<ProfileTimelineRequest, "targets"> & Omit<FollowsRequest, "targets" | "followType">;
|
|
21
|
+
export declare function followType(command: CliArgs["command"]): FollowType | undefined;
|
|
22
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/cli/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,cAAc,EACd,sBAAsB,EACtB,aAAa,EACd,MAAM,uBAAuB,CAAC;AAE/B,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EACf,QAAQ,GAAG,OAAO,GAAG,gBAAgB,GAAG,WAAW,GAAG,WAAW,GAAG,oBAAoB,GAAG,WAAW,CAAC;IACzG,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,MAAM,EAAE,CAAC,CAAC,CAAC;CAClF;AAED,qBAAa,aAAc,SAAQ,KAAK;CAAG;AAE3C,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAiG1D;AA2FD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,aAAa,CAoDjE;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,OAAO,GACZ,IAAI,CAAC,sBAAsB,EAAE,SAAS,CAAC,GAAG,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,YAAY,CAAC,CAsB1F;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,UAAU,GAAG,SAAS,CAQ9E"}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
export class CliUsageError extends Error {
|
|
2
|
+
}
|
|
3
|
+
export function parseArgs(argv) {
|
|
4
|
+
const global = { dbPath: "xtrawl_state.db", concurrency: 5, manifestScrapeOnInit: false, verbose: false };
|
|
5
|
+
let index = 0;
|
|
6
|
+
while (index < argv.length && (argv[index]?.startsWith("--") || argv[index] === "-v")) {
|
|
7
|
+
const flag = argv[index];
|
|
8
|
+
if (!flag)
|
|
9
|
+
break;
|
|
10
|
+
if (flag === "--help")
|
|
11
|
+
return { ...global, values: [], options: {}, command: undefined };
|
|
12
|
+
if (flag === "--verbose" || flag === "-v") {
|
|
13
|
+
global.verbose = true;
|
|
14
|
+
index += 1;
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (flag === "--manifest-scrape-on-init") {
|
|
18
|
+
global.manifestScrapeOnInit = true;
|
|
19
|
+
index += 1;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const [key, value] = splitFlag(flag, argv[index + 1]);
|
|
23
|
+
if (key === "auth-token")
|
|
24
|
+
global.authToken = value;
|
|
25
|
+
else if (key === "csrf-token" || key === "ct0")
|
|
26
|
+
global.csrfToken = value;
|
|
27
|
+
else if (key === "cookies-file")
|
|
28
|
+
global.cookiesFile = value;
|
|
29
|
+
else if (key === "env-file")
|
|
30
|
+
global.envFile = value;
|
|
31
|
+
else if (key === "db-path")
|
|
32
|
+
global.dbPath = value;
|
|
33
|
+
else if (key === "proxy")
|
|
34
|
+
global.proxy = value;
|
|
35
|
+
else if (key === "concurrency")
|
|
36
|
+
global.concurrency = positiveNumber(value, "concurrency");
|
|
37
|
+
else
|
|
38
|
+
throw new CliUsageError(`Unknown option: --${key}`);
|
|
39
|
+
index += flag.includes("=") ? 1 : 2;
|
|
40
|
+
}
|
|
41
|
+
const command = argv[index];
|
|
42
|
+
if (!command)
|
|
43
|
+
return { ...global, values: [], options: {}, command: undefined };
|
|
44
|
+
if (![
|
|
45
|
+
"search",
|
|
46
|
+
"tweet",
|
|
47
|
+
"profile-tweets",
|
|
48
|
+
"followers",
|
|
49
|
+
"following",
|
|
50
|
+
"verified-followers",
|
|
51
|
+
"user-info",
|
|
52
|
+
].includes(command))
|
|
53
|
+
throw new CliUsageError(`Unknown command: ${command}`);
|
|
54
|
+
index += 1;
|
|
55
|
+
const values = [];
|
|
56
|
+
const options = {};
|
|
57
|
+
while (index < argv.length) {
|
|
58
|
+
const value = argv[index];
|
|
59
|
+
if (!value.startsWith("--")) {
|
|
60
|
+
values.push(value);
|
|
61
|
+
index += 1;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
const equals = value.indexOf("=");
|
|
65
|
+
const key = equals > 2 ? value.slice(2, equals) : value.slice(2);
|
|
66
|
+
if (!KNOWN_OPTIONS.has(key))
|
|
67
|
+
throw new CliUsageError(`Unknown command option: --${key}`);
|
|
68
|
+
if (BOOLEAN_OPTIONS.has(key)) {
|
|
69
|
+
if (equals > 2)
|
|
70
|
+
throw new CliUsageError(`--${key} does not accept a value`);
|
|
71
|
+
options[key] = true;
|
|
72
|
+
index += 1;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (LIST_OPTIONS.has(key)) {
|
|
76
|
+
const items = equals > 2 ? [value.slice(equals + 1)] : followingValues(argv, index + 1);
|
|
77
|
+
if (items.length === 0)
|
|
78
|
+
throw new CliUsageError(`--${key} requires at least one value`);
|
|
79
|
+
const existing = Array.isArray(options[key]) ? options[key] : [];
|
|
80
|
+
options[key] = [...existing, ...items];
|
|
81
|
+
index += equals > 2 ? 1 : items.length + 1;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const flagValue = equals > 2 ? value.slice(equals + 1) : argv[index + 1];
|
|
85
|
+
if (!flagValue || flagValue.startsWith("--"))
|
|
86
|
+
throw new CliUsageError(`--${key} requires a value`);
|
|
87
|
+
validateCommandValue(key, flagValue);
|
|
88
|
+
options[key] = flagValue;
|
|
89
|
+
index += equals > 2 ? 1 : 2;
|
|
90
|
+
}
|
|
91
|
+
if (["tweet", "profile-tweets", "followers", "following", "verified-followers", "user-info"].includes(command) &&
|
|
92
|
+
values.length === 0)
|
|
93
|
+
throw new CliUsageError(command === "tweet"
|
|
94
|
+
? "tweet requires at least one post ID or status URL"
|
|
95
|
+
: `${command} requires at least one user`);
|
|
96
|
+
return { ...global, command, values, options };
|
|
97
|
+
}
|
|
98
|
+
const LIST_OPTIONS = new Set([
|
|
99
|
+
"from",
|
|
100
|
+
"to",
|
|
101
|
+
"mention",
|
|
102
|
+
"all-words",
|
|
103
|
+
"any-words",
|
|
104
|
+
"exact-phrases",
|
|
105
|
+
"hashtags-any",
|
|
106
|
+
"hashtags-exclude",
|
|
107
|
+
"exclude-words",
|
|
108
|
+
]);
|
|
109
|
+
const BOOLEAN_OPTIONS = new Set([
|
|
110
|
+
"save",
|
|
111
|
+
"pretty",
|
|
112
|
+
"resume",
|
|
113
|
+
"raw-json",
|
|
114
|
+
"has-images",
|
|
115
|
+
"has-videos",
|
|
116
|
+
"has-links",
|
|
117
|
+
"has-mentions",
|
|
118
|
+
"has-hashtags",
|
|
119
|
+
"verified-only",
|
|
120
|
+
"blue-verified-only",
|
|
121
|
+
]);
|
|
122
|
+
const VALUE_OPTIONS = new Set([
|
|
123
|
+
"limit",
|
|
124
|
+
"max-empty-pages",
|
|
125
|
+
"per-profile-limit",
|
|
126
|
+
"max-pages-per-profile",
|
|
127
|
+
"save-format",
|
|
128
|
+
"save-dir",
|
|
129
|
+
"save-name",
|
|
130
|
+
"since",
|
|
131
|
+
"until",
|
|
132
|
+
"lang",
|
|
133
|
+
"display-type",
|
|
134
|
+
"tweet-type",
|
|
135
|
+
"min-likes",
|
|
136
|
+
"min-replies",
|
|
137
|
+
"min-retweets",
|
|
138
|
+
"place",
|
|
139
|
+
"geocode",
|
|
140
|
+
"near",
|
|
141
|
+
"within",
|
|
142
|
+
]);
|
|
143
|
+
const KNOWN_OPTIONS = new Set([...LIST_OPTIONS, ...BOOLEAN_OPTIONS, ...VALUE_OPTIONS]);
|
|
144
|
+
const POSITIVE_OPTIONS = new Set(["limit", "max-empty-pages", "per-profile-limit", "max-pages-per-profile"]);
|
|
145
|
+
const NON_NEGATIVE_OPTIONS = new Set(["min-likes", "min-replies", "min-retweets"]);
|
|
146
|
+
function validateCommandValue(key, value) {
|
|
147
|
+
if (POSITIVE_OPTIONS.has(key))
|
|
148
|
+
positiveNumber(value, key);
|
|
149
|
+
if (NON_NEGATIVE_OPTIONS.has(key)) {
|
|
150
|
+
const parsed = Number(value);
|
|
151
|
+
if (!Number.isInteger(parsed) || parsed < 0)
|
|
152
|
+
throw new CliUsageError(`--${key} must be a non-negative integer`);
|
|
153
|
+
}
|
|
154
|
+
if (key === "save-format" && !["csv", "json", "both"].includes(value))
|
|
155
|
+
throw new CliUsageError("--save-format must be csv, json, or both");
|
|
156
|
+
if (key === "display-type" && value !== "Top" && value !== "Latest")
|
|
157
|
+
throw new CliUsageError("--display-type must be Top or Latest");
|
|
158
|
+
if (key === "tweet-type" &&
|
|
159
|
+
![
|
|
160
|
+
"all",
|
|
161
|
+
"originals_only",
|
|
162
|
+
"replies_only",
|
|
163
|
+
"retweets_only",
|
|
164
|
+
"exclude_replies",
|
|
165
|
+
"exclude_retweets",
|
|
166
|
+
].includes(value))
|
|
167
|
+
throw new CliUsageError(`Unsupported --tweet-type value: ${value}`);
|
|
168
|
+
}
|
|
169
|
+
function followingValues(argv, start) {
|
|
170
|
+
const values = [];
|
|
171
|
+
for (let index = start; index < argv.length; index += 1) {
|
|
172
|
+
const value = argv[index];
|
|
173
|
+
if (!value || value.startsWith("--"))
|
|
174
|
+
break;
|
|
175
|
+
values.push(value);
|
|
176
|
+
}
|
|
177
|
+
return values;
|
|
178
|
+
}
|
|
179
|
+
export function searchRequestFromCli(args) {
|
|
180
|
+
const option = args.options;
|
|
181
|
+
const list = (key) => Array.isArray(option[key]) ? option[key] : option[key] ? [String(option[key])] : undefined;
|
|
182
|
+
const boolean = (key) => option[key] === undefined ? undefined : Boolean(option[key]);
|
|
183
|
+
const number = (key) => option[key] === undefined ? undefined : Number(option[key]);
|
|
184
|
+
return {
|
|
185
|
+
...(args.values[0] ? { searchQuery: args.values[0] } : {}),
|
|
186
|
+
...(option.since ? { since: String(option.since) } : {}),
|
|
187
|
+
...(option.until ? { until: String(option.until) } : {}),
|
|
188
|
+
...(list("from") ? { fromUsers: list("from") } : {}),
|
|
189
|
+
...(list("to") ? { toUsers: list("to") } : {}),
|
|
190
|
+
...(list("mention") ? { mentioningUsers: list("mention") } : {}),
|
|
191
|
+
...(list("all-words") ? { allWords: list("all-words") } : {}),
|
|
192
|
+
...(list("any-words") ? { anyWords: list("any-words") } : {}),
|
|
193
|
+
...(list("exact-phrases") ? { exactPhrases: list("exact-phrases") } : {}),
|
|
194
|
+
...(list("hashtags-any") ? { hashtagsAny: list("hashtags-any") } : {}),
|
|
195
|
+
...(list("hashtags-exclude") ? { hashtagsExclude: list("hashtags-exclude") } : {}),
|
|
196
|
+
...(list("exclude-words") ? { excludeWords: list("exclude-words") } : {}),
|
|
197
|
+
...(option.lang ? { lang: String(option.lang) } : {}),
|
|
198
|
+
...(option.place ? { place: String(option.place) } : {}),
|
|
199
|
+
...(option.geocode ? { geocode: String(option.geocode) } : {}),
|
|
200
|
+
...(option.near ? { near: String(option.near) } : {}),
|
|
201
|
+
...(option.within ? { within: String(option.within) } : {}),
|
|
202
|
+
...(option["display-type"] ? { displayType: String(option["display-type"]) } : {}),
|
|
203
|
+
...(option["tweet-type"]
|
|
204
|
+
? { tweetType: String(option["tweet-type"]) }
|
|
205
|
+
: {}),
|
|
206
|
+
...(number("limit") !== undefined ? { limit: number("limit") } : {}),
|
|
207
|
+
...(number("max-empty-pages") !== undefined ? { maxEmptyPages: number("max-empty-pages") } : {}),
|
|
208
|
+
...(number("min-likes") !== undefined ? { minLikes: number("min-likes") } : {}),
|
|
209
|
+
...(number("min-replies") !== undefined ? { minReplies: number("min-replies") } : {}),
|
|
210
|
+
...(number("min-retweets") !== undefined ? { minRetweets: number("min-retweets") } : {}),
|
|
211
|
+
...(boolean("verified-only") !== undefined ? { verifiedOnly: boolean("verified-only") } : {}),
|
|
212
|
+
...(boolean("blue-verified-only") !== undefined
|
|
213
|
+
? { blueVerifiedOnly: boolean("blue-verified-only") }
|
|
214
|
+
: {}),
|
|
215
|
+
...(boolean("has-images") !== undefined ? { hasImages: boolean("has-images") } : {}),
|
|
216
|
+
...(boolean("has-videos") !== undefined ? { hasVideos: boolean("has-videos") } : {}),
|
|
217
|
+
...(boolean("has-links") !== undefined ? { hasLinks: boolean("has-links") } : {}),
|
|
218
|
+
...(boolean("has-mentions") !== undefined ? { hasMentions: boolean("has-mentions") } : {}),
|
|
219
|
+
...(boolean("has-hashtags") !== undefined ? { hasHashtags: boolean("has-hashtags") } : {}),
|
|
220
|
+
...(option.save ? { save: true } : {}),
|
|
221
|
+
...(option["save-format"]
|
|
222
|
+
? { saveFormat: String(option["save-format"]) }
|
|
223
|
+
: {}),
|
|
224
|
+
...(option["save-dir"] ? { saveDir: String(option["save-dir"]) } : {}),
|
|
225
|
+
...(option["save-name"] ? { saveName: String(option["save-name"]) } : {}),
|
|
226
|
+
...(option.resume ? { resume: true } : {}),
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
export function collectionOptionsFromCli(args) {
|
|
230
|
+
const option = args.options;
|
|
231
|
+
const number = (key) => {
|
|
232
|
+
const value = option[key];
|
|
233
|
+
return value === undefined || typeof value === "boolean" ? undefined : Number(value);
|
|
234
|
+
};
|
|
235
|
+
return {
|
|
236
|
+
...(number("limit") !== undefined ? { limit: number("limit") } : {}),
|
|
237
|
+
...(number("per-profile-limit") !== undefined ? { perProfileLimit: number("per-profile-limit") } : {}),
|
|
238
|
+
...(number("max-pages-per-profile") !== undefined
|
|
239
|
+
? { maxPagesPerProfile: number("max-pages-per-profile") }
|
|
240
|
+
: {}),
|
|
241
|
+
...(number("max-empty-pages") !== undefined ? { maxEmptyPages: number("max-empty-pages") } : {}),
|
|
242
|
+
...(option.resume === true ? { resume: true } : {}),
|
|
243
|
+
...(option.save === true ? { save: true } : {}),
|
|
244
|
+
...(option["save-format"]
|
|
245
|
+
? { saveFormat: String(option["save-format"]) }
|
|
246
|
+
: {}),
|
|
247
|
+
...(option["save-dir"] ? { saveDir: String(option["save-dir"]) } : {}),
|
|
248
|
+
...(option["save-name"] ? { saveName: String(option["save-name"]) } : {}),
|
|
249
|
+
...(option["raw-json"] === true ? { rawJson: true } : {}),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
export function followType(command) {
|
|
253
|
+
return command === "followers"
|
|
254
|
+
? "followers"
|
|
255
|
+
: command === "following"
|
|
256
|
+
? "following"
|
|
257
|
+
: command === "verified-followers"
|
|
258
|
+
? "verified_followers"
|
|
259
|
+
: undefined;
|
|
260
|
+
}
|
|
261
|
+
function splitFlag(flag, next) {
|
|
262
|
+
const equals = flag.indexOf("=");
|
|
263
|
+
if (equals > 2)
|
|
264
|
+
return [flag.slice(2, equals), flag.slice(equals + 1)];
|
|
265
|
+
if (!next || next.startsWith("--"))
|
|
266
|
+
return [flag.slice(2), "__flag__"];
|
|
267
|
+
return [flag.slice(2), next];
|
|
268
|
+
}
|
|
269
|
+
function positiveNumber(value, name) {
|
|
270
|
+
const parsed = Number(value);
|
|
271
|
+
if (!Number.isInteger(parsed) || parsed < 1)
|
|
272
|
+
throw new CliUsageError(`${name} must be a positive integer`);
|
|
273
|
+
return parsed;
|
|
274
|
+
}
|
|
275
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/cli/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ClientConfig } from "../config/types.js";
|
|
2
|
+
import type { FollowRecord, ProfileRecord, SearchResult, TweetRecord } from "../domain/records.js";
|
|
3
|
+
import type { FollowsRequest, ProfileTimelineRequest, SearchRequest, TargetInput, UserInfoRequest } from "../domain/requests.js";
|
|
4
|
+
import { type StorageBundle } from "../storage/index.js";
|
|
5
|
+
import type { ClientInspection, ClientOptions } from "./types.js";
|
|
6
|
+
import { XTrawlDatabase } from "./database.js";
|
|
7
|
+
export declare class XTrawl {
|
|
8
|
+
readonly config: ClientConfig;
|
|
9
|
+
readonly storage: StorageBundle;
|
|
10
|
+
readonly db: XTrawlDatabase;
|
|
11
|
+
private readonly pool;
|
|
12
|
+
private readonly engine;
|
|
13
|
+
constructor(options?: ClientOptions);
|
|
14
|
+
static create(options?: ClientOptions): Promise<XTrawl>;
|
|
15
|
+
search(query?: string, options?: SearchRequest): Promise<SearchResult>;
|
|
16
|
+
getUserInfo(targets: readonly (string | TargetInput)[], options?: UserInfoRequest): Promise<readonly ProfileRecord[]>;
|
|
17
|
+
getTweet(target: string): Promise<TweetRecord | undefined>;
|
|
18
|
+
getProfileTweets(targets: readonly (string | TargetInput)[], options?: Omit<ProfileTimelineRequest, "targets">): Promise<SearchResult>;
|
|
19
|
+
getFollowers(targets: readonly (string | TargetInput)[], options?: Omit<FollowsRequest, "targets" | "followType">): Promise<readonly FollowRecord[]>;
|
|
20
|
+
getFollowing(targets: readonly (string | TargetInput)[], options?: Omit<FollowsRequest, "targets" | "followType">): Promise<readonly FollowRecord[]>;
|
|
21
|
+
getVerifiedFollowers(targets: readonly (string | TargetInput)[], options?: Omit<FollowsRequest, "targets" | "followType">): Promise<readonly FollowRecord[]>;
|
|
22
|
+
inspect(): ClientInspection;
|
|
23
|
+
get poolSummary(): import("../domain/accounts.js").AccountSummary;
|
|
24
|
+
close(): void;
|
|
25
|
+
private collectionContext;
|
|
26
|
+
private provision;
|
|
27
|
+
private bootstrapMissingAccounts;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvD,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnG,OAAO,KAAK,EACV,cAAc,EACd,sBAAsB,EACtB,aAAa,EACb,WAAW,EACX,eAAe,EAChB,MAAM,uBAAuB,CAAC;AAQ/B,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAQtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,qBAAa,MAAM;IACjB,SAAgB,MAAM,EAAE,YAAY,CAAC;IACrC,SAAgB,OAAO,EAAE,aAAa,CAAC;IACvC,SAAgB,EAAE,EAAE,cAAc,CAAC;IACnC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;gBAEhB,OAAO,GAAE,aAAkB;WA6C1B,MAAM,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,MAAM,CAAC;IAM3D,MAAM,CAAC,KAAK,SAAK,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC;IAItE,WAAW,CACtB,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,EAC1C,OAAO,GAAE,eAAoB,GAC5B,OAAO,CAAC,SAAS,aAAa,EAAE,CAAC;IAKvB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAM1D,gBAAgB,CAC3B,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,EAC1C,OAAO,GAAE,IAAI,CAAC,sBAAsB,EAAE,SAAS,CAAM,GACpD,OAAO,CAAC,YAAY,CAAC;IAIX,YAAY,CACvB,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,EAC1C,OAAO,GAAE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,YAAY,CAAM,GAC3D,OAAO,CAAC,SAAS,YAAY,EAAE,CAAC;IAStB,YAAY,CACvB,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,EAC1C,OAAO,GAAE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,YAAY,CAAM,GAC3D,OAAO,CAAC,SAAS,YAAY,EAAE,CAAC;IAStB,oBAAoB,CAC/B,OAAO,EAAE,SAAS,CAAC,MAAM,GAAG,WAAW,CAAC,EAAE,EAC1C,OAAO,GAAE,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,YAAY,CAAM,GAC3D,OAAO,CAAC,SAAS,YAAY,EAAE,CAAC;IAS5B,OAAO,IAAI,gBAAgB;IAWlC,IAAW,WAAW,mDAErB;IAEM,KAAK,IAAI,IAAI;IAIpB,OAAO,CAAC,iBAAiB;IAIzB,OAAO,CAAC,SAAS;YAqBH,wBAAwB;CAYvC"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { loadAccountFromEnvironmentSync } from "../config/environment.js";
|
|
2
|
+
import { validateConfig } from "../config/validation.js";
|
|
3
|
+
import { ConfigError } from "../domain/errors.js";
|
|
4
|
+
import { ApiEngine } from "../engine/api-engine.js";
|
|
5
|
+
import { loadAccountsFileSync, loadInlineAccounts } from "../auth/loaders.js";
|
|
6
|
+
import { accountInputToRecord } from "../auth/records.js";
|
|
7
|
+
import { bootstrapCookiesFromAuthToken } from "../auth/bootstrap.js";
|
|
8
|
+
import { ManifestProvider } from "../manifest/provider.js";
|
|
9
|
+
import { AccountPool } from "../pool/account-pool.js";
|
|
10
|
+
import { openStorage } from "../storage/index.js";
|
|
11
|
+
import { GraphqlTransport } from "../transport/graphql.js";
|
|
12
|
+
import { SessionBuilder } from "../transport/session.js";
|
|
13
|
+
import { TransactionIdProvider } from "../transport/transaction-id.js";
|
|
14
|
+
import { normalizeTargets } from "../query/targets.js";
|
|
15
|
+
import { collectFollows, collectProfileTweets } from "./collectors.js";
|
|
16
|
+
import { collectSearch } from "./search.js";
|
|
17
|
+
import { collectProfiles } from "./profiles.js";
|
|
18
|
+
import { XTrawlDatabase } from "./database.js";
|
|
19
|
+
export class XTrawl {
|
|
20
|
+
config;
|
|
21
|
+
storage;
|
|
22
|
+
db;
|
|
23
|
+
pool;
|
|
24
|
+
engine;
|
|
25
|
+
constructor(options = {}) {
|
|
26
|
+
this.config = validateConfig(options);
|
|
27
|
+
this.storage = openStorage(this.config.dbPath, {
|
|
28
|
+
dailyRequestsLimit: this.config.dailyRequestsLimit,
|
|
29
|
+
dailyTweetsLimit: this.config.dailyTweetsLimit,
|
|
30
|
+
leaseTtlMs: this.config.leaseTtlMs,
|
|
31
|
+
});
|
|
32
|
+
this.db = new XTrawlDatabase(this.storage);
|
|
33
|
+
this.provision(options);
|
|
34
|
+
const manifestAccount = this.storage.accounts.list().find((account) => Boolean(account.authToken));
|
|
35
|
+
const manifests = new ManifestProvider(this.config, this.storage.manifests, undefined, manifestAccount?.authToken);
|
|
36
|
+
const transactions = new TransactionIdProvider(options.transactionIdSource, {
|
|
37
|
+
enabled: this.config.transactionIdEnabled && (!options.sessionFactory || Boolean(options.transactionIdSource)),
|
|
38
|
+
ttlMs: this.config.transactionIdTtlMs,
|
|
39
|
+
});
|
|
40
|
+
const sessions = new SessionBuilder({
|
|
41
|
+
bearerToken: this.config.bearerToken,
|
|
42
|
+
...(this.config.proxy ? { defaultProxy: this.config.proxy } : {}),
|
|
43
|
+
...(this.config.apiUserAgent ? { userAgent: this.config.apiUserAgent } : {}),
|
|
44
|
+
httpMode: this.config.apiHttpMode,
|
|
45
|
+
...(this.config.apiHttpImpersonate ? { impersonate: this.config.apiHttpImpersonate } : {}),
|
|
46
|
+
...(options.sessionFactory ? { factory: options.sessionFactory } : {}),
|
|
47
|
+
});
|
|
48
|
+
this.pool = new AccountPool(this.storage.accounts, sessions, this.config, async (account) => {
|
|
49
|
+
if (!account.authToken)
|
|
50
|
+
return false;
|
|
51
|
+
const cookies = await bootstrapCookiesFromAuthToken(account.authToken, undefined, account.proxy);
|
|
52
|
+
if (!cookies?.ct0)
|
|
53
|
+
return false;
|
|
54
|
+
this.storage.accounts.upsert({
|
|
55
|
+
...account,
|
|
56
|
+
status: 1,
|
|
57
|
+
availableUntil: 0,
|
|
58
|
+
csrfToken: cookies.ct0,
|
|
59
|
+
cookies: { ...account.cookies, ...cookies },
|
|
60
|
+
});
|
|
61
|
+
return true;
|
|
62
|
+
});
|
|
63
|
+
this.engine = new ApiEngine(this.config, manifests, new GraphqlTransport(transactions));
|
|
64
|
+
}
|
|
65
|
+
static async create(options = {}) {
|
|
66
|
+
const client = new XTrawl(options);
|
|
67
|
+
await client.bootstrapMissingAccounts();
|
|
68
|
+
return client;
|
|
69
|
+
}
|
|
70
|
+
async search(query = "", options = {}) {
|
|
71
|
+
return collectSearch(this.collectionContext(), query, options);
|
|
72
|
+
}
|
|
73
|
+
async getUserInfo(targets, options = {}) {
|
|
74
|
+
const normalized = normalizeTargets(targets).targets;
|
|
75
|
+
return collectProfiles(this.collectionContext(), normalized, options);
|
|
76
|
+
}
|
|
77
|
+
async getTweet(target) {
|
|
78
|
+
const tweetId = tweetIdFromTarget(target);
|
|
79
|
+
if (!tweetId)
|
|
80
|
+
throw new ConfigError("Tweet lookup requires a numeric tweet ID or status URL.");
|
|
81
|
+
return this.pool.execute("tweet", ({ session }) => this.engine.tweetResult(session, tweetId));
|
|
82
|
+
}
|
|
83
|
+
async getProfileTweets(targets, options = {}) {
|
|
84
|
+
return collectProfileTweets(this.collectionContext(), normalizeTargets(targets).targets, options);
|
|
85
|
+
}
|
|
86
|
+
async getFollowers(targets, options = {}) {
|
|
87
|
+
const normalized = normalizeTargets(targets).targets;
|
|
88
|
+
return collectFollows(this.collectionContext(), normalized, {
|
|
89
|
+
targets: normalized,
|
|
90
|
+
...options,
|
|
91
|
+
followType: "followers",
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async getFollowing(targets, options = {}) {
|
|
95
|
+
const normalized = normalizeTargets(targets).targets;
|
|
96
|
+
return collectFollows(this.collectionContext(), normalized, {
|
|
97
|
+
targets: normalized,
|
|
98
|
+
...options,
|
|
99
|
+
followType: "following",
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
async getVerifiedFollowers(targets, options = {}) {
|
|
103
|
+
const normalized = normalizeTargets(targets).targets;
|
|
104
|
+
return collectFollows(this.collectionContext(), normalized, {
|
|
105
|
+
targets: normalized,
|
|
106
|
+
...options,
|
|
107
|
+
followType: "verified_followers",
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
inspect() {
|
|
111
|
+
return {
|
|
112
|
+
config: {
|
|
113
|
+
...this.config,
|
|
114
|
+
bearerToken: "[redacted]",
|
|
115
|
+
...(this.config.proxy ? { proxy: redactProxy(this.config.proxy) } : {}),
|
|
116
|
+
},
|
|
117
|
+
accounts: this.db.listAccounts(),
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
get poolSummary() {
|
|
121
|
+
return this.pool.summary;
|
|
122
|
+
}
|
|
123
|
+
close() {
|
|
124
|
+
this.storage.database.close();
|
|
125
|
+
}
|
|
126
|
+
collectionContext() {
|
|
127
|
+
return { config: this.config, pool: this.pool, engine: this.engine, storage: this.storage };
|
|
128
|
+
}
|
|
129
|
+
provision(options) {
|
|
130
|
+
if (options.provision === false)
|
|
131
|
+
return;
|
|
132
|
+
const records = [];
|
|
133
|
+
if (options.accounts)
|
|
134
|
+
records.push(...options.accounts.map(accountInputToRecord));
|
|
135
|
+
if (options.cookies !== undefined)
|
|
136
|
+
records.push(...loadInlineAccounts(options.cookies));
|
|
137
|
+
const authToken = options.authToken ?? process.env.X_AUTH_TOKEN;
|
|
138
|
+
const csrfToken = options.csrfToken ?? process.env.X_CSRF_TOKEN;
|
|
139
|
+
if (authToken)
|
|
140
|
+
records.push(accountInputToRecord({
|
|
141
|
+
authToken,
|
|
142
|
+
...(csrfToken ? { csrfToken } : {}),
|
|
143
|
+
}));
|
|
144
|
+
if (options.cookiesFile)
|
|
145
|
+
records.push(...loadAccountsFileSync(options.cookiesFile));
|
|
146
|
+
if (options.accountsFile)
|
|
147
|
+
records.push(...loadAccountsFileSync(options.accountsFile));
|
|
148
|
+
if (options.envFile)
|
|
149
|
+
records.push(...loadAccountFromEnvironmentSync(options.envFile).map(accountInputToRecord));
|
|
150
|
+
for (const record of records)
|
|
151
|
+
this.storage.accounts.upsert(record);
|
|
152
|
+
}
|
|
153
|
+
async bootstrapMissingAccounts() {
|
|
154
|
+
for (const account of this.storage.accounts.list()) {
|
|
155
|
+
if (!account.authToken || account.csrfToken)
|
|
156
|
+
continue;
|
|
157
|
+
const cookies = await bootstrapCookiesFromAuthToken(account.authToken, undefined, account.proxy);
|
|
158
|
+
if (cookies?.ct0)
|
|
159
|
+
this.storage.accounts.upsert({
|
|
160
|
+
...account,
|
|
161
|
+
csrfToken: cookies.ct0,
|
|
162
|
+
cookies: { ...account.cookies, ...cookies },
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function redactProxy(proxy) {
|
|
168
|
+
if (typeof proxy !== "string")
|
|
169
|
+
return {
|
|
170
|
+
...proxy,
|
|
171
|
+
...(proxy.password ? { password: "[redacted]" } : {}),
|
|
172
|
+
};
|
|
173
|
+
try {
|
|
174
|
+
const value = new URL(proxy.includes("://") ? proxy : `http://${proxy}`);
|
|
175
|
+
if (value.username)
|
|
176
|
+
value.username = "[redacted]";
|
|
177
|
+
if (value.password)
|
|
178
|
+
value.password = "[redacted]";
|
|
179
|
+
return value.toString();
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return "[redacted]";
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function tweetIdFromTarget(value) {
|
|
186
|
+
const target = value.trim();
|
|
187
|
+
if (/^\d+$/u.test(target))
|
|
188
|
+
return target;
|
|
189
|
+
const match = target.match(/^https?:\/\/(?:www\.)?(?:x\.com|twitter\.com)\/[^/]+\/status\/(\d+)(?:[/?#].*)?$/u);
|
|
190
|
+
return match?.[1];
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=client.js.map
|