Compare commits

...

12 Commits

Author SHA1 Message Date
minicx db2d18bdad fixed issue of resetting working while updating 2022-12-22 22:50:48 +03:00
minicx 7be6cca95c fix? 2022-12-22 21:43:22 +03:00
minicx a37a5a2460 output error 2022-12-22 21:32:01 +03:00
minicx 7acd1b5b6b fixed issue with undefined `completedGroupsTasks` 2022-12-22 21:26:00 +03:00
minicx e0ae85e54b test migration to sqlite database 2022-12-22 20:39:36 +03:00
minicx accd9026a2 formatting 2022-12-20 21:05:39 +03:00
minicx 05414e2841 update package 2022-12-20 20:21:51 +03:00
minicx a436d91a26 same issue 2022-12-20 20:10:42 +03:00
minicx fa742746bf maybe fixed issue with multiple dockers 2022-12-20 20:06:42 +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
8 changed files with 246 additions and 87 deletions

2
.gitignore vendored
View File

@ -11,3 +11,5 @@ settings.json
yarn-error.log
yarn.lock
db.json1
db.sqlite

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` or `yarn install`
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'>

View File

@ -395,6 +395,7 @@ export const doChatsTasks = async function (
if (err instanceof telegram.errors.RPCError) {
switch (err.errorMessage) {
case "CHANNELS_TOO_MUCH":
worker.troubled=true
logger.warn(
`Too much groups | ${worker.phoneNumber}`
);
@ -451,6 +452,7 @@ export const doChatsTasks = async function (
if (err instanceof telegram.errors.RPCError) {
switch (err.errorMessage) {
case "CHANNELS_TOO_MUCH":
worker.troubled=true
logger.warn(
`Too much groups | ${worker.phoneNumber}`
);

View File

@ -4,25 +4,64 @@ import logger from "./logger";
import * as interfacesDB from "./interfaces/databaseInterface";
import * as interfacesSettings from "./interfaces/settingsInterface";
import { arrayWithoutElementAtIndex } from "./utils";
import {Knex} from "knex"
import knex from 'knex';
import { isEqual } from "lodash";
export class Database {
public readonly default: interfacesDB.DatabaseInterface = {
accounts: [],
};
private json: JsonDB;
private db: Knex<any, unknown[]>;
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();
this.db=knex({
client: 'better-sqlite3',
connection: {
filename:'db.sqlite'
}
})
this.db.schema.hasTable('workers').then((result)=>{
if (result!=true){
this.db.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.integer('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 in db')})
}
})
this.db.schema.hasTable('completedGroupsTasks').then((result)=>{
if (result!=true){
this.db.schema.createTable('completedGroupsTasks',(table)=>{
table.increments('id').primary(),
table.string('worker',12).notNullable(),
table.string('groupID').notNullable(),
table.timestamp('timeToLeave').notNullable()
}).then(()=> {logger.debug('Created `completedGroupsTasks` table in db')})
}
})
// this.db.schema.createTableIfNotExists('withdraws',(table)=>{
//
// })
}
convertWorker (worker: interfacesDB.AccountInterface){
let convertedWorker: Partial<interfacesDB.AccountInterface>=structuredClone(worker);
Object.keys(convertedWorker).forEach(function(key, index) {
if (convertedWorker[key] === Object(convertedWorker[key])){
convertedWorker[key]=JSON.stringify(convertedWorker[key]);
}
});
delete convertedWorker.withdraws;
delete convertedWorker.completedGroupsTasks;
return convertedWorker
}
async findWallet(
walletsFile: string
@ -89,18 +128,26 @@ export class Database {
}
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 updateUser(worker: interfacesDB.AccountInterface) {
await (this.db('workers')
.where({phoneNumber:worker.phoneNumber})
.update(this.convertWorker(worker)))
const oldGroups=await this.getCompletedGroups(worker);
worker.completedGroupsTasks.forEach(async (newGroup)=>{
oldGroups.forEach(async (oldGroup)=>{
if (!isEqual(oldGroup,newGroup)){
await this.addCompletedGroups(worker,newGroup)
}
})
if (oldGroups.length==0){
await this.addCompletedGroups(worker,newGroup)
}
})
}
async addUser(
account: Omit<
interfacesDB.AccountInterface,
@ -110,6 +157,7 @@ export class Database {
| "canBeRefferal"
| "browserFingerprint"
| "faucetMail"
| "troubled"
>,
wallets_json: string
): Promise<void> {
@ -141,17 +189,45 @@ export class Database {
canBeRefferal: canBeRefferal,
refferal: account.refferal,
browserFingerprint: fingerprint,
troubled: false
};
await this.json.push(`/accounts[]`, _account);
await this.db('workers').insert(this.convertWorker(_account));
}
await this.save();
}
async addCompletedGroups(worker: interfacesDB.AccountInterface,group: interfacesDB.AccountCompletedGroupsTasksInterface){
await this.db('completedGroupsTasks').insert({
worker: worker.phoneNumber,
groupID:group.groupID,
timeToLeave:group.timeToLeave
});
}
async getCompletedGroups (worker: interfacesDB.AccountInterface): Promise<interfacesDB.AccountCompletedGroupsTasksInterface[]> {
return (await this.db('completedGroupsTasks').where({worker:worker.phoneNumber}).select('timeToLeave','groupID'));
}
async getUsers(): Promise<interfacesDB.AccountInterface[]> {
try {
return await this.json.getObject<interfacesDB.AccountInterface[]>(
"/accounts"
);
return Promise.all((await this.db.select().from<Omit<interfacesDB.AccountInterface, 'withdraws' | 'completedGroupsTasks'>>('workers')).map(async (worker)=>{
function isJson(str: string) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
}
let convertedWorker=worker;
Object.keys(convertedWorker).forEach(function(key, index) {
if (isJson(convertedWorker[key])){
convertedWorker[key]=JSON.parse(convertedWorker[key])
}
});
convertedWorker['withdraws']=[];
const completedGroupsTasks=(await this.getCompletedGroups(convertedWorker as interfacesDB.AccountInterface));
convertedWorker['completedGroupsTasks']=completedGroupsTasks;
return convertedWorker as interfacesDB.AccountInterface
}));
} catch {
return [];
}

View File

@ -4,7 +4,10 @@ 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 {
AccountInterface,
AccountToLeaveInterface,
} from "./interfaces/databaseInterface";
import { sleep } from "telegram/Helpers";
import findFreePorts from "find-free-ports";
import {
@ -237,7 +240,6 @@ const farm: () => Promise<void> = async () => {
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,
@ -412,6 +414,7 @@ const farm: () => Promise<void> = async () => {
if (err instanceof telegram.errors.RPCError) {
switch (err.errorMessage) {
case "CHANNELS_TOO_MUCH":
worker.troubled = true;
logger.error(
`Too much groups | ${worker.phoneNumber}`
);
@ -600,7 +603,12 @@ const farm: () => Promise<void> = async () => {
startIndex,
startIndex + Math.ceil(workers.length / settings.pararels)
);
workersGroups.push(_workers);
if (_workers.length>0){
workersGroups.push(_workers);
} else {
break
}
}
const pararels: Promise<void>[] = [];
@ -657,10 +665,9 @@ const farm: () => Promise<void> = async () => {
logger.debug("Closing opened docker containers ");
for (const container of containers) {
try {
await container.stop();
await container.remove({ force: true, v: true });
} catch (err) {}
}
await db.save();
await miner.close(true);
};
@ -668,6 +675,7 @@ const farm: () => Promise<void> = async () => {
(async () => {
while (true) {
let accounts: AccountInterface[] = [];
let accountsToLeave: AccountToLeaveInterface[] = [];
try {
accounts = await db.getUsers();
} catch (err) {
@ -696,6 +704,24 @@ const farm: () => Promise<void> = async () => {
description: "Add accounts to database",
value: "addAccounts",
},
accounts.filter((worker) => {
worker.troubled === true;
}).length > 0
? {
title: "Solve issue with max amount of groups",
description: `Solve issue for ${
accounts.filter((worker) => {
worker.troubled === true;
}).length
} accounts`,
value: "leaveGroups",
}
: {
title: "Solve issue with max amount of groups",
description: `Nothing to solve`,
value: "leaveGroups",
disabled: true,
},
{
title: "Check balances",
value: "checkBalances",
@ -730,7 +756,8 @@ const farm: () => Promise<void> = async () => {
}
}
break;
case "leaveGroups":
break;
default:
logger.info("Bye bye");
process.exit();
@ -739,9 +766,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

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

View File

@ -1,31 +1,37 @@
{
"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",
"typescript": "^4.9.4",
"winston": "^3.8.2"
},
"engines": {
"node": "18.x"
},
"name": "hk_bot",
"version": "0.0.1",
"version": "0.0.3a",
"description": "Script which allow earn some crypto via telegram",
"main": "/build/index.js",
"devDependencies": {
"@types/better-sqlite3": "^7.6.3",
"@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,17 +3,24 @@ import logger from "./logger";
import { settings } from "./database";
// import Hero from "@ulixee/hero";
// import Miner from "@ulixee/miner";
import { Session } from "@ulixee/hero-core";
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";
import * as fs from "fs/promises";
export class CaptchaError extends Error {}
export class NoNewMessagesError extends Error {}
export class DockerIsBrockenError extends Error {}
Session.events.on("closed", (data) => {
if (isUndefined(data) != true) {
fs.unlink(data!.databasePath).catch();
}
});
export function randomAB(a: number, b: number) {
return Math.floor(Math.random() * (b - a + 1) + a);
}
@ -201,13 +208,13 @@ export async function startNewTorDocker(
while ((await container.inspect())!.State.Health!.Status != "healthy") {
const state = (await container.inspect())!.State.Health!.Status;
if (progressBar.getProgress() >= timeout) {
await container.kill();
await container.remove({force:true,v:true});
progressBar.update(timeout / 2, { status: "Failed" });
progressBar.stop();
throw new DockerIsBrockenError(`Docker ${container.id} is broken`);
}
if (state == "unhealthy") {
await container.kill();
await container.remove({force:true,v:true});
progressBar.update(timeout / 2, {
status: "Docker is unhealthy...",
});