748 lines
28 KiB
TypeScript
748 lines
28 KiB
TypeScript
import fs = require("fs");
|
|
import { database as db, settings } from "./database";
|
|
import prompt from "prompts";
|
|
import logger from "./logger";
|
|
import * as telegram from "telegram";
|
|
import { LogLevel } from "telegram/extensions/Logger";
|
|
import { AccountInterface } from "./interfaces/databaseInterface";
|
|
import { sleep } from "telegram/Helpers";
|
|
import findFreePorts from "find-free-ports";
|
|
import {
|
|
waitNewMessages,
|
|
startNewTorDocker,
|
|
getIP,
|
|
getMessagesByEntity,
|
|
isBannedByClient,
|
|
randomAB,
|
|
} from "./utils";
|
|
import TorControl from "tor-control";
|
|
import Miner from "@ulixee/miner";
|
|
import Hero from "@ulixee/hero";
|
|
import { portsInterface, usedIPInterface } from "./interfaces/otherInterfaces";
|
|
import {
|
|
doSitesTasks,
|
|
vertification,
|
|
doChatsTasks,
|
|
doBotTasks,
|
|
} from "./automatization";
|
|
import ExecuteJsPlugin from "@ulixee/execute-js-plugin";
|
|
import _ from "lodash";
|
|
import Docker from "dockerode";
|
|
import cliProgress from "cli-progress";
|
|
import BigInteger from "big-integer";
|
|
|
|
const addAccounts: () => Promise<void> = async () => {
|
|
let apiId = 0;
|
|
let apiHash = "";
|
|
let accounts = await db.getUsers();
|
|
if (accounts.length != 0) {
|
|
const usePrevData = await prompt({
|
|
type: "confirm",
|
|
name: "confirmed",
|
|
message: "Use apiId and apiHash from last added account?",
|
|
});
|
|
if (usePrevData.confirmed == true) {
|
|
apiId = accounts[accounts.length - 1].apiID;
|
|
apiHash = accounts[accounts.length - 1].apiHash;
|
|
}
|
|
}
|
|
if (apiId == 0 && apiHash == "") {
|
|
const api = await prompt([
|
|
{
|
|
type: "number",
|
|
name: "apiId",
|
|
message: "ApiId?",
|
|
},
|
|
{
|
|
type: "text",
|
|
name: "apiHash",
|
|
message: "ApiHash?",
|
|
},
|
|
]);
|
|
apiId = Number(api.apiId);
|
|
apiHash = api.apiHash;
|
|
}
|
|
const jsonFiles: prompt.Choice[] = [];
|
|
fs.readdirSync("./").forEach((file) => {
|
|
if (
|
|
file.search(".json") != -1 &&
|
|
file != "db.json" &&
|
|
file != "settings.json"
|
|
) {
|
|
jsonFiles.push({
|
|
title: file,
|
|
description: "json file",
|
|
value: file,
|
|
});
|
|
}
|
|
});
|
|
const walletsFile = (
|
|
await prompt({
|
|
type: "select",
|
|
name: "file",
|
|
message: "Choose wallets json file",
|
|
choices: jsonFiles,
|
|
})
|
|
).file;
|
|
await db.findWallet(walletsFile);
|
|
|
|
mainLoop: while (true) {
|
|
accounts = await db.getUsers();
|
|
const answers = await prompt([
|
|
{
|
|
type: "text",
|
|
name: "number",
|
|
message: "Phone number?",
|
|
},
|
|
{
|
|
type: "text",
|
|
name: "password",
|
|
message: "2FA Password?",
|
|
},
|
|
]);
|
|
for (const account of accounts) {
|
|
if (account.phoneNumber == answers.number) {
|
|
logger.warn("You already added this number");
|
|
continue mainLoop;
|
|
}
|
|
}
|
|
const session = new telegram.sessions.StringSession("");
|
|
const client = new telegram.TelegramClient(session, apiId, apiHash, {
|
|
deviceModel: "Samsung SM-G980",
|
|
connectionRetries: 5,
|
|
systemVersion: "10",
|
|
});
|
|
client.setLogLevel(LogLevel.ERROR);
|
|
await client.start({
|
|
phoneNumber: async () => answers.number.replace(/ /g, ""),
|
|
password: async () => answers.password.replace(/ /g, ""),
|
|
phoneCode: async () =>
|
|
(
|
|
await prompt({
|
|
type: "number",
|
|
name: "code",
|
|
message: "Enter recieved code",
|
|
})
|
|
).code.toString(),
|
|
onError: (err) => {
|
|
logger.error(
|
|
`Error due singning into telegram account\n${err}`
|
|
);
|
|
throw err;
|
|
},
|
|
});
|
|
if ((await client.isUserAuthorized()) == true) {
|
|
const account: telegram.Api.User | telegram.Api.InputPeerUser =
|
|
await client.getMe(false);
|
|
if (account instanceof telegram.Api.User) {
|
|
await db.addUser(
|
|
{
|
|
password: answers.password,
|
|
phoneNumber: answers.number,
|
|
apiHash: apiHash,
|
|
telegramID: Number(account.id),
|
|
apiID: apiId,
|
|
stringSession: String(client.session.save()),
|
|
refferal: await db.findRefferal(settings.maxRefferals),
|
|
},
|
|
walletsFile
|
|
);
|
|
} else {
|
|
logger.error("Wrong type of account?");
|
|
}
|
|
} else {
|
|
logger.error("Client not authorized");
|
|
}
|
|
if (
|
|
(
|
|
await prompt({
|
|
type: "confirm",
|
|
name: "continue",
|
|
message: "continue?",
|
|
})
|
|
).continue != true
|
|
) {
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
|
|
const farm: () => Promise<void> = async () => {
|
|
const usedIPs: usedIPInterface[] = [];
|
|
const proccesGroup = async (
|
|
workersGroups: AccountInterface[],
|
|
ports: portsInterface
|
|
) => {
|
|
workersLoop: for (const worker of workersGroups) {
|
|
logger.info(
|
|
`Current worker:${worker.phoneNumber}\nBalance:${worker.balance}`
|
|
);
|
|
{
|
|
let tor: any;
|
|
try {
|
|
tor = new TorControl({
|
|
host: "127.0.0.1",
|
|
port: ports.control,
|
|
password: "qwerty", // Your password for tor-control
|
|
persistent: false, // Keep connection (persistent)
|
|
});
|
|
} catch (err) {
|
|
logger.error(
|
|
`Cant connect to tor control via ${ports.control}\n${err}`
|
|
);
|
|
continue;
|
|
}
|
|
|
|
function isFree(currentIP: string) {
|
|
for (const usedIP of usedIPs) {
|
|
if (
|
|
usedIP.usedIps.indexOf(currentIP) != -1 ||
|
|
usedIP.current == currentIP
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
while (true) {
|
|
const currentIP = await getIP(ports.http);
|
|
if (currentIP != null) {
|
|
if (isFree(currentIP)) {
|
|
usedIPs.filter((value) => {
|
|
return _.isEqual(worker, value.worker);
|
|
})[0].current = currentIP;
|
|
usedIPs
|
|
.filter((value) => {
|
|
return _.isEqual(worker, value.worker);
|
|
})[0]
|
|
.usedIps.push(currentIP);
|
|
break;
|
|
} else {
|
|
await tor.signalNewnym();
|
|
await sleep(3_000);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const fingerprint = worker.browserFingerprint;
|
|
let hero: Hero;
|
|
while (true) {
|
|
hero = new Hero({
|
|
userProfile: {
|
|
deviceProfile: {
|
|
deviceMemory: fingerprint.navigator.deviceMemory,
|
|
},
|
|
},
|
|
name: `${worker.telegramID}`,
|
|
connectionToCore: {
|
|
host: `ws://127.0.0.1:${ports.minerPort}`,
|
|
},
|
|
disableDevtools: true, // to bypass hk site
|
|
showChrome: true, // to debug
|
|
userAgent: fingerprint.navigator.userAgent,
|
|
viewport: fingerprint.screen,
|
|
locale: fingerprint.navigator.language,
|
|
upstreamProxyUrl: `socks5://127.0.0.1:${ports.socks}`,
|
|
upstreamProxyIpMask: {
|
|
ipLookupService: "icanhazip.com",
|
|
proxyIp: usedIPs.filter((value) => {
|
|
return _.isEqual(worker, value.worker);
|
|
})[0].current,
|
|
},
|
|
});
|
|
hero.use(ExecuteJsPlugin);
|
|
try {
|
|
await hero.goto("https://www.google.com");
|
|
} catch (err) {
|
|
await hero.close();
|
|
logger.warn(
|
|
`Some error due openning Hero. Trying again...\n${err}`
|
|
);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
const client = new telegram.TelegramClient(
|
|
new telegram.sessions.StringSession(worker.stringSession),
|
|
worker.apiID,
|
|
worker.apiHash,
|
|
{
|
|
deviceModel: "Samsung SM-G980",
|
|
connectionRetries: 5,
|
|
systemVersion: "10",
|
|
}
|
|
);
|
|
client.setLogLevel(LogLevel.NONE);
|
|
await client.connect();
|
|
if ((await client.checkAuthorization()) == false) {
|
|
logger.error(
|
|
`Account ${worker.phoneNumber} is not authorizated`
|
|
);
|
|
continue workersLoop;
|
|
} else {
|
|
logger.debug(
|
|
`Successfully connected to account ${worker.phoneNumber}`
|
|
);
|
|
// https://gram.js.org/beta/modules/Api.channels.html
|
|
// https://gram.js.org/beta/classes/Api.messages.DeleteChat.html
|
|
let botEntity: telegram.Api.User;
|
|
try {
|
|
botEntity = (await client.getEntity(
|
|
settings.telegramLinks.botLink
|
|
)) as telegram.Api.User;
|
|
} catch (err) {
|
|
if (err instanceof telegram.errors.FloodWaitError) {
|
|
switch (err.errorMessage) {
|
|
case "FLOOD":
|
|
logger.warn(
|
|
`Account has flood(${err.seconds} secs timeout) error,skipping`
|
|
);
|
|
break;
|
|
default:
|
|
logger.error(
|
|
`Unknown error due getEntity ${err}`
|
|
);
|
|
break;
|
|
}
|
|
} else {
|
|
logger.error(`Unknown error due getEntity ${err}`);
|
|
}
|
|
usedIPs.filter((value) => {
|
|
return _.isEqual(worker, value.worker);
|
|
})[0].current = "";
|
|
usedIPs
|
|
.filter((value) => {
|
|
return _.isEqual(worker, value.worker);
|
|
})[0]
|
|
.usedIps.pop();
|
|
await hero.close();
|
|
continue workersLoop;
|
|
}
|
|
|
|
// leaving old groups
|
|
{
|
|
const now = new Date();
|
|
worker.completedGroupsTasks =
|
|
worker.completedGroupsTasks.filter(async (oldGroup) => {
|
|
if (+now > oldGroup.timeToLeave) {
|
|
const olderGroups =
|
|
worker.completedGroupsTasks.filter(
|
|
(value) => {
|
|
if (
|
|
value.groupID ==
|
|
oldGroup.groupID &&
|
|
value.timeToLeave >
|
|
oldGroup.timeToLeave
|
|
) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
);
|
|
if (olderGroups.length == 0) {
|
|
let peerChannel: telegram.Api.TypeInputPeer;
|
|
try {
|
|
peerChannel =
|
|
await client.getInputEntity(
|
|
BigInteger(oldGroup.groupID)
|
|
);
|
|
} catch (err) {
|
|
return false;
|
|
}
|
|
try {
|
|
await client.invoke(
|
|
new telegram.Api.channels.GetParticipant(
|
|
{
|
|
channel: peerChannel,
|
|
participant: "me",
|
|
}
|
|
)
|
|
);
|
|
} catch (err) {
|
|
if (err.code == 400) {
|
|
return false;
|
|
} else {
|
|
logger.error(
|
|
`Unknown error due GetParticipant | ${worker.phoneNumber}`
|
|
);
|
|
return true;
|
|
}
|
|
}
|
|
try {
|
|
await client.invoke(
|
|
new telegram.Api.channels.LeaveChannel(
|
|
{
|
|
channel: peerChannel,
|
|
}
|
|
)
|
|
);
|
|
} catch (err) {
|
|
logger.debug(
|
|
`Cant leave channel | ${worker.phoneNumber}`
|
|
);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
});
|
|
}
|
|
//
|
|
|
|
for (const chat of settings.telegramLinks.groupsToJoin) {
|
|
try {
|
|
await client.invoke(
|
|
new telegram.Api.channels.GetParticipant({
|
|
channel: chat,
|
|
participant: "me",
|
|
})
|
|
);
|
|
} catch (err) {
|
|
if (err.code == 400) {
|
|
try {
|
|
await client.invoke(
|
|
new telegram.Api.channels.JoinChannel({
|
|
channel: chat,
|
|
})
|
|
);
|
|
} catch (err) {
|
|
if (err instanceof telegram.errors.RPCError) {
|
|
switch (err.errorMessage) {
|
|
case "CHANNELS_TOO_MUCH":
|
|
logger.error(
|
|
`Too much groups | ${worker.phoneNumber}`
|
|
);
|
|
continue workersLoop;
|
|
default:
|
|
logger.error(
|
|
`Some error due joining HK groups | ${worker.phoneNumber}\n${err.message}\n${err.stack}`
|
|
);
|
|
continue workersLoop;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
logger.error(
|
|
`Unknown error due GetParticipant | ${worker.phoneNumber}`
|
|
);
|
|
throw err;
|
|
}
|
|
}
|
|
}
|
|
{
|
|
// unblock hkbot
|
|
if (
|
|
await isBannedByClient(
|
|
client,
|
|
settings.telegramLinks.botLink
|
|
)
|
|
) {
|
|
const result = await client.invoke(
|
|
new telegram.Api.contacts.Unblock({
|
|
id: settings.telegramLinks.botLink,
|
|
})
|
|
);
|
|
if (result != true) {
|
|
logger.error(
|
|
`Cant unblock ${settings.telegramLinks.botLink} | ${worker.phoneNumber}`
|
|
);
|
|
continue workersLoop;
|
|
} else {
|
|
logger.info(
|
|
`HK bot unblocked | ${worker.phoneNumber}`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
await sleep(Math.random() * 50_000);
|
|
if (
|
|
(await getMessagesByEntity(client, botEntity, botEntity))
|
|
.length == 0
|
|
) {
|
|
// sending start message
|
|
let id = -1;
|
|
if (worker.refferal !== null) {
|
|
id = (
|
|
await client.sendMessage(botEntity, {
|
|
message: `/start ${worker.refferal}`,
|
|
})
|
|
).id;
|
|
logger.debug(
|
|
`First start of ${botEntity.username} via refferal link of ${worker.refferal} | ${worker.phoneNumber}`
|
|
);
|
|
} else {
|
|
id = (
|
|
await client.sendMessage(botEntity, {
|
|
message: `/start`,
|
|
})
|
|
).id;
|
|
logger.debug(
|
|
`First start of ${botEntity.username} | ${worker.phoneNumber}`
|
|
);
|
|
}
|
|
await waitNewMessages(client, worker, botEntity, id);
|
|
}
|
|
logger.info(`Sending earn message... | ${worker.phoneNumber}`);
|
|
const requestTasksMessage = await client.sendMessage(
|
|
botEntity,
|
|
{
|
|
message: settings.botButtons.earn,
|
|
}
|
|
);
|
|
await waitNewMessages(
|
|
client,
|
|
worker,
|
|
botEntity,
|
|
requestTasksMessage.id
|
|
);
|
|
let tasksSelector: telegram.Api.Message = (
|
|
await client.getMessages(botEntity, {
|
|
minId: requestTasksMessage.id,
|
|
})
|
|
)[0];
|
|
if (
|
|
tasksSelector.message.includes(
|
|
settings.botMessages.verification
|
|
) == true
|
|
) {
|
|
// vertification
|
|
logger.warn(
|
|
`Account ${worker.phoneNumber} is unvertificated`
|
|
);
|
|
const url: string = tasksSelector.buttons![0][0].url!;
|
|
await vertification(hero, url);
|
|
const requestTasksMessage = await client.sendMessage(
|
|
botEntity,
|
|
{
|
|
message: settings.botButtons.earn,
|
|
}
|
|
);
|
|
await waitNewMessages(
|
|
client,
|
|
worker,
|
|
botEntity,
|
|
requestTasksMessage.id
|
|
);
|
|
tasksSelector = (
|
|
await client.getMessages(botEntity, {
|
|
minId: requestTasksMessage.id,
|
|
})
|
|
)[0];
|
|
}
|
|
|
|
try {
|
|
await doSitesTasks(
|
|
client,
|
|
worker,
|
|
hero,
|
|
botEntity,
|
|
tasksSelector
|
|
);
|
|
await doChatsTasks(
|
|
client,
|
|
worker,
|
|
hero,
|
|
botEntity,
|
|
tasksSelector
|
|
);
|
|
try {
|
|
await doBotTasks(
|
|
client,
|
|
worker,
|
|
hero,
|
|
botEntity,
|
|
tasksSelector
|
|
);
|
|
} catch (err) {
|
|
await client.sendMessage(botEntity, {
|
|
message: "/cancel",
|
|
});
|
|
throw err;
|
|
}
|
|
} catch (err) {
|
|
logger.error(
|
|
`Some error due doing tasks\n${err}\n${err.stack}`
|
|
);
|
|
}
|
|
db.updateUser(worker);
|
|
await hero.close();
|
|
await client.disconnect();
|
|
await sleep(
|
|
randomAB(
|
|
settings.sleepTime.betweenSessions[0],
|
|
settings.sleepTime.betweenSessions[1]
|
|
) * 1_000
|
|
);
|
|
}
|
|
}
|
|
};
|
|
|
|
const workers = await db.getUsers();
|
|
if (settings.shuffleAccounts) {
|
|
workers.sort(() => Math.random() - 0.5);
|
|
}
|
|
const workersGroups: Array<AccountInterface[]> = [];
|
|
|
|
for (const worker of workers) {
|
|
usedIPs.push({
|
|
worker: worker,
|
|
usedIps: [],
|
|
current: "",
|
|
});
|
|
}
|
|
|
|
for (let i = 0; i != settings.pararels; i++) {
|
|
const startIndex = i * Math.ceil(workers.length / settings.pararels);
|
|
const _workers = workers.slice(
|
|
startIndex,
|
|
startIndex + Math.ceil(workers.length / settings.pararels)
|
|
);
|
|
workersGroups.push(_workers);
|
|
}
|
|
const pararels: Promise<void>[] = [];
|
|
|
|
const miner = new Miner();
|
|
await miner.listen({ port: (await findFreePorts(1))[0] });
|
|
|
|
const _ports = await findFreePorts(workers.length * 3);
|
|
// logger.debug(`Is there port of miner:${_ports.indexOf(await miner.port)}, ${await miner.port}`)
|
|
let containers: Docker.Container[] = [];
|
|
|
|
{
|
|
const multibar = new cliProgress.MultiBar({
|
|
hideCursor: true,
|
|
barsize: 50,
|
|
barCompleteChar: "❌",
|
|
barIncompleteChar: "✔️",
|
|
clearOnComplete: true,
|
|
format: `{status} | {bar} | {value}/{total}sec timeout`,
|
|
});
|
|
const _containers: Promise<Docker.Container>[] = [];
|
|
for (let i = 0; i != workersGroups.length; i++) {
|
|
_containers.push(
|
|
startNewTorDocker(
|
|
_ports[i * 3],
|
|
_ports[i * 3 + 1],
|
|
_ports[i * 3 + 2],
|
|
multibar
|
|
)
|
|
);
|
|
}
|
|
containers = (await Promise.allSettled(_containers)).map((value) => {
|
|
return (value as PromiseFulfilledResult<Docker.Container>).value;
|
|
});
|
|
multibar.stop();
|
|
}
|
|
|
|
for (let i = 0; i != workersGroups.length; i++) {
|
|
pararels.push(
|
|
proccesGroup(workersGroups[i], {
|
|
http: _ports[i * 3],
|
|
socks: _ports[i * 3 + 1],
|
|
control: _ports[i * 3 + 2],
|
|
minerPort: await miner.port,
|
|
})
|
|
);
|
|
}
|
|
|
|
const results = await Promise.allSettled(pararels);
|
|
for (const result of results) {
|
|
if (result.status == "rejected") {
|
|
logger.warn(`Promise is canceled due ${result.reason}`);
|
|
}
|
|
}
|
|
logger.debug("Closing opened docker containers ");
|
|
for (const container of containers) {
|
|
try {
|
|
await container.stop();
|
|
} catch (err) {}
|
|
}
|
|
await db.save();
|
|
await miner.close(true);
|
|
};
|
|
|
|
// menu
|
|
(async () => {
|
|
while (true) {
|
|
let accounts: AccountInterface[] = [];
|
|
try {
|
|
accounts = await db.getUsers();
|
|
} catch (err) {
|
|
continue;
|
|
}
|
|
|
|
const answer = await prompt({
|
|
type: "select",
|
|
name: "menu",
|
|
message: "Choose action",
|
|
choices: [
|
|
accounts.length > 0
|
|
? {
|
|
title: "Start",
|
|
description: `Starting farm for ${accounts.length} accounts`,
|
|
value: "start",
|
|
}
|
|
: {
|
|
title: "Start",
|
|
description: "Add accounts first...",
|
|
value: "start",
|
|
disabled: true,
|
|
},
|
|
{
|
|
title: "Add accounts",
|
|
description: "Add accounts to database",
|
|
value: "addAccounts",
|
|
},
|
|
{
|
|
title: "Check balances",
|
|
value: "checkBalances",
|
|
disabled: true,
|
|
},
|
|
{ title: "Withdraw", value: "withdraw", disabled: true },
|
|
{
|
|
title: "Exit",
|
|
description: "You realy dont know what it mean?",
|
|
},
|
|
],
|
|
});
|
|
switch (answer.menu) {
|
|
case "addAccounts":
|
|
await addAccounts();
|
|
break;
|
|
case "start":
|
|
while (true) {
|
|
try {
|
|
await farm();
|
|
const sleepTime = randomAB(
|
|
settings.sleepTime.afterDoing[0],
|
|
settings.sleepTime.afterDoing[1]
|
|
);
|
|
logger.info(`Sleeping for ${sleepTime} secs`);
|
|
await sleep(sleepTime * 1_000);
|
|
} catch (err) {
|
|
logger.error(
|
|
`Unknown error\n${err.stack}\n${typeof err}`
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
logger.info("Bye bye");
|
|
process.exit();
|
|
}
|
|
}
|
|
})();
|
|
|
|
process.on("SIGINT", function () {
|
|
db.save().then(() => {
|
|
logger.info("Bye bye");
|
|
process.exit();
|
|
});
|
|
});
|
|
|