UNPKG

advanced-ussd-builder

Version:

Advanced USSD Menu Builder with persistent state and navigation

16 lines (15 loc) 21.7 kB
"use strict";var __createBinding=this&&this.__createBinding||(Object.create?function(k,e,t,s){s===void 0&&(s=t);var n=Object.getOwnPropertyDescriptor(e,t);(!n||("get"in n?!e.__esModule:n.writable||n.configurable))&&(n={enumerable:!0,get:function(){return e[t]}}),Object.defineProperty(k,s,n)}:function(k,e,t,s){s===void 0&&(s=t),k[s]=e[t]}),__exportStar=this&&this.__exportStar||function(k,e){for(var t in k)t!=="default"&&!Object.prototype.hasOwnProperty.call(e,t)&&__createBinding(e,k,t)};Object.defineProperty(exports,"__esModule",{value:!0}),exports.UssdBuilder=exports.UssdMenu=void 0;const lodash_1=require("lodash"),helper_1=require("./helper"),redis_1=require("./redis"),handler_registry_1=require("./handler-registry"),proxy_handler_1=require("./proxy-handler"),types_1=require("./types");__exportStar(require("./types"),exports),__exportStar(require("./security"),exports),__exportStar(require("./proxy-handler"),exports);class UssdMenu{constructor(e,t,s=!1){this.title=e,this.children=t,this.custom_input=s}}exports.UssdMenu=UssdMenu;class UssdBuilder{constructor(e,t){this.local_session=null,this.session=null,this.raw_args=null,this.raw_headers=null,this.raw_body=null,this.args=null,this.redis=null,this.providers=["mtn","nalo","cross-switch","custom","telecel","airtel-tigo"],this.calculate_gsm_7bits_size=s=>{const n=`@\xA3$\xA5\xE8\xE9\xF9\xEC\xF2\xC7\xD8\xF8\xC5\xE5\u0394_\u03A6\u0393\u039B\u03A9\u03A0\u03A8\u03A3\u0398\u039E^{}[~]|\u20AC\xC6\xE6\xDF\xC9!"#\xA4%&'()*+,-./0123456789:;<=>? \xA1ABCDEFGHIJKLMNOPQRSTUVWXYZ\xC4\xD6\xD1\xDC\xA7\xBFabcdefghijklmnopqrstuvwxyz\xE4\xF6\xF1\xFC\xE0 `,r=`^{}[~]| \u20AC`;let i=0;for(const o of s)n.includes(o)&&(r.includes(o)?i+=3:i+=1);return i},this.str_includes_non_gsm_char=s=>{const n=`@\xA3$\xA5\xE8\xE9\xF9\xEC\xF2\xC7\xD8\xF8\xC5\xE5\u0394_\u03A6\u0393\u039B\u03A9\u03A0\u03A8\u03A3\u0398\u039E^{}[~]|\u20AC\xC6\xE6\xDF\xC9!"#\xA4%&'()*+,-./0123456789:;<=>? \xA1ABCDEFGHIJKLMNOPQRSTUVWXYZ\xC4\xD6\xD1\xDC\xA7\xBFabcdefghijklmnopqrstuvwxyz\xE4\xF6\xF1\xFC\xE0 `;return s.split("").map(r=>n.indexOf(r)!==-1).includes(!1)};try{if(this.opts=e,this.root_menu=t,this.handlerRegistry=handler_registry_1.HandlerRegistry.getInstance(),this.session_ttl=e.session_ttl||120,(0,lodash_1.get)(this.opts,"make_provider_response",!0)&&!this.providers.includes(this.opts.provider))throw new Error("Invalid provider or not supported. You can turn this off by disabling make_provider_response");if(this.opts.require_pin&&!this.opts.authentication_handler)throw new Error("Provide authentication handler if require pin is true")}catch(s){throw s}}get session_authenticated(){return(0,lodash_1.get)(this.session,"menu_metadata.authenticated",!1)}get from_login(){return(0,lodash_1.get)(this.session,"menu_metadata.from_login",!1)}get redis_session_key(){return`${this.opts.session_prefix||"ussd"}:${this.args?.phone_number}:${this.opts.service_code}`}get ussd_args(){return this.args}async update_user_input(e){if(this.session){this.session.user_inputs.push(e),this.session.last_activity_timestamp=new Date;const t=await this.redis?.client.set(this.redis_session_key,JSON.stringify(this.session),{EX:this.session_ttl});(0,helper_1.assert)(t,"Session update failed. Check your redis connection")}}set_args(e){if(this.raw_args=e,this.opts.provider==="nalo")this.args={msisdn:e.MSISDN,user_id:e.USERID,network:e.NETWORK,user_input:e.USERDATA,session_id:e.SESSIONID,is_new_request:e.MSGTYPE,phone_number:e.MSISDN.trim().replace("233","0")};else if(this.opts.provider==="mtn")this.args={network:"MTN",user_id:e.imsi,msisdn:e.msisdn,session_id:e.sessionId,user_input:e.ussdString,is_new_request:e.messageType==="0",phone_number:e.msisdn.trim().replace("233","0")};else if(this.opts.provider==="cross-switch")this.args={msisdn:e.Mobile,user_id:e.SessionId,network:e.Operator,user_input:e.Message,session_id:e.SessionId,is_new_request:e.Type.toLowerCase()==="initiation",phone_number:e.Mobile.trim().replace("233","0")};else if(this.opts.provider==="telecel"){const t=e;if(!t.msisdn||!t.sessionid)throw new Error("Invalid Telecel request: missing required fields (msisdn, sessionid)");let s=t.msg;s&&s.endsWith("#")&&(s=s.slice(0,-1)),this.args={network:"TELECEL",msisdn:t.msisdn,user_input:s,user_id:t.sessionid,session_id:t.sessionid,phone_number:t.msisdn.trim().replace("233","0"),is_new_request:t.type===types_1.eTelecelUssdRequestTypes.REQUEST}}else if(this.opts.provider==="airtel-tigo"){const s=e.dataSet.param.find(i=>i.id==="IS_NEW_REQ"),n=(0,lodash_1.get)(s,"value","1");let r=e.userData;r&&r.endsWith("#")&&(r=r.slice(0,-1)),this.args={network:"ARTLTIGO",is_new_request:n==="1",msisdn:e.msisdn,user_id:e.requestId,user_input:r,session_id:e.sessionId,phone_number:e.msisdn.trim().replace("233","0")}}else this.opts.provider==="custom"&&(this.args=e)}make_provider_response(e,t){if(this.opts.provider==="nalo")return{MSG:e,MSGTYPE:t,MSISDN:this.args?.msisdn,USERID:this.args?.user_id};if(this.opts.provider==="cross-switch")return{Message:e,Type:t?"Response":"Release"};if(this.opts.provider==="mtn")return{statusCode:"0000",statusMessage:"Success",transactionId:this.generate_id(),data:{inboundResponse:e,userInputRequired:t,serviceCode:this.opts.service_code,msisdn:this.args?.msisdn,messageType:t?1:2}};if(this.opts.provider==="telecel")return{ussd:{msg:e,msisdn:this.args?.msisdn,sessionid:this.args?.session_id,type:t?types_1.eTelecelUssdRequestTypes.RESPONSE:types_1.eTelecelUssdRequestTypes.RELEASE}};if(this.opts.provider==="airtel-tigo")return{USSDDynMenuResponse:{requestId:this.args?.user_id,sessionId:this.args?.session_id,msisdn:this.args?.msisdn,starCode:this.raw_args?.starCode,langId:"1",encodingScheme:"0",transferCode:"success",dataSet:{param:{id:"1",value:e,rspFlag:t?"1":"2",rspURL:"",appendIndex:"0",default:"1"}},ErrCode:"1",errURL:"",timeStamp:this.format_date_for_timestamp(new Date)}};if(this.opts.provider==="custom")return{message:e,require_feedback:t};throw new Error("Provider not found")}async initialize_session(){if(this.args?.is_new_request)await this.start_session();else{const e=await this.redis?.client.get(this.redis_session_key);(0,helper_1.assert)(e,"Unexpected error occurred. Kindly try again"),this.session=JSON.parse(e)}}async start_session(){const e={menu_metadata:{},custom_metadata:{},last_activity_timestamp:new Date,service_code:this.opts.service_code,network:this.args?.network,msisdn:this.args?.phone_number,session_id:this.args?.session_id,user_inputs:[this.args?.user_input],phone_number:this.args?.phone_number.replace("233","0")},t=await this.redis?.client.set(this.redis_session_key,JSON.stringify(e),{EX:this.session_ttl});(0,helper_1.assert)(t,"Session update failed. Check your redis connection"),this.session=e}generate_id(){return new Array(16).fill("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ").map(n=>n[Math.floor(Math.random()*n.length)]).join("")}make_ussd_response(e,t){return{message:e,require_feedback:t}}generate_invalid_input_response(e=!1){return this.make_ussd_response("Your input is not valid. Kindly try again.",!e)}async update_session(e={}){if(this.session){const t=(0,lodash_1.get)(e,"$set",{});for(const i of Object.keys(t)){const o=t[i];(0,lodash_1.set)(this.session,i,o)}const s=(0,lodash_1.get)(e,"$push",{});for(const i of Object.keys(s)){const o=s[i];if(!Array.isArray((0,lodash_1.get)(this.session,i)))(0,lodash_1.set)(this.session,i,[o]);else{const a=(0,lodash_1.get)(this.session,i),_=(0,lodash_1.concat)(a,o);(0,lodash_1.set)(this.session,i,_)}}const n=(0,lodash_1.get)(e,"$unset",{});for(const i of Object.keys(n))(0,lodash_1.unset)(this.session,i);const r=await this.redis?.client.set(this.redis_session_key,JSON.stringify(this.session),{EX:this.session_ttl});(0,helper_1.assert)(r,"Session creation failed. Check your redis connection")}}async generate_paginated_menu(e){const{title:t,builder:s,options:n,menu_ref:r,user_input:i,separator:o=` `,is_main_menu:a=!1,hide_options:_=!1,back_index_exceeded_handler:u,next_page_key_label:h="Next",back_page_key_label:c="Back",forward_index_exceeded_handler:d,next_page_key:w=this.opts.next_page_key,back_page_key:m=this.opts.back_page_key}=e;let g=+(0,lodash_1.get)(this.session,`menu_pagination.${r}.cursor`,0);const $=(0,lodash_1.get)(this.session,`menu_pagination.${r}.cursors`,[]);if(!a&&g>0&&![w,m].includes(i))return this.generate_invalid_input_response();let b=0;if(i===w)if(g<n.length)b=g;else{if(d){const f=await d(i,s);if(f instanceof Promise)return await f;if(f)return f}throw new Error("Invalid input. You have exceeded page index")}if(i===m){if($.length<2){if(u){await this.update_session({$unset:{"menu_pagination.root":"",[`menu_pagination.${r}`]:""}});const v=u(i,s);if(v instanceof Promise)return await v;if(v)return v}throw new Error("Invalid input. You have exceeded page index")}$.pop();const f=$.pop();(0,helper_1.assert)(f,"Unexpected error occurred. Kindly try again"),b=f.last_cursor,g=f.last_cursor}const E=this.str_includes_non_gsm_char(t||""),S=` ${w}) ${h} ${m}) ${c}`,l=80-Buffer.byteLength(S,"ucs-2"),p=140-this.calculate_gsm_7bits_size(S),y=E?l:p,I=i===w&&g===0&&b===0;let R=!1,x=t||"";if(I&&t){const f=t+(n.length>0?` `+n[0]:"");(E?Buffer.byteLength(f,"ucs-2"):this.calculate_gsm_7bits_size(f))>y&&(x="")}x&&!_&&(x+=` `);const q=n.slice(b);let P=!1;if(!_)for(let f=0;f<q.length;f++){const v=q[f],M=P?x+o+v:x+v,C=this.str_includes_non_gsm_char(M),A=C?l:p;let U=0;if(C?U=Buffer.byteLength(M,"ucs-2"):U=this.calculate_gsm_7bits_size(M),U>A){f+1<=q.length&&(a||(x+="..."),R=!0);break}else P=!0,x=M.trim();b++}return _||(a&&g===0?R&&(x+=` ${w}) ${h}`):(R&&(x+=` ${w}) ${h}`),b>0&&(x+=` ${m}) ${c}`))),$.push({cursor:b,last_cursor:g}),await this.update_session({$set:{[`menu_pagination.${r}.cursor`]:b,[`menu_pagination.${r}.cursors`]:$}}),x}async render_main_menu(e=""){[this.opts.next_page_key,this.opts.back_page_key].includes(e)?await this.update_session({$set:{last_menu_route:"root"}}):await this.update_session({$set:{last_menu_route:"root"},$unset:{menu_metadata:"",menu_pagination:""}});const s=this.root_menu.children.map((r,i)=>`${i+1}) ${r.title}`),n=await this.generate_paginated_menu({options:s,user_input:e,builder:this,menu_ref:"root",is_main_menu:!0,title:this.root_menu.title,forward_index_exceeded_handler:async()=>this.generate_invalid_input_response()});return typeof n=="object"&&"message"in n?n:this.make_ussd_response(n,!0)}on_forward_index_exceeded(){return this.generate_invalid_input_response()}async on_back_index_exceeded(e){const t=(0,lodash_1.get)(this.session,"menu_metadata.parent_menus",[]);if(t.length>0){const s=t.pop();if((0,helper_1.assert)(s,"Unexpected error occurred. Please try again"),await this.update_session({$set:{last_menu_route:s.menu_route,"menu_metadata.parent_menus":t},$unset:{[`menu_pagination.${s.menu_route}`]:""}}),s.menu_route==="root")return await this.render_main_menu("");{const n=this.handlerRegistry.deserialize(s.menu);return await this.render_menu(n,"",s.menu_route)}}else return await this.render_main_menu()}generate_hops(){let e=this.args?.user_input;if(this.opts.require_pin&&this.session_authenticated&&this.from_login&&(e=this.session?.user_inputs[0]),this.session?.menu_metadata?.active_proxy_route&&!this.args?.is_new_request)return[()=>this.process_proxy_continuation(e||"")];if(this.args?.is_new_request&&e&&e!==""){const s=e.replace(this.opts.service_code,"");return!s||s===""?[()=>this.process_menu("")]:s.split("*").filter(r=>r!=="").map(r=>()=>this.process_menu(r))}if(!this.args?.is_new_request&&e){const s=e?.replace(this.opts.service_code,"");return(0,helper_1.assert)(s!==void 0,"Error parsing ussd input"),s.split("*").map(n=>()=>this.process_menu(n))}return[()=>this.process_menu("")]}async pipe(e,t,s,n){const r=a=>(this.redis?.client.disconnect(),t(a));s&&(this.raw_headers=s),n&&(this.raw_body=n);try{this.set_args(e)}catch(a){return t(a)}this.redis=new redis_1.UssdMenuRedis(this.opts.redis_connection_url,this.opts.encryption_key),await this.redis.connect(),this.opts.middleware&&(this.local_session=await this.opts.middleware(this.args)),await this.initialize_session();const i=(0,lodash_1.get)(this.opts,"make_provider_response",!0);if(this.opts.require_pin&&this.opts.authentication_handler&&!this.session_authenticated){const{authenticated:a,message:_,require_feedback:u}=await this.opts.authentication_handler(this.args);if(!u&&!a)return r(i?this.make_provider_response(_,u):this.make_ussd_response(_,u));if(a)await this.update_session({$set:{"menu_metadata.from_login":!0,"menu_metadata.authenticated":!0}});else return r(i?this.make_provider_response(_,u):this.make_ussd_response(_,u))}const o=this.generate_hops();try{const a=[];for(const u of o){const h=await u();if(a.push(h),this.session?.menu_metadata?.active_proxy_route&&o.indexOf(u)<o.length-1)break}const _=a.pop();if(_)return r(i?this.make_provider_response(_.message,_.require_feedback):_);throw new Error("Invalid response from menu")}catch(a){const _=a instanceof Error?a.message:String(a);return r(i?this.make_provider_response(_,!1):this.make_ussd_response(_,!1))}}get_menu_by_route(e){const t=e.split("*").filter(n=>n!=="");if(t.length>1){const n=t.slice(0,-1).join("*"),r=(0,lodash_1.get)(this.session,`menu_metadata.menu_${n}`);if(r?.response){const o=this.handlerRegistry.deserialize(r.response),a=t[t.length-1];if(!a)return;const _=parseInt(a)-1;if(o.children&&o.children[_])return o.children[_]}}let s=this.root_menu;for(const n of t){if(!s)return;const r=parseInt(n)-1;if("children"in s&&s.children&&s.children[r])s=s.children[r];else return}return s}async process_proxy_continuation(e){const t=this.session?.menu_metadata?.active_proxy_route;if(!t)throw new Error("No active proxy session found");const s=this.get_menu_by_route(t);if(!s||!s.proxy_config)throw new Error("Invalid proxy configuration");const n=s.proxy_config,i=await new proxy_handler_1.ProxyHandler(n).forwardRequest(this,e);return await this.update_session({$set:{...i.require_feedback?{}:{"menu_metadata.active_proxy_route":null}}}),i}async process_menu(e){const t=this.session;(0,helper_1.assert)(t,"Unexpected error. Kindly try again");const s=t.last_menu_route;if(s===void 0&&e&&e!==""){const n=+e-1;if(!this.root_menu.children||n<0||n>=this.root_menu.children.length)return await this.render_main_menu();const r=this.root_menu.children[n];if(!r)return await this.render_main_menu();const i=e;return await this.update_session({$set:{last_menu_route:i}}),await this.render_menu(r,e,i)}switch(s){case void 0:return await this.render_main_menu();case"root":if([this.opts.back_page_key,this.opts.next_page_key].includes(e))return await this.render_main_menu(e);const n=+e-1;if(!this.root_menu.children||n<0||n>=this.root_menu.children.length)return this.generate_invalid_input_response();const r=this.root_menu.children[n];if(!r)return this.generate_invalid_input_response();const i=String(n+1);return await this.update_session({$set:{last_menu_route:i}}),await this.render_menu(r,e,i);default:const o=(0,lodash_1.get)(this.session,`menu_metadata.menu_${s}`);if(o){const a=o.response;if(a){const h=this.handlerRegistry.deserialize(a);if([this.opts.next_page_key,this.opts.back_page_key].includes(e)){const c=h.children.map((w,m)=>`${m+1}) ${w.title}`),d=await this.generate_paginated_menu({options:c,user_input:e,builder:this,menu_ref:s,title:h.title,back_index_exceeded_handler:this.on_back_index_exceeded.bind(this),forward_index_exceeded_handler:this.on_forward_index_exceeded.bind(this)});return typeof d=="object"&&"message"in d?d:this.make_ussd_response(d,!0)}else{let c,d;if(h.custom_input&&h.children.length)c=h.children[0],d=1;else{const m=+e-1;c=h.children[m],d=+e}const w=`${s}*${d}`;if(c)return await this.update_session({$set:{last_menu_route:w},$unset:{[`menu_pagination.${s}`]:""},$push:{"menu_metadata.parent_menus":{menu_route:s,menu:o.menu,user_input:o.user_input}}}),await this.render_menu(c,e,w)}return this.generate_invalid_input_response(!0)}const _=this.handlerRegistry.deserialize(o.menu);return await this.render_menu(_,e,s)}return this.generate_invalid_input_response(!0)}}async render_menu(e,t,s){const n=`menu_metadata.menu_${s}.menu`,r=`menu_metadata.menu_${s}.response`,i=`menu_metadata.menu_${s}.user_input`;switch(e.type){case"handler":(0,helper_1.assert)(e.handler,"Menu handler not defined.");let o;const _=(e.handler_type==="path"?await(0,helper_1.import_menu_handler)(e):e).handler(this,t);if(_ instanceof Promise?o=await _:o=_,await this.update_session({$set:{[n]:this.handlerRegistry.serialize(e),[i]:t}}),typeof o=="object"&&"title"in o){await this.update_session({$set:{[r]:this.handlerRegistry.serialize(o)}});const l=o.custom_input||!1,p=o.children.map((I,R)=>`${R+1}) ${I.title}`),y=await this.generate_paginated_menu({options:p,user_input:t,hide_options:l,builder:this,menu_ref:s,title:o.title,back_index_exceeded_handler:this.on_back_index_exceeded.bind(this),forward_index_exceeded_handler:this.on_forward_index_exceeded.bind(this)});return typeof y=="object"&&"message"in y?y:this.make_ussd_response(y,!0)}if(typeof o=="object"&&"message"in o)return o;let u="";typeof o=="string"&&(u=o);const h=u.split(" "),c=await this.generate_paginated_menu({user_input:t,builder:this,title:e.title,menu_ref:s,options:h,back_index_exceeded_handler:this.on_back_index_exceeded.bind(this),forward_index_exceeded_handler:this.on_forward_index_exceeded.bind(this)});return typeof c=="object"&&"message"in c?c:this.make_ussd_response(c,!0);case"proxy":(0,helper_1.assert)(e.proxy_config,"Proxy configuration not defined."),(0,helper_1.assert)(e.proxy_config.target_url||e.proxy_config.proxy_handler,"Either target_url or proxy_handler must be defined in proxy_config.");try{const p=await new proxy_handler_1.ProxyHandler(e.proxy_config).forwardRequest(this,t);return await this.update_session({$set:{[n]:this.handlerRegistry.serialize(e),[i]:t,...p.require_feedback?{"menu_metadata.active_proxy_route":s}:{"menu_metadata.active_proxy_route":null}}}),p}catch(l){const p=l instanceof Error?l.message:String(l);return console.error("Proxy request error:",p),{message:"An error occurred. Please try again later.",require_feedback:!1}}case"render":(0,helper_1.assert)(e.render,"Menu render not defined."),await this.update_session({$set:{[n]:this.handlerRegistry.serialize(e),[i]:t}});let d=e.render.success_message;const w=(0,lodash_1.get)(e,"render.error_message");if(e.handler){let l;try{const y=(e.handler_type==="path"?await(0,helper_1.import_menu_handler)(e):e).handler(this,t);y instanceof Promise?l=await y:l=y}catch(p){const y=p instanceof Error?p.message:String(p);throw new Error(w||y)}if(typeof l=="string"&&(d=l),Array.isArray(l))for(let p=0;p<l.length;p++){const y=l[p];y!==void 0&&(d=d.replace(new RegExp(`%${p+1}`,"ig"),y))}}const m=(0,lodash_1.get)(e.render,"pagination",{}),{next_page_key:g,back_page_key:$,next_page_key_label:b,back_page_key_label:E}={next_page_key_label:"More",back_page_key_label:"Back",next_page_key:this.opts.next_page_key,back_page_key:this.opts.back_page_key,...m},S=await this.paginate_text_content({text:d,user_input:t,builder:this,title:e.title,menu_ref:s,back_page_key:$,next_page_key:g,next_page_key_label:b,back_page_key_label:E,back_index_exceeded_handler:this.on_back_index_exceeded.bind(this),forward_index_exceeded_handler:this.on_forward_index_exceeded.bind(this)});return typeof S=="object"&&"message"in S?S:this.make_ussd_response(S,!0);default:return this.generate_invalid_input_response(!0)}}async paginate_text_content(e){const{text:t,user_input:s,menu_ref:n,title:r,back_page_key:i,next_page_key:o,next_page_key_label:a,back_page_key_label:_,back_index_exceeded_handler:u,forward_index_exceeded_handler:h}=e,c=this.split_message_for_pagination(t);let d=+(0,lodash_1.get)(this.session,`menu_pagination.${n}.page`,0);if(!(![o,i].includes(s)&&d===0)){if(s===o)if(d<c.length-1)d++;else{if(h){const g=await h(s,this);if(g)return g}throw new Error("Invalid input. You have exceeded page index")}else if(s===i)if(d>0)d--;else{if(u){await this.update_session({$unset:{[`menu_pagination.${n}`]:""}});const g=await u(s,this);if(g)return g}throw new Error("Invalid input. You have exceeded page index")}else if(d>0)return this.generate_invalid_input_response()}await this.update_session({$set:{[`menu_pagination.${n}.page`]:d}});let m=r?`${r} `:"";return m+=c[d],d<c.length-1&&(m+=` ${o}) ${a}`),(d>0||c.length===1&&n!=="root")&&(m+=` ${i}) ${_}`),m}split_message_for_pagination(e){const s=e.split(/\. (?=[A-Z])/);if(s.length>1){const o=[];let a="";for(const _ of s){const u=_+(_.endsWith(".")?"":".");if(a.length+u.length+1<=100)a+=(a?" ":"")+u;else if(a&&o.push(a),u.length>100){const h=u.split(" ");a="";for(const c of h)a.length+c.length+1<=100?a+=(a?" ":"")+c:(a&&o.push(a),a=c)}else a=u}return a&&o.push(a),o.length>0?o:[e]}if(e.length<=100)return[e];const n=e.split(" "),r=[];let i="";for(const o of n)i.length+o.length+1<=100?i+=(i?" ":"")+o:(i&&r.push(i),i=o);return i&&r.push(i),r.length>0?r:[e]}format_date_for_timestamp(e){const t=e.getFullYear(),s=String(e.getMonth()+1).padStart(2,"0"),n=String(e.getDate()).padStart(2,"0"),r=String(e.getHours()).padStart(2,"0"),i=String(e.getMinutes()).padStart(2,"0"),o=String(e.getSeconds()).padStart(2,"0");return`${t}/${s}/${n} ${r}:${i}:${o}`}async set_session(e={}){if(this.session){const t=(0,lodash_1.get)(e,"$set",{});for(const r of Object.keys(t)){if(r.split(".").shift()!=="custom_metadata")continue;const i=t[r];(0,lodash_1.set)(this.session,r,i)}const s=(0,lodash_1.get)(e,"$unset",{});for(const r of Object.keys(s))(0,lodash_1.unset)(this.session,r);const n=await this.redis?.client.set(this.redis_session_key,JSON.stringify(this.session),{EX:this.session_ttl});(0,helper_1.assert)(n,"Session creation failed. Check your redis connection")}}}exports.UssdBuilder=UssdBuilder;