Compare commits
12 commits
master
...
playground
Author | SHA1 | Date | |
---|---|---|---|
db2d18bdad | |||
7be6cca95c | |||
a37a5a2460 | |||
7acd1b5b6b | |||
e0ae85e54b | |||
accd9026a2 | |||
05414e2841 | |||
a436d91a26 | |||
fa742746bf | |||
88b34acc45 | |||
22361c3cb2 | |||
d02929d114 |
8 changed files with 246 additions and 87 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -11,3 +11,5 @@ settings.json
|
|||
yarn-error.log
|
||||
yarn.lock
|
||||
db.json1
|
||||
|
||||
db.sqlite
|
69
README.md
69
README.md
|
@ -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'>
|
||||
|
|
|
@ -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}`
|
||||
);
|
||||
|
|
138
database.ts
138
database.ts
|
@ -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 [];
|
||||
}
|
||||
|
|
46
index.ts
46
index.ts
|
@ -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();
|
||||
});
|
||||
|
||||
|
|
45
interfaces/databaseInterface.d.ts
vendored
45
interfaces/databaseInterface.d.ts
vendored
|
@ -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[];
|
||||
}
|
||||
|
|
18
package.json
18
package.json
|
@ -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": {
|
||||
|
|
13
utils.ts
13
utils.ts
|
@ -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...",
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue