Compare commits

...

7 Commits

Author SHA1 Message Date
minicx 175cc1376a ? 2023-01-03 23:21:07 +03:00
minicx 12aae08fb8 major init 2022-12-28 23:45:11 +03:00
minicx 80df4af3b9 splitted database functions into files 2022-12-19 21:57:28 +03:00
minicx fe8a7ee92f refactoring 2022-12-16 16:59:36 +03:00
minicx 88b34acc45 patch 2022-12-15 15:46:04 +03:00
minicx 22361c3cb2 added skeleton of solving system 2022-12-15 15:41:01 +03:00
minicx d02929d114 Changed README file 2022-12-15 00:11:06 +03:00
24 changed files with 1127 additions and 2216 deletions

20
.gitignore vendored
View File

@ -1,13 +1,19 @@
build
// env files
.vscode
node_modules
test.ts
// yarn files
yarn-error.log
yarn.lock
// npm files
package-lock.json
db.json
// project files
test.ts
db.sqlite
build
errors.log
info.log
data.json
settings.json
yarn-error.log
yarn.lock
db.json1
settings.json

View File

@ -1,31 +1,50 @@
<div align="center">
<h1><b>HK_BOT🤖</b></h1>
<a href="https://hits.sh/git.disroot.org/minicx/hk_bot/"><img alt="Hits" src="https://hits.sh/git.disroot.org/minicx/hk_bot.svg"/></a>
<a href="https://git.disroot.org/minicx/hk_bot/LiCENSE"><img src="https://shields.io/badge/license-MIT-green" alt='License'/></a>
<a href="https://git.disroot.org/minicx/hk_bot/src/commit/0c7f8c9e6c43f0f3784b39b7fbf8b3cea1c27832/LICENSE"><img src="https://shields.io/badge/license-MIT-green" alt='License'/></a>
</div>
# List of content
* [Installing](#installing)
* [Prerequirements](#prerequirements)
* [Cloning repo](#cloning-repo)
* [Installing all dependencies](#installing-all-dependencies)
* [Starting](#starting)
* [Format of Faucet wallets](#format-of-faucet-https-faucetpay-io-wallets)
* [Settings](#settings)
# Installing
1. #### Prerequirements
You need to install this packages:
* `NodeJS>=14.x` version
* `Docker`
* `yarn` or `npm`
#### Linux:
After installing packages, you must add your user at `docker` group to have access to `docker`'s commands without root(**Without this the script will not work ❗**)
2. #### Cloning repo
1. #### Cloning repo
To do this run `git clone https://git.disroot.org/minicx/hk_bot.git`
3. #### Installing all dependencies
To do this run `git clone https://git.disroot.org/minicx/hk_bot.git`
2. #### Installing all dependencies
Run in `hk_bot` directory `npm install --production` or `yarn install --production`
4. #### Starting
Run in `hk_bot` directory `npm install` or `yarn install`
3. #### Starting
Run `npm start` or `yarn start`
Run `npm start` or `yarn start`
<details>
<summary>Other commands</summary>
<details>
<summary>Other commands</summary>
| Command | Description |
| ------- | ------------------------------------------------------------------ |
| build | just compile all `.ts` files to `build` directory into `.js` |
| Command | Description |
| ------- | ------------------------------------------------------------------ |
| build | just compile all `.ts` files to `build` directory into `.js` |
</details>
</details>
# Format of [Faucet](https://faucetpay.io) wallets data
# Format of [Faucet](https://faucetpay.io) wallets
* **❗ The script currently does not use the email or password of the faucet account ❗**
* **JSON file must be in root of `hk_bot` directory**
@ -45,8 +64,26 @@
}
```
</details>
</details>
# Settings
Script will create `settings.json` file
from there you can change some settings
| Setting | Description |
| ------- | ------------------------------------------------------------------ |
| logLevel | Sets level of logs |
| pararels | This setting affects the number of running groups of accounts |
| mainCrypto | Temp setting |
| minimalToWithdraw | This setting sets the minimum balance at which the withdrawal will be made |
| maxRefferals | This setting affects the maximum depth of the referral tree |
| botMessages | This setting specifies the messages the script is searching for |
| botButtons | This setting specifies the text buttons that are available in `hk_bot` |
| telegramLinks | This setting sets the links to the bot and the groups you need to join in order to use it |
| sleepTime | This setting sets the delay time |
| shuffleAccounts | This setting determines whether or not accounts will be migrated (this is needed to simulate a live user) |
| bypassMode | This parameter manages that the script will bypass the timer on timed ad pages |
# Creator and contributors
<div align='center'>

162
addAccounts.ts Normal file
View File

@ -0,0 +1,162 @@
import prompt from "prompts";
import { TelegramWorker } from "./classes/Telegram";
import * as telegram from "telegram";
import fs from "fs";
import { logger } from "./logger";
import { db, JSONFaucetMails } from "./classes/Database";
import { LogLevel } from "telegram/extensions/Logger";
import { settings } from "./classes/Settings";
import { isUndefined } from "lodash";
export const addAccounts = async (): Promise<void> => {
// need rework this shit
let apiId: number | undefined = undefined;
let apiHash: string | undefined = undefined;
let workers = await db.getWorkers();
if (workers.length != 0) {
const usePrevData = await prompt({
type: "confirm",
name: "confirmed",
message: "Use apiId and apiHash from last added worker?",
});
if (usePrevData.confirmed == true) {
apiId = workers[workers.length - 1].apiID;
apiHash = workers[workers.length - 1].apiHash;
}
}
if (isUndefined(apiId) || isUndefined(apiHash)) {
const api = await prompt([
{
type: "number",
name: "apiId",
message: "ApiId?",
},
{
type: "text",
name: "apiHash",
message: "ApiHash?",
},
],{onCancel(prompt, answers) {
// there cancel
},});
apiId = Number(api.apiId);
apiHash = api.apiHash;
}
const jsonFiles: prompt.Choice[] = [];
for (const file of fs.readdirSync("./")) {
if (file.search(".json") != -1 && file != "settings.json") {
const jsonFile = new JSONFaucetMails(file);
try {
await jsonFile.load();
} catch (e) {
jsonFiles.push({
title: file,
description: "invalid json file",
value: file,
disabled: true,
});
continue;
}
jsonFiles.push({
title: file,
description: "json file",
value: file,
});
}
}
if (jsonFiles.length == 0) {
throw new Error("There is no valid `.json` file");
}
const walletsFile = (
await prompt({
type: "select",
name: "file",
message: "Choose wallets json file",
choices: jsonFiles,
})
).file;
await db.findWallet(walletsFile);
mainLoop: while (true) {
workers = await db.getWorkers();
const answers = await prompt([
{
type: "text",
name: "number",
message: "Phone number?",
},
{
type: "text",
name: "password",
message: "2FA Password?",
},
]);
for (const worker of workers) {
if (worker.phoneNumber == answers.number) {
logger.warn("You already added this number");
continue mainLoop;
}
}
const session = new telegram.sessions.StringSession("");
const telegramWorker = new TelegramWorker(
session,
apiId!,
apiHash!.trim(),
{
deviceModel: "Samsung SM-G980",
connectionRetries: 5,
systemVersion: "10",
}
);
telegramWorker.setLogLevel(LogLevel.ERROR);
await telegramWorker.start({
phoneNumber: async () => answers.number.replace(/ /g, "").trim(),
password: async () => answers.password.replace(/ /g, "").trim(),
phoneCode: async () =>
(
await prompt({
type: "number",
name: "code",
message: "Enter recieved code",
})
).code.toString(),
onError: (err) => {
logger.error(`Error due singning into telegram worker\n${err}`);
throw err;
},
});
if ((await telegramWorker.isUserAuthorized()) === true) {
const worker: telegram.Api.User | telegram.Api.InputPeerUser =
await telegramWorker.getMe(false);
if (worker instanceof telegram.Api.User) {
await db.addUser(
{
password: answers.password,
phoneNumber: answers.number,
apiHash: apiHash!,
telegramID: Number(worker.id),
apiID: apiId!,
stringSession: String(telegramWorker.session.save()),
refferal: await db.findRefferal(settings.maxRefferals),
},
walletsFile
);
} else {
logger.error("Wrong type of worker?");
}
} else {
logger.error("telegramWorker not authorized");
}
if (
(
await prompt({
type: "confirm",
name: "continue",
message: "continue?",
})
).continue != true
) {
return;
}
}
};

View File

@ -1,932 +0,0 @@
import Hero from "@ulixee/hero";
import * as telegram from "telegram";
import { sleep } from "telegram/Helpers";
import { AccountInterface } from "./interfaces/databaseInterface";
import { prevTaskInterface } from "./interfaces/otherInterfaces";
import logger from "./logger";
import {
CaptchaError,
getMessagesByEntity,
NoNewMessagesError,
waitNewMessages,
} from "./utils";
import { settings } from "./database";
import { isNull } from "lodash";
export const vertification = async function (hero: Hero, url: string) {
await hero.goto(url, { timeoutMs: 2_000_000 });
await hero.waitForLoad("AllContentLoaded", { timeoutMs: 1180_000 });
const captchaButton = hero.document.querySelector(
"button.g-recaptcha.btn.btn-primary"
);
if (captchaButton != null) {
let verify = hero.document.querySelector("body > section > h2");
while (
isNull(verify) != true &&
(await verify.textContent)!.includes("All right!") == false
) {
const isCaptchaPassed = hero.document.querySelector(
"input[id='recaptcha-token']"
);
if (
isNull(isCaptchaPassed) ||
(await isCaptchaPassed.$isVisible) == false
) {
await hero.interact({
click: {
element: captchaButton,
verification: "exactElement",
},
});
await sleep(5000);
verify = hero.document.querySelector("body > section > h2");
} else {
logger.error("Captcha detected");
throw new CaptchaError("Auhtung captcha detected");
}
}
} else {
throw new Error("Cant find captcha button");
}
};
export const doSitesTasks = async function (
client: telegram.TelegramClient,
worker: AccountInterface,
hero: Hero,
botEntity: telegram.Api.Chat | telegram.Api.User,
tasksSelector: telegram.Api.Message
) {
const taskSitesButton = tasksSelector.buttons![1][0];
const prevTask: prevTaskInterface = {
task: tasksSelector,
lastMessage: tasksSelector,
};
mainLoop: while (true) {
await sleep(2000);
await taskSitesButton.click({ sharePhone: false });
await waitNewMessages(
client,
worker,
botEntity,
prevTask.lastMessage.id
);
const task = (
await client.getMessages(botEntity, {
minId: prevTask.lastMessage.id,
// search: (await settings.botMessages()).tasksSelector,
})
)[0];
if (task.buttonCount == 2) {
logger.debug(`New ad task | ${worker.phoneNumber}`);
const urlButton = task.buttons![0][0];
const skipButton = task.buttons![0][1];
await hero.goto(urlButton.url!, { timeoutMs: 2_000_000 });
await hero.waitForLoad("AllContentLoaded", { timeoutMs: 1180_000 });
let captchaButton = hero.document.querySelector(
"button.g-recaptcha.btn.btn-primary"
);
if (
isNull(captchaButton) &&
(await hero.activeTab.url).includes("hkbots")
) {
if (prevTask.task.message == task.message) {
logger.warn(
`Same tasks detected on ${worker.phoneNumber}... Skipping due captcha isnt detected......`
);
await skipButton.click({ sharePhone: false });
await waitNewMessages(client, worker, botEntity, task.id);
prevTask.task = task;
prevTask.lastMessage = (
await client.getMessages(botEntity, {
minId: task.id,
})
)[0];
} else {
prevTask.task = task;
logger.warn(
`Captcha isnt detected... | ${worker.phoneNumber}`
);
}
} else {
prevTask.task = task;
if ((await hero.activeTab.url).includes("hkbots")) {
if ((await captchaButton.$isClickable) != true) {
// timer page
logger.warn(`Found timer... | ${worker.phoneNumber}`);
if (settings.bypassMode == true) {
logger.warn(
`Bypassing timer... | ${worker.phoneNumber}`
);
try {
await hero.executeJs(
"document.getElementById('form').setAttribute('style', '')"
);
} catch (err) {
logger.error(`Something went wrong due bypass`);
}
} else {
while (
hero.document.querySelector("i[id='timer']") !=
null &&
(await captchaButton.$isClickable) != true
) {
await sleep(1000);
}
}
}
const now = new Date();
while ((await hero.activeTab.url) == urlButton.url!) {
if ((+new Date() - +now) / 1000 > 2 * 60) {
await skipButton.click({ sharePhone: false });
await waitNewMessages(
client,
worker,
botEntity,
task.id
);
prevTask.task = task;
prevTask.lastMessage = (
await client.getMessages(botEntity, {
minId: task.id,
})
)[0];
logger.warn(
`Anomaly activity in browser. Please checkout`
);
continue mainLoop;
} else {
const isCaptchaPassed = hero.document.querySelector(
"input[id='recaptcha-token']"
);
if (
isNull(isCaptchaPassed) ||
(await isCaptchaPassed.$isVisible) == false
) {
if (await captchaButton.$isClickable) {
try {
await hero.interact({
click: {
element: captchaButton,
verification: "exactElement",
},
});
} catch (err) {
logger.warn(
`Some error due clicking | ${worker.phoneNumber}`
);
continue mainLoop;
}
}
await sleep(1000);
captchaButton = hero.document.querySelector(
"button.g-recaptcha.btn.btn-primary"
);
} else {
logger.error(
`Captcha detected | ${worker.phoneNumber}`
);
throw new Error("Captcha detected");
}
}
}
}
await waitNewMessages(client, worker, botEntity, task.id);
const afterTask: telegram.Api.Message = (
await client.getMessages(botEntity, {
minId: task.id,
limit: 100,
})
).filter((value) => {
return value.message.includes(
settings.botMessages.taskComplete
);
})[0];
prevTask.lastMessage = (
await client.getMessages(botEntity, {
minId: task.id,
})
)[0];
if (afterTask != null) {
// need update
if (
afterTask.message.includes(
settings.botMessages.taskComplete
) == true
) {
logger.info(
`Site task complete | ${worker.phoneNumber}`
);
continue;
}
}
}
} else if (
task.message.includes(settings.botMessages.taskOver) == true
) {
logger.info(`Ad tasks completed | ${worker.phoneNumber}`);
break;
} else {
logger.error(`Check out last message of ${worker.phoneNumber}`);
throw new Error(`Check out last message of ${worker.phoneNumber}`);
}
}
};
export const doChatsTasks = async function (
client: telegram.TelegramClient,
worker: AccountInterface,
hero: Hero,
botEntity: telegram.Api.Chat | telegram.Api.User,
tasksSelector: telegram.Api.Message
) {
const taskChatsButton = tasksSelector.buttons![1][1];
const prevTask: prevTaskInterface = {
task: tasksSelector,
lastMessage: tasksSelector,
};
mainLoop: while (true) {
await sleep(2000);
await taskChatsButton.click({ sharePhone: false });
await waitNewMessages(
client,
worker,
botEntity,
prevTask.lastMessage.id
);
const task = (
await client.getMessages(botEntity, {
minId: prevTask.lastMessage.id,
// search: (await settings.botMessages()).tasksSelector,
})
)[0];
if (task.buttonCount == 3) {
logger.info(`New chat task | ${worker.phoneNumber}`);
const urlButton = task.buttons![0][0];
const confirmButton = task.buttons![0][1];
const skipButton = task.buttons![0][2];
let groupLink = "";
if (urlButton.url?.includes("hkbot") == true) {
await hero.goto(urlButton.url, { timeoutMs: 2_000_000 });
await hero.waitForLoad("AllContentLoaded", {
timeoutMs: 1180_000,
});
let captchaButton = hero.document.querySelector(
"button.g-recaptcha.btn.btn-primary"
);
if (isNull(captchaButton)) {
if (prevTask.task.message == task.message) {
logger.warn(
`Same tasks detected on ${worker.phoneNumber}... Skipping due captcha isnt detected......`
);
await skipButton.click({ sharePhone: false });
await waitNewMessages(
client,
worker,
botEntity,
task.id
);
prevTask.task = task;
prevTask.lastMessage = (
await client.getMessages(botEntity, {
minId: task.id,
})
)[0];
} else {
prevTask.task = task;
prevTask.lastMessage = task;
logger.warn(
`Captcha isnt detected... | ${worker.phoneNumber}`
);
}
continue mainLoop;
}
const now = new Date();
while (
captchaButton != null &&
(await hero.activeTab.url) == urlButton.url!
) {
if ((+new Date() - +now) / 1000 > 2 * 60) {
if ((await captchaButton.$isClickable) == false) {
await skipButton.click({ sharePhone: false });
await waitNewMessages(
client,
worker,
botEntity,
task.id
);
prevTask.task = task;
prevTask.lastMessage = (
await client.getMessages(botEntity, {
minId: task.id,
})
)[0];
continue mainLoop;
}
logger.warn(
`Anomaly activity in browser. Please checkout`
);
await hero.reload();
}
const isCaptchaPassed = hero.document.querySelector(
"input[id='recaptcha-token']"
);
if (
isCaptchaPassed == null ||
(await isCaptchaPassed.$isVisible) == false
) {
if (await captchaButton.$isClickable) {
try {
await hero.interact({
click: {
element: captchaButton,
verification: "exactElement",
},
});
} catch (err) {
logger.warn(
`Some error due clicking | ${worker.phoneNumber}`
);
continue mainLoop;
}
}
await sleep(1000);
captchaButton = hero.document.querySelector(
"button.g-recaptcha.btn.btn-primary"
);
} else {
logger.error("Captcha detected");
throw new CaptchaError("Auhtung captcha detected");
}
}
groupLink = await hero.url;
} else {
groupLink = urlButton.url!;
}
// https://gram.js.org/tl/messages/GetFullChat
// hash checking
let group: telegram.Api.Channel | undefined;
if (
!/(^\w+:|^)\/\/(?:t|telegram)\.(?:me|dog)\/(joinchat\/|\+)([\w-]+)/i.test(
groupLink
)
) {
groupLink = groupLink.replace(
/(^\w+:|^)\/\/(?:t|telegram)\.(?:me|dog)\//i,
""
);
try {
group = (
(await client.invoke(
new telegram.Api.channels.JoinChannel({
channel: groupLink,
})
)) as telegram.Api.Updates
).chats[0] as telegram.Api.Channel;
} catch (err: unknown) {
if (err instanceof telegram.errors.RPCError) {
switch (err.errorMessage) {
case "CHANNELS_TOO_MUCH":
logger.warn(
`Too much groups | ${worker.phoneNumber}`
);
return;
case "USER_ALREADY_PARTICIPANT":
logger.warn(
`Already in group | ${worker.phoneNumber}`
);
group = (
await client.invoke(
new telegram.Api.channels.GetChannels({
id: [groupLink],
})
)
)[0];
break;
default:
logger.warn(
`Something wrong with chat ${err.errorMessage} | ${worker.phoneNumber}`
);
await skipButton.click({ sharePhone: false });
await waitNewMessages(
client,
worker,
botEntity,
task.id
);
prevTask.task = task;
prevTask.lastMessage = (
await client.getMessages(botEntity, {
minId: task.id,
})
)[0];
continue mainLoop;
}
}
}
} else {
// hash
groupLink = groupLink.replace(
/(^\w+:|^)\/\/(?:t|telegram)\.(?:me|dog)\/(joinchat\/|\+)/i,
""
);
try {
group = (
(await client.invoke(
new telegram.Api.messages.ImportChatInvite({
hash: groupLink,
})
)) as telegram.Api.Updates
).chats[0] as telegram.Api.Channel;
} catch (err: unknown) {
if (err instanceof telegram.errors.RPCError) {
switch (err.errorMessage) {
case "CHANNELS_TOO_MUCH":
logger.warn(
`Too much groups | ${worker.phoneNumber}`
);
return;
case "USER_ALREADY_PARTICIPANT":
logger.warn(
`Already in group | ${worker.phoneNumber}`
);
group = (
(await client.invoke(
new telegram.Api.messages.CheckChatInvite(
{
hash: groupLink,
}
)
)) as telegram.Api.ChatInviteAlready
).chat as telegram.Api.Channel;
break;
default:
logger.warn(
`Something wrong with chat ${err.errorMessage} | ${worker.phoneNumber}`
);
await skipButton.click({ sharePhone: false });
await waitNewMessages(
client,
worker,
botEntity,
task.id
);
prevTask.task = task;
prevTask.lastMessage = (
await client.getMessages(botEntity, {
minId: task.id,
})
)[0];
continue mainLoop;
}
}
}
}
await confirmButton.click({ sharePhone: false });
await waitNewMessages(client, worker, botEntity, task.id);
const afterTask = (
await client.getMessages(botEntity, {
minId: task.id,
limit: 100,
})
).filter((value) => {
if (
value.message.includes(settings.botMessages.notInGroup) ||
value.message.includes(settings.botMessages.taskComplete)
) {
return true;
} else {
return false;
}
})[0];
const lastMessage = (
await client.getMessages(botEntity, {
minId: task.id,
})
)[0];
prevTask.task = task;
prevTask.lastMessage =
lastMessage !== undefined ? lastMessage : afterTask;
if (
afterTask.message.includes(settings.botMessages.taskComplete) ==
true
) {
worker.completedGroupsTasks.push({
timeToLeave:
+new Date() +
Number(afterTask.message.replace(/\D/g, "")) *
3600 *
1_000,
groupID: await client.getPeerId(group!),
});
logger.info(`Chat task complete | ${worker.phoneNumber}`);
continue;
} else if (
afterTask.message.includes(settings.botMessages.notInGroup) ==
true
) {
logger.warn(`User joined but hk says not. Skipping...`);
await skipButton.click({ sharePhone: false });
await waitNewMessages(client, worker, botEntity, task.id);
prevTask.task = task;
prevTask.lastMessage = (
await client.getMessages(botEntity, {
minId: task.id,
// search: (await settings.botMessages()).tasksSelector,
})
)[0];
worker.completedGroupsTasks.push({
timeToLeave: 1,
groupID: await client.getPeerId(group!),
});
} else {
logger.debug(`Anomaly | ${worker.phoneNumber}`);
logger.debug(task.message);
}
} else if (
task.message.includes(settings.botMessages.taskOver) == true
) {
logger.info(`Chats tasks completed | ${worker.phoneNumber}`);
break;
} else {
logger.error(`Check out last message of ${worker.phoneNumber}`);
throw new Error(`Check out last message of ${worker.phoneNumber}`);
}
}
};
export const doBotTasks = async function (
client: telegram.TelegramClient,
worker: AccountInterface,
hero: Hero,
botEntity: telegram.Api.Chat | telegram.Api.User,
tasksSelector: telegram.Api.Message
) {
const taskBotsButton = tasksSelector.buttons![1][2];
const prevTask: prevTaskInterface = {
task: tasksSelector,
lastMessage: tasksSelector,
};
mainLoop: while (true) {
await sleep(2000);
await taskBotsButton.click({ sharePhone: false });
await waitNewMessages(
client,
worker,
botEntity,
prevTask.lastMessage.id
);
let task: telegram.Api.Message;
[prevTask.lastMessage, task] = await client.getMessages(botEntity, {
minId: prevTask.lastMessage.id,
limit: 2,
});
if (task === undefined) {
task = prevTask.lastMessage;
}
if (task.buttonCount == 3) {
logger.info(`New bot task | ${worker.phoneNumber}`);
const urlButton = task.buttons![0][0];
const skipButton = task.buttons![0][1];
let botLink = "@";
if (urlButton.url?.includes("hkbot") == true) {
await hero.goto(urlButton.url, { timeoutMs: 2_000_000 });
await hero.waitForLoad("AllContentLoaded", {
timeoutMs: 1180_000,
});
let captchaButton = hero.document.querySelector(
"button.g-recaptcha.btn.btn-primary"
);
if (isNull(captchaButton)) {
if (prevTask.task.message == task.message) {
logger.warn(
`Same tasks detected on ${worker.phoneNumber}... Skipping due captcha isnt detected......`
);
await skipButton.click({ sharePhone: false });
} else {
logger.warn(
`Captcha isnt detected... | ${worker.phoneNumber}`
);
}
await waitNewMessages(
client,
worker,
botEntity,
prevTask.lastMessage.id
);
const cancelMessage = await client.sendMessage(botEntity, {
message: "/cancel",
});
await waitNewMessages(
client,
worker,
botEntity,
cancelMessage.id
);
prevTask.lastMessage = (
await client.getMessages(botEntity, {
minId: cancelMessage.id,
})
)[0];
prevTask.task = task;
continue mainLoop;
}
const now = new Date();
while (
captchaButton != null &&
(await hero.activeTab.url) == urlButton.url!
) {
if ((+new Date() - +now) / 1000 > 2 * 60) {
if ((await captchaButton.$isClickable) == false) {
await skipButton.click({ sharePhone: false });
await waitNewMessages(
client,
worker,
botEntity,
prevTask.lastMessage.id
);
const cancelMessage = await client.sendMessage(
botEntity,
{ message: "/cancel" }
);
await waitNewMessages(
client,
worker,
botEntity,
cancelMessage.id
);
prevTask.lastMessage = (
await client.getMessages(botEntity, {
minId: cancelMessage.id,
})
)[0];
prevTask.task = task;
continue mainLoop;
}
logger.warn(
`Anomaly activity in browser. Please checkout`
);
await hero.reload();
}
const isCaptchaPassed = hero.document.querySelector(
"input[id='recaptcha-token']"
);
if (
isCaptchaPassed == null ||
(await isCaptchaPassed.$isVisible) == false
) {
if (await captchaButton.$isClickable) {
try {
await hero.interact({
click: {
element: captchaButton,
verification: "exactElement",
},
});
} catch (err) {
logger.warn(
`Some error due clicking | ${worker.phoneNumber}`
);
continue mainLoop;
}
}
await sleep(1000);
captchaButton = hero.document.querySelector(
"button.g-recaptcha.btn.btn-primary"
);
} else {
logger.error("Captcha detected");
throw new CaptchaError("Auhtung captcha detected");
}
}
botLink += (await hero.url)
.replace(/(^\w+:|^)\/\/(?:t|telegram)\.(?:me|dog)\//i, "")!
.replace(/(?:\?start=)[A-Za-z0-9]+/i, "")!;
} else {
botLink += urlButton.url
?.replace(/(^\w+:|^)\/\/(?:t|telegram)\.(?:me|dog)\//i, "")!
.replace(/(?:\?start=)[A-Za-z0-9]+/i, "")!;
}
await client.invoke(
new telegram.Api.contacts.Unblock({
id: botLink,
})
);
try {
await waitNewMessages(
client,
worker,
botLink,
(
await client.sendMessage(botLink, { message: "/start" })
).id
);
} catch (err) {
if (err instanceof NoNewMessagesError) {
logger.warn(
`Target bot doesnt work | ${worker.phoneNumber}`
);
logger.debug(err.message);
await skipButton.click({ sharePhone: false });
await waitNewMessages(
client,
worker,
botEntity,
prevTask.lastMessage.id
);
const cancelMessage = await client.sendMessage(botEntity, {
message: "/cancel",
});
await waitNewMessages(
client,
worker,
botEntity,
cancelMessage.id
);
prevTask.lastMessage = (
await client.getMessages(botEntity, {
minId: cancelMessage.id,
})
)[0];
prevTask.task = task;
continue mainLoop;
} else {
throw err;
}
}
let forwardMessage: telegram.Api.Message;
try {
forwardMessage = (await (
await getMessagesByEntity(client, botLink, botLink, {
limit: 1000,
})
)[0].forwardTo(botEntity))![0];
} catch (err) {
if (err instanceof telegram.errors.RPCError) {
client.invoke(
new telegram.Api.messages.DeleteHistory({
justClear: true,
peer: botLink,
})
);
client.invoke(
new telegram.Api.contacts.Block({
id: botLink,
})
);
logger.warn(
`Error when forwarding a message. Skipping task... | ${worker.phoneNumber}`
);
logger.debug(err.message);
await skipButton.click({ sharePhone: false });
await waitNewMessages(
client,
worker,
botEntity,
prevTask.lastMessage.id
);
const cancelMessage = await client.sendMessage(botEntity, {
message: "/cancel",
});
await waitNewMessages(
client,
worker,
botEntity,
cancelMessage.id
);
prevTask.lastMessage = (
await client.getMessages(botEntity, {
minId: cancelMessage.id,
})
)[0];
prevTask.task = task;
continue mainLoop;
} else {
throw err;
}
}
await waitNewMessages(
client,
worker,
botEntity,
prevTask.lastMessage.id
);
const afterTask = (
await client.getMessages(botEntity, {
minId: prevTask.lastMessage.id,
limit: 10,
})
).filter((value) => {
if (
value.message.includes(settings.botMessages.taskComplete) ||
value.message.includes(settings.botMessages.oldMessage)
) {
return true;
} else {
return false;
}
})[0];
if (afterTask !== undefined) {
if (
afterTask.message.includes(
settings.botMessages.taskComplete
) == true
) {
await client.invoke(
new telegram.Api.messages.DeleteHistory({
justClear: true,
peer: botLink,
})
);
await client.invoke(
new telegram.Api.contacts.Block({
id: botLink,
})
);
logger.info(`Bot task complete | ${worker.phoneNumber}`);
} else if (
afterTask.message.includes(settings.botMessages.oldMessage)
) {
logger.warn(
`Forwarded message was old | ${worker.phoneNumber}`
);
}
} else {
client.invoke(
new telegram.Api.messages.DeleteHistory({
justClear: true,
peer: botLink,
})
);
client.invoke(
new telegram.Api.contacts.Block({
id: botLink,
})
);
logger.warn(
`Message that i found is unknown | ${worker.phoneNumber}`
);
await skipButton.click({ sharePhone: false });
await waitNewMessages(
client,
worker,
botEntity,
prevTask.lastMessage.id
);
const cancelMessage = await client.sendMessage(botEntity, {
message: "/cancel",
});
await waitNewMessages(
client,
worker,
botEntity,
cancelMessage.id
);
prevTask.lastMessage = (
await client.getMessages(botEntity, {
minId: cancelMessage.id,
})
)[0];
prevTask.task = task;
continue mainLoop;
}
const cancelMessage = await client.sendMessage(botEntity, {
message: "/cancel",
});
await waitNewMessages(client, worker, botEntity, cancelMessage.id);
const lastMessage = (
await client.getMessages(botEntity, {
minId: cancelMessage.id,
})
)[0];
prevTask.task = task;
prevTask.lastMessage =
lastMessage !== undefined ? lastMessage : afterTask;
continue;
} else if (
task.message.includes(settings.botMessages.taskOver) == true
) {
logger.info(`Bots tasks completed | ${worker.phoneNumber}`);
break;
} else {
logger.error(`Check out last message of ${worker.phoneNumber}`);
throw new Error(`Check out last message of ${worker.phoneNumber}`);
}
}
};

View File

@ -0,0 +1,46 @@
import { JsonDB, Config } from "node-json-db";
import { Knex } from "knex";
import knex from "knex";
export abstract class JSONDatabase {
protected readonly rawDB: JsonDB;
protected isLoaded: boolean = false;
constructor(file: string) {
this.rawDB = new JsonDB(new Config(file, false, true, "/"));
return new Proxy(this, {
get(target, name, reciever) {
if (
target.isLoaded ||
!(name in Object.getOwnPropertyNames(target))
) {
return target[name];
} else {
throw new Error("Database is not loaded");
}
},
set(target, name, newValue, reciever) {
if (target.isLoaded || name == "isLoaded") {
target[name] = newValue;
} else {
return false;
}
return true;
},
});
}
async load() {
this.isLoaded = true;
await this.rawDB.load();
}
}
export abstract class SQLDatabase {
protected readonly rawDB: Knex<any, unknown[]>;
constructor(file: string) {
this.rawDB = knex({
client: "better-sqlite3",
connection: {
filename: file,
},
});
}
}

164
classes/Automatization.ts Normal file
View File

@ -0,0 +1,164 @@
import Miner from "@ulixee/miner";
import findFreePorts from "find-free-ports";
import { IGroupPorts, IUsedIPInterface } from "../interfaces/IExecutorHelper";
import { IWorker } from "../interfaces/IWorker";
import { db } from "./Database";
import { settings } from "./Settings";
import Docker from "dockerode";
import { dockerWrapper } from "./Docker";
import { logger } from "../logger";
import { sleep } from "telegram/Helpers";
import TorControl from "tor-control";
import { getIP } from "../utils";
import { isEqual } from "lodash";
import { HeroWrapper } from "./HeroWrapper";
export const executor = new (class Executor {
protected usedIPs: IUsedIPInterface[] = [];
async proccessWorkers() {
const workers = await db.getWorkers();
if (settings.shuffleAccounts) {
workers.sort(() => Math.random() - 0.5);
}
const workersGroups: Array<IWorker[]> = [];
for (const worker of workers) {
this.usedIPs.push({
worker: worker,
usedIps: [],
current: "",
});
}
for (let i = 0; i != settings.pararels; i++) {
// spliting the workers into a groups
const startIndex =
i * Math.ceil(workers.length / settings.pararels);
const _workers = workers.slice(
startIndex,
startIndex + Math.ceil(workers.length / settings.pararels)
);
if (_workers.length > 0) {
workersGroups.push(_workers);
} else {
break;
}
}
const pararels: Promise<void>[] = [];
const miner = new Miner();
await miner.listen({ port: (await findFreePorts(1))[0] });
const dockerPorts = await findFreePorts(workersGroups.length * 3);
// creating docker containers
let containers: Docker.Container[] = [];
{
const promises: Promise<Docker.Container>[] = [];
for (let i = 0; i != workersGroups.length; i++) {
promises.push(
dockerWrapper.createTorDocker(
dockerPorts[i * 3],
dockerPorts[i * 3 + 1],
dockerPorts[i * 3 + 2]
)
);
}
containers = (await Promise.allSettled(promises)).map((value) => {
if (value.status=='fulfilled'){
return value.value;
} else{
throw new Error(value.reason)
}
});
}
//
for (let i = 0; i != workersGroups.length; i++) {
pararels.push(
this.proccessGroupWorkers(workersGroups[i], {
http: dockerPorts[i * 3],
socks: dockerPorts[i * 3 + 1],
control: dockerPorts[i * 3 + 2],
miner: 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}`);
}
}
await dockerWrapper.closeOpenedDockers(containers);
await miner.close(true);
}
protected async proccessGroupWorkers(
workerGroup: IWorker[],
ports: IGroupPorts
) {
workersLoop: for (const worker of workerGroup) {
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 this.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)) {
this.usedIPs.filter((value) => {
return isEqual(worker, value.worker);
})[0].current = currentIP;
this.usedIPs
.filter((value) => {
return isEqual(worker, value.worker);
})[0]
.usedIps.push(currentIP);
break;
} else {
await tor.signalNewnym();
await sleep(3_000);
}
}
}
}
const currentIP = this.usedIPs.filter((value) => {
return isEqual(worker, value.worker);
})[0].current;
const browser = new HeroWrapper(
worker.browserFingerprint,
worker.phoneNumber,
ports.miner,
ports.socks,
currentIP
);
}
}
})();

