Karl e2fc5059e2
All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 22s
perf(checker): run account checks concurrently using Promise.any
Refactors the `singleAccountCheck` function to use `Promise.any` for validating accounts against multiple stream URLs. This replaces the previous sequential `for` loop.

By running the checks concurrently, the process is significantly faster as it no longer waits for each request to finish before trying the next. The function now resolves as soon as the first successful validation occurs.

The fallback logic for 403 errors (from a tor instance to a direct request) is now cleanly encapsulated within each promise.
2025-07-13 17:01:31 +01:00

228 lines
7.7 KiB
JavaScript

const { getStreamsNew } = require('../routes/getStreams')
const { updateStreamData, updateOtherURLs } = require('./updateStreams')
const { decryptPassword } = require('./password')
const { getUserAccountsCheck, getAllUniqueAccounts, getAllUserAccounts, getUserUniqueAccounts } = require('./getUser')
const { storeAccountToDB } = require('../lib/Accounts')
const { syncApache } = require('./apache_functions')
const axios = require('axios')
const tor_axios = require('tor-axios');
const tor = tor_axios.torSetup({
ip: process.env.TORSSRV,
port: 9050,
controlPort: 9051,
controlPassword: process.env.TORSPWD,
})
axios.defaults.timeout = 3000;
tor.defaults.timeout = 3000
const inst = axios.create({
httpAgent: tor.httpAgent(),
httpsagent: tor.httpsAgent(),
});
let axiosOptions = {
headers: {
'User-Agent': 'IPTV Smarters Pro'
}
}
// const delay = ms => new Promise(res => setTimeout(res, ms));
function buildURL(streamURL, username, password) {
return `${streamURL}/player_api.php?username=${username}&password=${password}`
}
async function splitURL(url) {
try {
let extractedURL = url.match(/[^/]*\/\/[^/]*/)[0]
let extractedUserPass = url.match(/\/player_api\.php\?username=([\s\S]*)$/)[1]
let UserPass = extractedUserPass.split('&password=')
return {
"extractedUrl": extractedURL,
"username": UserPass[0],
"password": UserPass[1]
}
} catch {
}
}
async function updateAccounts(userAccounts, data, url) {
let urlData = await splitURL(url)
let account = userAccounts.filter(account => account.username == urlData.username && account.passwordDecryped == urlData.password)[0]
await updateStreamData({
"username": account.username,
"password": account.password,
"userId": account.userId
}, urlData.extractedUrl, data.user_info.exp_date)
console.log(`${urlData.extractedUrl}.`)
await updateOtherURLs(account.stream, urlData.extractedUrl)
}
async function addNewAccount(userAccount, data, url) {
try {
let URL = await splitURL(url)
await storeAccountToDB({
"username": userAccount.username,
"password": userAccount.password,
"userId": userAccount.userId,
"streamName": userAccount.stream,
"streamURL": URL.extractedUrl,
"expiryDate": data.user_info.exp_date,
"maxConnections": data.user_info.max_connections
}, URL.extractedUrl, data.user_info.exp_date)
var date = new Date(data.user_info.exp_date * 1000).toLocaleDateString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })
console.log(`${userAccount.username} - ${URL.extractedUrl} - Expires ${date}.`)
} catch (error) {
}
}
async function urlChecker(userAccounts) {
let ip = await inst.get('http://api.ipify.org')
console.log(ip.data);
let updateCount = 0
for (let index = 0; index < userAccounts.length; index++) {
const userAccount = userAccounts[index];
let count = index + 1
console.log(count + ' of ' + userAccounts.length + ' ' + userAccount.username)
for (let index2 = 0; index2 < userAccount.urls.length; index2++) {
const url = userAccount.urls[index2];
// console.log('url ' + index2 + 1 + ' of ' + userAccount.urls.length)
// console.log('.')
try {
let response = await inst.get(url, axiosOptions)
if (response.data.user_info.auth) {
await updateAccounts(userAccounts, response.data, url)
updateCount++
break
}
} catch (error) {
try {
if (error.response.status == 403) {
let response2 = await axios.get(url, axiosOptions)
if (response2.data.user_info.auth) {
try {
await updateAccounts(userAccounts, response2.data, url)
updateCount++
break
} catch (error) {
continue
}
}
}
} catch (error) {
continue
}
}
}
}
syncApache()
return {
"updatedCount": updateCount,
"totalAccounts": userAccounts.length
}
}
async function userCheck(accountData) {
let start = Date.now()
console.log(start)
streamURLS = await getStreamsNew()
let userAccounts = await getUserUniqueAccounts(accountData.userId)
userAccounts = userAccounts.map(user => {
user.userId = accountData.userId
user.passwordDecryped = decryptPassword(user.password)
return user
})
urlList = []
for (let index = 0; index < userAccounts.length; index++) {
let userAccount = userAccounts[index];
userAccount.urls = []
for (let index = 0; index < streamURLS.length; index++) {
const streamURL = streamURLS[index];
userAccount.urls.push(await buildURL(streamURL, userAccount.username, userAccount.passwordDecryped))
urlList.push(await buildURL(streamURL, userAccount.username, userAccount.passwordDecryped))
}
userAccount.urls = userAccount.urls.filter(function (e) { return !e.includes('stormtv') && !e.includes('megaott') })
}
values = await urlChecker(userAccounts)
if (values.updatedCount > 0) {
return {
"update": true,
"count": values.updatedCount
}
}
else {
return endresponse = false
}
}
async function singleAccountCheck(accountData) {
const streamURLS = await getStreamsNew();
const urlList = streamURLS.map(streamURL => buildURL(streamURL, accountData.username, accountData.password));
const checkUrl = (url) => new Promise(async (resolve, reject) => {
try {
// Use tor instance first
const response = await inst.get(url, axiosOptions);
if (response.data && response.data.user_info && response.data.user_info.auth) {
return resolve({ url, data: response.data });
}
} catch (error) {
// Fallback to regular axios on 403 error
if (error.response && error.response.status === 403) {
try {
const response2 = await axios.get(url, axiosOptions);
if (response2.data && response2.data.user_info && response2.data.user_info.auth) {
return resolve({ url, data: response2.data });
}
} catch (e) {
// If fallback fails, proceed to reject
}
}
}
// Reject if any check fails or encounters an error
return reject(new Error(`Check failed for ${url}`));
});
const promises = urlList.map(checkUrl);
try {
// Wait for the first promise to resolve
const { url, data } = await Promise.any(promises);
await addNewAccount(accountData, data, url);
console.log('New Account Added');
// syncApache() was commented out, preserving that.
return true;
} catch (error) {
// This block runs if all promises reject
console.log(`All checks failed for user ${accountData.username}.`);
return false;
}
}
module.exports = {
userCheck,
singleAccountCheck,
}
// singleAccountCheck({ "username": "Karl0ss01", "password": "YqQjYH9Nzw", "userId": 1, "stream": "Badger" })
// singleAccountCheck({ "username": "Chris0207B", "password": "nfAf53tR4w", "userId": 1, "stream": "Bonsai" })