Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fef2bcb6e2 | ||
|
|
2f6ed2fb58 | ||
|
|
4eb23d2c09 | ||
|
|
ec3de8dbcf | ||
|
|
f6f09747dc | ||
|
|
27a534a605 | ||
|
|
3cbd940843 | ||
|
|
e728eec01a | ||
|
|
d06d2b78a8 | ||
|
|
5878a9196e | ||
|
|
85dd061f96 | ||
|
|
8381c3e946 | ||
|
|
6ff8a9705d |
No files matched your search
@@ -11,6 +11,7 @@ examples
|
||||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
.yarn
|
||||
|
||||
# Logs
|
||||
logs
|
||||
|
||||
@@ -25,6 +25,7 @@ module.exports = {
|
||||
"@typescript-eslint/interface-name-prefix": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-inferrable-types": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-ts-ignore": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
# Idea from
|
||||
# https://stackoverflow.com/a/65539398/6753144
|
||||
#
|
||||
|
||||
OWNER=devlikeapro
|
||||
REPO=whatsapp-http-api-plus
|
||||
|
||||
# list workflows
|
||||
gh api -X GET /repos/$OWNER/$REPO/actions/workflows | jq '.workflows[] | .name,.id'
|
||||
|
||||
# copy the ID of the workflow you want to clear and set it
|
||||
WORKFLOW_ID=40218610
|
||||
|
||||
# list runs
|
||||
gh api -X GET /repos/$OWNER/$REPO/actions/workflows/$WORKFLOW_ID/runs --paginate | jq '.workflow_runs[] | .id'
|
||||
|
||||
# delete runs
|
||||
gh api -X GET /repos/$OWNER/$REPO/actions/workflows/$WORKFLOW_ID/runs --paginate | jq '.workflow_runs[] | .id' | xargs -I{} gh api -X DELETE /repos/$OWNER/$REPO/actions/runs/{} --silent
|
||||
@@ -31,6 +31,7 @@ jobs:
|
||||
with:
|
||||
path: docs/site/public
|
||||
name: public
|
||||
retention-days: 5
|
||||
|
||||
deploy-github-pages:
|
||||
needs: build
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-3.6.0.cjs
|
||||
yarnPath: .yarn/releases/yarn-3.6.1.cjs
|
||||
+3
-3
@@ -1,14 +1,14 @@
|
||||
#
|
||||
# Build
|
||||
#
|
||||
FROM node:18 as build
|
||||
FROM node:20-bullseye as build
|
||||
ENV PUPPETEER_SKIP_DOWNLOAD=True
|
||||
|
||||
# npm packages
|
||||
WORKDIR /src
|
||||
COPY package.json .
|
||||
COPY yarn.lock .
|
||||
RUN yarn set version berry
|
||||
RUN yarn set version 3.6.1
|
||||
RUN yarn install
|
||||
|
||||
# App
|
||||
@@ -20,7 +20,7 @@ RUN yarn build && find ./dist -name "*.d.ts" -delete
|
||||
#
|
||||
# Final
|
||||
#
|
||||
FROM node:18 as release
|
||||
FROM node:20-bullseye as release
|
||||
ENV PUPPETEER_SKIP_DOWNLOAD=True
|
||||
ARG USE_BROWSER=chromium
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
title : "Chats"
|
||||
description: "Chats"
|
||||
lead: ""
|
||||
date: 2020-10-06T08:48:45+00:00
|
||||
lastmod: 2020-10-06T08:48:45+00:00
|
||||
draft: false
|
||||
images: []
|
||||
weight: 126
|
||||
---
|
||||
|
||||
Chats methods.
|
||||
|
||||
Parameters in path that you can find in below endpoints:
|
||||
- `{session}` - use the session name for Whatsapp instance that you created with `POST /api/session/start` endpoint
|
||||
- `{chatId}` - chat id in format `123123123123@[c.us|g.us]`, `c.us` for direct chats and `g.us` for groups.
|
||||
|
||||
## Methods
|
||||
### Get all chats
|
||||
Get all chats
|
||||
`GET /api/{session}/chats`
|
||||
|
||||
### Get messages from chat
|
||||
Get 100 messages from the chat
|
||||
|
||||
`GET /api/{session}/chats/{chatId/messages?limit=100`
|
||||
|
||||
|
||||
Get 100 messages from the chat, skip downloading media (images, files)
|
||||
|
||||
`GET /api/{session}/chats/{chatId/messages?limit=100&downloadMedia=false`
|
||||
|
||||
### Delete chat
|
||||
Use the method to delete chat
|
||||
|
||||
`DELETE /api/{session}/chats/{chatId}`
|
||||
|
||||
### Clear messages
|
||||
Use the method to clear all messages from the chat
|
||||
|
||||
`DELETE /api/{session}/chats/{chatId}/messages`
|
||||
@@ -16,30 +16,19 @@ weight: 900
|
||||
1. Core  the basic version that meet almost 80% people’s needs. 100% free and open source.
|
||||
2. Plus  the version with advanced messages, security, and reliability features.
|
||||
|
||||
### Features
|
||||
|
||||
The Plus additional features are:
|
||||
|
||||
1. [**Send images\files\voices** →]({{< relref "/docs/how-to/send-messages" >}})
|
||||
2. [**Receive images\files\voices** →]({{< relref "/docs/how-to/receive-messages" >}})
|
||||
3. [**Webhook retries** →]({{< relref "/docs/how-to/receive-messages#retries" >}})
|
||||
4. [**Security** →]({{< relref "/docs/how-to/security" >}})
|
||||
5. [**Saving sessions** →]({{< relref "/docs/how-to/sessions" >}})
|
||||
6. [**Support multiple sessions** →]({{< relref "/docs/how-to/sessions" >}})
|
||||
7. **Priority in support** - on both bugs and features
|
||||
|
||||
### Differences
|
||||
|
||||
What is the difference between **WAHA Plus** and other SaaS solutions for WhatsApp HTTP API?
|
||||
|
||||
| | WAHA Plus | Others |
|
||||
|--------------------|:-------------------------------------------------:|----------------------------------------------------------------------------------:|
|
||||
| **Sessions** | ✔️ No limits on **accounts** or **servers**! | ➖ One account for $50/month. |
|
||||
| **Infrastructure** | ✔️ On-Premise - your server, your infrastructure! | ➖ Insecure cloud solution. |
|
||||
| **Security** | ✔️ No data leaks! | ➖ You have to give access to customers' data - phone number, name, your messages. |
|
||||
| **License Term** | ✔️ No license checks! | ➖ One month |
|
||||
| **Message price** | ✔️ All messages are Free! | ➖ Some solutions have a price for messages - $1/100 messages. |
|
||||
| **Expenses** | ✔️ $19 **donation**! | ➖ Monthly payment starts from $50 |
|
||||
You support the project and get **WAHA Plus** on
|
||||
<a href="https://boosty.to/wa-http-api" target="_blank">
|
||||
<b>
|
||||
Boosty ->
|
||||
</b>
|
||||
</a>
|
||||
or
|
||||
<a href="https://patreon.com/wa_http_api" target="_blank">
|
||||
<b>
|
||||
Patreon ->
|
||||
</b>
|
||||
</a>
|
||||
.
|
||||
|
||||
### Donations
|
||||
|
||||
@@ -60,17 +49,37 @@ Treat **WAHA Plus** it as an additional episode, behind-the-scenes videos, our n
|
||||
We donate most of the donations down to the stream to the underlying libraries and tools - JS-community, Node, other libraries authors, personal contributors.
|
||||
By donating to the **WAHA** you donate to the Open Source world!
|
||||
|
||||
### Features
|
||||
|
||||
The Plus additional features are:
|
||||
|
||||
1. [**Send images\files\voices** →]({{< relref "/docs/how-to/send-messages" >}})
|
||||
2. [**Receive images\files\voices** →]({{< relref "/docs/how-to/receive-messages" >}})
|
||||
3. [**Webhook retries** →]({{< relref "/docs/how-to/receive-messages#retries" >}})
|
||||
4. [**Security** →]({{< relref "/docs/how-to/security" >}})
|
||||
5. [**Saving sessions** →]({{< relref "/docs/how-to/sessions" >}})
|
||||
6. [**Support multiple sessions** →]({{< relref "/docs/how-to/sessions" >}})
|
||||
7. **Priority in support** - on both bugs and features
|
||||
|
||||
### Why WAHA?
|
||||
|
||||
What is the difference between **WAHA Plus** and **other SaaS solutions** for WhatsApp HTTP API?
|
||||
|
||||
| | WAHA Plus | Others |
|
||||
|--------------------|:-------------------------------------------------:|----------------------------------------------------------------------------------:|
|
||||
| **Sessions** | ✔️ No limits on **accounts** or **servers**! | ➖ One account for $50/month. |
|
||||
| **Infrastructure** | ✔️ On-Premise - your server, your infrastructure! | ➖ Insecure cloud solution. |
|
||||
| **Security** | ✔️ No data leaks! | ➖ You have to give access to customers' data - phone number, name, your messages. |
|
||||
| **License Term** | ✔️ No license checks! | ➖ One month |
|
||||
| **Message price** | ✔️ All messages are Free! | ➖ Some solutions have a price for messages - $1/100 messages. |
|
||||
| **Expenses** | ✔️ $19 **donation**! | ➖ Monthly payment starts from $50 |
|
||||
|
||||
|
||||
## Get Plus 
|
||||
|
||||
For **$19** donation you get:
|
||||
1. **Secret password** for Docker Hub to download Plus image: `devlikeapro/whatsapp-http-api-plus`.
|
||||
2. **All updates for one month** for the Plus image.
|
||||
3. **NO LICENSE CHECKS** on installed instances!
|
||||
4. **NO LICENSE EXPIRATION** on installed instances! We repeated it twice to make sure that you've got it 😊
|
||||
The more you support the project - the more you get!
|
||||
|
||||
{{< alert icon="💡" text="If you stay subscribed - the donation amount will never change for you and always be $19 to get monthly updates!" />}}
|
||||
|
||||
Here's all available tiers with their perks:
|
||||
Here's available tiers for our supporters:
|
||||
|
||||
| | Plus | Advanced | Pro |
|
||||
|-------------------------------------------------------------------------------------|---------|:--------:|:-------:|
|
||||
@@ -79,6 +88,13 @@ Here's all available tiers with their perks:
|
||||
| Access to a Telegram group where we provide support and assistance. | ➖️ | ➖ | ✔️ |
|
||||
| **Donation** | **$19** | **$39** | **$99** |
|
||||
|
||||
For **$19** donation you get:
|
||||
1. **Secret password** for Docker Hub to download Plus image: `devlikeapro/whatsapp-http-api-plus`.
|
||||
2. **All updates for one month** for the Plus image.
|
||||
3. **NO LICENSE CHECKS** on installed instances!
|
||||
4. **NO LICENSE EXPIRATION** on installed instances! We repeated it twice to make sure that you've got it 😊
|
||||
|
||||
{{< alert icon="💡" text="If you stay subscribed - the donation amount will never change for you and always be $19 to get monthly updates!" />}}
|
||||
|
||||
|
||||
### Boosty
|
||||
|
||||
@@ -12,7 +12,55 @@ weight: 801
|
||||
You can get presence information (online, offline with last seen, typing status) for a contact if they share their
|
||||
presence information.
|
||||
|
||||
{{< alert icon="👉" text="Presence endpoints and webhook is available only in **NOWEB** engine." />}}
|
||||
Possible presence statuses that you can set or get for chats:
|
||||
- `online`
|
||||
- `offline`
|
||||
- `typing`
|
||||
- `recording`
|
||||
- `paused` resets the chat presence after you were `typing`
|
||||
|
||||
|
||||
## Endpoints
|
||||
|
||||
### Set presence
|
||||
You can set your global or chat-related presence with `POST /api/{session}/presence` endpoint
|
||||
|
||||
Start typing to a chat (you can use `POST /startTyping` instead)
|
||||
```json
|
||||
{
|
||||
"chatId": "111111111@c.us",
|
||||
"presence": "typing"
|
||||
}
|
||||
```
|
||||
|
||||
Clear "typing" state (you can use `POST /stopTyping` instead)
|
||||
```json
|
||||
{
|
||||
"chatId": "111111111@c.us",
|
||||
"presence": "paused"
|
||||
}
|
||||
```
|
||||
|
||||
Set global "online", all contacts will see it
|
||||
```json
|
||||
{
|
||||
"presence": "online"
|
||||
}
|
||||
```
|
||||
|
||||
💡 In the multi-device version of WhatsApp - if a desktop client is active, WhatsApp doesn't send push notifications
|
||||
to the device.
|
||||
If you would like to receive said notifications - you need to mark a session's presence as `offline`.
|
||||
|
||||
```json
|
||||
{
|
||||
"presence": "offline"
|
||||
}
|
||||
```
|
||||
|
||||
### Get all chats presence
|
||||
|
||||
{{< alert icon="👉" text="Get presence endpoints and webhook is available only in **NOWEB** engine." />}}
|
||||
[Read more about engines to choose right for you ->]({{< relref "/docs/how-to/engines" >}})
|
||||
|
||||
Here's few notes about fields:
|
||||
@@ -22,9 +70,6 @@ Here's few notes about fields:
|
||||
- `lastKnownPresence` - contains the last known presence status, which can be
|
||||
`offline`, `online`, `typing`, `recording`, or `paused`
|
||||
|
||||
## Endpoints
|
||||
|
||||
### Get all presence
|
||||
|
||||
You can get all presence information available for a session by calling `GET /api/{session}/presence/`.
|
||||
It returns both groups' and personal chats' presence information.
|
||||
|
||||
@@ -155,7 +155,10 @@ If you want to limit files lifetime, specify file types for download or change d
|
||||
## Endpoints
|
||||
|
||||
### Get messages
|
||||
You can read messages from the history by using `GET /api/messages` endpoint.
|
||||
Read messages from the history - use `GET /api/messages` endpoint.
|
||||
|
||||
You also can get messages for chats by using
|
||||
[**Get messages from chat ->**]({{< relref "/docs/how-to/chats" >}}) endpoint.
|
||||
|
||||
{{< alert icon="👉" text="We do not recommend using this method in production, consider using webhooks instead!" />}}
|
||||
|
||||
|
||||
@@ -12,6 +12,18 @@ menu:
|
||||
weight: 199
|
||||
toc: true
|
||||
---
|
||||
## 2023.8
|
||||
August 2023
|
||||
- Added [chats endpoints](https://waha.devlike.pro/docs/how-to/chats)
|
||||
- Get all chats - `GET /api/{session/chats`
|
||||
- Delete chat - `DELETE /api/{session/chats/{chatId}`
|
||||
- Get messages from chat - `GET /api/{session/chats/{chatId}/messages?limit=1000?downloadMedia=true`
|
||||
- Clear messages in the chat `DELETE /api/{session/chats/{chatId}/messages`
|
||||
- Added `downloadMedia` flag to speed up `GET /api/messages` and `GET /api/{session/chats/{chatId}/messages` performance.
|
||||
- Added [set presence endpoint](https://waha.devlike.pro/docs/how-to/presence)!
|
||||
- With `online` presence you can show your status as online [#121](https://github.com/devlikeapro/whatsapp-http-api/issues/121)
|
||||
- With `offline` presence you can get notifications for your device [#28](https://github.com/devlikeapro/whatsapp-http-api/issues/28)
|
||||
|
||||
## 2023.7
|
||||
July 2023
|
||||
- Now session can have their own [Webhooks](https://waha.devlike.pro/docs/how-to/webhooks) -
|
||||
|
||||
@@ -16,6 +16,8 @@ toc: true
|
||||
On this page you're going to install WAHA, run it, authenticate the client using QR code and send the first text message
|
||||
to WhatsApp!
|
||||
|
||||

|
||||
|
||||
## Requirements
|
||||
|
||||
Only thing that you must have - installed docker. Please follow the original
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Controller, Delete, Get, Param, Post, Query } from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { WhatsappSession } from '../core/abc/session.abc';
|
||||
import { SessionApiParam, SessionParam } from './helpers';
|
||||
import { GetChatMessagesQuery } from '../structures/chats.dto';
|
||||
import { parseBool } from '../helpers';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/:session/chats')
|
||||
@ApiTags('chats')
|
||||
class ChatsController {
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Get('')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Get all chats' })
|
||||
getAllChats(@SessionParam session: WhatsappSession) {
|
||||
return session.getChats();
|
||||
}
|
||||
|
||||
@Delete(':chatId')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Deletes the chat' })
|
||||
deleteChat(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('chatId') chatId: string,
|
||||
) {
|
||||
return session.deleteChat(chatId);
|
||||
}
|
||||
|
||||
@Get(':chatId/messages')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Gets messages in the chat' })
|
||||
getChatMessages(
|
||||
@Query() query: GetChatMessagesQuery,
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('chatId') chatId: string,
|
||||
) {
|
||||
const downloadMedia = parseBool(query.downloadMedia);
|
||||
return session.getChatMessages(chatId, query.limit, downloadMedia);
|
||||
}
|
||||
|
||||
@Delete(':chatId/messages')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Clears all messages from the chat' })
|
||||
clearMessages(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Param('chatId') chatId: string,
|
||||
) {
|
||||
return session.clearMessages(chatId);
|
||||
}
|
||||
}
|
||||
|
||||
export { ChatsController };
|
||||
@@ -1,10 +1,21 @@
|
||||
import { Controller, Get, Param, Post } from '@nestjs/common';
|
||||
import {
|
||||
BadRequestException,
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
} from '@nestjs/common';
|
||||
import { ApiOperation, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { SessionManager } from '../core/abc/manager.abc';
|
||||
import { WhatsappSession } from '../core/abc/session.abc';
|
||||
import { WAHAChatPresences } from '../structures/presence.dto';
|
||||
import {
|
||||
WAHAChatPresences,
|
||||
WAHASessionPresence,
|
||||
} from '../structures/presence.dto';
|
||||
import { SessionApiParam, SessionParam } from './helpers';
|
||||
import { WAHAPresenceStatus } from '../structures/enums.dto';
|
||||
|
||||
@ApiSecurity('api_key')
|
||||
@Controller('api/:session/presence')
|
||||
@@ -12,6 +23,36 @@ import { SessionApiParam, SessionParam } from './helpers';
|
||||
export class PresenceController {
|
||||
constructor(private manager: SessionManager) {}
|
||||
|
||||
@Post('')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Set session presence' })
|
||||
setPresence(
|
||||
@SessionParam session: WhatsappSession,
|
||||
@Body() request: WAHASessionPresence,
|
||||
) {
|
||||
// Validate request
|
||||
const presencesWithoutChatId = [
|
||||
WAHAPresenceStatus.ONLINE,
|
||||
WAHAPresenceStatus.OFFLINE,
|
||||
];
|
||||
const requiresNoChatId = presencesWithoutChatId.includes(request.presence);
|
||||
const requiresChatId = !requiresNoChatId;
|
||||
|
||||
if (requiresNoChatId && request.chatId) {
|
||||
const msg = {
|
||||
detail: `'${request.presence}' presence works on the global scope and doesn't require 'chatId' field.`,
|
||||
};
|
||||
throw new BadRequestException(msg);
|
||||
} else if (requiresChatId && !request.chatId) {
|
||||
const msg = {
|
||||
detail: `'${request.presence}' presence requires 'chatId' field.`,
|
||||
};
|
||||
throw new BadRequestException(msg);
|
||||
}
|
||||
|
||||
return session.setPresence(request.presence, request.chatId);
|
||||
}
|
||||
|
||||
@Get('')
|
||||
@SessionApiParam
|
||||
@ApiOperation({ summary: 'Get all subscribed presence information.' })
|
||||
|
||||
@@ -22,6 +22,7 @@ import { ContactQuery, ContactRequest } from '../../structures/contacts.dto';
|
||||
import {
|
||||
WAHAEngine,
|
||||
WAHAEvents,
|
||||
WAHAPresenceStatus,
|
||||
WAHASessionStatus,
|
||||
} from '../../structures/enums.dto';
|
||||
import {
|
||||
@@ -42,6 +43,7 @@ export function getBrowserExecutablePath() {
|
||||
}
|
||||
return CHROMIUM_PATH;
|
||||
}
|
||||
|
||||
export function ensureSuffix(phone) {
|
||||
const suffix = '@c.us';
|
||||
if (phone.includes('@')) {
|
||||
@@ -167,10 +169,35 @@ export abstract class WhatsappSession {
|
||||
|
||||
abstract stopTyping(chat: ChatRequest);
|
||||
|
||||
abstract getMessages(query: GetMessageQuery);
|
||||
getMessages(query: GetMessageQuery) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
abstract setReaction(request: MessageReactionRequest);
|
||||
|
||||
/**
|
||||
* Chats methods
|
||||
*/
|
||||
public getChats() {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public deleteChat(chatId) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getChatMessages(
|
||||
chatId: string,
|
||||
limit: number,
|
||||
downloadMedia: boolean,
|
||||
) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public clearMessages(chatId) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
/**
|
||||
* Contacts methods
|
||||
*/
|
||||
@@ -257,6 +284,10 @@ export abstract class WhatsappSession {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public setPresence(presence: WAHAPresenceStatus, chatId?: string) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
public getPresences(): Promise<WAHAChatPresences[]> {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ abstract class SessionStorage {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
abstract init(): Promise<void>;
|
||||
abstract init(sessionName?: string): Promise<void>;
|
||||
|
||||
abstract clean(sessionName: string): Promise<void>;
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import { VersionController } from '../api/version.controller';
|
||||
import { WhatsappConfigService } from '../config.service';
|
||||
import { SessionManager } from './abc/manager.abc';
|
||||
import { SessionManagerCore } from './manager.core';
|
||||
import { ChatsController } from '../api/chats.controller';
|
||||
|
||||
export const IMPORTS = [
|
||||
ConfigModule.forRoot({
|
||||
@@ -36,6 +37,7 @@ export const IMPORTS = [
|
||||
export const CONTROLLERS = [
|
||||
SessionsController,
|
||||
ChattingController,
|
||||
ChatsController,
|
||||
ContactsController,
|
||||
GroupsController,
|
||||
PresenceController,
|
||||
|
||||
@@ -107,9 +107,6 @@ export class SessionManagerCore extends SessionManager {
|
||||
const storage = new this.MediaStorageClass();
|
||||
const webhookLog = new ConsoleLogger(`Webhook - ${name}`);
|
||||
const webhook = new this.WebhookConductorClass(webhookLog);
|
||||
|
||||
await this.sessionStorage.init();
|
||||
|
||||
const proxyConfig = this.getProxyConfig(request);
|
||||
const sessionConfig: SessionParams = {
|
||||
name,
|
||||
@@ -119,6 +116,7 @@ export class SessionManagerCore extends SessionManager {
|
||||
proxyConfig: proxyConfig,
|
||||
sessionConfig: request.config,
|
||||
};
|
||||
await this.sessionStorage.init(name);
|
||||
// @ts-ignore
|
||||
const session = new this.EngineClass(sessionConfig);
|
||||
this.session = session;
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Agent } from 'https';
|
||||
import * as lodash from 'lodash';
|
||||
import { Message } from 'whatsapp-web.js';
|
||||
|
||||
import { flipObject } from '../helpers';
|
||||
import {
|
||||
Button,
|
||||
ChatRequest,
|
||||
@@ -78,6 +79,7 @@ const PresenceStatuses = {
|
||||
recording: WAHAPresenceStatus.RECORDING,
|
||||
paused: WAHAPresenceStatus.PAUSED,
|
||||
};
|
||||
const ToEnginePresenceStatus = flipObject(PresenceStatuses);
|
||||
|
||||
export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
engine = WAHAEngine.NOWEB;
|
||||
@@ -305,10 +307,6 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return this.sock.sendPresenceUpdate('paused', request.chatId);
|
||||
}
|
||||
|
||||
async getMessages(query: GetMessageQuery) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
async setReaction(request: MessageReactionRequest) {
|
||||
const key = parseMessageId(request.messageId);
|
||||
const reactionMessage = {
|
||||
@@ -414,6 +412,16 @@ export class WhatsappSessionNoWebCore extends WhatsappSession {
|
||||
return this.sock.groupParticipantsUpdate(id, participants, 'demote');
|
||||
}
|
||||
|
||||
public async setPresence(presence: WAHAPresenceStatus, chatId?: string) {
|
||||
const enginePresence = ToEnginePresenceStatus[presence];
|
||||
if (!enginePresence) {
|
||||
throw new NotImplementedByEngineError(
|
||||
`NOWEB engine doesn't support '${presence}' presence.`,
|
||||
);
|
||||
}
|
||||
await this.sock.sendPresenceUpdate(enginePresence, chatId);
|
||||
}
|
||||
|
||||
public async getPresences(): Promise<WAHAChatPresences[]> {
|
||||
const result: WAHAChatPresences[] = [];
|
||||
for (const remoteJid in this.store.presences) {
|
||||
|
||||
@@ -228,16 +228,16 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
|
||||
// return this.whatsapp.stopTyping(chat.chatId);
|
||||
}
|
||||
|
||||
async getMessages(query: GetMessageQuery) {
|
||||
async getChatMessages(chatId: string, limit: number) {
|
||||
const messages = await this.whatsapp.getAllMessagesInChat(
|
||||
query.chatId,
|
||||
chatId,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
// Go over messages, download media, and convert to right format.
|
||||
const result = [];
|
||||
for (const [count, message] of messages.entries()) {
|
||||
if (count > query.limit) {
|
||||
if (count > limit) {
|
||||
// Have enough in the list, stop processing
|
||||
break;
|
||||
}
|
||||
@@ -246,6 +246,10 @@ export class WhatsappSessionVenomCore extends WhatsappSession {
|
||||
return result;
|
||||
}
|
||||
|
||||
async getMessages(query: GetMessageQuery) {
|
||||
return this.getChatMessages(query.chatId, query.limit);
|
||||
}
|
||||
|
||||
setReaction(request: MessageReactionRequest) {
|
||||
throw new NotImplementedByEngineError();
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import { ContactQuery, ContactRequest } from '../structures/contacts.dto';
|
||||
import {
|
||||
WAHAEngine,
|
||||
WAHAEvents,
|
||||
WAHAPresenceStatus,
|
||||
WAHASessionStatus,
|
||||
} from '../structures/enums.dto';
|
||||
import {
|
||||
@@ -211,18 +212,7 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
}
|
||||
|
||||
async getMessages(query: GetMessageQuery) {
|
||||
const chat: Chat = await this.whatsapp.getChatById(
|
||||
this.ensureSuffix(query.chatId),
|
||||
);
|
||||
const messages = await chat.fetchMessages({
|
||||
limit: query.limit,
|
||||
});
|
||||
// Go over messages, download media, and convert to right format.
|
||||
const result = [];
|
||||
for (const message of messages) {
|
||||
result.push(await this.processIncomingMessage(message));
|
||||
}
|
||||
return result;
|
||||
return this.getChatMessages(query.chatId, query.limit, query.downloadMedia);
|
||||
}
|
||||
|
||||
async setReaction(request: MessageReactionRequest) {
|
||||
@@ -235,6 +225,39 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return message.react(request.reaction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chats methods
|
||||
*/
|
||||
getChats() {
|
||||
return this.whatsapp.getChats();
|
||||
}
|
||||
|
||||
async getChatMessages(chatId: string, limit: number, downloadMedia: boolean) {
|
||||
const chat: Chat = await this.whatsapp.getChatById(
|
||||
this.ensureSuffix(chatId),
|
||||
);
|
||||
const messages = await chat.fetchMessages({
|
||||
limit: limit,
|
||||
});
|
||||
// Go over messages, download media, and convert to right format.
|
||||
const result = [];
|
||||
for (const message of messages) {
|
||||
const msg = await this.processIncomingMessage(message, downloadMedia);
|
||||
result.push(msg);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async deleteChat(chatId) {
|
||||
const chat = await this.whatsapp.getChatById(chatId);
|
||||
return chat.delete();
|
||||
}
|
||||
|
||||
async clearMessages(chatId) {
|
||||
const chat = await this.whatsapp.getChatById(chatId);
|
||||
return chat.clearMessages();
|
||||
}
|
||||
|
||||
/**
|
||||
* Contacts methods
|
||||
*/
|
||||
@@ -366,6 +389,34 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
return groupChat.demoteParticipants(participantIds);
|
||||
}
|
||||
|
||||
public async setPresence(presence: WAHAPresenceStatus, chatId?: string) {
|
||||
let chat: Chat;
|
||||
switch (presence) {
|
||||
case WAHAPresenceStatus.ONLINE:
|
||||
await this.whatsapp.sendPresenceAvailable();
|
||||
break;
|
||||
case WAHAPresenceStatus.OFFLINE:
|
||||
await this.whatsapp.sendPresenceUnavailable();
|
||||
break;
|
||||
case WAHAPresenceStatus.TYPING:
|
||||
chat = await this.whatsapp.getChatById(chatId);
|
||||
await chat.sendStateTyping();
|
||||
break;
|
||||
case WAHAPresenceStatus.RECORDING:
|
||||
chat = await this.whatsapp.getChatById(chatId);
|
||||
await chat.sendStateRecording();
|
||||
break;
|
||||
case WAHAPresenceStatus.PAUSED:
|
||||
chat = await this.whatsapp.getChatById(chatId);
|
||||
await chat.clearState();
|
||||
break;
|
||||
default:
|
||||
throw new NotImplementedByEngineError(
|
||||
`WEBJS engine doesn't support '${presence}' presence.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* END - Methods for API
|
||||
*/
|
||||
@@ -397,8 +448,16 @@ export class WhatsappSessionWebJSCore extends WhatsappSession {
|
||||
}
|
||||
}
|
||||
|
||||
private processIncomingMessage(message: Message) {
|
||||
return this.downloadMedia(message).then(this.toWAMessage);
|
||||
private async processIncomingMessage(message: Message, downloadMedia = true) {
|
||||
if (downloadMedia) {
|
||||
try {
|
||||
message = await this.downloadMedia(message);
|
||||
} catch (e) {
|
||||
this.log.error('Failed when tried to download media for a message');
|
||||
this.log.error(e, e.stack);
|
||||
}
|
||||
}
|
||||
return await this.toWAMessage(message);
|
||||
}
|
||||
|
||||
protected toWAMessage(message: Message): Promise<WAMessage> {
|
||||
|
||||
@@ -39,11 +39,22 @@ class LocalSessionConfigRepository extends SessionConfigRepository {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const content = await fs.readFile(filepath, 'utf-8');
|
||||
// Try to load config
|
||||
let content;
|
||||
try {
|
||||
content = await fs.readFile(filepath, 'utf-8');
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return JSON.parse(content);
|
||||
}
|
||||
|
||||
async save(sessionName: string, config: SessionConfig) {
|
||||
// Create folder if not exist
|
||||
const folder = this.storage.getFolderPath(sessionName);
|
||||
await fs.mkdir(folder, { recursive: true });
|
||||
// Save config
|
||||
const filepath = this.getFilePath(sessionName);
|
||||
const content = JSON.stringify(config || null);
|
||||
await fs.writeFile(filepath, content);
|
||||
@@ -67,8 +78,11 @@ export class SessionStorageCore extends LocalSessionStorage {
|
||||
return path.join(this.sessionsFolder, this.engine);
|
||||
}
|
||||
|
||||
async init() {
|
||||
await fs.mkdir(this.engineFolder, { recursive: true });
|
||||
async init(sessionName?: string) {
|
||||
const folder = sessionName
|
||||
? this.getFolderPath(sessionName)
|
||||
: this.engineFolder;
|
||||
await fs.mkdir(folder, { recursive: true });
|
||||
}
|
||||
|
||||
getFolderPath(name: string): string {
|
||||
|
||||
@@ -16,7 +16,8 @@ export class SwaggerModuleCore {
|
||||
.setVersion(VERSION.version)
|
||||
.addTag('sessions', 'Control WhatsApp sessions')
|
||||
.addTag('screenshot', 'Get screenshot of WhatsApp and show QR code')
|
||||
.addTag('chatting', 'Chat methods')
|
||||
.addTag('chatting', 'Chatting methods')
|
||||
.addTag('chats', `Chats methods`)
|
||||
.addTag(
|
||||
'contacts',
|
||||
`Contacts methods.<br>
|
||||
@@ -24,10 +25,7 @@ export class SwaggerModuleCore {
|
||||
'E.g: \`12312312310\` OR \`12312312310@c.us\`<br>`,
|
||||
)
|
||||
.addTag('groups', `Groups methods.<br>`)
|
||||
.addTag(
|
||||
'presence',
|
||||
`Presence information. Available in <b>NOWEB</b> engine only.<br>`,
|
||||
)
|
||||
.addTag('presence', `Presence information`)
|
||||
.addTag('other', 'Other methods')
|
||||
.addApiKey({
|
||||
type: 'apiKey',
|
||||
|
||||
+9
-1
@@ -1,4 +1,4 @@
|
||||
export function parseBool(value: any): boolean {
|
||||
function parseBool(value: any): boolean {
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
@@ -18,3 +18,11 @@ export function parseBool(value: any): boolean {
|
||||
'Error: parseBool got unexpected value - use "true" or "false" values',
|
||||
);
|
||||
}
|
||||
|
||||
function flipObject(object) {
|
||||
return Object.fromEntries(
|
||||
Object.entries(object).map(([key, value]) => [value, key]),
|
||||
);
|
||||
}
|
||||
|
||||
export { flipObject, parseBool };
|
||||
@@ -0,0 +1,21 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
import { SessionBaseRequest, SessionQuery } from './base.dto';
|
||||
|
||||
/**
|
||||
* Queries
|
||||
*/
|
||||
|
||||
export class GetChatMessagesQuery extends SessionQuery {
|
||||
limit: number = 100;
|
||||
@ApiProperty({
|
||||
example: true,
|
||||
required: false,
|
||||
description: 'Download media for messages',
|
||||
})
|
||||
downloadMedia: boolean = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests
|
||||
*/
|
||||
@@ -28,6 +28,12 @@ export class ChatQuery extends SessionQuery {
|
||||
export class GetMessageQuery extends ChatQuery {
|
||||
@IsNumber()
|
||||
limit: number;
|
||||
@ApiProperty({
|
||||
example: true,
|
||||
required: false,
|
||||
description: 'Download media for messages',
|
||||
})
|
||||
downloadMedia: true;
|
||||
}
|
||||
|
||||
export class GetPresenceQuery extends ChatQuery {}
|
||||
|
||||
@@ -22,3 +22,13 @@ export class WAHAChatPresences {
|
||||
id: string;
|
||||
presences: WAHAPresenceData[];
|
||||
}
|
||||
|
||||
export class WAHASessionPresence {
|
||||
presence: WAHAPresenceStatus;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Chat ID - either group id or contact id',
|
||||
example: '11111111111@c.us',
|
||||
})
|
||||
chatId: string;
|
||||
}
|
||||
+1
-1
@@ -26,7 +26,7 @@ export function getWAHAVersion(): WAHAVersion {
|
||||
}
|
||||
|
||||
export const VERSION = {
|
||||
version: '2023.7.1',
|
||||
version: '2023.7.16',
|
||||
tier: getWAHAVersion(),
|
||||
browser: getBrowserExecutablePath(),
|
||||
};
|
||||
Reference in new issue
Block a user