diff --git a/src/api/contacts.controller.ts b/src/api/contacts.controller.ts new file mode 100644 index 00000000..d85aa59a --- /dev/null +++ b/src/api/contacts.controller.ts @@ -0,0 +1,54 @@ +import {Body, Controller, Get, Post, Query} from '@nestjs/common'; +import {ApiOperation, ApiSecurity, ApiTags} from "@nestjs/swagger"; +import {SessionManager} from "../core/abc/manager.abc"; +import {ContactQuery, ContactRequest} from "../structures/contacts.dto"; +import {SessionQuery} from "../structures/base.dto"; + +@ApiSecurity('api_key') +@Controller('api/contacts') +@ApiTags('contacts') +export class ContactsController { + constructor(private manager: SessionManager) { + } + + @Get('/') + @ApiOperation({summary: 'Get contact basic info'}) + get(@Query() query: ContactQuery) { + const whatsapp = this.manager.getSession(query.session) + return whatsapp.getContact(query) + } + + @Get('/all') + @ApiOperation({summary: 'Get all contacts'}) + getAll(@Query() query: SessionQuery) { + const whatsapp = this.manager.getSession(query.session) + return whatsapp.getContacts() + } + + @Get('/about') + @ApiOperation({summary: 'Gets the Contact\'s current "about" info. Returns null if you don\'t have permission to read their status.'}) + getAbout(@Query() query: ContactQuery) { + const whatsapp = this.manager.getSession(query.session) + return whatsapp.getContactAbout(query) + } + + @Get('/profile-picture') + @ApiOperation({summary: 'Returns the contact\'s profile picture URL, if privacy settings allow it.'}) + getProfilePicture(@Query() query: ContactQuery) { + const whatsapp = this.manager.getSession(query.session) + return whatsapp.getContactProfilePicture(query) + } + + @Post('/block') + @ApiOperation({summary:"Block contact"}) + block(@Body() request: ContactRequest) { + const whatsapp = this.manager.getSession(request.session) + return whatsapp.blockContact(request) + } + @Post('/unblock') + @ApiOperation({summary:"Unblock contact"}) + unblock(@Body() request: ContactRequest) { + const whatsapp = this.manager.getSession(request.session) + return whatsapp.unblockContact(request) + } +} diff --git a/src/api/screenshot.controller.ts b/src/api/screenshot.controller.ts index 8ee95893..7aef0bc8 100644 --- a/src/api/screenshot.controller.ts +++ b/src/api/screenshot.controller.ts @@ -2,8 +2,8 @@ import {Controller, Get, Query, Res} from '@nestjs/common'; import {ApiSecurity, ApiTags} from "@nestjs/swagger"; import {Readable} from "stream"; import {Response} from 'express'; -import {SessionQuery} from "../structures/chatting.dto"; import {SessionManager} from "../core/abc/manager.abc"; +import {SessionQuery} from "../structures/base.dto"; @ApiSecurity('api_key') diff --git a/src/core/abc/session.abc.ts b/src/core/abc/session.abc.ts index 91a07726..bece051f 100644 --- a/src/core/abc/session.abc.ts +++ b/src/core/abc/session.abc.ts @@ -16,6 +16,8 @@ import { } from "../../structures/chatting.dto"; import {MediaStorage} from "./storage.abc"; import {MessageId} from "whatsapp-web.js"; +import {ContactQuery, ContactRequest} from "../../structures/contacts.dto"; +import {NotImplementedByEngineError} from "../exceptions"; export abstract class WhatsappSession { public status: WhatsappStatus; @@ -26,7 +28,7 @@ export abstract class WhatsappSession { this.log = log } - getBrowserExecutablePath(){ + getBrowserExecutablePath() { return "/usr/bin/google-chrome-stable" } @@ -103,6 +105,34 @@ export abstract class WhatsappSession { abstract setReaction(request: MessageReactionRequest) + /** + * Contacts methods + */ + public getContact(query: ContactQuery) { + throw new NotImplementedByEngineError() + } + + public getContacts() { + throw new NotImplementedByEngineError() + } + + public getContactAbout(query: ContactQuery) { + throw new NotImplementedByEngineError() + } + + public getContactProfilePicture(query: ContactQuery) { + throw new NotImplementedByEngineError() + } + + public blockContact(request: ContactRequest) { + throw new NotImplementedByEngineError() + } + + public unblockContact(request: ContactRequest) { + throw new NotImplementedByEngineError() + } + + /** * END - Methods for API */ diff --git a/src/core/app.module.core.ts b/src/core/app.module.core.ts index f089c7a4..d10da38e 100644 --- a/src/core/app.module.core.ts +++ b/src/core/app.module.core.ts @@ -5,6 +5,7 @@ import {WhatsappConfigService} from "../config.service"; import {ServeStaticModule} from "@nestjs/serve-static"; import {SessionsController} from "../api/sessions.controller"; import {ChattingController} from "../api/chatting.controller"; +import {ContactsController} from "../api/contacts.controller"; import {VersionController} from "../api/version.controller"; import {PassportModule} from "@nestjs/passport"; import {SessionManager} from "./abc/manager.abc"; @@ -30,6 +31,7 @@ export const IMPORTS = [ export const CONTROLLERS = [ SessionsController, ChattingController, + ContactsController, ScreenshotController, VersionController, ] diff --git a/src/core/session.venom.core.ts b/src/core/session.venom.core.ts index 689b1190..8966dad1 100644 --- a/src/core/session.venom.core.ts +++ b/src/core/session.venom.core.ts @@ -20,6 +20,7 @@ import {NotImplementedByEngineError} from "./exceptions"; import {MediaStorage} from "./abc/storage.abc"; import {UnprocessableEntityException} from "@nestjs/common/exceptions/unprocessable-entity.exception"; import {ConsoleLogger} from "@nestjs/common"; +import {ContactQuery} from "../structures/contacts.dto"; class QR { private base64: string; diff --git a/src/core/session.webjs.core.ts b/src/core/session.webjs.core.ts index f39c3a9d..970a724c 100644 --- a/src/core/session.webjs.core.ts +++ b/src/core/session.webjs.core.ts @@ -1,5 +1,5 @@ import {UnprocessableEntityException} from "@nestjs/common/exceptions/unprocessable-entity.exception"; -import {Buttons, Chat, Client, Events, Message} from "whatsapp-web.js"; +import {Buttons, Chat, Client, Contact, Events, Message} from "whatsapp-web.js"; import {Message as MessageInstance} from "whatsapp-web.js/src/structures" import {WAEvents, WhatsappStatus} from "../structures/enums.dto"; import {WhatsappSession} from "./abc/session.abc"; @@ -19,6 +19,7 @@ import { MessageVoiceRequest } from "../structures/chatting.dto"; import {AvailableInPlusVersion, NotImplementedByEngineError} from "./exceptions"; +import {ContactQuery, ContactRequest} from "../structures/contacts.dto"; // eslint-disable-next-line @typescript-eslint/no-var-requires const qrcode = require('qrcode-terminal'); @@ -144,6 +145,38 @@ export class WhatsappSessionWebJSCore extends WhatsappSession { return message.react(request.reaction) } + + /** + * Contacts methods + */ + getContact(query: ContactQuery) { + return this.whatsapp.getContactById(this.ensureSuffix(query.contactId)).then(this.toWAContact) + } + + getContacts() { + return this.whatsapp.getContacts().then(contacts => contacts.map(this.toWAContact)) + } + + public async getContactAbout(query: ContactQuery) { + const contact = await this.whatsapp.getContactById(this.ensureSuffix(query.contactId)) + return {"about": await contact.getAbout()} + } + + public async getContactProfilePicture(query: ContactQuery) { + const contact = await this.whatsapp.getContactById(this.ensureSuffix(query.contactId)) + return {"profilePictureURL": await contact.getProfilePicUrl()} + } + + public async blockContact(request: ContactRequest) { + const contact = await this.whatsapp.getContactById(this.ensureSuffix(request.contactId)) + await contact.block() + } + + public async unblockContact(request: ContactRequest) { + const contact = await this.whatsapp.getContactById(this.ensureSuffix(request.contactId)) + await contact.unblock() + } + /** * END - Methods for API */ @@ -192,6 +225,12 @@ export class WhatsappSessionWebJSCore extends WhatsappSession { }) } + protected toWAContact(contact: Contact) { + // @ts-ignore + contact.id = contact.id._serialized + return contact + } + protected async downloadMedia(message: Message) { if (!message.hasMedia) { return message @@ -202,6 +241,5 @@ export class WhatsappSessionWebJSCore extends WhatsappSession { return message } - } diff --git a/src/core/swagger.module.core.ts b/src/core/swagger.module.core.ts index e92b0dfd..f593e515 100644 --- a/src/core/swagger.module.core.ts +++ b/src/core/swagger.module.core.ts @@ -3,7 +3,7 @@ import {INestApplication} from "@nestjs/common"; import {VERSION} from "../version"; export class SwaggerModuleCore { - configure(app: INestApplication){ + configure(app: INestApplication) { this.setUpAuth(app) const options = new DocumentBuilder() @@ -11,10 +11,16 @@ export class SwaggerModuleCore { .setDescription('WhatsApp HTTP API that you can configure in a click!') .setExternalDoc("Documentation", "https://waha.devlike.pro/") .setVersion(VERSION.version) - .addTag('sessions', 'Control your WhatsApp sessions') + .addTag('sessions', 'Control WhatsApp sessions') .addTag('screenshot', 'Get screenshot of WhatsApp and show QR code') .addTag('chatting', 'Chat methods') - .addTag('other', 'Other endpoints') + .addTag( + 'contacts', + `Contacts methods.
+ Use phone number (without +) or phone number and \`@c.us\` at the end as \`contactId\`.
+ 'E.g: \`12312312310\` OR \`12312312310@c.us\`
` + ) + .addTag('other', 'Other methods') .addApiKey({ type: 'apiKey', description: 'Your secret api key', @@ -27,6 +33,7 @@ export class SwaggerModuleCore { SwaggerModule.setup('', app, document); } + protected setUpAuth(app: INestApplication) { return undefined } diff --git a/src/structures/base.dto.ts b/src/structures/base.dto.ts new file mode 100644 index 00000000..5c906927 --- /dev/null +++ b/src/structures/base.dto.ts @@ -0,0 +1,19 @@ +import {IsNotEmpty} from "class-validator"; + +export const WHATSAPP_DEFAULT_SESSION_NAME = "default" + +/** + * Queries + */ +export class SessionQuery { + @IsNotEmpty() + session: string = WHATSAPP_DEFAULT_SESSION_NAME; +} + +/** + * Requests + */ + +export class SessionBaseRequest { + session = WHATSAPP_DEFAULT_SESSION_NAME +} diff --git a/src/structures/chatting.dto.ts b/src/structures/chatting.dto.ts index 21766054..ab9d892d 100644 --- a/src/structures/chatting.dto.ts +++ b/src/structures/chatting.dto.ts @@ -1,17 +1,11 @@ import {IsNotEmpty, IsString} from "class-validator"; import {ApiExtraModels, ApiProperty, getSchemaPath} from "@nestjs/swagger"; - -export const WHATSAPP_DEFAULT_SESSION_NAME = "default" +import {SessionBaseRequest, SessionQuery} from "./base.dto"; /** * Queries */ -export class SessionQuery { - @IsNotEmpty() - session: string = WHATSAPP_DEFAULT_SESSION_NAME; -} - export class CheckNumberStatusQuery extends SessionQuery { @IsString() phone: string @@ -28,11 +22,6 @@ export class MessageTextQuery extends SessionQuery { /** * Requests */ - -export class SessionBaseRequest { - session = WHATSAPP_DEFAULT_SESSION_NAME -} - export class ChatRequest extends SessionBaseRequest { @ApiProperty({ example: '11111111111@c.us', diff --git a/src/structures/contacts.dto.ts b/src/structures/contacts.dto.ts new file mode 100644 index 00000000..af418984 --- /dev/null +++ b/src/structures/contacts.dto.ts @@ -0,0 +1,22 @@ +import {IsString} from "class-validator"; +import {SessionBaseRequest, SessionQuery, WHATSAPP_DEFAULT_SESSION_NAME} from "./base.dto"; + + +/** + * Queries + */ + +export class ContactQuery extends SessionQuery { + @IsString() + contactId: string +} + + +/** + * Requests + */ + +export class ContactRequest extends SessionBaseRequest { + @IsString() + contactId: string +}