266
classes/Database.ts Normal file
View File

@ -0,0 +1,266 @@
import { isEqual } from "lodash";
import { logger } from "../logger";
import {
IWorker,
IWorkerCompletedGroupTask,
IWorkerDatabase,
} from "../interfaces/IWorker";
import type { SetOptional } from "type-fest";
import { FingerprintGenerator } from "fingerprint-generator";
import { IFaucetMail } from "../interfaces/IFaucetMail";
import { JSONDatabase, SQLDatabase } from "./AbstractDatabase";
export class JSONFaucetMails extends JSONDatabase {
async getWallets(): Promise<IFaucetMail[]> {
const rawWallets = await this.rawDB.getData("/");
const wallets: IFaucetMail[] = [];
for (const rawWallet of Object.keys(rawWallets)) {
try {
const wallet: IFaucetMail = {
faucet: {
username: rawWallets[rawWallet].faucetpay.username,
password: rawWallets[rawWallet].faucetpay.password,
},
mail: {
address: rawWallet,
password: rawWallets[rawWallet].mail_passphrase,
},
};
wallets.push(wallet);
} catch (e) {
continue;
}
}
return wallets;
}
}
export const db = new (class SQLWorkerDatabase extends SQLDatabase {
constructor() {
super("db.sqlite");
this.rawDB.schema.hasTable("workers").then((result) => {
if (result != true) {
this.rawDB.schema
.createTable("workers", (table) => {
table
.string("phoneNumber", 12)
.notNullable()
.primary()
.unique(),
table.integer("telegramID").notNullable().unique(),
table.bigInteger("apiID").notNullable(),
table.string("apiHash").notNullable(),
table.string("password").notNullable(),
table.jsonb("faucetMail").notNullable().unique(),
table
.string("stringSession")
.notNullable()
.unique(),
table.float("balance").notNullable().defaultTo(0.0),
table.boolean("canBeRefferal").notNullable(),
table
.jsonb("browserFingerprint")
.notNullable()
.unique()
.comment(
"I save it in json data, so as not to create another table"
),
table.integer("refferal").defaultTo(null),
table
.boolean("troubled")
.notNullable()
.comment("if worker has maximum groups");
})
.then(() => {
logger.debug(
"Created `workers` table into `db.sqlite`"
);
});
}
});
this.rawDB.schema.hasTable("completedGroupsTasks").then((result) => {
if (result != true) {
this.rawDB.schema
.createTable("completedGroupsTasks", (table) => {
table.increments("id").primary().unique(),
table.string("worker", 12).notNullable(),
table.string("groupID").notNullable(),
table.timestamp("timeToLeave").notNullable();
})
.then(() => {
logger.debug(
"Created `completedGroupsTasks` table into `db.sqlite`"
);
});
}
});
}
convertWorker(worker: IWorker): IWorkerDatabase {
let convertedWorker: IWorkerDatabase = structuredClone(
worker
) as unknown as IWorkerDatabase;
Object.keys(worker).forEach(function (key, index) {
if (worker[key] === Object(worker[key])) {
convertedWorker[key] = JSON.stringify(worker[key]);
} else {
convertedWorker[key] = worker[key];
}
});
delete convertedWorker["completedGroupsTasks"];
delete convertedWorker["withdraws"];
return convertedWorker;
}
async getCompletedGroups(
worker: Pick<IWorker, "phoneNumber">
): Promise<IWorkerCompletedGroupTask[]> {
return await this.rawDB("completedGroupsTasks")
.where({ worker: worker.phoneNumber })
.select("timeToLeave", "groupID");
}
async getWorkers(): Promise<IWorker[]> {
try {
return Promise.all(
(
await this.rawDB.select().from<IWorkerDatabase>("workers")
).map(async (worker) => {
Object.keys(worker).forEach(function (key, index) {
try {
worker[key] = JSON.parse(worker[key]);
} catch (e) {
// normal prop
}
});
worker["withdraws"] = [];
const completedGroupsTasks = await this.getCompletedGroups(
worker
);
worker["completedGroupsTasks"] = completedGroupsTasks;
return worker as unknown as IWorker;
})
);
} catch {
return [];
}
}
async findRefferal(maxRefferals: number): Promise<number | null> {
let refferal: number | null = null;
const workers = await this.getWorkers();
if (workers.length > 0) {
for (const mainWorker of workers) {
let counter = 0;
if (mainWorker.canBeRefferal == true) {
const _workers = workers.filter((worker, workerIndex) => {
return workerIndex !== workers.indexOf(mainWorker);
});
for (const _worker of _workers) {
if (_worker.refferal == mainWorker.telegramID) {
counter += 1;
}
}
if (counter < maxRefferals) {
refferal = Number(mainWorker.telegramID);
}
}
}
}
return refferal;
}
async addCompletedGroups(
worker: Pick<IWorker, "phoneNumber">,
group: IWorkerCompletedGroupTask
) {
await this.rawDB("completedGroupsTasks").insert({
worker: worker.phoneNumber,
groupID: group.groupID,
timeToLeave: group.timeToLeave,
});
}
async addUser(
worker: Omit<
IWorker,
| "balance"
| "withdraws"
| "completedGroupsTasks"
| "canBeRefferal"
| "browserFingerprint"
| "faucetMail"
| "troubled"
>,
wallets_json: string
): Promise<void> {
const fingerprintGenerator = new FingerprintGenerator();
const fingerprint = fingerprintGenerator.getFingerprint({
devices: ["desktop"],
browsers: ["chrome"],
}).fingerprint;
const faucet: IFaucetMail | undefined = await this.findWallet(
wallets_json
);
if (faucet === undefined) {
logger.error("Add new faucet accounts");
throw new Error("Add new faucet accounts");
} else {
const canBeRefferal: boolean = Math.random() < 0.5;
const _worker: IWorker = {
phoneNumber: worker.phoneNumber,
telegramID: worker.telegramID,
apiID: worker.apiID,
apiHash: worker.apiHash,
password: worker.password,
faucetMail: faucet,
stringSession: worker.stringSession,
balance: 0.0,
withdraws: [],
completedGroupsTasks: [],
canBeRefferal: canBeRefferal,
refferal: worker.refferal,
browserFingerprint: fingerprint,
troubled: false,
};
await this.rawDB("workers").insert(this.convertWorker(_worker));
}
}
async findWallet(walletsJson: string): Promise<IFaucetMail | undefined> {
const walletsJsonDB = new JSONFaucetMails(walletsJson);
await walletsJsonDB.load();
const wallets = await walletsJsonDB.getWallets();
if (Object.keys(wallets).length == 0) {
logger.error("File which you choose doesnt have anything");
throw new Error("File which you choose doesnt have anything");
}
const workers = await this.getWorkers();
let faucetWallet: IFaucetMail | undefined = undefined;
for (const wallet in wallets) {
let flag = false;
if (workers.length > 0) {
for (const worker of workers) {
if (worker.faucetMail.mail.address == wallet) {
flag = true;
break;
}
}
}
if (flag == false) {
faucetWallet = {
faucet: {
username: wallets[wallet].faucet.username,
password: wallets[wallet].faucet.password,
},
mail: {
address: wallet,
password: wallets[wallet].mail.password,
},
};
break;
}
}
return faucetWallet;
}
})();

