@bdcode/sms
Version:
Unified (BD) SMS Providers client package for all TS/JS applications
188 lines (183 loc) • 5.53 kB
JavaScript
// lib/adapters/smsnetbd.ts
import { z } from "zod";
// lib/common.ts
import axios from "axios";
var composeHeaders = (headers) => {
const composedHeaders = {};
for (const [key, value] of Object.entries(headers)) {
if (value !== void 0) {
composedHeaders[key] = String(value);
}
}
return composedHeaders;
};
var safeComposeRequestBodyData = (body, target = "json") => {
if (typeof body === "object" && body !== null) {
if (target === "form") {
const formData = new URLSearchParams();
for (const [key, value] of Object.entries(body)) {
if (value !== void 0) {
formData.append(key, String(value));
}
}
return formData;
}
return JSON.stringify(body);
}
return body;
};
var axApiCall = async (method, endpoint, headers = {}, body = void 0, target = "json") => {
try {
const isWriteMethod = ["post", "put", "patch", "delete"].includes(
method.toLowerCase()
);
const formattedBody = body ? safeComposeRequestBodyData(body, target) : void 0;
if (formattedBody) {
headers["Content-Type"] = target === "form" ? "application/x-www-form-urlencoded" : "application/json";
}
const axiosConfig = {
headers
};
const response = isWriteMethod ? await axios[method](endpoint, formattedBody, axiosConfig) : await axios[method](endpoint, axiosConfig);
if (!response || !response.data) {
throw new Error("No response data received from the API.");
}
return response.data;
} catch (err) {
if (axios.isAxiosError(err)) {
throw new Error(`API Error: ${err.response?.data?.msg || err.message}`);
}
throw new Error(
`Unexpected Error: ${err instanceof Error ? err.message : "Unknown error"}`
);
}
};
// lib/adapters/smsnetbd.ts
var SmsNetBdEndpoints = {
SEND_SMS: "/sendsms",
GET_BALANCE: "/user/balance/",
GET_REPORT: "/report/request/{id}/"
// {id} is a placeholder for request_id
// ... add more if newly added in their API_DOCS
};
var validatePayloadWithReqID = z.object({
request_id: z.union([z.string(), z.number()]).transform((val) => String(val))
});
var validateSendSmsPayload = z.object({
msg: z.string().min(1, "Message content is required"),
to: z.string().min(1, "Recipient number(s) are required").refine(
(val) => val.split(",").map((num) => num.trim()).every((num) => /^(\+?8801\d{9}|01\d{9})$/.test(num)),
{
message: "Each recipient number must start with country code (880) or Standard 01X and be valid."
}
),
schedule: z.string().optional().refine(
(val) => !val || /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(val),
{
message: "Schedule must be formatted as Y-m-d H:i:s (e.g. 2021-10-13 16:00:52)"
}
),
sender_id: z.string().optional(),
content_id: z.string().optional()
});
var SmsNetBd = class {
constructor(config) {
this.config = config;
this.base = config.base ?? "https://api.sms.net.bd";
this.api_key = config.api_key;
}
base;
api_key;
// SEND: SMS
async sendSms(payload) {
const parsed = validateSendSmsPayload.safeParse(payload);
if (!parsed.success) {
throw new Error(
`Validation Error: ${parsed.error.issues.map((issue) => issue.message).join(", ")}`
);
}
const res = await axApiCall(
"post",
`${this.base}${SmsNetBdEndpoints.SEND_SMS}`,
composeHeaders({
"Content-Type": "application/x-www-form-urlencoded"
}),
{ ...parsed.data, api_key: this.api_key },
// Include api_key from config
"form"
// Use form-urlencoded for sending SMS
);
if (res.error !== 0) {
throw new Error(`${res.error}: ${res.msg}`);
}
return res;
}
// GET: SMS Account Balance
async getBalance() {
const res = await axApiCall(
"post",
`${this.base}${SmsNetBdEndpoints.GET_BALANCE}`,
composeHeaders({
"Content-Type": "application/x-www-form-urlencoded"
}),
{ api_key: this.api_key },
"form"
// Use form-urlencoded for getting balance
);
if (res.error !== 0) {
throw new Error(`${res.error}: ${res.msg}`);
}
return res;
}
// GET: SMS Delivery Report \\ {id} is a placeholder for request_id
async getReport(request_id) {
const data = validatePayloadWithReqID.parse({ request_id });
const url = `${this.base}${SmsNetBdEndpoints.GET_REPORT}`.replace(
"{id}",
String(data.request_id)
);
const res = await axApiCall(
"post",
url,
composeHeaders({
"Content-Type": "application/x-www-form-urlencoded"
}),
{ api_key: this.api_key },
"form"
// Use form-urlencoded for getting report
);
if (res.error !== 0) {
throw new Error(`${res.error}: ${res.msg}`);
}
return res;
}
};
// lib/gateways.ts
var smsAdapters = ["sms-net-bd", "bulk-sms-dhaka"];
var SmsGateway = class {
constructor(_adapter, config) {
this._adapter = _adapter;
this.config = config;
this.client = this.loadAdapter(this._adapter, this.config);
}
client;
loadAdapter(adapter, config) {
switch (adapter) {
case "sms-net-bd":
return new SmsNetBd(
config
);
case "bulk-sms-dhaka":
throw new Error("Adapter not available yet.");
default:
throw new Error(`Adapter "${adapter}" is invalid or not supported.`);
}
}
};
// lib/index.ts
var index_default = SmsGateway;
export {
SmsGateway,
index_default as default,
smsAdapters
};