import { ISMSSender } from '../../interfaces'; import { SendResult } from '../../types'; import { USERNAME, PASSWORD, FROM } from '../../utils/constants'; import { NotProvidedCredentialsError } from '../../errors'; import type { Vonage } from '@vonage/server-sdk'; import type { SMSMessages } from '@vonage/sms'; export default class VonageSMSSenderClient implements ISMSSender { private clientPromise: Promise | undefined; constructor() { if (!(USERNAME && PASSWORD && FROM)) throw new NotProvidedCredentialsError(); } public async send(phones: string[], message: string): Promise { const result: SendResult = { sent: [], failed: [], }; await Promise.all( phones.map((phone) => this.sendOne(phone, message).then((data) => { if ( data.messages?.some((message) => message.status !== '0') ) { result.failed.push({ phone, error: data.messages[0].errorText, }); } else { result.sent.push({ phone }); } }), ), ); return result; } private async getClient(): Promise { this.clientPromise = this.clientPromise ?? (async () => { const [{ Vonage }, { Auth }] = await Promise.all([ import('@vonage/server-sdk'), import('@vonage/auth'), ]); const vonageAuth = new Auth({ apiKey: USERNAME as string, apiSecret: PASSWORD as string, }); return new Vonage(vonageAuth); })(); return this.clientPromise; } private sendOne = async ( phone: string, message: string, ): Promise => { const client = await this.getClient(); return client.sms.send({ to: phone, text: message, from: FROM as string, }); }; }