116
classes/Docker.ts Normal file
View File

@ -0,0 +1,116 @@
import Docker from "dockerode";
import cliProgress from "cli-progress";
import { settings } from "./Settings";
import { sleep } from "telegram/Helpers";
import { logger } from "../logger";
export const dockerWrapper= new class DockerController{
protected multibar = new cliProgress.MultiBar({
hideCursor: true,
barsize: 50,
barCompleteChar: "❌",
barIncompleteChar: "✔️",
clearOnComplete: true,
format: `{status} | {bar} | {value}/{total}sec timeout`,
});
async closeOpenedDockers(containers: Docker.Container[]){
logger.debug("Closing opened docker containers ");
for (const container of containers) {
try {
await container.remove({ force: true, v: true });
} catch (err) {}
}
}
async createTorDocker(proxyPort: number,socksPort: number,controlPort: number,timeout = 200): Promise<Docker.Container>{
timeout *= 2;
const docker = new Docker({ socketPath: "/var/run/docker.sock" });
let isPulled = false;
mainLoop: for (const image of await docker.listImages()) {
const tags = image.RepoTags;
if (tags !== undefined)
for (const repoTag of tags) {
if (repoTag.search("dperson/torproxy:latest") != -1) {
isPulled = true;
break mainLoop;
}
}
}
if (isPulled != true) {
await docker.pull("dperson/torproxy:latest");
}
const options: Docker.ContainerCreateOptions = {
Image: "dperson/torproxy:latest",
Env: ["PASSWORD=qwerty", "LOCATION=US", "TOR_NewCircuitPeriod=50"],
ExposedPorts: {},
HostConfig: {
PortBindings: {
"8118/tcp": [
{
HostPort: `${proxyPort}`,
},
],
"9050/tcp": [
{
HostPort: `${socksPort}`,
},
],
"9051/tcp": [
{
HostPort: `${controlPort}`,
},
],
},
},
Healthcheck: {
Interval: 2 * 1_000_000_000,
Timeout: 50 * 1_000_000_000,
StartPeriod: 50 * 1_000_000_000,
// Test:['CMD_SHELL',`curl -sx localhost:8118 'https://check.torproject.org/' | grep -qm1 Congratulations`]
},
};
const container = await docker.createContainer(options);
try{
await container.start();
} catch (e){
logger.error(`Some error when starting docker container\n${e}`)
throw e
}
const progressBar = this.multibar.create(timeout / 2, 0, {
status: `Starting docker${
settings.logLevel == "debug"
? ` ${proxyPort} ${socksPort} ${controlPort}`
: ""
}`,
});
while ((await container.inspect())!.State.Health!.Status != "healthy") {
const state = (await container.inspect())!.State.Health!.Status;
if (progressBar.getProgress() >= timeout) {
await container.remove({force:true,v:true});
progressBar.update(timeout / 2, { status: "Failed" });
progressBar.stop();
throw new Error(`Docker ${container.id} is broken`);
}
if (state == "unhealthy") {
await container.remove({force:true,v:true});
progressBar.update(timeout / 2, {
status: "Docker is unhealthy...",
});
progressBar.stop();
return await this.createTorDocker(
proxyPort,
socksPort,
controlPort,
timeout / 2
);
}
progressBar.increment();
await sleep(2000);
}
progressBar.update(0, { status: "Started" });
progressBar.stop();
return container;
}
}

