magfa-api
Version:
Magfa SMS API client for JS/TS/ESM/Node.js
353 lines (301 loc) • 11.5 kB
text/typescript
type IMagfaResponse = {
status: number;
messages: any[];
};
/**
* The Magfa API Class
* @author Shahab Movahhedi
* @see {@link https://shmovahhedi.com Shahab Movahhedi's Website}
* @see {@link https://github.com/movahhedi/magfa-api magfa-api's Repository}
* @license MIT
*/
export class Magfa {
public readonly apiUrl = "https://sms.magfa.com/api/http/sms/v2";
private username: string;
private domain: string;
private password: string;
private from: string;
private authHeader: string;
constructor(username: string, password: string, domain: string, from: string) {
this.username = username || "";
this.password = password;
this.domain = domain;
this.from = from;
this.authHeader =
"Basic " +
Buffer.from(`${this.username}/${this.domain}:${this.password}`).toString(
"base64",
);
return this;
}
/**
* Make an API request to the Magfa API
* @private
* @param {string} urlSuffix - The URL suffix for the API endpoint
* @param {("GET"|"POST")} [method="GET"] - The HTTP method to use for the request
* @param {object|null} [data=null] - The data to send with the request
* @returns {Promise} The response from the API
*/
private async Api(
urlSuffix: string,
method: "GET" | "POST" = "GET",
data?: object,
): Promise<any> {
let response: Response, responseBody: any;
try {
response = await fetch(`${this.apiUrl}/${urlSuffix}`, {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Authorization: this.authHeader,
},
method,
body: data && JSON.stringify(data),
});
} catch (error) {
throw new MagfaError(undefined, "Server connection failed");
}
try {
responseBody = await response.json();
} catch (error) {
throw new MagfaError(undefined, "The server didn't respond correctly");
}
if (typeof responseBody?.status === "undefined") {
throw new MagfaError(undefined, "The server didn't respond correctly");
}
if (responseBody.status === 0) {
return responseBody;
}
if (Object.keys(magfaErrors).includes("" + responseBody.status)) {
throw new MagfaError(responseBody.status);
}
return responseBody;
}
async Send({
recipients,
messages,
encodings,
uids,
udhs,
}: {
recipients: string | string[];
messages: string | string[];
encodings?: Encoding | Encoding[];
uids?: number[];
udhs?: string[];
}): Promise<ISendResponseWithCount> {
if (!recipients || !messages) {
throw new MagfaError(
undefined,
"recipients and messages should not be empty",
);
}
if (typeof recipients === "string") {
recipients = [recipients];
}
if (typeof messages === "string") {
messages = [messages];
}
if (
!messages.length ||
(recipients.length !== messages.length && messages.length !== 1)
) {
throw new MagfaError(
undefined,
"recipients and messages should have the same length",
);
}
// const senders = messages.map(() => this.from);
const senders = Array(recipients.length).fill(this.from);
if (encodings && !Array.isArray(encodings)) {
encodings = Array(recipients.length).fill(encodings);
}
const sendData = {
recipients,
messages,
senders,
encodings,
uids,
udhs,
};
const result: IMagfaResponse_Send = await this.Api("send", "POST", sendData);
const messagesMutated: ISentMessage[] = [];
let successCount: number = 0,
failCount: number = 0;
result.messages.map((messageResult: ISentMessage) => {
if (messageResult.status === 0) {
const obj = { ...messageResult, message: "پیام با موفقیت رسید" };
messagesMutated.push(obj);
successCount++;
} else {
const obj = {
...messageResult,
message: GetStatusText(messageResult.status),
};
messagesMutated.push(obj);
failCount++;
}
});
return {
...result,
messages: messagesMutated,
count: {
success: successCount,
fail: failCount,
},
};
}
async Statuses(mids: number[]): Promise<IMagfaResponse_Statuses> {
return await this.Api(`statuses/${mids.join(",")}`);
}
async Mid(uid: number): Promise<IMagfaResponse_Mid> {
const result = (await this.Api(`mid/${uid}`)) as IMagfaResponse_Mid;
return {
...result,
mid: result.mid || undefined,
};
}
async Balance(): Promise<IMagfaResponse_Balance> {
return await this.Api("balance");
}
async ReceivedMessages(
count: number = 100,
): Promise<IMagfaResponse_ReceivedMessages> {
return await this.Api(`messages/${count}`);
}
}
export class MagfaError extends Error {
status: keyof typeof magfaErrors | undefined;
constructor(statusParam: number | undefined, messageParam?: string) {
let message: string, status: keyof typeof magfaErrors | undefined;
if (statusParam && Object.keys(magfaErrors).includes("" + statusParam)) {
message = GetStatusText(statusParam);
status = statusParam as any;
} else {
message = messageParam || "Unknown Magfa error";
status = undefined;
}
super(message);
this.message = message;
this.status = status;
}
}
type IStatusCode = 0 | keyof typeof magfaErrors;
interface IMagfaResponse_Base {
status: IStatusCode;
}
interface IMagfaResponse_Balance extends IMagfaResponse_Base {
balance: number | null;
}
interface IMagfaResponse_Send extends IMagfaResponse_Base {
messages: ISentMessage[];
}
interface IMagfaResponse_Statuses extends IMagfaResponse_Base {
dlrs: {
/** شناسه یکتای پیامک */
mid: number;
/** وضعیت */
status: number;
/** `yyyy-mm-dd hh:mm:ss` */
date: string;
}[];
}
interface IMagfaResponse_ReceivedMessages extends IMagfaResponse_Base {
messages: {
/** پیام */
body: string;
/** فرستنده */
senderNumber: string;
/** گیرنده */
recipientNumber: string;
/** `yyyy-mm-dd hh:mm:ss` */
date: string;
}[];
}
interface IMagfaResponse_Mid extends IMagfaResponse_Base {
mid: number | undefined;
}
interface ISendResponseWithCount extends IMagfaResponse_Send {
count: {
success: number;
fail: number;
};
}
interface ISentMessage {
/** نشانگر وضعیت درخواست. مقدار صفر به معنای انجام بدون خطای درخواست و هر عدد غیر از صفر کد خطای مربوطه است. */
status: IStatusCode;
/** شناسه یکتای پیامک */
id: number;
/** شناسهی یکتای کاربر */
userId: number;
/** تعداد بخشهای پیامک */
parts: number;
/** تعرفه */
tariff: number;
/** `DEFAULT` for English (ASCII), `UCS2` for Persian */
alphabet: "DEFAULT" | "UCS2";
/** گیرنده */
recipient: string;
}
export const enum Encoding {
/** تشخیص خودکار زبان پیامک (پیش فرض) */
Auto = 0,
/** فارسی */
Persian = 2,
/** 8bit */
EightBit = 5,
/** Binary */
Binary = 6,
}
export function GetStatusText(status: number) {
if (status === 0) {
return "success";
}
if (Object.keys(magfaErrors).includes("" + status)) {
return magfaErrors[status];
}
return "";
}
export const magfaErrors = {
1: "شماره گیرنده نادرست است",
2: "شماره فرستنده نادرست است",
3: "پارامتر encoding نامعتبر است. (بررسی صحت و همخوانی متن پیامک با encoding انتخابی)",
4: "پارامتر mclass نامعتبر است",
6: "پارامتر UDH نامعتبر است",
13: "محتویات پیامک (ترکیب UDH و متن) خالی است. (بررسی دوبارهی متن پیامک و پارامتر UDH)",
14: "مانده اعتبار ریالی مورد نیاز برای ارسال پیامک کافی نیست",
15: "سرور در هنگام ارسال پیام مشغول برطرف نمودن ایراد داخلی بوده است. (ارسال مجدد درخواست)",
16: "حساب غیرفعال است. (تماس با واحد فروش سیستمهای ارتباطی)",
17: "حساب منقضی شده است. (تماس با واحد فروش سیستمهای ارتباطی)",
18: "نام کاربری و یا کلمه عبور نامعتبر است. (بررسی مجدد نام کاربری و کلمه عبور)",
19: "درخواست معتبر نیست. (ترکیب نام کاربری، رمز عبور و دامنه اشتباه است. تماس با واحد فروش برای دریافت کلمه عبور جدید)",
20: "شماره فرستنده به حساب تعلق ندارد",
22: "این سرویس برای حساب فعال نشده است",
23: "در حال حاضر امکان پردازش درخواست جدید وجود ندارد، لطفا دوباره سعی کنید. (ارسال مجدد درخواست)",
24: "شناسه پیامک معتبر نیست. (ممکن است شناسه پیامک اشتباه و یا متعلق به پیامکی باشد که بیش از یک روز از ارسال آن گذشته)",
25: "نام متد درخواستی معتبر نیست. (بررسی نگارش نام متد با توجه به بخش متدها در این راهنما)",
27: "شماره گیرنده در لیست سیاه اپراتور قرار دارد. (ارسال پیامکهای تبلیغاتی برای این شماره امکانپذیر نیست)",
28: "شماره گیرنده، بر اساس پیششماره در حال حاضر در مگفا مسدود است",
29: "آدرس IP مبدا، اجازه دسترسی به این سرویس را ندارد",
30: "تعداد بخشهای پیامک بیش از حد مجاز استاندارد (۲۶۵ عدد) است",
31: "دادههای موردنیاز برای ارسال کافی نیستند. (اصلاح HTTP Request)",
101: "طول آرایه پارامتر messageBodies با طول آرایه گیرندگان تطابق ندارد",
102: "طول آرایه پارامتر messageClass با طول آرایه گیرندگان تطابق ندارد",
103: "طول آرایه پارامتر senderNumbers با طول آرایه گیرندگان تطابق ندارد",
104: "طول آرایه پارامتر udhs با طول آرایه گیرندگان تطابق ندارد",
105: "طول آرایه پارامتر priorities با طول آرایه گیرندگان تطابق ندارد",
106: "آرایهی گیرندگان خالی است",
107: "طول آرایه پارامتر گیرندگان بیشتر از طول مجاز است",
108: "آرایهی فرستندگان خالی است",
109: "طول آرایه پارامتر encoding با طول آرایه گیرندگان تطابق ندارد",
110: "طول آرایه پارامتر checkingMessageIds با طول آرایه گیرندگان تطابق ندارد",
} as const;
export const messageStatuses = {
[-1]: "شناسه موجود نیست (شناسه نادرست یا گذشت بیش از ۲۴ ساعت از ارسال پیامک)",
0: "وضعیتی دریافت نشده",
1: "رسیده به گوشی",
2: "نرسیده به گوشی",
8: "رسیده به مخابرات",
16: "نرسیده به مخابرات",
} as const;
export default Magfa;