This commit is contained in:
NAME
2026-05-12 08:33:40 +00:00
parent d9766dc7f0
commit a3decd63de
10 changed files with 143 additions and 91 deletions
-14
View File
@@ -19,20 +19,6 @@ export function buildXHeaders(authToken: string, ct0: string) {
};
}
export function buildXCookies() {
return [
{
name: "ct0",
value: process.env.X_COOKIE_CT0,
},
{
name: "auth_token",
value: process.env.X_COOKIE_AUTH_TOKEN,
},
{name: "lang", value: "en"},
];
}
export function getAccount() {
return {
id: process.env.X_USERNAME!,
+41 -12
View File
@@ -100,7 +100,7 @@ export class XBrowserService implements OnModuleInit, OnModuleDestroy {
});
console.log('getOrCreateContext:6')
console.log(account.cookies);
// console.log(account.cookies);
await ctx.addCookies(
account.cookies.map((c) => ({
@@ -136,11 +136,40 @@ export class XBrowserService implements OnModuleInit, OnModuleDestroy {
domain: c.domain || '.x.com',
path: c.path || '/',
}));
console.log('cookies:', cookies);
// console.log('cookies:', cookies);
await ctx.addCookies(cookies);
return ctx.newPage();
}
async verifyCookie() {
const page = await this.newPage();
await page.goto('https://x.com/', {
waitUntil: 'domcontentloaded',
timeout: 30_000,
});
await page.waitForTimeout(2000 + (Math.random() + Math.random()) * 3000);
await page.mouse.wheel(0, rand(300, 500));
// Detect login/challenge screen
if (page.url().includes('/login') || page.url().includes('/flow')) {
this.logger.error('Cookies is die, please get news');
return false;
// return {
// success: false,
// error: 'Redirected to login',
// needsRelogin: true,
// };
}
const isLoggedIn = await page
.locator('[data-testid="SideNav_AccountSwitcher_Button"], [data-testid="AppTabBar_Home_Link"]')
.first()
.isVisible()
.catch(() => false);
this.logger.log(`🔐 Session restore: ${isLoggedIn ? 'LOGGED IN' : 'GUEST (cookie có thể expired)'}`);
await page.close();
return isLoggedIn;
}
async postTweet(
account: BrowserAccount,
text: string,
@@ -158,16 +187,16 @@ export class XBrowserService implements OnModuleInit, OnModuleDestroy {
// Intercept để lấy tweet id từ response
let capturedTweetId: string | undefined;
page.on('response', async (resp) => {
if (resp.url().includes('/CreateTweet')) {
try {
const json = await resp.json();
capturedTweetId =
json?.data?.create_tweet?.tweet_results?.result?.rest_id;
} catch {
}
}
});
// page.on('response', async (resp) => {
// if (resp.url().includes('/CreateTweet')) {
// try {
// const json = await resp.json();
// capturedTweetId =
// json?.data?.create_tweet?.tweet_results?.result?.rest_id;
// } catch {
// }
// }
// });
// await page.keyboard.press('Mở trang ...');
await page.goto('https://x.com/home', {
waitUntil: 'domcontentloaded',
+6 -3
View File
@@ -200,7 +200,7 @@ export class XCookieService {
* @param account
* @param userHandle Optional: Nếu biết @username
*/
async verifyCookie(account: XCookieAccount, userHandle?: string): Promise<{ screen_name: string; name: string }> {
async verifyCookie(account: XCookieAccount, userHandle?: string): Promise<boolean> {
try {
let variables: GraphQLVariables;
let queryId: string;
@@ -234,10 +234,13 @@ export class XCookieService {
const user = res?.data.data.user; // Hoặc res.data.data.result
this.screenName = user.rest_id ? user.legacy.screen_name : user.screen_name;
this.logger.log(`Verified: @${this.screenName}`);
return {screen_name: this.screenName!, name: ''};
// return {screen_name: this.screenName!, name: ''};
return true;
} catch (error: any) {
console.log(error);
this.logger.error(`Verify failed: ${error.response?.data?.errors?.[0]?.message || error.message}`);
throw error;
return false;
// throw error;
}
}
}
+7 -47
View File
@@ -97,62 +97,22 @@ export class XPosterController {
async xreply(
@Body() tweet: ReplyTweetDto,
) {
const account = {
accountId: process.env.X_USERNAME!,
cookies: [
{
name: 'auth_token',
value: process.env.X_COOKIE_AUTH_TOKEN!,
},
{
name: 'ct0',
value: process.env.X_COOKIE_CT0!,
},
],
proxy: '',
userAgent: ''
};
return this.xBrowserService.postReply(account, tweet.tweetUrl, tweet.text)
// return this.xBrowserService.postReply(account, tweet.tweetUrl, tweet.text)
}
@Post('tweet')
async tweet(
@Body() tweet: CreateTweetDto,
) {
const authToken = 'f5574950a0d98cf49ca2e574cc6a4139cc8a2d81';
const ct0 = '75db96b40f4814925670722e3335840467b87c7eb403aab14fbe719297bf465347810f92dc2116c3d30f15153a332976c50d856983dfe19046d4f10413b3d1cd8bac6f7a99e5b03c6949bafdad0f01c0';
// return this.service.postTweet(
// {
// account: {
// id: 'realflashkaze',
// browser: {
// accountId: 'realflashkaze',
// cookies: [
// {
// name: "ct0",
// value: '75db96b40f4814925670722e3335840467b87c7eb403aab14fbe719297bf465347810f92dc2116c3d30f15153a332976c50d856983dfe19046d4f10413b3d1cd8bac6f7a99e5b03c6949bafdad0f01c0'
// },
// {
// name: "auth_token",
// value: 'f5574950a0d98cf49ca2e574cc6a4139cc8a2d81'
// },
// {name: "dnt", value: "1"},
// {name: "lang", value: "en"},
// {name: "twid", value: "u%3D2043937828644536320"},
// ]
// }
// },
// text: tweet.text,
// strategy: XStrategy.BROWSER_ONLY
// });
}
@Get('verify')
verify() {
const account = {
authToken: process.env.X_COOKIE_AUTH_TOKEN!, // auth_token cookie
ct0: process.env.X_COOKIE_CT0!,
}
return this.xCookieService.verifyCookie(account);
// const account = {
// authToken: process.env.X_COOKIE_AUTH_TOKEN!, // auth_token cookie
// ct0: process.env.X_COOKIE_CT0!,
// }
// return this.xCookieService.verifyCookie(account);
}
}
+25 -3
View File
@@ -6,6 +6,7 @@ import {XApiService} from "./x-api.service";
import {XCookieService} from "./x-cookie.service";
import {NotifyService} from "../notify.service";
import {getAccount} from "./utils/x-headers.util";
import {XCacheService} from "../x-cache/x-cache.service";
export enum SUPPORT_SOCIAL_PROVIDERS {
FB = 'fb',
@@ -51,6 +52,7 @@ export class XPosterRouterService {
private readonly cookieSvc: XCookieService,
private readonly browserSvc: XBrowserService,
private readonly notifyService: NotifyService,
private readonly xCacheService: XCacheService,
) {
this.X_UNIFIED_ACCOUNT = getAccount();
@@ -58,8 +60,18 @@ export class XPosterRouterService {
}
async verifyCookie(account: XCookieAccount): Promise<any> {
return this.cookieSvc.verifyCookie(account, 'UserByScreenName')
async verifyCookie(): Promise<any> {
// const isAlive = await this.cookieSvc.verifyCookie();
const isAlive = await this.browserSvc.verifyCookie();
if (!isAlive) {
await this.xCacheService.setStateXCookiesIsDie();
await this.notifyService.sendUrgentMessageToTele('Cookie đã hết hạn vui lòng cập nhập để sử dụng.')
}
await this.xCacheService.setStateXCookiesIsSillALive();
}
async canUseXCookies(): Promise<boolean> {
return this.xCacheService.isXCookiesAlive()
}
async postTweet(params: {
@@ -71,8 +83,18 @@ export class XPosterRouterService {
const chain = this.buildChain(strategy, account);
const attempts: RouterResult['attempts'] = [];
const canUseCookie = this.canUseXCookies();
for (const method of chain) {
this.logger.log(`[${account.id}] Trying via ${method}`);
if (['cookie', 'browser'].includes(method)) {
if (!canUseCookie) {
await this.notifyService.sendUrgentMessageToTele('❌ Vui lòng cập nhập cookie để sử dụng ');
this.logger.error('Cookie đã hết hạn, vui lòng cập nhập');
continue;
}
}
const result = await this.executeTweet(method, account, params.text);
attempts.push({method, error: result.error});
@@ -100,7 +122,7 @@ export class XPosterRouterService {
};
}
this.logger.log(`Dăng bài thất bại, thử phương pháp khác`);
this.logger.log(`Đăng bài thất bại, thử phương pháp khác`);
}