[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:
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 
|
||||
- `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 
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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({});
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in new issue
Block a user