Add engine switch and Sweetistics endpoints for tweet reading

This commit is contained in:
Peter Steinberger
2025-12-02 14:53:35 +00:00
parent b48c4ba15f
commit 0f6cc5c663
7 changed files with 740 additions and 149 deletions
+80
View File
@@ -10,6 +10,33 @@ export interface SweetisticsTweetResult {
error?: string;
}
export interface SweetisticsTweet {
id: string;
text: string;
author: {
username: string;
name: string;
};
createdAt?: string;
replyCount?: number;
retweetCount?: number;
likeCount?: number;
conversationId?: string;
inReplyToStatusId?: string;
}
export interface SweetisticsTimelineResult {
success: boolean;
tweets?: SweetisticsTweet[];
error?: string;
}
export interface SweetisticsReadResult {
success: boolean;
tweet?: SweetisticsTweet;
error?: string;
}
function normalizeBaseUrl(value: string): string {
const trimmed = value.trim();
if (!trimmed) return 'https://sweetistics.com';
@@ -67,4 +94,57 @@ export class SweetisticsClient {
return { success: true, tweetId };
}
async read(tweetId: string): Promise<SweetisticsReadResult> {
return this.fetchJson<SweetisticsReadResult>(`${this.baseUrl}/api/twitter/tweet/${tweetId}`);
}
async replies(tweetId: string): Promise<SweetisticsTimelineResult> {
return this.fetchJson<SweetisticsTimelineResult>(`${this.baseUrl}/api/twitter/tweet/${tweetId}/replies`);
}
async thread(tweetId: string): Promise<SweetisticsTimelineResult> {
return this.fetchJson<SweetisticsTimelineResult>(`${this.baseUrl}/api/twitter/tweet/${tweetId}/thread`);
}
async search(query: string, count: number): Promise<SweetisticsTimelineResult> {
const url = new URL(`${this.baseUrl}/api/twitter/search`);
url.searchParams.set('q', query);
url.searchParams.set('count', String(count));
return this.fetchJson<SweetisticsTimelineResult>(url.toString());
}
private async fetchJson<T extends { success: boolean; error?: string }>(
url: string,
init?: RequestInit,
): Promise<T> {
const response = await fetch(url, {
...init,
headers: {
authorization: `Bearer ${this.apiKey}`,
...(this.userAgent ? { 'user-agent': this.userAgent } : {}),
...(init?.headers ?? {}),
},
});
let data: unknown;
try {
data = await response.json();
} catch (error) {
return {
success: false,
error: `Sweetistics response parse failed: ${error instanceof Error ? error.message : String(error)}`,
} as T;
}
if (!response.ok || !(data as { success?: unknown })?.success) {
const message =
(typeof (data as { error?: unknown })?.error === 'string'
? (data as { error?: string }).error
: undefined) || `HTTP ${response.status}`;
return { ...(data as object), success: false, error: message } as T;
}
return data as T;
}
}