Files
x-poster-client/src/x-poster/x-api.service.ts
T
2026-05-11 03:18:19 +00:00

131 lines
4.8 KiB
TypeScript

import {Inject, Injectable} from "@nestjs/common";
import {TwitterApi} from 'twitter-api-v2';
import {TwitterApiAutoTokenRefresher} from '@twitter-api-v2/plugin-token-refresher'
import {EUploadMimeType} from "twitter-api-v2/dist/esm/types";
import {MediaV2MediaCategory} from "twitter-api-v2/dist/esm/types/v2/media.v2.types";
import {XCacheService} from "../x-cache/x-cache.service";
@Injectable()
export class XApiService {
private clientId = process.env.TWITTER_CLIENT_ID + '';
private clientSecret = process.env.TWITTER_CLIENT_SECRET;
private readonly userClient: any;
constructor(
@Inject() private cacheService: XCacheService,
) {
}
async getTwitterClientV2() {
return new TwitterApi(
{
clientId: this.clientId,
clientSecret: this.clientSecret,
},
)
}
async getTwitterClientV2ViaAccessToken() {
const accessToken = await this.getCacheAccessToken() as string;
const refreshToken = await this.getCacheRefreshToken() as string;
// console.log({refreshToken});
// @ts-ignore
// @ts-ignore
return new TwitterApi(accessToken,
{
plugins: [
new TwitterApiAutoTokenRefresher({
refreshCredentials: {
clientId: '' + this.clientId,
clientSecret: this.clientSecret,
},
refreshToken,
// Hàm này được gọi tự động khi token được refresh thành công
onTokenUpdate: async (newTokens) => {
console.log('===> Token đã được làm mới:', newTokens);
await this.setCacheRefreshToken('' + newTokens.refreshToken);
await this.setCacheAccessToken(newTokens.accessToken);
},
// Hàm xử lý khi refresh thất bại (ví dụ: refresh token cũng hết hạn)
onTokenRefreshError: async (error) => {
console.error('Không thể refresh token:', error);
throw error;
},
}),
],
});
}
async uploadImageV1(media: Buffer, options: {
media_type: `${EUploadMimeType}` | EUploadMimeType;
media_category?: MediaV2MediaCategory;
additional_owners?: string[];
}, chunkSize?: number): Promise<string> {
const client = await this.getTwitterClientV2ViaAccessToken();
// return client.v2.uploadMedia(media, options, chunkSize);
return client.v2.uploadMedia(media, options, chunkSize);
}
async postSimpleTweet(content) {
const client = await this.getTwitterClientV2ViaAccessToken();
return client.v2.tweet(content)
}
async postReply(content: string, tweetId: string) {
const client = await this.getTwitterClientV2ViaAccessToken();
return client.v2.reply(content, tweetId)
}
async qoute(content: string, tweetId: string) {
const client = await this.getTwitterClientV2ViaAccessToken();
// @ts-ignore
if (content.indexOf(`https://x.com/${process.env.TWITTER_USERNAME}/status`) === -1) {
//hacking, use tweet instead of quote, because limit x
return client.v2.tweet(content)
}
return client.v2.quote(content, tweetId)
}
async setCacheAccessToken(accessToken: string) {
return this.cacheService.setCachedKey('tw_accesstoken', '' + accessToken, 24 * 3600);
}
async delCacheAccessToken() {
return this.cacheService.delCachedKey('tw_accesstoken');
}
async getCacheAccessToken() {
return this.cacheService.getCachedData('tw_accesstoken')
}
async getCacheRefreshToken() {
return this.cacheService.getCachedData('tw_refreshtoken')
}
async setCacheRefreshToken(refreshToken: string) {
//30day
return this.cacheService.setCachedKey('tw_refreshtoken', refreshToken, 30 * 24 * 3600);
}
async refreshAccessToken() {
const client = new TwitterApi({clientId: this.clientId, clientSecret: this.clientSecret});
const refreshToken = await this.getCacheRefreshToken();
const {
client: refreshedClient,
accessToken,
refreshToken: newRefreshToken
} = await client.refreshOAuth2Token('' + refreshToken);
await this.cacheService.setCachedKey('tw_accesstoken_time_add', Date.now(), 3 * 24 * 3600);
await this.setCacheAccessToken(accessToken);
await this.setCacheRefreshToken('' + refreshToken);
// Example request
await refreshedClient.v2.me();
}
}