Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/api/controllers/contact.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { SaveContactDto } from '@api/dto/contact.dto';
import { InstanceDto } from '@api/dto/instance.dto';
import { WAMonitoringService } from '@api/services/monitor.service';
import { BadRequestException } from '@exceptions';

export class ContactController {
constructor(private readonly waMonitor: WAMonitoringService) {}

public async saveContact({ instanceName }: InstanceDto, data: SaveContactDto) {
const instance = this.waMonitor.waInstances[instanceName];
if (!instance || typeof instance.saveContact !== 'function') {
throw new BadRequestException('Contact creation is only supported on WhatsApp Baileys instances.');
}

return await instance.saveContact(data);
}
}
6 changes: 6 additions & 0 deletions src/api/dto/contact.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export class SaveContactDto {
number: string;
name: string;
firstName?: string;
saveOnDevice?: boolean;
}
48 changes: 48 additions & 0 deletions src/api/integrations/channel/whatsapp/whatsapp.baileys.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
UpdateMessageDto,
WhatsAppNumberDto,
} from '@api/dto/chat.dto';
import { SaveContactDto } from '@api/dto/contact.dto';
import {
AcceptGroupInvite,
CreateGroupDto,
Expand Down Expand Up @@ -3768,6 +3769,53 @@ export class BaileysStartupService extends ChannelStartupService {
}
}

public async saveContact(data: SaveContactDto) {
try {
const jid = createJid(data.number);
const firstName = data.firstName || data.name.trim().split(' ')[0];

await this.client.chatModify(
{
contact: {
fullName: data.name,
firstName: firstName,
saveOnPrimaryAddressbook: data.saveOnDevice ?? true,
},
},
jid,
);

if (this.configService.get<Database>('DATABASE').SAVE_DATA.CONTACTS) {
await this.prismaRepository.contact.upsert({
where: { remoteJid_instanceId: { remoteJid: jid, instanceId: this.instanceId } },
create: {
remoteJid: jid,
pushName: data.name,
profilePicUrl: null,
instanceId: this.instanceId,
},
update: {
pushName: data.name,
},
});
}

return {
saved: true,
number: data.number,
name: data.name,
firstName: firstName,
saveOnDevice: data.saveOnDevice ?? true,
};
} catch (error) {
this.logger.error(`Error saving contact: ${error}`);
throw new InternalServerErrorException({
saved: false,
message: ['Failed to save contact to WhatsApp.', error?.message || error.toString()],
});
}
}

public async deleteMessage(del: DeleteMessage) {
try {
const response = await this.client.sendMessage(del.remoteJid, { delete: del });
Expand Down
25 changes: 25 additions & 0 deletions src/api/routes/contact.router.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { RouterBroker } from '@api/abstract/abstract.router';
import { SaveContactDto } from '@api/dto/contact.dto';
import { contactController } from '@api/server.module';
import { saveContactSchema } from '@validate/validate.schema';
import { RequestHandler, Router } from 'express';

import { HttpStatus } from './index.router';

export class ContactRouter extends RouterBroker {
constructor(...guards: RequestHandler[]) {
super();
this.router.post(this.routerPath('save'), ...guards, async (req, res) => {
const response = await this.dataValidate<SaveContactDto>({
request: req,
schema: saveContactSchema,
ClassRef: SaveContactDto,
execute: (instance, data) => contactController.saveContact(instance, data),
});

return res.status(HttpStatus.CREATED).json(response);
});
}

public readonly router: Router = Router();
}
2 changes: 2 additions & 0 deletions src/api/routes/index.router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import path from 'path';
import { BusinessRouter } from './business.router';
import { CallRouter } from './call.router';
import { ChatRouter } from './chat.router';
import { ContactRouter } from './contact.router';
import { GroupRouter } from './group.router';
import { InstanceRouter } from './instance.router';
import { LabelRouter } from './label.router';
Expand Down Expand Up @@ -218,6 +219,7 @@ router
.use('/message', new MessageRouter(...guards).router)
.use('/call', new CallRouter(...guards).router)
.use('/chat', new ChatRouter(...guards).router)
.use('/contact', new ContactRouter(...guards).router)
.use('/business', new BusinessRouter(...guards).router)
.use('/group', new GroupRouter(...guards).router)
.use('/template', new TemplateRouter(configService, ...guards).router)
Expand Down
2 changes: 2 additions & 0 deletions src/api/server.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Logger } from '@config/logger.config';
import { BusinessController } from './controllers/business.controller';
import { CallController } from './controllers/call.controller';
import { ChatController } from './controllers/chat.controller';
import { ContactController } from './controllers/contact.controller';
import { GroupController } from './controllers/group.controller';
import { InstanceController } from './controllers/instance.controller';
import { LabelController } from './controllers/label.controller';
Expand Down Expand Up @@ -103,6 +104,7 @@ export const instanceController = new InstanceController(
export const sendMessageController = new SendMessageController(waMonitor);
export const callController = new CallController(waMonitor);
export const chatController = new ChatController(waMonitor);
export const contactController = new ContactController(waMonitor);
export const businessController = new BusinessController(waMonitor);
export const groupController = new GroupController(waMonitor);
export const labelController = new LabelController(waMonitor);
Expand Down
30 changes: 30 additions & 0 deletions src/validate/contact.schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { JSONSchema7 } from 'json-schema';
import { v4 } from 'uuid';

export const saveContactSchema: JSONSchema7 = {
$id: v4(),
type: 'object',
properties: {
number: {
type: 'string',
minLength: 8,
maxLength: 20,
pattern: '^[0-9]+$',
description: 'The "number" must contain only digits (8-20 characters)',
},
name: {
type: 'string',
minLength: 1,
maxLength: 100,
description: 'The "name" cannot be empty',
},
firstName: {
type: 'string',
maxLength: 50,
},
saveOnDevice: {
type: 'boolean',
},
},
required: ['number', 'name'],
};
1 change: 1 addition & 0 deletions src/validate/validate.schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Integrations Schema
export * from './business.schema';
export * from './chat.schema';
export * from './contact.schema';
export * from './group.schema';
export * from './instance.schema';
export * from './label.schema';
Expand Down