hk_bot/index.ts

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