42
classes/HeroWrapper.ts Normal file
View File

@ -0,0 +1,42 @@
import Miner from "@ulixee/miner";
import Hero from "@ulixee/hero";
import ExecuteJsPlugin from "@ulixee/execute-js-plugin";
import { Fingerprint } from "fingerprint-generator";
import { logger } from "../logger";
export class HeroWrapper {
rawHero: Hero;
constructor(settings: {
fingerprint: Fingerprint;
name: string;
minerPort: number;
socksPort: number;
currentIP: string;
}) {
this.rawHero = new Hero({
userProfile: {
deviceProfile: {
deviceMemory: settings.fingerprint.navigator.deviceMemory,
},
},
name: settings.name,
connectionToCore: {
host: `ws://127.0.0.1:${settings.minerPort}`,
},
showChrome: true, // to debug
userAgent: settings.fingerprint.navigator.userAgent,
viewport: settings.fingerprint.screen,
locale: settings.fingerprint.navigator.language,
upstreamProxyUrl: `socks5://127.0.0.1:${settings.socksPort}`,
upstreamProxyIpMask: {
ipLookupService: "icanhazip.com",
proxyIp: settings.currentIP,
},
});
this.rawHero.use(ExecuteJsPlugin);
}
async close() {
await this.rawHero.close();
}
}

