Compare commits

..
4 Commits
Author SHA1 Message Date
allburov fef2bcb6e2 [core] create directory before saving file
Release / deploy-docker (push) Waiting to run
2023-07-16 16:25:41 +07:00
allburov 2f6ed2fb58 [core] Add chats endpoints
Release / deploy-docker (push) Waiting to run
2023-07-16 16:14:12 +07:00
allburov 4eb23d2c09 [core] Bump version to 2023.7.16 2023-07-16 16:14:08 +07:00
allburov ec3de8dbcf [core] Init folder for a session 2023-07-16 13:30:40 +07:00
17 changed files with 237 additions and 32 deletions

No files matched your search

+1
View File
@@ -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,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`
@@ -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!" />}}
@@ -14,6 +14,12 @@ 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)
+56
View File
@@ -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 };
+27 -1
View File
@@ -43,6 +43,7 @@ export function getBrowserExecutablePath() {
}
return CHROMIUM_PATH;
}
export function ensureSuffix(phone) {
const suffix = '@c.us';
if (phone.includes('@')) {
@@ -168,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
*/
+1 -1
View File
@@ -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>;
+2
View File
@@ -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,
+1 -3
View File
@@ -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;
-4
View File
@@ -307,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 = {
+7 -3
View File
@@ -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();
}
+44 -14
View File
@@ -212,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) {
@@ -236,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
*/
@@ -426,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> {
+17 -3
View File
@@ -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 {
+2 -1
View File
@@ -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>
+21
View File
@@ -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
*/
+6
View File
@@ -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 {}
+1 -1
View File
@@ -26,7 +26,7 @@ export function getWAHAVersion(): WAHAVersion {
}
export const VERSION = {
version: '2023.7.12',
version: '2023.7.16',
tier: getWAHAVersion(),
browser: getBrowserExecutablePath(),
};