Spaces:
Runtime error
Runtime error
| import { Controller, Get, Post, Delete, Param, Query, HttpCode, HttpStatus } from '@nestjs/common'; | |
| import { ApiTags, ApiOperation, ApiResponse, ApiParam, ApiQuery } from '@nestjs/swagger'; | |
| import { ContactService } from './contact.service'; | |
| import { RequireRole } from '../auth/decorators/auth.decorators'; | |
| import { ApiKeyRole } from '../auth/entities/api-key.entity'; | |
| ('contacts') | |
| ('sessions/:sessionId/contacts') | |
| export class ContactController { | |
| constructor(private readonly contactService: ContactService) {} | |
| () | |
| ({ summary: 'Get all contacts for a session' }) | |
| ({ name: 'sessionId', description: 'Session ID' }) | |
| ({ | |
| status: 200, | |
| description: 'List of contacts', | |
| }) | |
| ({ status: 400, description: 'Session not ready' }) | |
| ({ status: 404, description: 'Session not found' }) | |
| ({ name: 'limit', required: false, description: 'Max contacts to return (1–1000, default 1000)' }) | |
| ({ name: 'offset', required: false, description: 'Number of contacts to skip (for paging)' }) | |
| async findAll( | |
| ('sessionId') sessionId: string, | |
| ('limit') limit?: string, | |
| ('offset') offset?: string, | |
| ) { | |
| return this.contactService.getContacts(sessionId, { | |
| limit: limit ? parseInt(limit, 10) : undefined, | |
| offset: offset ? parseInt(offset, 10) : undefined, | |
| }); | |
| } | |
| ('profile-pictures') | |
| ({ | |
| summary: 'Batch-resolve profile picture URLs for up to 50 contacts', | |
| description: | |
| 'One request for a whole chat sidebar — avoids the burst of parallel single fetches that ' + | |
| 'would exhaust the per-IP throttle. Engine lookups run 3 at a time; per-id failures return null.', | |
| }) | |
| ({ name: 'sessionId', description: 'Session ID' }) | |
| ({ name: 'ids', required: true, description: 'Comma-separated contact ids (max 50 used)' }) | |
| // NOTE: declared BEFORE @Get(':contactId') so the literal segment wins over the param route. | |
| async getProfilePictures(('sessionId') sessionId: string, ('ids') ids?: string) { | |
| const list = (ids ?? '') | |
| .split(',') | |
| .map(s => s.trim()) | |
| .filter(Boolean); | |
| const pictures = await this.contactService.getProfilePictures(sessionId, list); | |
| return { pictures }; | |
| } | |
| (':contactId') | |
| ({ summary: 'Get a specific contact by ID' }) | |
| ({ name: 'sessionId', description: 'Session ID' }) | |
| ({ name: 'contactId', description: 'Contact ID (e.g., 628xxx@c.us)' }) | |
| ({ | |
| status: 200, | |
| description: 'Contact details', | |
| }) | |
| ({ status: 404, description: 'Contact not found' }) | |
| async findOne(('sessionId') sessionId: string, ('contactId') contactId: string) { | |
| return this.contactService.getContactById(sessionId, contactId); | |
| } | |
| ('check/:number') | |
| ({ | |
| summary: 'Check if a phone number exists on WhatsApp', | |
| description: | |
| 'Returns whether the number is a registered WhatsApp account and its canonical id. Use this to ' + | |
| 'pre-validate a recipient before sending: the send endpoints return 201 on accepting a message ' + | |
| 'even for numbers that are not on WhatsApp, so this is the only way to confirm a new number is ' + | |
| 'reachable before you send to it.', | |
| }) | |
| ({ name: 'sessionId', description: 'Session ID' }) | |
| ({ name: 'number', description: 'Phone number to check (e.g., 628123456789)' }) | |
| ({ | |
| status: 200, | |
| description: 'Number existence check result', | |
| }) | |
| async checkNumber(('sessionId') sessionId: string, ('number') number: string) { | |
| // The engine returns the canonical chat id in its native format; we don't build the JID here | |
| // (decoupled from the whatsapp-web.js `@c.us` scheme). | |
| const whatsappId = await this.contactService.getNumberId(sessionId, number); | |
| return { | |
| number, | |
| exists: whatsappId !== null, | |
| whatsappId, | |
| }; | |
| } | |
| // ========== Gap Quick Wins: Profile Picture, Block/Unblock ========== | |
| (':contactId/profile-picture') | |
| ({ summary: 'Get profile picture URL for a contact' }) | |
| ({ name: 'sessionId', description: 'Session ID' }) | |
| ({ name: 'contactId', description: 'Contact ID (e.g., 628xxx@c.us)' }) | |
| ({ | |
| status: 200, | |
| description: 'Profile picture URL', | |
| }) | |
| async getProfilePicture(('sessionId') sessionId: string, ('contactId') contactId: string) { | |
| const url = await this.contactService.getProfilePicture(sessionId, contactId); | |
| return { url }; | |
| } | |
| (':contactId/phone') | |
| ({ summary: 'Resolve a contact id (e.g. an @lid) to a phone number — best-effort' }) | |
| ({ name: 'sessionId', description: 'Session ID' }) | |
| ({ name: 'contactId', description: 'Contact ID / JID to resolve (e.g., an @lid)' }) | |
| ({ | |
| status: 200, | |
| description: 'Resolved phone number (MSISDN digits), or null when the engine cannot map it', | |
| }) | |
| async resolvePhone(('sessionId') sessionId: string, ('contactId') contactId: string) { | |
| const phone = await this.contactService.resolveContactPhone(sessionId, contactId); | |
| return { contactId, phone }; | |
| } | |
| (':contactId/block') | |
| (ApiKeyRole.OPERATOR) | |
| (HttpStatus.OK) | |
| ({ summary: 'Block a contact' }) | |
| ({ name: 'sessionId', description: 'Session ID' }) | |
| ({ name: 'contactId', description: 'Contact ID (e.g., 628xxx@c.us)' }) | |
| ({ | |
| status: 200, | |
| description: 'Contact blocked', | |
| }) | |
| async blockContact(('sessionId') sessionId: string, ('contactId') contactId: string) { | |
| await this.contactService.blockContact(sessionId, contactId); | |
| return { success: true, message: 'Contact blocked' }; | |
| } | |
| (':contactId/block') | |
| (ApiKeyRole.OPERATOR) | |
| ({ summary: 'Unblock a contact' }) | |
| ({ name: 'sessionId', description: 'Session ID' }) | |
| ({ name: 'contactId', description: 'Contact ID (e.g., 628xxx@c.us)' }) | |
| ({ | |
| status: 200, | |
| description: 'Contact unblocked', | |
| }) | |
| async unblockContact(('sessionId') sessionId: string, ('contactId') contactId: string) { | |
| await this.contactService.unblockContact(sessionId, contactId); | |
| return { success: true, message: 'Contact unblocked' }; | |
| } | |
| } | |