@granitebps/twitter-mcp 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.
- package/LICENSE +7 -0
- package/README.md +326 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +35 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/env.d.ts +22 -0
- package/dist/config/env.js +44 -0
- package/dist/config/env.js.map +1 -0
- package/dist/domain/requests.d.ts +36 -0
- package/dist/domain/requests.js +2 -0
- package/dist/domain/requests.js.map +1 -0
- package/dist/domain/schemas.d.ts +95 -0
- package/dist/domain/schemas.js +53 -0
- package/dist/domain/schemas.js.map +1 -0
- package/dist/errors/twitter-error.d.ts +20 -0
- package/dist/errors/twitter-error.js +35 -0
- package/dist/errors/twitter-error.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/api/mapper.d.ts +34 -0
- package/dist/providers/api/mapper.js +45 -0
- package/dist/providers/api/mapper.js.map +1 -0
- package/dist/providers/api/provider.d.ts +44 -0
- package/dist/providers/api/provider.js +150 -0
- package/dist/providers/api/provider.js.map +1 -0
- package/dist/providers/deadline.d.ts +1 -0
- package/dist/providers/deadline.js +15 -0
- package/dist/providers/deadline.js.map +1 -0
- package/dist/providers/factory.d.ts +7 -0
- package/dist/providers/factory.js +17 -0
- package/dist/providers/factory.js.map +1 -0
- package/dist/providers/provider.d.ts +9 -0
- package/dist/providers/provider.js +2 -0
- package/dist/providers/provider.js.map +1 -0
- package/dist/providers/rettiwt/mapper.d.ts +29 -0
- package/dist/providers/rettiwt/mapper.js +42 -0
- package/dist/providers/rettiwt/mapper.js.map +1 -0
- package/dist/providers/rettiwt/provider.d.ts +30 -0
- package/dist/providers/rettiwt/provider.js +161 -0
- package/dist/providers/rettiwt/provider.js.map +1 -0
- package/dist/server/create-server.d.ts +9 -0
- package/dist/server/create-server.js +10 -0
- package/dist/server/create-server.js.map +1 -0
- package/dist/server/input.d.ts +17 -0
- package/dist/server/input.js +34 -0
- package/dist/server/input.js.map +1 -0
- package/dist/server/output.d.ts +8 -0
- package/dist/server/output.js +34 -0
- package/dist/server/output.js.map +1 -0
- package/dist/server/register-tools.d.ts +9 -0
- package/dist/server/register-tools.js +106 -0
- package/dist/server/register-tools.js.map +1 -0
- package/package.json +83 -0
- package/server.json +30 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export class TwitterError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
safeMessage;
|
|
4
|
+
provider;
|
|
5
|
+
retryable;
|
|
6
|
+
retryAfterSeconds;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
super(options.safeMessage, { cause: options.cause });
|
|
9
|
+
this.name = "TwitterError";
|
|
10
|
+
this.code = options.code;
|
|
11
|
+
this.safeMessage = options.safeMessage;
|
|
12
|
+
this.provider = options.provider;
|
|
13
|
+
this.retryable = options.retryable;
|
|
14
|
+
this.retryAfterSeconds = options.retryAfterSeconds;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const SECRET_PATTERNS = [
|
|
18
|
+
[/\bAuthorization:\s*(?:Bearer\s+)?[^\s,;]+/gi, "Authorization: [REDACTED]"],
|
|
19
|
+
[/\b(auth_token|ct0|RETTIWT_API_KEY)=([^\s,;]+)/gi, "$1=[REDACTED]"],
|
|
20
|
+
];
|
|
21
|
+
export function redactSecrets(value) {
|
|
22
|
+
return SECRET_PATTERNS.reduce((redacted, [pattern, replacement]) => redacted.replace(pattern, replacement), value);
|
|
23
|
+
}
|
|
24
|
+
export function toTwitterError(error, provider) {
|
|
25
|
+
if (error instanceof TwitterError)
|
|
26
|
+
return error;
|
|
27
|
+
return new TwitterError({
|
|
28
|
+
code: "INTERNAL_ERROR",
|
|
29
|
+
safeMessage: "The X provider returned an unexpected error",
|
|
30
|
+
provider,
|
|
31
|
+
retryable: false,
|
|
32
|
+
cause: error,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=twitter-error.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"twitter-error.js","sourceRoot":"","sources":["../../src/errors/twitter-error.ts"],"names":[],"mappings":"AAsBA,MAAM,OAAO,YAAa,SAAQ,KAAK;IAC5B,IAAI,CAAmB;IACvB,WAAW,CAAS;IACpB,QAAQ,CAAe;IACvB,SAAS,CAAU;IACnB,iBAAiB,CAAqB;IAE/C,YAAY,OAA4B;QACtC,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,iBAAiB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IACrD,CAAC;CACF;AAED,MAAM,eAAe,GAA4B;IAC/C,CAAC,6CAA6C,EAAE,2BAA2B,CAAC;IAC5E,CAAC,iDAAiD,EAAE,eAAe,CAAC;CACrE,CAAC;AAEF,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,OAAO,eAAe,CAAC,MAAM,CAC3B,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,WAAW,CAAC,EAC5E,KAAK,CACN,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,QAAsB;IACnE,IAAI,KAAK,YAAY,YAAY;QAAE,OAAO,KAAK,CAAC;IAEhD,OAAO,IAAI,YAAY,CAAC;QACtB,IAAI,EAAE,gBAAgB;QACtB,WAAW,EAAE,6CAA6C;QAC1D,QAAQ;QACR,SAAS,EAAE,KAAK;QAChB,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;AACL,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { loadConfig } from "./config/env.js";
|
|
2
|
+
export type { ApiConfig, ApiCredentials, RettiwtConfig, RuntimeConfig } from "./config/env.js";
|
|
3
|
+
export { ProviderWarningSchema, TweetPageSchema, TweetSchema, UserProfileSchema, } from "./domain/schemas.js";
|
|
4
|
+
export type { ProviderWarning, Tweet, TweetPage, UserProfile } from "./domain/schemas.js";
|
|
5
|
+
export type { GetProfileRequest, GetRepliesRequest, GetTweetRequest, ProviderCapabilities, RequestContext, SearchTweetsRequest, } from "./domain/requests.js";
|
|
6
|
+
export { redactSecrets, toTwitterError, TwitterError } from "./errors/twitter-error.js";
|
|
7
|
+
export type { ProviderName, TwitterErrorCode, TwitterErrorOptions, } from "./errors/twitter-error.js";
|
|
8
|
+
export { createProvider } from "./providers/factory.js";
|
|
9
|
+
export type { ProviderLoaders } from "./providers/factory.js";
|
|
10
|
+
export type { TwitterProvider } from "./providers/provider.js";
|
|
11
|
+
export { createTwitterServer } from "./server/create-server.js";
|
|
12
|
+
export type { CreateServerOptions } from "./server/create-server.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { loadConfig } from "./config/env.js";
|
|
2
|
+
export { ProviderWarningSchema, TweetPageSchema, TweetSchema, UserProfileSchema, } from "./domain/schemas.js";
|
|
3
|
+
export { redactSecrets, toTwitterError, TwitterError } from "./errors/twitter-error.js";
|
|
4
|
+
export { createProvider } from "./providers/factory.js";
|
|
5
|
+
export { createTwitterServer } from "./server/create-server.js";
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,WAAW,EACX,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAU7B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAMxF,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAGxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { Tweet, UserProfile } from "../../domain/schemas.js";
|
|
2
|
+
export interface ApiPublicMetricsLike {
|
|
3
|
+
like_count?: number;
|
|
4
|
+
retweet_count?: number;
|
|
5
|
+
reply_count?: number;
|
|
6
|
+
quote_count?: number;
|
|
7
|
+
bookmark_count?: number;
|
|
8
|
+
impression_count?: number;
|
|
9
|
+
followers_count?: number;
|
|
10
|
+
following_count?: number;
|
|
11
|
+
tweet_count?: number;
|
|
12
|
+
listed_count?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface ApiUserLike {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
username: string;
|
|
18
|
+
description?: string;
|
|
19
|
+
created_at?: string;
|
|
20
|
+
verified?: boolean;
|
|
21
|
+
profile_image_url?: string;
|
|
22
|
+
location?: string;
|
|
23
|
+
url?: string;
|
|
24
|
+
public_metrics?: ApiPublicMetricsLike;
|
|
25
|
+
}
|
|
26
|
+
export interface ApiTweetLike {
|
|
27
|
+
id: string;
|
|
28
|
+
text: string;
|
|
29
|
+
created_at?: string;
|
|
30
|
+
author_id?: string;
|
|
31
|
+
public_metrics?: ApiPublicMetricsLike;
|
|
32
|
+
}
|
|
33
|
+
export declare function mapApiTweet(tweet: ApiTweetLike, users?: ReadonlyMap<string, ApiUserLike>): Tweet;
|
|
34
|
+
export declare function mapApiUser(user: ApiUserLike): UserProfile;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export function mapApiTweet(tweet, users = new Map()) {
|
|
2
|
+
const author = tweet.author_id ? users.get(tweet.author_id) : undefined;
|
|
3
|
+
const metrics = tweet.public_metrics;
|
|
4
|
+
return {
|
|
5
|
+
id: tweet.id,
|
|
6
|
+
text: tweet.text,
|
|
7
|
+
...(tweet.created_at ? { created_at: tweet.created_at } : {}),
|
|
8
|
+
author: author
|
|
9
|
+
? {
|
|
10
|
+
name: author.name,
|
|
11
|
+
username: author.username,
|
|
12
|
+
verified: author.verified ?? false,
|
|
13
|
+
}
|
|
14
|
+
: null,
|
|
15
|
+
metrics: {
|
|
16
|
+
likes: metrics?.like_count ?? 0,
|
|
17
|
+
retweets: metrics?.retweet_count ?? 0,
|
|
18
|
+
replies: metrics?.reply_count ?? 0,
|
|
19
|
+
quotes: metrics?.quote_count ?? 0,
|
|
20
|
+
bookmarks: metrics?.bookmark_count ?? 0,
|
|
21
|
+
...(metrics?.impression_count === undefined ? {} : { views: metrics.impression_count }),
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function mapApiUser(user) {
|
|
26
|
+
const metrics = user.public_metrics;
|
|
27
|
+
return {
|
|
28
|
+
id: user.id,
|
|
29
|
+
name: user.name,
|
|
30
|
+
username: user.username,
|
|
31
|
+
...(user.description ? { description: user.description } : {}),
|
|
32
|
+
...(user.created_at ? { created_at: user.created_at } : {}),
|
|
33
|
+
verified: user.verified ?? false,
|
|
34
|
+
...(user.profile_image_url ? { profile_image_url: user.profile_image_url } : {}),
|
|
35
|
+
...(user.location ? { location: user.location } : {}),
|
|
36
|
+
...(user.url ? { url: user.url } : {}),
|
|
37
|
+
metrics: {
|
|
38
|
+
followers_count: metrics?.followers_count ?? 0,
|
|
39
|
+
following_count: metrics?.following_count ?? 0,
|
|
40
|
+
tweet_count: metrics?.tweet_count ?? 0,
|
|
41
|
+
listed_count: metrics?.listed_count ?? 0,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapper.js","sourceRoot":"","sources":["../../../src/providers/api/mapper.ts"],"names":[],"mappings":"AAoCA,MAAM,UAAU,WAAW,CACzB,KAAmB,EACnB,QAA0C,IAAI,GAAG,EAAE;IAEnD,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACxE,MAAM,OAAO,GAAG,KAAK,CAAC,cAAc,CAAC;IAErC,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,MAAM,EAAE,MAAM;YACZ,CAAC,CAAC;gBACE,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;gBACzB,QAAQ,EAAE,MAAM,CAAC,QAAQ,IAAI,KAAK;aACnC;YACH,CAAC,CAAC,IAAI;QACR,OAAO,EAAE;YACP,KAAK,EAAE,OAAO,EAAE,UAAU,IAAI,CAAC;YAC/B,QAAQ,EAAE,OAAO,EAAE,aAAa,IAAI,CAAC;YACrC,OAAO,EAAE,OAAO,EAAE,WAAW,IAAI,CAAC;YAClC,MAAM,EAAE,OAAO,EAAE,WAAW,IAAI,CAAC;YACjC,SAAS,EAAE,OAAO,EAAE,cAAc,IAAI,CAAC;YACvC,GAAG,CAAC,OAAO,EAAE,gBAAgB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,gBAAgB,EAAE,CAAC;SACxF;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAiB;IAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC;IACpC,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE;QACX,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK;QAChC,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChF,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtC,OAAO,EAAE;YACP,eAAe,EAAE,OAAO,EAAE,eAAe,IAAI,CAAC;YAC9C,eAAe,EAAE,OAAO,EAAE,eAAe,IAAI,CAAC;YAC9C,WAAW,EAAE,OAAO,EAAE,WAAW,IAAI,CAAC;YACtC,YAAY,EAAE,OAAO,EAAE,YAAY,IAAI,CAAC;SACzC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ApiConfig } from "../../config/env.js";
|
|
2
|
+
import type { Tweet, TweetPage, UserProfile } from "../../domain/schemas.js";
|
|
3
|
+
import type { GetProfileRequest, GetRepliesRequest, GetTweetRequest, ProviderCapabilities, RequestContext, SearchTweetsRequest } from "../../domain/requests.js";
|
|
4
|
+
import type { TwitterProvider } from "../provider.js";
|
|
5
|
+
import { type ApiTweetLike, type ApiUserLike } from "./mapper.js";
|
|
6
|
+
interface ApiTweetResponse {
|
|
7
|
+
data: ApiTweetLike;
|
|
8
|
+
includes?: {
|
|
9
|
+
users?: ApiUserLike[];
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
interface ApiSearchResponse {
|
|
13
|
+
data: {
|
|
14
|
+
data?: ApiTweetLike[];
|
|
15
|
+
meta?: {
|
|
16
|
+
next_token?: string;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
includes?: {
|
|
20
|
+
users?: ApiUserLike[];
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
interface ApiUserResponse {
|
|
24
|
+
data?: ApiUserLike;
|
|
25
|
+
}
|
|
26
|
+
export interface OfficialApiClient {
|
|
27
|
+
v2: {
|
|
28
|
+
singleTweet(id: string, options: Record<string, unknown>): Promise<ApiTweetResponse>;
|
|
29
|
+
search(query: string, options: Record<string, unknown>): Promise<ApiSearchResponse>;
|
|
30
|
+
userByUsername(username: string, options: Record<string, unknown>): Promise<ApiUserResponse>;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export declare class OfficialApiProvider implements TwitterProvider {
|
|
34
|
+
private readonly client;
|
|
35
|
+
readonly capabilities: ProviderCapabilities;
|
|
36
|
+
constructor(client: OfficialApiClient);
|
|
37
|
+
static create(config: ApiConfig): OfficialApiProvider;
|
|
38
|
+
getTweet(request: GetTweetRequest, context: RequestContext): Promise<Tweet>;
|
|
39
|
+
getTweetReplies(request: GetRepliesRequest, context: RequestContext): Promise<TweetPage>;
|
|
40
|
+
getUserProfile(request: GetProfileRequest, context: RequestContext): Promise<UserProfile>;
|
|
41
|
+
searchTweets(request: SearchTweetsRequest, context: RequestContext): Promise<TweetPage>;
|
|
42
|
+
private search;
|
|
43
|
+
}
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { TwitterApi } from "twitter-api-v2";
|
|
2
|
+
import { TwitterError, toTwitterError } from "../../errors/twitter-error.js";
|
|
3
|
+
import { withDeadline } from "../deadline.js";
|
|
4
|
+
import { mapApiTweet, mapApiUser } from "./mapper.js";
|
|
5
|
+
function timeoutError() {
|
|
6
|
+
return new TwitterError({
|
|
7
|
+
code: "TIMEOUT",
|
|
8
|
+
safeMessage: "The official X API request timed out",
|
|
9
|
+
provider: "api",
|
|
10
|
+
retryable: true,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
function mapApiFailure(error) {
|
|
14
|
+
if (error instanceof TwitterError)
|
|
15
|
+
return error;
|
|
16
|
+
const status = typeof error === "object" && error !== null && "code" in error ? Number(error.code) : undefined;
|
|
17
|
+
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
18
|
+
if (status === 401 || status === 403) {
|
|
19
|
+
return new TwitterError({
|
|
20
|
+
code: "AUTH_FAILED",
|
|
21
|
+
safeMessage: "Official X API authentication failed",
|
|
22
|
+
provider: "api",
|
|
23
|
+
retryable: false,
|
|
24
|
+
cause: error,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
if (status === 404) {
|
|
28
|
+
return new TwitterError({
|
|
29
|
+
code: "NOT_FOUND",
|
|
30
|
+
safeMessage: "The requested X resource was not found",
|
|
31
|
+
provider: "api",
|
|
32
|
+
retryable: false,
|
|
33
|
+
cause: error,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (status === 429) {
|
|
37
|
+
return new TwitterError({
|
|
38
|
+
code: "RATE_LIMITED",
|
|
39
|
+
safeMessage: "Official X API rate limit reached",
|
|
40
|
+
provider: "api",
|
|
41
|
+
retryable: true,
|
|
42
|
+
cause: error,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (message.includes("timeout") || message.includes("econnaborted"))
|
|
46
|
+
return timeoutError();
|
|
47
|
+
return toTwitterError(error, "api");
|
|
48
|
+
}
|
|
49
|
+
function userMap(users) {
|
|
50
|
+
return new Map((users ?? []).map((user) => [user.id, user]));
|
|
51
|
+
}
|
|
52
|
+
const TWEET_FIELDS = ["created_at", "public_metrics", "author_id", "conversation_id", "text"];
|
|
53
|
+
const USER_FIELDS = [
|
|
54
|
+
"name",
|
|
55
|
+
"username",
|
|
56
|
+
"description",
|
|
57
|
+
"created_at",
|
|
58
|
+
"verified",
|
|
59
|
+
"profile_image_url",
|
|
60
|
+
"public_metrics",
|
|
61
|
+
"location",
|
|
62
|
+
"url",
|
|
63
|
+
];
|
|
64
|
+
export class OfficialApiProvider {
|
|
65
|
+
client;
|
|
66
|
+
capabilities = {
|
|
67
|
+
provider: "api",
|
|
68
|
+
authenticated: true,
|
|
69
|
+
operations: {
|
|
70
|
+
getTweet: true,
|
|
71
|
+
getTweetReplies: true,
|
|
72
|
+
getUserProfile: true,
|
|
73
|
+
searchTweets: true,
|
|
74
|
+
},
|
|
75
|
+
pagination: { replies: true, search: true },
|
|
76
|
+
limits: { replies: 100, search: 100 },
|
|
77
|
+
};
|
|
78
|
+
constructor(client) {
|
|
79
|
+
this.client = client;
|
|
80
|
+
}
|
|
81
|
+
static create(config) {
|
|
82
|
+
const credentials = config.credentials;
|
|
83
|
+
const client = credentials.type === "bearer"
|
|
84
|
+
? new TwitterApi(credentials.token)
|
|
85
|
+
: new TwitterApi({
|
|
86
|
+
appKey: credentials.appKey,
|
|
87
|
+
appSecret: credentials.appSecret,
|
|
88
|
+
accessToken: credentials.accessToken,
|
|
89
|
+
accessSecret: credentials.accessSecret,
|
|
90
|
+
});
|
|
91
|
+
return new OfficialApiProvider(client);
|
|
92
|
+
}
|
|
93
|
+
async getTweet(request, context) {
|
|
94
|
+
try {
|
|
95
|
+
const response = await withDeadline(this.client.v2.singleTweet(request.tweetId, {
|
|
96
|
+
"tweet.fields": TWEET_FIELDS,
|
|
97
|
+
expansions: ["author_id"],
|
|
98
|
+
"user.fields": USER_FIELDS,
|
|
99
|
+
}), context.signal, context.deadline, timeoutError);
|
|
100
|
+
return mapApiTweet(response.data, userMap(response.includes?.users));
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
throw mapApiFailure(error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
getTweetReplies(request, context) {
|
|
107
|
+
return this.search(`conversation_id:${request.tweetId} is:reply`, request.maxResults, context);
|
|
108
|
+
}
|
|
109
|
+
async getUserProfile(request, context) {
|
|
110
|
+
try {
|
|
111
|
+
const response = await withDeadline(this.client.v2.userByUsername(request.username, { "user.fields": USER_FIELDS }), context.signal, context.deadline, timeoutError);
|
|
112
|
+
if (!response.data) {
|
|
113
|
+
throw new TwitterError({
|
|
114
|
+
code: "NOT_FOUND",
|
|
115
|
+
safeMessage: `User @${request.username} was not found`,
|
|
116
|
+
provider: "api",
|
|
117
|
+
retryable: false,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return mapApiUser(response.data);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
throw mapApiFailure(error);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
searchTweets(request, context) {
|
|
127
|
+
return this.search(request.query, request.maxResults, context);
|
|
128
|
+
}
|
|
129
|
+
async search(query, maxResults, context) {
|
|
130
|
+
try {
|
|
131
|
+
const response = await withDeadline(this.client.v2.search(query, {
|
|
132
|
+
max_results: Math.max(10, Math.min(maxResults, 100)),
|
|
133
|
+
"tweet.fields": TWEET_FIELDS,
|
|
134
|
+
expansions: ["author_id"],
|
|
135
|
+
"user.fields": USER_FIELDS,
|
|
136
|
+
}), context.signal, context.deadline, timeoutError);
|
|
137
|
+
const users = userMap(response.includes?.users);
|
|
138
|
+
return {
|
|
139
|
+
items: (response.data.data ?? [])
|
|
140
|
+
.slice(0, maxResults)
|
|
141
|
+
.map((tweet) => mapApiTweet(tweet, users)),
|
|
142
|
+
...(response.data.meta?.next_token ? { nextCursor: response.data.meta.next_token } : {}),
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
throw mapApiFailure(error);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../src/providers/api/provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function withDeadline<T>(operation: Promise<T>, signal: AbortSignal, deadline: number, createTimeoutError: () => Error): Promise<T>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function withDeadline(operation, signal, deadline, createTimeoutError) {
|
|
2
|
+
const remaining = deadline - Date.now();
|
|
3
|
+
if (signal.aborted || remaining <= 0)
|
|
4
|
+
throw createTimeoutError();
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
const timer = setTimeout(() => reject(createTimeoutError()), remaining);
|
|
7
|
+
const abort = () => reject(createTimeoutError());
|
|
8
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
9
|
+
operation.then(resolve, reject).finally(() => {
|
|
10
|
+
clearTimeout(timer);
|
|
11
|
+
signal.removeEventListener("abort", abort);
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=deadline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deadline.js","sourceRoot":"","sources":["../../src/providers/deadline.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,YAAY,CAC1B,SAAqB,EACrB,MAAmB,EACnB,QAAgB,EAChB,kBAA+B;IAE/B,MAAM,SAAS,GAAG,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACxC,IAAI,MAAM,CAAC,OAAO,IAAI,SAAS,IAAI,CAAC;QAAE,MAAM,kBAAkB,EAAE,CAAC;IAEjE,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACxC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC;QACxE,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,kBAAkB,EAAE,CAAC,CAAC;QACjD,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAExD,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE;YAC3C,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ApiConfig, RettiwtConfig, RuntimeConfig } from "../config/env.js";
|
|
2
|
+
import type { TwitterProvider } from "./provider.js";
|
|
3
|
+
export interface ProviderLoaders {
|
|
4
|
+
rettiwt(config: RettiwtConfig): Promise<TwitterProvider>;
|
|
5
|
+
api(config: ApiConfig): Promise<TwitterProvider>;
|
|
6
|
+
}
|
|
7
|
+
export declare function createProvider(config: RuntimeConfig, loaders?: ProviderLoaders): Promise<TwitterProvider>;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const defaultLoaders = {
|
|
2
|
+
async rettiwt(config) {
|
|
3
|
+
const { RettiwtProvider } = await import("./rettiwt/provider.js");
|
|
4
|
+
return RettiwtProvider.create(config);
|
|
5
|
+
},
|
|
6
|
+
async api(config) {
|
|
7
|
+
const { OfficialApiProvider } = await import("./api/provider.js");
|
|
8
|
+
return OfficialApiProvider.create(config);
|
|
9
|
+
},
|
|
10
|
+
};
|
|
11
|
+
export async function createProvider(config, loaders = defaultLoaders) {
|
|
12
|
+
if (config.mode === "rettiwt") {
|
|
13
|
+
return loaders.rettiwt(config);
|
|
14
|
+
}
|
|
15
|
+
return loaders.api(config);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/providers/factory.ts"],"names":[],"mappings":"AAQA,MAAM,cAAc,GAAoB;IACtC,KAAK,CAAC,OAAO,CAAC,MAAM;QAClB,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,uBAAuB,CAAC,CAAC;QAClE,OAAO,eAAe,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IACD,KAAK,CAAC,GAAG,CAAC,MAAM;QACd,MAAM,EAAE,mBAAmB,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;QAClE,OAAO,mBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,MAAqB,EACrB,UAA2B,cAAc;IAEzC,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAED,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Tweet, TweetPage, UserProfile } from "../domain/schemas.js";
|
|
2
|
+
import type { GetProfileRequest, GetRepliesRequest, GetTweetRequest, ProviderCapabilities, RequestContext, SearchTweetsRequest } from "../domain/requests.js";
|
|
3
|
+
export interface TwitterProvider {
|
|
4
|
+
readonly capabilities: ProviderCapabilities;
|
|
5
|
+
getTweet(request: GetTweetRequest, context: RequestContext): Promise<Tweet>;
|
|
6
|
+
getTweetReplies(request: GetRepliesRequest, context: RequestContext): Promise<TweetPage>;
|
|
7
|
+
getUserProfile(request: GetProfileRequest, context: RequestContext): Promise<UserProfile>;
|
|
8
|
+
searchTweets(request: SearchTweetsRequest, context: RequestContext): Promise<TweetPage>;
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/providers/provider.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Tweet, UserProfile } from "../../domain/schemas.js";
|
|
2
|
+
export interface RettiwtUserLike {
|
|
3
|
+
id?: string;
|
|
4
|
+
fullName: string;
|
|
5
|
+
userName: string;
|
|
6
|
+
createdAt?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
isVerified: boolean;
|
|
9
|
+
followersCount?: number;
|
|
10
|
+
followingsCount?: number;
|
|
11
|
+
statusesCount?: number;
|
|
12
|
+
likeCount?: number;
|
|
13
|
+
profileImage?: string;
|
|
14
|
+
location?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface RettiwtTweetLike {
|
|
17
|
+
id: string;
|
|
18
|
+
fullText: string;
|
|
19
|
+
createdAt?: string;
|
|
20
|
+
tweetBy?: Pick<RettiwtUserLike, "fullName" | "userName" | "isVerified"> | undefined;
|
|
21
|
+
likeCount?: number;
|
|
22
|
+
retweetCount?: number;
|
|
23
|
+
replyCount?: number;
|
|
24
|
+
quoteCount?: number;
|
|
25
|
+
bookmarkCount?: number;
|
|
26
|
+
viewCount?: number;
|
|
27
|
+
}
|
|
28
|
+
export declare function mapRettiwtTweet(tweet: RettiwtTweetLike): Tweet;
|
|
29
|
+
export declare function mapRettiwtUser(user: RettiwtUserLike): UserProfile;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function mapRettiwtTweet(tweet) {
|
|
2
|
+
return {
|
|
3
|
+
id: tweet.id,
|
|
4
|
+
text: tweet.fullText,
|
|
5
|
+
...(tweet.createdAt ? { created_at: tweet.createdAt } : {}),
|
|
6
|
+
author: tweet.tweetBy
|
|
7
|
+
? {
|
|
8
|
+
name: tweet.tweetBy.fullName,
|
|
9
|
+
username: tweet.tweetBy.userName,
|
|
10
|
+
verified: tweet.tweetBy.isVerified,
|
|
11
|
+
}
|
|
12
|
+
: null,
|
|
13
|
+
metrics: {
|
|
14
|
+
likes: tweet.likeCount ?? 0,
|
|
15
|
+
retweets: tweet.retweetCount ?? 0,
|
|
16
|
+
replies: tweet.replyCount ?? 0,
|
|
17
|
+
quotes: tweet.quoteCount ?? 0,
|
|
18
|
+
bookmarks: tweet.bookmarkCount ?? 0,
|
|
19
|
+
views: tweet.viewCount ?? 0,
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function mapRettiwtUser(user) {
|
|
24
|
+
return {
|
|
25
|
+
id: user.id ?? "",
|
|
26
|
+
name: user.fullName,
|
|
27
|
+
username: user.userName,
|
|
28
|
+
...(user.description ? { description: user.description } : {}),
|
|
29
|
+
...(user.createdAt ? { created_at: user.createdAt } : {}),
|
|
30
|
+
verified: user.isVerified,
|
|
31
|
+
...(user.profileImage ? { profile_image_url: user.profileImage } : {}),
|
|
32
|
+
...(user.location ? { location: user.location } : {}),
|
|
33
|
+
metrics: {
|
|
34
|
+
followers_count: user.followersCount ?? 0,
|
|
35
|
+
following_count: user.followingsCount ?? 0,
|
|
36
|
+
tweet_count: user.statusesCount ?? 0,
|
|
37
|
+
listed_count: 0,
|
|
38
|
+
likes_count: user.likeCount ?? 0,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=mapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapper.js","sourceRoot":"","sources":["../../../src/providers/rettiwt/mapper.ts"],"names":[],"mappings":"AA8BA,MAAM,UAAU,eAAe,CAAC,KAAuB;IACrD,OAAO;QACL,EAAE,EAAE,KAAK,CAAC,EAAE;QACZ,IAAI,EAAE,KAAK,CAAC,QAAQ;QACpB,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,MAAM,EAAE,KAAK,CAAC,OAAO;YACnB,CAAC,CAAC;gBACE,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ;gBAC5B,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ;gBAChC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,UAAU;aACnC;YACH,CAAC,CAAC,IAAI;QACR,OAAO,EAAE;YACP,KAAK,EAAE,KAAK,CAAC,SAAS,IAAI,CAAC;YAC3B,QAAQ,EAAE,KAAK,CAAC,YAAY,IAAI,CAAC;YACjC,OAAO,EAAE,KAAK,CAAC,UAAU,IAAI,CAAC;YAC9B,MAAM,EAAE,KAAK,CAAC,UAAU,IAAI,CAAC;YAC7B,SAAS,EAAE,KAAK,CAAC,aAAa,IAAI,CAAC;YACnC,KAAK,EAAE,KAAK,CAAC,SAAS,IAAI,CAAC;SAC5B;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAqB;IAClD,OAAO;QACL,EAAE,EAAE,IAAI,CAAC,EAAE,IAAI,EAAE;QACjB,IAAI,EAAE,IAAI,CAAC,QAAQ;QACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACzD,QAAQ,EAAE,IAAI,CAAC,UAAU;QACzB,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACtE,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACrD,OAAO,EAAE;YACP,eAAe,EAAE,IAAI,CAAC,cAAc,IAAI,CAAC;YACzC,eAAe,EAAE,IAAI,CAAC,eAAe,IAAI,CAAC;YAC1C,WAAW,EAAE,IAAI,CAAC,aAAa,IAAI,CAAC;YACpC,YAAY,EAAE,CAAC;YACf,WAAW,EAAE,IAAI,CAAC,SAAS,IAAI,CAAC;SACjC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { RettiwtConfig } from "../../config/env.js";
|
|
2
|
+
import type { Tweet, TweetPage, UserProfile } from "../../domain/schemas.js";
|
|
3
|
+
import type { GetProfileRequest, GetRepliesRequest, GetTweetRequest, ProviderCapabilities, RequestContext, SearchTweetsRequest } from "../../domain/requests.js";
|
|
4
|
+
import type { TwitterProvider } from "../provider.js";
|
|
5
|
+
import { type RettiwtTweetLike, type RettiwtUserLike } from "./mapper.js";
|
|
6
|
+
interface CursoredTweets {
|
|
7
|
+
list: RettiwtTweetLike[];
|
|
8
|
+
next: string;
|
|
9
|
+
}
|
|
10
|
+
export interface RettiwtClient {
|
|
11
|
+
tweet: {
|
|
12
|
+
details(id: string): Promise<RettiwtTweetLike | undefined>;
|
|
13
|
+
replies(id: string): Promise<CursoredTweets>;
|
|
14
|
+
search(filter: Record<string, unknown>, count?: number): Promise<CursoredTweets>;
|
|
15
|
+
};
|
|
16
|
+
user: {
|
|
17
|
+
details(id: string): Promise<RettiwtUserLike | undefined>;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export declare class RettiwtProvider implements TwitterProvider {
|
|
21
|
+
private readonly client;
|
|
22
|
+
readonly capabilities: ProviderCapabilities;
|
|
23
|
+
constructor(client: RettiwtClient);
|
|
24
|
+
static create(config: RettiwtConfig): RettiwtProvider;
|
|
25
|
+
getTweet(request: GetTweetRequest, context: RequestContext): Promise<Tweet>;
|
|
26
|
+
getTweetReplies(request: GetRepliesRequest, context: RequestContext): Promise<TweetPage>;
|
|
27
|
+
getUserProfile(request: GetProfileRequest, context: RequestContext): Promise<UserProfile>;
|
|
28
|
+
searchTweets(request: SearchTweetsRequest, context: RequestContext): Promise<TweetPage>;
|
|
29
|
+
}
|
|
30
|
+
export {};
|