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 { 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(); } }