import { GetFlagValueHandler } from "~/server/controllers/flags.controller"; import { db } from "~/server/db"; import { NewMail } from "~/server/services/mailer"; const BASEURL = "https://api.skebby.it/API/v1.0/REST/"; enum MessageType { HIGH_QUALITY = "GP", MEDIUM_QUALITY = "TI", LOW_QUALITY = "SI", } interface Auth { user_key: string; session_key: string; } interface SMSData { /** The type of SMS */ message_type: MessageType; /** The body of the message. *Message max length could be 160 chars when using low-quality SMSs. New line char needs to be codified as “\n”. */ message: string; /** A list of recipents phone numbers. The recipients’ numbers must be in the international format (+393471234567 or 00393471234567) */ recipient: string[]; /** The Sender name. If the message type allows a custom TPOA and the field is left empty, the user’s preferred TPOA is used. Must be empty if the message type does not allow a custom TPOA */ sender?: string; /** The messages will be sent at the given scheduled time * formats: ddMMyy, yyyyMMdd, ddMMyyHHmm, yyyyMMddHHmmss, yyyy-MM-dd HH:mm, yyyy-MM-dd HH:mm.0 */ scheduled_delivery_time?: string; /** IANA time zone: Europe/Rome, America/New_York, etc. */ scheduled_delivery_timezone?: string; /** Specifies a custom order ID * optional, max 32 characters */ order_id?: string; /** Returns the number of used sms credits. */ returnCredits?: boolean; /** Returns the number of remaining SMS credits for the quality used in the sent SMS and for the default user nation */ returnRemaining?: boolean; /** if true, allows sending to invalid recipients */ allowInvalidRecipients?: boolean; /** default is "gsm" */ encoding?: "gsm" | "ucs2"; id_landing?: number; /** optional, max 64 characters */ campaign_name?: string; /** max number of fragments for the message, default is 7 */ max_fragments?: number; /** if true, truncates the message to fit the max fragments , default is true */ truncate?: boolean; /** Period after which the SMS message will be deleted from the SMS center. */ validity_period_min?: number; richsms_url?: string; } export type MinimalSMSData = Omit; interface SMSResponse { result: unknown; // or "OK" | any if you're less strict [key: string]: unknown; // allow additional unknown keys } /** * Authenticates the user given their username and password. */ async function login(username: string, password: string): Promise { const url = `${BASEURL}login?username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`; const response = await fetch(url); if (response.status === 401) { throw new Error("Authentication failed: Invalid username or password"); } if (response.status === 400) { throw new Error(`Error in request: ${await response.text()}`); } if (!response.ok || response.status !== 200) { throw new Error(`Login failed: ${response.status} ${response.statusText}`); } const text = await response.text(); const [user_key, session_key] = text.split(";"); if (!user_key || !session_key) { throw new Error("Invalid login response format"); } return { user_key, session_key }; } /** * Sends an SMS message */ async function sendSMS(auth: Auth, smsData: SMSData) { const url = `${BASEURL}sms`; const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", user_key: auth.user_key, Session_key: auth.session_key, }, body: JSON.stringify(smsData), }); if (response.status === 400) { throw new Error(`Invalid input: ${await response.text()}`); } if (response.status === 401) { throw new Error("Authentication failed: Invalid user key or session key"); } if (response.status === 404) { throw new Error("Not Found: The User_key was not found"); } if (response.status !== 201) { const errorText = await response.text(); throw new Error(`SMS send failed: ${response.status} ${errorText}`); } const data = (await response.json()) as SMSResponse; if (data.result !== "OK") { throw new Error("SMS send returned non-OK result"); } } // Example usage /*const smsData: SMSData = { recipient: ["+393407246729"], message: "Hello world!", message_type: MessageType.HIGH_QUALITY, sender: "InfoAlloggi", };*/ export const SendMessage = async (data: MinimalSMSData) => { const smsFlag = await GetFlagValueHandler({ db: db, id: "SMS_OFF", }); if (smsFlag === "true") { console.warn( `SMS sending is disabled to:${data.recipient.join(", ")} Mgs:${data.message}`, ); return { success: true, message: `SMS sending is disabled: to:${data.recipient.join(", ")} Mgs:${data.message}`, }; } try { const auth = await login("arcasms2018", "123qwe456rtyA."); await sendSMS(auth, { ...data, message_type: MessageType.HIGH_QUALITY, sender: "InfoAlloggi", }); } catch (error) { console.error("Error:", error); await NewMail({ to: "web@infoalloggi.it", subject: "Errore invio SMS", mailType: "generic", props: { title: "Errore invio SMS", testo: `Errore durante l'invio dell'SMS: to:${data.recipient.join(", ")} Mgs:${data.message}; ${error instanceof Error ? error.message : String(error)}`, }, }); } };