[core] Add proxy support

Co-authored-by: Pierluca Lino <pierluca.lino@dontouch.ch>

Co-authored-by: puntolino <pierluca.lino@dontouch.ch>
This commit is contained in:
allburovandPierluca Lino committed 2023-06-07 17:02:18 +07:00
1 parent ee5678ccfb
commit a9ee68fe1a
11 files changed
+207 -18

No files matched your search

@@ -39,15 +39,27 @@ The following environment variables can be used to configure the WAHA.
- `WHATSAPP_START_SESSION=session1,session2`: This variable can be used to start sessions with the specified names right
after launching the API. Separate session names with a comma.
### Swagger
- `WHATSAPP_SWAGGER_CONFIG_ADVANCED=true` - enables advanced configuration options for Swagger documentation - you can customize host, port and base URL for the requests.
Disabled by default.
### Proxy
If you need to use a proxy, you can set the following environment variables:
- `WHATSAPP_PROXY_SERVER=localhost:3128`: Use this variable to set the proxy server in the format `host:port`, without http or https.
- `WHATSAPP_PROXY_SERVER_USERNAME=username` and `WHATSAPP_PROXY_SERVER_PASSWORD=password`: Use these variables to set up credentials for the proxy.
- `WHATSAPP_PROXY_SERVER_LIST=host1.example.com:3138,host2.example.com:3138`: Use this variable to set a comma-separated list of addresses to use, using a round-robin algorithm to choose the server for the session.
- `WHATSAPP_PROXY_SERVER_INDEX_PREFIX=proxy-`: Use this variable to parse the session name for the prefix and find the appropriate session.
For example, if you have set `WHATSAPP_PROXY_SERVER_LIST=host-first:80,host-second:80,host-third:80` and `WHATSAPP_PROXY_SERVER_INDEX_PREFIX=proxy-` and you run `proxy-3` session, the `host-third:80` proxy will be chosen for that session.
This is a way to select a proxy from while you start session.
Note that a proxy configuration will be added in the `POST /api/session/start` request soon, so you'll be able to define the proxy there.
### Security ![](/images/versions/plus.png)
- `WHATSAPP_API_KEY=mysecret`: If you set this variable, you must include the `X-Api-Key: mysecret` header in all
requests to the API. This will protect the API with a secret code.
- `WHATSAPP_SWAGGER_USERNAME=admin` and `WHATSAPP_SWAGGER_PASSWORD=admin`: These variables can be used to protect the
Swagger panel with `admin / admin` credentials. This does not affect API access.
### Swagger
- `WHATSAPP_SWAGGER_CONFIG_ADVANCED=true` - enables advanced configuration options for Swagger documentation - you can customize host, port and base URL for the requests.
Disabled by default.
### Files ![](/images/versions/plus.png)
@@ -12,6 +12,10 @@ menu:
weight: 199
toc: true
---
## 2023.7
July 2023
- Added [proxy configuration](https://waha.devlike.pro/docs/how-to/config/#proxy) with supporting proxy authentication.
Thanks **puntolino** for the contribution!
## 2023.6
June 2023
+1
View File
@@ -37,6 +37,7 @@
"del": "^6.0.0",
"express-basic-auth": "^1.2.1",
"file-type": "16.5.4",
"https-proxy-agent": "^7.0.0",
"link-preview-js": "^3.0.4",
"lodash": "^4.17.21",
"mime-types": "^2.1.27",
+25
View File
@@ -54,6 +54,31 @@ export class WhatsappConfigService {
return parseBool(value);
}
get proxyServer(): string[] | string | undefined {
const single = this.configService.get('WHATSAPP_PROXY_SERVER', undefined);
const multipleValues = this.configService.get(
'WHATSAPP_PROXY_SERVER_LIST',
undefined,
);
const multiple = multipleValues ? multipleValues.split(',') : undefined;
return single ? single : multiple;
}
get proxyServerIndexPrefix(): string | undefined {
return this.configService.get(
'WHATSAPP_PROXY_SERVER_INDEX_PREFIX',
undefined,
);
}
get proxyServerUsername(): string | undefined {
return this.configService.get('WHATSAPP_PROXY_SERVER_USERNAME', undefined);
}
get proxyServerPassword(): string | undefined {
return this.configService.get('WHATSAPP_PROXY_SERVER_PASSWORD', undefined);
}
getWebhookUrl(): string | undefined {
return this.get('WHATSAPP_HOOK_URL');
}
+15 -1
View File
@@ -56,8 +56,15 @@ export interface WhatsAppSessionConfig {
storage: MediaStorage;
log: ConsoleLogger;
sessionStorage: LocalSessionStorage;
proxyConfig?: ProxyConfig;
}
export type ProxyConfig = {
proxyServer: string;
proxyUsername?: string;
proxyPassword?: string;
};
export abstract class WhatsappSession {
public status: WhatsappStatus;
public events: EventEmitter;
@@ -67,9 +74,16 @@ export abstract class WhatsappSession {
protected storage: MediaStorage;
protected log: ConsoleLogger;
protected sessionStorage: LocalSessionStorage;
protected proxyConfig?: ProxyConfig;
public constructor({ name, log, sessionStorage }: WhatsAppSessionConfig) {
public constructor({
name,
log,
sessionStorage,
proxyConfig,
}: WhatsAppSessionConfig) {
this.name = name;
this.proxyConfig = proxyConfig;
this.status = WhatsappStatus.STARTING;
this.log = log;
this.sessionStorage = sessionStorage;
+67
View File
@@ -0,0 +1,67 @@
import { WhatsappConfigService } from '../config.service';
import { WhatsappSession, ProxyConfig } from './abc/session.abc';
import { HttpsProxyAgent } from 'https-proxy-agent';
import { Agent } from 'https';
import { URL } from 'url';
export function getProxyConfig(
config: WhatsappConfigService,
sessions: Record<string, WhatsappSession>,
sessionName?: string,
): ProxyConfig | undefined {
// Single proxy server configuration
if (typeof config.proxyServer === 'string') {
return {
proxyServer: config.proxyServer,
proxyUsername: config.proxyServerUsername,
proxyPassword: config.proxyServerPassword,
};
}
// Multiple proxy server configuration
if (Array.isArray(config.proxyServer)) {
const prefix = config.proxyServerIndexPrefix;
let indexToUse: number = undefined;
if (prefix && sessionName.includes(prefix)) {
// match numbers at the end of the string
const matches = sessionName.match(/\d+$/);
indexToUse = matches ? parseInt(matches[0], 10) : undefined;
}
let idx = 0;
idx = Object.keys(sessions).length % config.proxyServer.length;
const index = indexToUse ? indexToUse % config.proxyServer.length : idx;
return {
proxyServer: config.proxyServer[index],
proxyUsername: config.proxyServerUsername,
proxyPassword: config.proxyServerPassword,
};
}
// No proxy server configuration
return undefined;
}
function getAuthenticatedUrl(
url: string,
username: string,
password: string,
): string {
const parsedUrl = new URL(url);
parsedUrl.protocol = parsedUrl.protocol || 'http';
parsedUrl.username = username;
parsedUrl.password = password;
return parsedUrl.toString();
}
/**
* Return https Agent proxy for the config
* @param proxyConfig
*/
export function createAgentProxy(proxyConfig: ProxyConfig): Agent | undefined {
const url = getAuthenticatedUrl(
proxyConfig.proxyServer,
proxyConfig.proxyUsername,
proxyConfig.proxyPassword,
);
return new HttpsProxyAgent(url);
}
+7
View File
@@ -6,6 +6,7 @@ import {
} from '@nestjs/common';
import { SessionManager } from './abc/manager.abc';
import {
ProxyConfig,
WAHAInternalEvent,
WhatsappSession,
WhatsAppSessionConfig,
@@ -25,6 +26,7 @@ import { WebhookConductorCore } from './webhooks.core';
import { MediaStorageCore, SessionStorageCore } from './storage.core';
import { WhatsappSessionNoWebCore } from './session.noweb.core';
import { LocalSessionStorage } from './abc/storage.abc';
import { getProxyConfig } from './helpers.proxy';
export class OnlyDefaultSessionIsAllowed extends UnprocessableEntityException {
constructor() {
@@ -116,6 +118,7 @@ export class SessionManagerCore extends SessionManager {
storage,
log,
sessionStorage: this.sessionStorage,
proxyConfig: this.getProxyConfig(),
};
// @ts-ignore
const session = new this.EngineClass(sessionConfig);
@@ -128,6 +131,10 @@ export class SessionManagerCore extends SessionManager {
return { name: session.name, status: session.status };
}
protected getProxyConfig(): ProxyConfig | undefined {
return getProxyConfig(this.config, { [this.DEFAULT]: this.session });
}
async stop(request: SessionStopRequest): Promise<void> {
this.onlyDefault(request.name);
+13 -4
View File
@@ -44,12 +44,11 @@ import {
ParticipantsRequest,
} from '../structures/groups.dto';
import { QR } from './QR';
import { MediaStorage } from './abc/storage.abc';
import { ConsoleLogger, UnprocessableEntityException } from '@nestjs/common';
import { UnprocessableEntityException } from '@nestjs/common';
import { Message } from 'whatsapp-web.js';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import { Agent } from 'https';
import { createAgentProxy } from './helpers.proxy';
// eslint-disable-next-line @typescript-eslint/no-var-requires
const QRCode = require('qrcode');
@@ -88,7 +87,10 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
async makeSocket() {
const { state, saveCreds } = await useMultiFileAuthState(this.authFolder);
const agent = this.makeAgent();
const sock: any = makeWASocket({
agent: agent,
fetchAgent: agent,
auth: state,
printQRInTerminal: true,
browser: ['Linux', 'Chrome', '111.0.5563.64'],
@@ -98,6 +100,13 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
return sock;
}
protected makeAgent(): Agent {
if (!this.proxyConfig) {
return undefined;
}
return createAgentProxy(this.proxyConfig);
}
connectStore() {
if (!this.store) {
this.store = makeInMemoryStore({});
+18 -3
View File
@@ -15,7 +15,7 @@ import {
MessageTextRequest,
} from '../structures/chatting.dto';
import { WAMessage, WANumberExistResult } from '../structures/responses.dto';
import { create, Message, Whatsapp } from 'venom-bot';
import { create, CreateConfig, Message, Whatsapp } from 'venom-bot';
import {
WAEvents,
WhatsappEngine,
@@ -37,7 +37,7 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
}
protected buildClient() {
return create(this.name, this.getCatchQR(), undefined, {
const venomOptions: CreateConfig = {
headless: true,
devtools: false,
debug: false,
@@ -45,7 +45,22 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
browserArgs: this.getBrowserArgsForPuppeteer(),
autoClose: 60000,
puppeteerOptions: {},
});
};
this.addProxyConfig(venomOptions);
return create(this.name, this.getCatchQR(), undefined, venomOptions);
}
protected addProxyConfig(venomOptions: CreateConfig) {
if (this.proxyConfig?.proxyServer !== undefined) {
venomOptions.addProxy = [this.proxyConfig?.proxyServer];
}
if (
this.proxyConfig?.proxyUsername !== undefined &&
this.proxyConfig?.proxyPassword !== undefined
) {
venomOptions.userProxy = this.proxyConfig?.proxyUsername;
venomOptions.userPass = this.proxyConfig?.proxyPassword;
}
}
protected getCatchQR() {
+22 -2
View File
@@ -3,6 +3,7 @@ import {
Buttons,
Chat,
Client,
ClientOptions,
Contact,
Events,
GroupChat,
@@ -53,13 +54,32 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
whatsapp: Client;
protected buildClient() {
return new Client({
const clientOptions: ClientOptions = {
puppeteer: {
headless: true,
executablePath: this.getBrowserExecutablePath(),
args: this.getBrowserArgsForPuppeteer(),
},
});
};
this.addProxyConfig(clientOptions);
return new Client(clientOptions);
}
protected addProxyConfig(clientOptions: ClientOptions) {
if (this.proxyConfig?.proxyServer !== undefined) {
// push the proxy server to the args
clientOptions.puppeteer.args.push(
`--proxy-server=${this.proxyConfig?.proxyServer}`,
);
// Authenticate
if (this.proxyConfig?.proxyUsername && this.proxyConfig?.proxyPassword) {
clientOptions.proxyAuthentication = {
username: this.proxyConfig?.proxyUsername,
password: this.proxyConfig?.proxyPassword,
};
}
}
}
async start() {
+20 -5
View File
@@ -1194,9 +1194,9 @@
integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==
"@types/node@*":
version "20.2.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.1.tgz#de559d4b33be9a808fd43372ccee822c70f39704"
integrity sha512-DqJociPbZP1lbZ5SQPk4oag6W7AyaGMO6gSfRwq3PWl4PXTwJpRQJhDq4W0kzrg3w6tJ1SwlvGZ5uKFHY13LIg==
version "20.2.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-20.2.5.tgz#26d295f3570323b2837d322180dfbf1ba156fefb"
integrity sha512-JJulVEQXmiY9Px5axXHeYGLSjhkZEnD+MDPDGbCbIAbMslkKwmygtZFy1X6s/075Yo94sf8GuSlFfPzysQrWZQ==
"@types/node@>=13.7.0":
version "18.15.5"
@@ -1686,6 +1686,13 @@ agent-base@6:
dependencies:
debug "4"
agent-base@^7.0.2:
version "7.1.0"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434"
integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==
dependencies:
debug "^4.3.4"
aggregate-error@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
@@ -4621,6 +4628,14 @@ https-proxy-agent@5.0.1, https-proxy-agent@^5.0.0:
agent-base "6"
debug "4"
https-proxy-agent@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.0.tgz#75cb70d04811685667183b31ab158d006750418a"
integrity sha512-0euwPCRyAPSgGdzD1IVN9nJYHtBhJwb6XPfbpQcYbPCwrBidX6GzxmchnaF4sfF/jPb74Ojx5g4yTg3sixlyPw==
dependencies:
agent-base "^7.0.2"
debug "4"
human-signals@2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
@@ -9048,8 +9063,8 @@ webpack@5.75.0:
webpack-sources "^3.2.3"
"whatsapp-web.js@https://github.com/pedroslopez/whatsapp-web.js":
version "1.20.0"
resolved "https://github.com/pedroslopez/whatsapp-web.js#915763a8d04c37c2ddcb93faa512cfe208f1798e"
version "1.21.0"
resolved "https://github.com/pedroslopez/whatsapp-web.js#a91bf1c762536055ca09a862e963ddb5e29ffe3a"
dependencies:
"@pedroslopez/moduleraid" "^5.0.2"
fluent-ffmpeg "^2.1.2"