89
classes/Settings.ts Normal file
View File

@ -0,0 +1,89 @@
import { JSONDatabase } from "./AbstractDatabase";
import { ISettings } from "../interfaces/ISettings";
import { logger } from "../logger";
export const settings = new (class Settings
extends JSONDatabase
implements ISettings
{
logLevel: "debug" | "info" | "error" = "debug";
pararels: number = 3;
mainCrypto: "TRX" = "TRX";
minimalToWithdraw: number = 0.003;
maxRefferals: number = 5;
// to find
botMessages = {
verification: "To continue using this bot,",
tasksSelector: "Choose an option to start earning your TRX",
taskOver: "Sorry, there are no new ads available.",
taskComplete: "Success, ",
notInGroup: "not entered the chat",
oldMessage: "forwarded is old",
};
// to send
botButtons = {
earn: "❇️ Earn cryptocurrency",
balance: "💰 Balance",
withdraw: "📤 Withdraw",
};
// to join
telegramLinks = {
botLink: "@hkearn_trx_bot",
groupsToJoin: ["@hkearn_transactions", "@hkearn_updates"],
};
sleepTime = {
afterDoing: [3600, 7200],
betweenSessions: [35, 70],
};
shuffleAccounts: boolean = true;
bypassMode: boolean = false;
constructor() {
super("settings.json");
}
async load(): Promise<void> {
await super.load();
const settings = await this.rawDB.getObject<ISettings>("/");
const defaults = {};
Object.getOwnPropertyNames(this).forEach((name) => {
if (name != "rawDB" && name != "isLoaded") {
defaults[name] = this[name];
}
});
if (Object.keys(settings).length == 0) {
logger.warn("Setup config first...");
await this.rawDB.push("/", defaults, true);
await this.rawDB.save();
} else {
mainLoop: for (const setting of Object.keys(defaults)) {
if (
settings[setting] === undefined ||
typeof settings[setting] != typeof defaults[setting]
) {
this[setting] = defaults[setting];
logger.warn(
`Setting '${setting}' corrupted or undefined. Check out it...`
);
} else {
if (typeof settings[setting] == "object") {
for (const attr of Object.keys(defaults[setting])) {
if (
Object.keys(settings[setting]).indexOf(attr) ==
-1
) {
this[setting] = defaults[setting];
logger.warn(
`Setting '${setting}' corrupted. Check out it...`
);
continue mainLoop;
}
}
}
this[setting] = settings[setting];
}
}
logger.level = this.logLevel;
}
}
})();

