91 lines
2.5 KiB
TypeScript
91 lines
2.5 KiB
TypeScript
import { Injectable, Logger } from '@nestjs/common';
|
|
import { ConfigService } from '@nestjs/config';
|
|
|
|
/** HTTP endpoint documented by 0098sms.com for sending via query string. */
|
|
const SMS_0098_SEND_URL =
|
|
'http://www.0098sms.com/sendsmslink.aspx';
|
|
|
|
@Injectable()
|
|
export class SmsService {
|
|
private readonly logger = new Logger(SmsService.name);
|
|
|
|
constructor(private readonly config: ConfigService) {}
|
|
|
|
/** True when username, password, and sender line are all set. */
|
|
isConfigured(): boolean {
|
|
const username = this.config.get<string>('SMS_USERNAME');
|
|
const password = this.getPassword();
|
|
const sender = this.config.get<string>('SMS_SENDER');
|
|
return Boolean(username && password && sender);
|
|
}
|
|
|
|
/**
|
|
* Formats mobile for 0098 `TO` (Iranian mobiles as 09xxxxxxxxx).
|
|
*/
|
|
normalizePhoneForSms(mobile: string): string {
|
|
const digits = String(mobile ?? '').replace(/\D/g, '');
|
|
if (digits.startsWith('98') && digits.length === 12) {
|
|
return `0${digits.slice(2)}`;
|
|
}
|
|
if (digits.startsWith('0') && digits.length === 11) {
|
|
return digits;
|
|
}
|
|
if (digits.length === 10 && digits.startsWith('9')) {
|
|
return `0${digits}`;
|
|
}
|
|
return digits;
|
|
}
|
|
|
|
async sendOtpSms(mobile: string, code: string): Promise<boolean> {
|
|
const text = `کد ورود شما: ${code}`;
|
|
return this.sendSms(mobile, text);
|
|
}
|
|
|
|
async sendSms(phone: string, text: string): Promise<boolean> {
|
|
const username = this.config.get<string>('SMS_USERNAME');
|
|
const password = this.getPassword();
|
|
const sender = this.config.get<string>('SMS_SENDER');
|
|
const domain = this.config.get<string>('SMS_DOMAIN') ?? '0098';
|
|
|
|
if (!username || !password || !sender) {
|
|
return false;
|
|
}
|
|
|
|
const params = new URLSearchParams({
|
|
FROM: sender,
|
|
TO: this.normalizePhoneForSms(phone),
|
|
TEXT: text,
|
|
USERNAME: username,
|
|
PASSWORD: password,
|
|
DOMAIN: domain,
|
|
});
|
|
|
|
const url = `${SMS_0098_SEND_URL}?${params.toString()}`;
|
|
|
|
try {
|
|
const res = await fetch(url);
|
|
const body = await res.text();
|
|
if (!res.ok) {
|
|
this.logger.warn(
|
|
`SMS HTTP ${res.status}: ${body.slice(0, 300)}`,
|
|
);
|
|
return false;
|
|
}
|
|
return true;
|
|
} catch (e) {
|
|
this.logger.error(
|
|
'[SMS] request failed',
|
|
e instanceof Error ? e.stack : e,
|
|
);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private getPassword(): string | undefined {
|
|
return (
|
|
this.config.get<string>('SMS_PASSWORD') ??
|
|
this.config.get<string>('SMS_API_KEY')
|
|
);
|
|
}
|
|
}
|