2025-10-08 11:32:06 +02:00
import { env } from "~/env.mjs" ;
2025-08-04 17:45:44 +02:00
import { GetFlagValueHandler } from "~/server/controllers/flags.controller" ;
import { db } from "~/server/db" ;
2025-08-28 18:27:07 +02:00
import { NewMail } from "~/server/services/mailer" ;
2025-08-04 17:45:44 +02:00
const BASEURL = "https://api.skebby.it/API/v1.0/REST/" ;
enum MessageType {
2025-08-28 18:27:07 +02:00
HIGH_QUALITY = "GP" ,
MEDIUM_QUALITY = "TI" ,
LOW_QUALITY = "SI" ,
2025-08-04 17:45:44 +02:00
}
interface Auth {
2025-08-28 18:27:07 +02:00
user_key : string ;
session_key : string ;
2025-08-04 17:45:44 +02:00
}
interface SMSData {
2025-08-28 18:27:07 +02:00
/** 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 ;
/ * * T h e m e s s a g e s w i l l b e s e n t a t t h e g i v e n s c h e d u l e d t i m e
* 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 ;
/ * * S p e c i f i e s a c u s t o m o r d e r I D
* 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 ;
2025-08-04 17:45:44 +02:00
}
export type MinimalSMSData = Omit < SMSData , "message_type" | "sender" > ;
interface SMSResponse {
2025-08-28 18:27:07 +02:00
result : unknown ; // or "OK" | any if you're less strict
[ key : string ] : unknown ; // allow additional unknown keys
2025-08-04 17:45:44 +02:00
}
/ * *
* Authenticates the user given their username and password .
* /
async function login ( username : string , password : string ) : Promise < Auth > {
2025-08-28 18:27:07 +02:00
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" ) ;
}
2025-08-29 16:18:32 +02:00
return { session_key , user_key } ;
2025-08-04 17:45:44 +02:00
}
/ * *
* Sends an SMS message
* /
async function sendSMS ( auth : Auth , smsData : SMSData ) {
2025-08-28 18:27:07 +02:00
const url = ` ${ BASEURL } sms ` ;
const response = await fetch ( url , {
2025-08-29 16:18:32 +02:00
body : JSON.stringify ( smsData ) ,
2025-08-28 18:27:07 +02:00
headers : {
"Content-Type" : "application/json" ,
Session_key : auth.session_key ,
2025-08-29 16:18:32 +02:00
user_key : auth.user_key ,
2025-08-28 18:27:07 +02:00
} ,
2025-08-29 16:18:32 +02:00
method : "POST" ,
2025-08-28 18:27:07 +02:00
} ) ;
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" ) ;
}
2025-08-04 17:45:44 +02:00
}
// Example usage
/ * c o n s t s m s D a t a : S M S D a t a = {
recipient : [ "+393407246729" ] ,
message : "Hello world!" ,
message_type : MessageType.HIGH_QUALITY ,
sender : "InfoAlloggi" ,
} ; * /
export const SendMessage = async ( data : MinimalSMSData ) = > {
2025-08-28 18:27:07 +02:00
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 {
message : ` SMS sending is disabled: to: ${ data . recipient . join ( ", " ) } Mgs: ${ data . message } ` ,
2025-08-29 16:18:32 +02:00
success : true ,
2025-08-28 18:27:07 +02:00
} ;
}
try {
2025-10-08 11:32:06 +02:00
const auth = await login ( env . SKEBBY_USER , env . SKEBBY_PASS ) ;
2025-08-28 18:27:07 +02:00
await sendSMS ( auth , {
. . . data ,
message_type : MessageType.HIGH_QUALITY ,
sender : "InfoAlloggi" ,
} ) ;
} catch ( error ) {
console . error ( "Error:" , error ) ;
await NewMail ( {
mailType : "generic" ,
props : {
testo : ` Errore durante l'invio dell'SMS: to: ${ data . recipient . join ( ", " ) } Mgs: ${ data . message } ; ${ error instanceof Error ? error.message : String ( error ) } ` ,
2025-08-29 16:18:32 +02:00
title : "Errore invio SMS" ,
2025-08-28 18:27:07 +02:00
} ,
2025-08-29 16:18:32 +02:00
subject : "Errore invio SMS" ,
to : "web@infoalloggi.it" ,
2025-08-28 18:27:07 +02:00
} ) ;
}
2025-08-04 17:45:44 +02:00
} ;