UNPKG

vesal

Version:

Armaghan Vesal SMS API client for JS/TS/ESM/Node.js

309 lines (272 loc) 8.65 kB
/** * The Vesal API Class * @author Shahab Movahhedi * @see {@link https://shmovahhedi.com Shahab Movahhedi's Website} * @see {@link https://github.com/movahhedi/vesal vesal's Repository} * @license MIT */ export class Vesal { public readonly apiUrl = "http://vesal.armaghan.net:8080/rest"; private username: string; private password: string; private from: string; constructor(username: string, password: string, from: string) { this.username = username || ""; this.password = password; this.from = from; return this; } /** * Make an API request to the Vesal 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" = "POST", data?: object, ): Promise<any> { let response: Response, responseBody: any; data = { ...data, username: this.username, password: this.password, }; try { response = await fetch(`${this.apiUrl}/${urlSuffix}`, { headers: { Accept: "application/json", "Content-Type": "application/json", }, method, body: data && JSON.stringify(data), }); } catch (error) { throw new VesalError(undefined, "Server connection failed"); } try { responseBody = await response.json(); } catch (error) { throw new VesalError(undefined, "The server didn't respond correctly"); } if (!responseBody.references) { throw new VesalError(undefined, "The server didn't respond correctly"); } return responseBody; } async Send({ recipients, messages, from, }: { recipients: string | string[]; messages: string | string[]; from?: string | string[]; }): Promise<IVesalResponse_Send_WithCount> { if (!recipients || !messages) { throw new VesalError( undefined, "recipients and messages should not be empty", ); } if (typeof recipients === "string") { recipients = [recipients]; } if (!from) { from = this.from; } const isManyToMany = Array.isArray(messages) || Array.isArray(from); if (isManyToMany) { if (!recipients.length) { throw new VesalError( undefined, "recipients and messages should have the same length", ); } if (Array.isArray(messages) && !messages.length) { throw new VesalError( undefined, "recipients and messages should have the same length", ); } if (typeof messages === "string") { // messages = recipients.map(() => messages); messages = Array(recipients.length).fill(messages); } if (typeof from === "string") { // from = recipients.map(() => from); from = Array(recipients.length).fill(from); } if ( recipients.length !== messages.length || recipients.length !== from.length ) { throw new VesalError( undefined, "recipients and messages should have the same length", ); } } const sendData = { destinations: recipients, ...(isManyToMany ? { originators: from } : { originator: from }), ...(isManyToMany ? { contents: messages } : { content: messages }), }; const result: IVesalResponse_Send = await this.Api( isManyToMany ? "ManyToMany" : "OneToMany", "POST", sendData, ); let successCount: number = 0, failCount: number = 0; result.references?.map((messageResult) => { if (messageResult) { successCount++; } else { failCount++; } }); return { ...result, references: result.references || [], count: { success: successCount, fail: failCount, }, }; } async GetMessageStatus( referencesIds: number[], ): Promise<IVesalResponse_MessageState> { return await this.Api("messageState", "POST", { referenceids: referencesIds, }); } async GetReceivedMessages(): Promise<IVesalResponse_ReceivedMessages> { return await this.Api("pullReceivedMessages"); } async GetReceivedMessagesCount(): Promise<IVesalResponse_ReceivedMessagesCount> { return await this.Api("receivedMessageCount"); } async GetUserInfo(): Promise<IVesalResponse_UserInfo> { return await this.Api("userInfo"); } } export class VesalError extends Error { status: keyof typeof vesalErrors | undefined; constructor(statusParam: number | undefined, messageParam?: string) { let message: string, status: keyof typeof vesalErrors | undefined; if (statusParam && Object.keys(vesalErrors).includes("" + statusParam)) { message = GetStatusText(statusParam); status = statusParam as any; } else { message = messageParam || "Unknown Vesal error"; status = undefined; } super(message); this.message = message; this.status = status; } } interface IVesalResponse_Base { errorModel: { errorCode: number; timestamp: string | number | null; }; } interface IVesalResponse_Send extends IVesalResponse_Base { references: (number | Omit<keyof typeof vesalErrors, 0>)[]; } interface IVesalResponse_Send_WithCount extends IVesalResponse_Send { count: { success: number; fail: number; }; } interface IVesalResponse_MessageState extends IVesalResponse_Base { states: { id: number; state: number; }[]; } interface IVesalResponse_ReceivedMessages extends IVesalResponse_Base { messageModels: { originator: string; destination: string; content: string; }[]; } interface IVesalResponse_ReceivedMessagesCount extends IVesalResponse_Base { count: number; } interface IVesalResponse_UserInfo extends IVesalResponse_Base { user: { credit: number; deliveryUrl: unknown; receiveUrl: unknown; iPs: string[]; numbers: string[]; id: number; username: string; password: unknown; firstName: unknown; lastName: unknown; mobile: unknown; email: unknown; nationalId: number; expirationDate: string; active: boolean; deleted: boolean; dbProxyStandalone: boolean; insertDate: unknown; updateDate: unknown; customer: unknown; roles: unknown; }; } export function GetStatusText(status: number) { if (status === 0) { return "success"; } if (Object.keys(vesalErrors).includes("" + status)) { return vesalErrors[status]; } return ""; } export const messageStates = { 0: "پیامک در صف ارسال قرار دارد", 1: "ارسال شده", 2: "پیامک به موبایل گیرنده تحویل شده است", 3: "پیامک به موبایل گیرنده تحویل نشده است", 4: "وضعیت نامشخص", 5: "پیامک توسط وب سرویس به شرکت ارمغان راه طلایی رسیده است", 6: "پیام از سمت اپراتور لغو شده است", 7: "پیام از سمت اپراتور منقضی شده است", 8: "پیام از سمت اپراتور reject شده است", } as const; export const vesalErrors = { 0: "عملیات با موفقیت انجام شد", [-100]: "refrenceId مورد نظر یافت نشد", [-101]: "احراز هویت کاربر موفقیت آمیز نبود", [-102]: "نام کاربری یافت نشد", [-103]: "شماره originator اشتباه یا در بازه شماره های کاربر نیست", [-104]: "اعتبار کم است", [-105]: "فرمت درخواست اشتباه است", [-106]: "تعداد refrenceId ها بیش از 1000 عدد است", [-107]: "شماره گیرنده پیامک اشتباه است", [-109]: "تاریخ انقضای حساب کاربری فرارسیده است", [-110]: "درخواست از ip مجاز کاربر ارسال نشده است", [-111]: "شماره گیرنده در بلک لیست قرار دارد", [-112]: "حساب مشتری فعال نیست", [-115]: "فرمت UDH اشتباه است", [-117]: "مقدار mclass وارد شده اشتباه است", [-118]: "شماره پورت وارد شده صحیح نیست", [-119]: "کاربر به سرویس مورد نظر دسترسی ندارد", [-120]: "پیام ارسال شده دارای هیچ شماره معتبری نیست", [-200]: "خطای داخلی در پایگاه داده رخ داده است", [-201]: "خطای نامشخص داخل پایگاه داده", [-137]: "پیام نباید حاوی کلمات غیرمجاز می باشد", } as const;