UNPKG

@bdcode/sms

Version:

Unified (BD) SMS Providers client package for all TS/JS applications

188 lines (183 loc) 5.53 kB
// 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 };