71
classes/Telegram.ts Normal file
View File

@ -0,0 +1,71 @@
import * as telegram from "telegram";
import { EntityLike } from "telegram/define";
import { isEqual, isUndefined } from "lodash";
import { BadRequestError, TimedOutError } from "telegram/errors";
export class TelegramWorker extends telegram.TelegramClient {
async getMessagesByEntity(
entity: EntityLike,
getMessagesParams?: Omit<
Partial<telegram.client.message.IterMessagesParams>,
"fromUser"
>
): Promise<telegram.helpers.TotalList<telegram.Api.Message>> {
/*
Getting messages from entity
In original TelegramClient its not working
*/
const messages = new telegram.helpers.TotalList<telegram.Api.Message>();
for (const message of await super.getMessages(
entity,
getMessagesParams
)) {
if (isEqual(message.sender, entity)) {
messages.push(message);
}
}
return messages;
}
async waitForNewMassages(
entity: EntityLike,
lastMessage: telegram.Api.Message,
timeout: number = 20
): Promise<telegram.Api.Message> {
const start = new Date();
const params = { minId: lastMessage.id, limit: 2147483647 };
let lastMessages = await this.getMessagesByEntity(entity, params);
while (lastMessages.length == 0) {
if ((+new Date() - +start) / 1000 >= timeout) {
throw new TimedOutError(
"No new messages",
new telegram.Api.messages.GetMessages({})
);
}
lastMessages = await this.getMessagesByEntity(entity, params);
}
return lastMessage;
}
async isBannedByClient(entity: telegram.Api.User): Promise<boolean> {
if (isUndefined(entity.username) != true) {
const blockedUsers = (
await super.invoke(
new telegram.Api.contacts.GetBlocked({ limit: 2147483647 })
)
).users as telegram.Api.User[];
for (const blockedUser of blockedUsers) {
if (
isUndefined(blockedUser.username) != false &&
blockedUser.username == entity.username!.replace("@", "")
) {
return true;
}
}
return false;
} else {
throw new BadRequestError(
"Entity has no nickname",
new telegram.Api.contacts.GetBlocked({ limit: 2147483647 })
);
}
}
}

View File

