mirror of
https://github.com/vernu/textbee.git
synced 2026-02-20 07:34:00 -05:00
chore(api): better handle batch sms
This commit is contained in:
@@ -6,6 +6,7 @@ import { GatewayService } from './gateway.service'
|
||||
import { AuthModule } from '../auth/auth.module'
|
||||
import { UsersModule } from '../users/users.module'
|
||||
import { SMS, SMSSchema } from './schemas/sms.schema'
|
||||
import { SMSBatch, SMSBatchSchema } from './schemas/sms-batch.schema'
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -18,6 +19,10 @@ import { SMS, SMSSchema } from './schemas/sms.schema'
|
||||
name: SMS.name,
|
||||
schema: SMSSchema,
|
||||
},
|
||||
{
|
||||
name: SMSBatch.name,
|
||||
schema: SMSBatchSchema,
|
||||
},
|
||||
]),
|
||||
AuthModule,
|
||||
UsersModule,
|
||||
|
||||
@@ -13,11 +13,14 @@ import { User } from '../users/schemas/user.schema'
|
||||
import { AuthService } from 'src/auth/auth.service'
|
||||
import { SMS } from './schemas/sms.schema'
|
||||
import { SMSType } from './sms-type.enum'
|
||||
import { SMSBatch } from './schemas/sms-batch.schema'
|
||||
import { Message } from 'firebase-admin/lib/messaging/messaging-api'
|
||||
@Injectable()
|
||||
export class GatewayService {
|
||||
constructor(
|
||||
@InjectModel(Device.name) private deviceModel: Model<DeviceDocument>,
|
||||
@InjectModel(SMS.name) private smsModel: Model<SMS>,
|
||||
@InjectModel(SMSBatch.name) private smsBatchModel: Model<SMSBatch>,
|
||||
private authService: AuthService,
|
||||
) {}
|
||||
|
||||
@@ -85,14 +88,6 @@ export class GatewayService {
|
||||
}
|
||||
|
||||
async sendSMS(deviceId: string, smsData: SendSMSInputDTO): Promise<any> {
|
||||
const updatedSMSData = {
|
||||
message: smsData.message || smsData.smsBody,
|
||||
recipients: smsData.recipients || smsData.receivers,
|
||||
|
||||
// Legacy fields to be removed in the future
|
||||
smsBody: smsData.message || smsData.smsBody,
|
||||
receivers: smsData.recipients || smsData.receivers,
|
||||
}
|
||||
const device = await this.deviceModel.findById(deviceId)
|
||||
|
||||
if (!device?.enabled) {
|
||||
@@ -105,23 +100,76 @@ export class GatewayService {
|
||||
)
|
||||
}
|
||||
|
||||
const stringifiedSMSData = JSON.stringify(updatedSMSData)
|
||||
const payload: any = {
|
||||
data: {
|
||||
smsData: stringifiedSMSData,
|
||||
},
|
||||
}
|
||||
const message = smsData.message || smsData.smsBody
|
||||
const recipients = smsData.recipients || smsData.receivers
|
||||
|
||||
// TODO: Save SMS and Implement a queue to send the SMS if recipients are too many
|
||||
// TODO: Implement a queue to send the SMS if recipients are too many
|
||||
|
||||
let smsBatch: SMSBatch
|
||||
|
||||
try {
|
||||
const response = await firebaseAdmin
|
||||
.messaging()
|
||||
.sendToDevice(device.fcmToken, payload, { priority: 'high' })
|
||||
smsBatch = await this.smsBatchModel.create({
|
||||
device: device._id,
|
||||
message,
|
||||
metadata: {
|
||||
recipientCount: recipients.length,
|
||||
recipientPreview: this.getRecipientsPreview(recipients),
|
||||
},
|
||||
})
|
||||
} catch (e) {
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
error: 'Failed to create SMS batch',
|
||||
additionalInfo: e,
|
||||
},
|
||||
HttpStatus.BAD_REQUEST,
|
||||
)
|
||||
}
|
||||
|
||||
const fcmMessages: Message[] = []
|
||||
|
||||
for (const recipient of recipients) {
|
||||
const sms = await this.smsModel.create({
|
||||
device: device._id,
|
||||
smsBatch: smsBatch._id,
|
||||
message: message,
|
||||
type: SMSType.SENT,
|
||||
recipient,
|
||||
requestedAt: new Date(),
|
||||
})
|
||||
const updatedSMSData = {
|
||||
smsId: sms._id,
|
||||
smsBatchId: smsBatch._id,
|
||||
message,
|
||||
recipients: [recipient],
|
||||
|
||||
// Legacy fields to be removed in the future
|
||||
smsBody: message,
|
||||
receivers: [recipient],
|
||||
}
|
||||
const stringifiedSMSData = JSON.stringify(updatedSMSData)
|
||||
|
||||
const fcmMessage: Message = {
|
||||
data: {
|
||||
smsData: stringifiedSMSData,
|
||||
},
|
||||
token: device.fcmToken,
|
||||
android: {
|
||||
priority: 'high',
|
||||
},
|
||||
}
|
||||
fcmMessages.push(fcmMessage)
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await firebaseAdmin.messaging().sendAll(fcmMessages)
|
||||
|
||||
console.log(response)
|
||||
|
||||
this.deviceModel
|
||||
.findByIdAndUpdate(deviceId, {
|
||||
$inc: { sentSMSCount: updatedSMSData.recipients.length },
|
||||
$inc: { sentSMSCount: recipients.length },
|
||||
})
|
||||
.exec()
|
||||
.catch((e) => {
|
||||
@@ -132,7 +180,9 @@ export class GatewayService {
|
||||
} catch (e) {
|
||||
throw new HttpException(
|
||||
{
|
||||
success: false,
|
||||
error: 'Failed to send SMS',
|
||||
additionalInfo: e,
|
||||
},
|
||||
HttpStatus.BAD_REQUEST,
|
||||
)
|
||||
@@ -235,4 +285,20 @@ export class GatewayService {
|
||||
totalApiKeyCount,
|
||||
}
|
||||
}
|
||||
|
||||
private getRecipientsPreview(recipients: string[]): string {
|
||||
if (recipients.length === 0) {
|
||||
return null
|
||||
} else if (recipients.length === 1) {
|
||||
return recipients[0]
|
||||
} else if (recipients.length === 2) {
|
||||
return `${recipients[0]} and ${recipients[1]}`
|
||||
} else if (recipients.length === 3) {
|
||||
return `${recipients[0]}, ${recipients[1]}, and ${recipients[2]}`
|
||||
} else {
|
||||
return `${recipients[0]}, ${recipients[1]}, and ${
|
||||
recipients.length - 2
|
||||
} others`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
api/src/gateway/schemas/sms-batch.schema.ts
Normal file
22
api/src/gateway/schemas/sms-batch.schema.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
|
||||
import { Document, Types } from 'mongoose'
|
||||
import { Device } from './device.schema'
|
||||
|
||||
export type SMSBatchDocument = SMSBatch & Document
|
||||
|
||||
@Schema({ timestamps: true })
|
||||
export class SMSBatch {
|
||||
_id?: Types.ObjectId
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: Device.name })
|
||||
device: Device
|
||||
|
||||
@Prop({ type: String })
|
||||
message: string
|
||||
|
||||
// misc metadata for debugging
|
||||
@Prop({ type: Object })
|
||||
metadata: Record<string, any>
|
||||
}
|
||||
|
||||
export const SMSBatchSchema = SchemaFactory.createForClass(SMSBatch)
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
|
||||
import { Document, Types } from 'mongoose'
|
||||
import { Device } from './device.schema'
|
||||
import { SMSBatch } from './sms-batch.schema'
|
||||
|
||||
export type SMSDocument = SMS & Document
|
||||
|
||||
@@ -11,6 +12,9 @@ export class SMS {
|
||||
@Prop({ type: Types.ObjectId, ref: Device.name, required: true })
|
||||
device: Device
|
||||
|
||||
@Prop({ type: Types.ObjectId, ref: SMSBatch.name, required: true })
|
||||
smsBatch: SMSBatch
|
||||
|
||||
@Prop({ type: String })
|
||||
message: string
|
||||
|
||||
|
||||
Reference in New Issue
Block a user