UNPKG

zixineabra

Version:

A Telegram utility bot for managing sessions, logging in accounts, and retrieving WebView data.

380 lines (343 loc) 12.3 kB
const fs = require("fs").promises; const axios = require("axios"); const path = require("path"); const qs = require("qs"); const { DateTime } = require("luxon"); const logger = require("./log/logger.js"); const printBanner = require("./banner/galkurta-banner.js"); class Coub { constructor() { this.headers = { Accept: "application/json, text/plain, */*", "Accept-Encoding": "gzip, deflate, br, zstd", "Accept-Language": "vi-VN,vi;q=0.9,fr-FR;q=0.8,fr;q=0.7,en-US;q=0.6,en;q=0.5", "Content-Type": "application/x-www-form-urlencoded", Origin: "https://coub.com", Referer: "https://coub.com/", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36", "sec-ch-ua": '"Not)A;Brand";v="99", "Google Chrome";v="127", "Chromium";v="127"', "sec-ch-ua-mobile": "?0", "sec-ch-ua-platform": '"Windows"', "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-site", }; this.tokenFile = path.join(__dirname, "./token/coub-token.json"); } async countdown(seconds) { const formatTime = (timeInSeconds) => { const hours = Math.floor(timeInSeconds / 3600); const minutes = Math.floor((timeInSeconds % 3600) / 60); const seconds = timeInSeconds % 60; return `${hours.toString().padStart(2, "0")}:${minutes .toString() .padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`; }; logger.info("Waiting to continue..."); process.stdout.write(`Time remaining: ${formatTime(seconds)}`); for (let i = seconds - 1; i >= 0; i--) { await new Promise((resolve) => setTimeout(resolve, 1000)); process.stdout.clearLine(); process.stdout.cursorTo(0); process.stdout.write(`Time remaining: ${formatTime(i)}`); } logger.info("\nResuming operations..."); } async makeRequest(method, url, headers, data = null) { let retryCount = 0; while (true) { await new Promise((resolve) => setTimeout(resolve, 2000)); try { const config = { headers, method }; if (method.toUpperCase() === "GET" && data) { config.params = data; } else if (method.toUpperCase() === "POST") { config.data = data; } const response = await axios(url, config); if (response.status >= 200 && response.status < 300) { return response.data; } else if (response.status >= 500) { if (retryCount >= 3) { logger.error(`Status Code : ${response.status} | Server Down`); return null; } retryCount++; } else { logger.warn(`Status Code : ${response.status}`); break; } } catch (error) { console.log(error); logger.error(`Error: ${error.message}`); if (retryCount >= 3) return null; retryCount++; } } } async getRewards(token, xTgAuth) { const headers = { ...this.headers, authorization: `Bearer ${token}`, "x-tg-authorization": xTgAuth, }; const url = "https://rewards.coub.com/api/v2/get_user_rewards"; try { return await this.makeRequest("GET", url, headers); } catch (error) { logger.error(`Unable to read rewards. Error: ${error.message}`); return null; } } async claimTask(token, xTgAuth, taskId, taskTitle) { const headers = { ...this.headers, authorization: `Bearer ${token}`, "x-tg-authorization": xTgAuth, }; const url = "https://rewards.coub.com/api/v2/complete_task"; const params = { task_reward_id: taskId }; try { const response = await this.makeRequest("GET", url, headers, params); if (response) { logger.info(`Task ${taskTitle} Completed`); return response; } else { logger.warn(`Task ${taskTitle} Failed`); return null; } } catch (error) { logger.error( `Task ${taskTitle} Unable to claim reward | error: ${error.message}` ); return null; } } async loadTask() { try { const data = await fs.readFile(path.join(__dirname, "./json/coub-task.json"), "utf8"); return JSON.parse(data); } catch (error) { logger.error(`Unable to read tasks: ${error.message}`); return []; } } parseAccountData(rawData) { const parsedData = qs.parse(rawData); const user = JSON.parse(decodeURIComponent(parsedData.user)); return { user: JSON.stringify(user), chat_instance: parsedData.chat_instance, chat_type: parsedData.chat_type, start_param: parsedData.start_param, auth_date: parsedData.auth_date, hash: parsedData.hash, }; } async getAndSaveToken(rawAccountData, accountIndex) { const loginUrl = "https://coub.com/api/v2/sessions/login_mini_app"; const signupUrl = "https://coub.com/api/v2/sessions/signup_mini_app"; const parsedAccountData = this.parseAccountData(rawAccountData); const data = qs.stringify(parsedAccountData); const config = { headers: { ...this.headers, }, }; let apiToken; try { const loginResponse = await axios.post(loginUrl, data, config); apiToken = loginResponse.data.api_token; } catch (error) { if (error.response && error.response.status === 404) { logger.warn("Registering account..."); try { const signupResponse = await axios.post(signupUrl, data, config); apiToken = signupResponse.data.api_token; } catch (signupError) { logger.error(`Error during registration: ${signupError.message}`); throw signupError; } } else { logger.error(`Error during login: ${error.message}`); throw error; } } if (!apiToken) { throw new Error("Unable to obtain api_token"); } try { const torusUrl = "https://coub.com/api/v2/torus/token"; const torusConfig = { headers: { ...this.headers, "x-auth-token": apiToken, }, }; const torusResponse = await axios.post(torusUrl, null, torusConfig); const token = torusResponse.data.access_token; await this.updateTokenFile(token, accountIndex); return token; } catch (error) { logger.error(`Error getting token from torus: ${error.message}`); throw error; } } async readTokens() { try { const data = await fs.readFile(this.tokenFile, "utf8"); return JSON.parse(data); } catch (error) { if (error.code === "ENOENT") { logger.info("coub-token.json file not found, creating new"); return {}; } logger.error(`Error reading coub-token.json file: ${error.message}`); return {}; } } async updateTokenFile(token, accountIndex) { try { let tokens = await this.readTokens(); const accountKey = `${accountIndex + 1}`; if (tokens[accountKey] !== token) { tokens[accountKey] = token; await fs.writeFile(this.tokenFile, JSON.stringify(tokens, null, 2)); logger.info(`Successfully obtained token | ${accountIndex + 1}`); } else { logger.info(`Token for account ${accountIndex + 1} has been updated`); } } catch (error) { logger.error(`Error: ${error.message}`); throw error; } } async readAccountData() { try { const dataFile = path.join(__dirname, "./../data/coub.txt"); const data = await fs.readFile(dataFile, "utf8"); return data .split("\n") .map((line) => line.trim()) .filter(Boolean) .map((line) => line.replace(/\r$/, "")); } catch (error) { throw new Error(`Unable to read data.txt file: ${error.message}`); } } isExpired(token) { const [header, payload, sign] = token.split("."); const decodedPayload = Buffer.from(payload, "base64").toString(); try { const parsedPayload = JSON.parse(decodedPayload); const now = Math.floor(DateTime.now().toSeconds()); if (parsedPayload.exp) { const expirationDate = DateTime.fromSeconds( parsedPayload.exp ).toLocal(); logger.info( `Token expires | ${expirationDate.toFormat("yyyy-MM-dd HH:mm:ss")}` ); const isExpired = now > parsedPayload.exp; logger.info( `Has the token expired? ${ isExpired ? "Yes, you need to replace the token" : "No run at full speed" }` ); return isExpired; } else { logger.warn(`Perpetual token, unable to read expiration time`); return false; } } catch (error) { logger.error(`Error: ${error.message}`); return true; } } async main() { try { printBanner(); const accountsData = await this.readAccountData(); if (accountsData.length === 0) { throw new Error("No valid data found in data.txt"); } const tasks = await this.loadTask(); let tokens = await this.readTokens(); while (true) { for (let i = 0; i < accountsData.length; i++) { const accountKey = `${i + 1}`; let token = tokens[accountKey]; const parsedAccountData = this.parseAccountData(accountsData[i]); const user = JSON.parse(parsedAccountData.user); logger.info( `Account ${i + 1} - ${user.first_name} ${user.last_name}` ); if (!token || this.isExpired(token)) { logger.info( `Token for | ${user.first_name} ${user.last_name} does not exist or has expired. Getting new token` ); try { const rawAccountData = accountsData[i]; token = await this.getAndSaveToken(rawAccountData, i); if (token) { tokens = await this.readTokens(); } else { logger.error( `Failed to get token | ${user.first_name} ${user.last_name} | Moving to next account` ); continue; } } catch (error) { logger.error( `Failed to get token | ${user.first_name} ${user.last_name} | ${error.message}` ); continue; } } const xTgAuth = qs.stringify(parsedAccountData); const listId = []; const dataReward = await this.getRewards(token, xTgAuth); if (dataReward) { dataReward.forEach((data) => { const id = data.id || 0; listId.push(id); }); } else { logger.warn( `Unable to get rewards | ${user.first_name} ${user.last_name}` ); } for (const task of tasks) { const id = task.id; if (listId.includes(id)) { logger.info( `${task.title} | Completed | ${user.first_name} ${user.last_name}` ); } else { logger.info( `Performing task ${task.title} | ${user.first_name} ${user.last_name}` ); await this.claimTask(token, xTgAuth, task.id, task.title); } } await new Promise((resolve) => setTimeout(resolve, 5000)); } const delay = 24 * 3600 + Math.floor(Math.random() * 3600); await this.countdown(delay); } } catch (error) { console.log(error); logger.error(`Error: ${error.message}`); if (error.stack) { logger.error(`Stack trace: ${error.stack}`); } } } } const coub = new Coub(); coub.main().catch((error) => logger.error(`Unhandled error: ${error.message}`));