@ -1,272 +0,0 @@
import { JsonDB, Config } from "node-json-db";
import { FingerprintGenerator } from "fingerprint-generator";
import logger from "./logger";
import * as interfacesDB from "./interfaces/databaseInterface";
import * as interfacesSettings from "./interfaces/settingsInterface";
import { arrayWithoutElementAtIndex } from "./utils";
export class Database {
public readonly default: interfacesDB.DatabaseInterface = {
accounts: [],
};
private json: JsonDB;
constructor() {
this.json = new JsonDB(new Config("db.json", false, true, "/"));
this.json.getData("/").then((result) => {
if (Object.keys(result).length == 0) {
this.json
.push("/", this.default, true)
.catch((err) => logger.error(`${err} due setting defaults`))
.then(() => {
logger.info("Setting DB to defaults...");
});
this.save();
}
});
}
async findWallet(
walletsFile: string
): Promise<interfacesDB.faucetMailInterface | undefined> {
const wallets_json = new JsonDB(
new Config(walletsFile, true, true, "/")
);
const wallets = await wallets_json.getData("/");
if (Object.keys(wallets).length == 0) {
logger.error("File which you choose doesnt have anything");
throw new Error("File which you choose doesnt have anything");
}
const accounts = await this.getUsers();
let faucetWallet: interfacesDB.faucetMailInterface | undefined =
undefined;
for (const _wallet in wallets) {
let flag = false;
if (accounts.length > 0) {
for (const account of accounts) {
if (account.faucetMail.mail.address == _wallet) {
flag = true;
break;
}
}
}
if (flag == false) {
faucetWallet = {
faucet: {
username: wallets[_wallet].faucetpay.username,
password: wallets[_wallet].faucetpay.password,
},
mail: {
address: _wallet,
password: wallets[_wallet].mail_passphrase,
},
};
break;
}
}
return faucetWallet;
}
async findRefferal(maxRefferals: number): Promise<number | null> {
let refferal: number | null = null;
const accounts = await this.getUsers();
if (accounts.length > 0) {
for (const mainAccount of accounts) {
let counter = 0;
if (mainAccount.canBeRefferal == true) {
const _accounts = arrayWithoutElementAtIndex(
accounts,
accounts.indexOf(mainAccount)
);
for (const _account of _accounts) {
if (_account.refferal == mainAccount.telegramID) {
counter += 1;
}
}
if (counter < maxRefferals) {
refferal = Number(mainAccount.telegramID);
}
}
}
}
return refferal;
}
async save() {
await this.json.save(true);
}
async updateUser(account: interfacesDB.AccountInterface) {
const index = await this.json.getIndex(
"/accounts",
account.phoneNumber,
"phoneNumber"
);
logger.debug(`Index in database is ${index} of ${account.phoneNumber}`);
await this.json.push(`/accounts[${index}]`, account, true);
}
async addUser(
account: Omit<
interfacesDB.AccountInterface,
| "balance"
| "withdraws"
| "completedGroupsTasks"
| "canBeRefferal"
| "browserFingerprint"
| "faucetMail"
>,
wallets_json: string
): Promise<void> {
const fingerprintGenerator = new FingerprintGenerator();
const fingerprint = fingerprintGenerator.getFingerprint({
devices: ["desktop"],
browsers: ["chrome"],
}).fingerprint;
const faucet: interfacesDB.faucetMailInterface | undefined =
await this.findWallet(wallets_json);
if (faucet === undefined) {
logger.error("Add new faucet accounts");
throw new Error("Add new faucet accounts");
} else {
const canBeRefferal: boolean = Math.random() < 0.5;
const _account: interfacesDB.AccountInterface = {
phoneNumber: account.phoneNumber,
telegramID: account.telegramID,
apiID: account.apiID,
apiHash: account.apiHash,
password: account.password,
faucetMail: faucet,
stringSession: account.stringSession,
balance: 0.0,
withdraws: [],
completedGroupsTasks: [],
canBeRefferal: canBeRefferal,
refferal: account.refferal,
browserFingerprint: fingerprint,
};
await this.json.push(`/accounts[]`, _account);
}
await this.save();
}
async getUsers(): Promise<interfacesDB.AccountInterface[]> {
try {
return await this.json.getObject<interfacesDB.AccountInterface[]>(
"/accounts"
);
} catch {
return [];
}
}
}
export class Settings implements interfacesSettings.settingsInterface {
public readonly default: interfacesSettings.settingsInterface = {
logLevel: "debug",
pararels: 2,
mainCrypto: "TRX",
minimalToWithdraw: 0.0003,
maxRefferals: 3,
botMessages: {
verification: "To continue using this bot,",
tasksSelector: "Choose an option to start earning your TRX",
taskOver: "Sorry, there are no new ads available.",
taskComplete: "Success, ",
notInGroup: "not entered the chat",
oldMessage: "forwarded is old",
},
botButtons: {
earn: "❇️ Earn cryptocurrency",
balance: "💰 Balance",
withdraw: "📤 Withdraw",
},
telegramLinks: {
botLink: "@hkearn_trx_bot",
groupsToJoin: ["@hkearn_transactions", "@hkearn_updates"],
},
bypassMode: false,
sleepTime: {
afterDoing: [1 * 3600, 2 * 3600],
betweenSessions: [35, 70],
},
shuffleAccounts: true,
};
logLevel: "debug" | "info" | "error";
mainCrypto: "TRX";
minimalToWithdraw: number;
maxRefferals: number;
botMessages: {
verification: string;
tasksSelector: string;
taskOver: string;
taskComplete: string;
notInGroup: string;
oldMessage: string;
};
botButtons: { earn: string; balance: string; withdraw: string };
telegramLinks: { botLink: string; groupsToJoin: string[] };
bypassMode: boolean;
pararels: number;
sleepTime: {
afterDoing: number[];
betweenSessions: number[];
};
shuffleAccounts: boolean;
private json: JsonDB;
constructor() {
this.json = new JsonDB(new Config("settings.json", false, true, "/"));
this.json
.getObject<interfacesSettings.settingsInterface>("/")
.then((result) => {
if (Object.keys(result).length == 0) {
logger.warn("Setup config first...");
this.json
.push("/", this.default, true)
.catch((err) =>
logger.error(`${err} due setting defaults`)
)
.then(() => {
this.json.save(true).then(() => {
throw new Error("Config doesn't setup");
});
});
} else {
mainLoop: for (const setting of Object.keys(this.default)) {
if (
result[setting] === undefined ||
typeof result[setting] !=
typeof this.default[setting]
) {
this[setting] = this.default[setting];
logger.warn(
`Setting '${setting}' corrupted or undefined. Check out it...`
);
} else {
if (typeof result[setting] == "object") {
for (const attr of Object.keys(
this.default[setting]
)) {
if (
Object.keys(result[setting]).indexOf(
attr
) == -1
) {
this[setting] = this.default[setting];
logger.warn(
`Setting '${setting}' corrupted. Check out it...`
);
continue mainLoop;
}
}
}
this[setting] = result[setting];
}
}
logger.level = this.logLevel;
}
});
}
}
const database = new Database();
const settings = new Settings();
export { database, settings };

718
index.ts
View File

@ -1,688 +1,26 @@
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
import { db } from "./classes/Database";
import { settings } from "./classes/Settings";
import { addAccounts } from "./addAccounts";
import { logger } from "./logger";
import { executor } from "./classes/Automatization";
(async () => {
while (true) {
let accounts: AccountInterface[] = [];
try {
accounts = await db.getUsers();
} catch (err) {
continue;
}
// databases load
await settings.load();
//
const workers = await db.getWorkers();
while (true) {
const answer = await prompt({
type: "select",
name: "menu",
message: "Choose action",
choices: [
accounts.length > 0
workers.length > 0
? {
title: "Start",
description: `Starting farm for ${accounts.length} accounts`,
description: `Starting farm for ${workers.length} accounts`,
value: "start",
}
: {
@ -708,29 +46,14 @@ const farm: () => Promise<void> = async () => {
},
],
});
switch (answer.menu) {
case "start":
await executor.proccessWorkers()
break;
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();
@ -739,9 +62,6 @@ const farm: () => Promise<void> = async () => {
})();
process.on("SIGINT", function () {
db.save().then(() => {
logger.info("Bye bye");
process.exit();
});
logger.info("Bye bye");
process.exit();
});

View File

@ -0,0 +1,14 @@
import { IWorker } from "./IWorker";
export interface IUsedIPInterface {
worker: Pick<IWorker,"phoneNumber">;
usedIps: string[];
current: string;
}
export interface IGroupPorts {
http: number,
socks: number,
control: number,
miner: number
}

10
interfaces/IFaucetMail.ts Normal file
View File

@ -0,0 +1,10 @@
export interface IFaucetMail {
readonly faucet: {
readonly username: string;
readonly password: string;
};
readonly mail: {
readonly address: string;
readonly password: string;
};
}

View File

@ -1,4 +1,4 @@
export interface settingsInterface {
export interface ISettings {
logLevel: "debug" | "info" | "error";
pararels: number;
mainCrypto: "TRX";
@ -30,4 +30,4 @@ export interface settingsInterface {
};
shuffleAccounts: boolean;
bypassMode: boolean;
}
}

6
interfaces/IWithdraw.ts Normal file
View File

@ -0,0 +1,6 @@
export interface IWorkerWithdraw {
timestamp: number,
amount: number,
toMail: string,
isWithdrawed: boolean
}

36
interfaces/IWorker.ts Normal file
View File

@ -0,0 +1,36 @@
import { Fingerprint } from "fingerprint-generator";
import { IFaucetMail } from "./IFaucetMail";
import { IWorkerWithdraw } from "./IWithdraw";
export interface IWorkerCompletedGroupTask {
readonly timeToLeave: number;
readonly groupID: string;
}
interface IWorkerBase {
readonly phoneNumber: string;
readonly telegramID: number;
readonly apiID: number;
readonly apiHash: string;
readonly password: string;
readonly stringSession: string;
readonly balance: number;
readonly canBeRefferal: boolean;
readonly refferal: number | null;
}
export interface IWorkerDatabase extends IWorkerBase {
readonly faucetMail: string;
readonly browserFingerprint: string;
troubled: boolean
}
export interface IWorker extends IWorkerBase{
readonly faucetMail: IFaucetMail;
readonly browserFingerprint: Fingerprint;
withdraws: IWorkerWithdraw[];
completedGroupsTasks: IWorkerCompletedGroupTask[];
troubled: boolean
}

View File

@ -1,35 +0,0 @@
import { Fingerprint } from "fingerprint-generator";
export interface faucetMailInterface {
faucet: {
username: string;
password: string;
};
mail: {
address: string;
password: string;
};
}
export interface AccountCompletedGroupsTasksInterface {
timeToLeave: number;
groupID: string;
}
export interface AccountWithdrawInterface {}
export interface AccountInterface {
phoneNumber: string;
telegramID: number;
apiID: number;
apiHash: string;
password: string;
faucetMail: faucetMailInterface;
stringSession: string;
balance: number;
withdraws: AccountWithdrawInterface[];
completedGroupsTasks: AccountCompletedGroupsTasksInterface[];
canBeRefferal: boolean;
browserFingerprint: Fingerprint;
refferal: number | null;
}
export interface DatabaseInterface {
accounts: AccountInterface[];
}

View File

@ -1,20 +0,0 @@
import * as telegram from "telegram";
import { AccountInterface } from "./databaseInterface";
export interface prevTaskInterface {
task: telegram.Api.Message;
lastMessage: telegram.Api.Message;
}
export interface portsInterface {
http: number;
socks: number;
control: number;
minerPort?: number;
}
export interface usedIPInterface {
worker: AccountInterface;
usedIps: string[];
current: string;
}

View File

@ -1,10 +1,10 @@
import winston = require("winston");
const logger = winston.createLogger({
import winston from "winston";
export const logger = winston.createLogger({
level: "debug",
format: winston.format.combine(
winston.format.colorize(),
winston.format.printf((info) => {
return `${new Date().toLocaleTimeString()} | ${info.level} - ${
return `${(new Date()).toLocaleTimeString()} | ${info.level} - ${
info.message
} `;
})
@ -15,5 +15,3 @@ const logger = winston.createLogger({
new winston.transports.File({ filename: "info.log" }),
],
});
export default logger;

View File

@ -1,19 +1,22 @@
{
"dependencies": {
"@ulixee/execute-js-plugin": "^2.0.0-alpha.16",
"@ulixee/hero": "^2.0.0-alpha.16",
"@ulixee/miner": "^2.0.0-alpha.16",
"@ulixee/execute-js-plugin": "^2.0.0-alpha.17",
"@ulixee/hero": "^2.0.0-alpha.17",
"@ulixee/miner": "^2.0.0-alpha.17",
"axios": "^1.2.1",
"better-sqlite3": "^8.0.1",
"cli-progress": "^3.11.2",
"dockerode": "^3.3.4",
"find-free-ports": "^3.0.0",
"fingerprint-generator": "^2.1.7",
"knex": "^2.3.0",
"lodash": "^4.17.21",
"node-json-db": "^2.1.3",
"prompts": "^2.4.2",
"socks5-https-client": "^1.2.1",
"telegram": "^2.14.7",
"telegram": "^2.15.2",
"tor-control": "^0.0.3",
"type-fest": "^3.4.0",
"typescript": "^4.9.4",
"winston": "^3.8.2"
},
@ -25,7 +28,7 @@
"@types/cli-progress": "^3.11.0",
"@types/dockerode": "^3.3.14",
"@types/lodash": "^4.14.191",
"@types/node": "^18.11.12",
"@types/node": "^18.11.17",
"@types/prompts": "^2.4.2"
},
"scripts": {

View File

@ -3,7 +3,6 @@
"module": "CommonJS",
"moduleResolution": "Node",
"target": "ES2022",
"jsx": "react",
"strictNullChecks": true,
"strictFunctionTypes": true,
"esModuleInterop": true,
@ -13,7 +12,7 @@
},
"exclude": [
"interfaces",
"clasess",
"node_modules",
"**/node_modules/*"
]

227
utils.ts
View File

@ -1,230 +1,15 @@
import Docker from "dockerode";
import logger from "./logger";
import { settings } from "./database";
// import Hero from "@ulixee/hero";
// import Miner from "@ulixee/miner";
import { sleep } from "telegram/Helpers";
import { AccountInterface } from "./interfaces/databaseInterface";
import * as telegram from "telegram";
import axios from "axios";
import cliProgress from "cli-progress";
import { isNull, isUndefined } from "lodash";
export class CaptchaError extends Error {}
export class NoNewMessagesError extends Error {}
export class DockerIsBrockenError extends Error {}
export function randomAB(a: number, b: number) {
return Math.floor(Math.random() * (b - a + 1) + a);
}
export async function isBannedByClient(
client: telegram.TelegramClient,
username: string
): Promise<boolean> {
const blockedUsers = (
await client.invoke(
new telegram.Api.contacts.GetBlocked({ limit: 2147483647 })
)
).users as telegram.Api.User[];
for (const blockedUser of blockedUsers) {
if (
blockedUser.username !== undefined &&
blockedUser.username == username.replace("@", "")
) {
return true;
}
}
return false;
}
export async function getMessagesByEntity(
client: telegram.TelegramClient,
chatEntity: telegram.Api.Chat | telegram.Api.User | string,
entity: telegram.Api.User | string,
params = {}
) {
const messages = new telegram.helpers.TotalList<telegram.Api.Message>();
if (entity instanceof telegram.Api.User) {
for (const message of await client.getMessages(chatEntity, params)) {
if (
(message.sender as telegram.Api.User).username ==
entity.username
) {
messages.push(message);
}
}
} else {
for (const message of await client.getMessages(chatEntity, params)) {
if (
isNull((message.sender as telegram.Api.User).username) !=
true &&
isUndefined((message.sender as telegram.Api.User).username) !=
true
) {
if (
(message.sender as telegram.Api.User).username!.includes(
entity.replace("@", "")
)
) {
messages.push(message);
}
}
}
}
return messages;
}
import { IWorker } from "./interfaces/IWorker";
import lookupPublicIp from "@ulixee/default-browser-emulator/lib/helpers/lookupPublicIp"
export function arrayWithoutElementAtIndex(
arr: AccountInterface[] | [],
arr: IWorker[] | [],
index: number
) {
return arr.filter(function (value, arrIndex) {
return index !== arrIndex;
});
}
export async function getIP(proxyPort: number): Promise<string | null> {
// https://github.com/ulixee/unblocked/blob/main/plugins/default-browser-emulator/lib/helpers/lookupPublicIp.ts
let data: string | PromiseLike<string | null> | null;
try {
data = (
await axios.get("http://icanhazip.com/", {
proxy: {
host: "127.0.0.1",
port: proxyPort,
},
timeout: 100000,
})
).data;
} catch (err) {
logger.debug(err);
return null;
}
if ((await data) == null || (await data)!.trim() == "") {
return null;
}
return await data;
}
export async function waitNewMessages(
client: telegram.TelegramClient,
worker: AccountInterface,
chatEntity: telegram.Api.Chat | telegram.Api.User | string,
idOfLastMessage: number,
timeout = 20
): Promise<void> {
const start_time = new Date();
while (
(
await getMessagesByEntity(
client,
chatEntity,
chatEntity as telegram.Api.User | string,
{ minId: idOfLastMessage, limit: 2147483647 }
)
).length == 0
) {
if ((+new Date() - +start_time) / 1000 >= timeout) {
logger.error(
`${worker.phoneNumber} | Bot didnt answer for ${timeout}s`
);
throw new NoNewMessagesError("Is bot working?");
}
}
logger.debug(`${worker.phoneNumber} | Bot answered`);
}
export async function startNewTorDocker(
proxyPort: number,
socksPort: number,
controlPort: number,
mainProgressBar: cliProgress.MultiBar,
timeout = 200
): Promise<Docker.Container> {
timeout *= 2;
const docker = new Docker({ socketPath: "/var/run/docker.sock" });
let isPulled = false;
mainLoop: for (const image of await docker.listImages()) {
const tags = image.RepoTags;
if (tags !== undefined)
for (const repoTag of tags) {
if (repoTag.search("dperson/torproxy:latest") != -1) {
isPulled = true;
break mainLoop;
}
}
}
if (isPulled != true) {
await docker.pull("dperson/torproxy:latest");
}
// bugy shit
const options: Docker.ContainerCreateOptions = {
Image: "dperson/torproxy",
Env: ["PASSWORD=qwerty", "LOCATION=US", "TOR_NewCircuitPeriod=50"],
ExposedPorts: {},
HostConfig: {
PortBindings: {
"8118/tcp": [
{
HostPort: `${proxyPort}`,
},
],
"9050/tcp": [
{
HostPort: `${socksPort}`,
},
],
"9051/tcp": [
{
HostPort: `${controlPort}`,
},
],
},
},
Healthcheck: {
Interval: 2 * 1000000000,
Timeout: 50 * 1000000000,
StartPeriod: 50 * 1000000000,
// Test:['CMD_SHELL',`curl -sx localhost:8118 'https://check.torproject.org/' | grep -qm1 Congratulations`]
},
};
const container = await docker.createContainer(options);
await container.start();
const progressBar = mainProgressBar.create(timeout / 2, 0, {
status: `Starting docker${
settings.logLevel == "debug"
? ` ${proxyPort} ${socksPort} ${controlPort}`
: ""
}`,
});
while ((await container.inspect())!.State.Health!.Status != "healthy") {
const state = (await container.inspect())!.State.Health!.Status;
if (progressBar.getProgress() >= timeout) {
await container.kill();
progressBar.update(timeout / 2, { status: "Failed" });
progressBar.stop();
throw new DockerIsBrockenError(`Docker ${container.id} is broken`);
}
if (state == "unhealthy") {
await container.kill();
progressBar.update(timeout / 2, {
status: "Docker is unhealthy...",
});
progressBar.stop();
return await startNewTorDocker(
proxyPort,
socksPort,
controlPort,
mainProgressBar,
timeout / 2
);
}
progressBar.increment();
await sleep(2000);
}
progressBar.update(0, { status: "Started" });
progressBar.stop();
return container;
}
export async function getIP(httpPort: number) {
return await lookupPublicIp('icanhazip.com',undefined,`http://127.0.0.1:${httpPort}`)
}