@apostlejs/whatsapp 0.0.20 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +791 -1
- package/dist/index.d.cts +489 -2222
- package/{LICENSE.md → license} +1 -1
- package/package.json +17 -27
package/dist/index.cjs
CHANGED
|
@@ -1 +1,791 @@
|
|
|
1
|
-
"use strict";function e(e){return e&&e.__esModule?e:{default:e}}var t=e(require("node:crypto")),a=Object.defineProperty,r=(e,t)=>{for(var r in t)a(e,r,{get:t[r],enumerable:!0})},n={};r(n,{WhatsappErrorCode:()=>d,endpoints:()=>l,flowsEndpoints:()=>s,mediaEndpoints:()=>c,messagesEndpoints:()=>o,wabaEndpoints:()=>i});var s={create:{url:"/{waba_id}/flows",method:"post",headers:{"Content-Type":"application/json"},request:{body:null},response:null},updateMetadata:{url:"/{flow_id}",method:"post",headers:{"Content-Type":"application/json"},request:{body:null},response:null},readMany:{url:"/{waba_id}/flows",method:"get",response:null},delete:{url:"/{flow_id}",method:"delete",response:null},read:{url:"/{flow_id}",method:"get",request:{query:null},response:null},updateJson:{url:"/{flow_id}/assets",method:"post",request:{body:null},response:null},getPreview:{url:"/{flow_id}?fields=preview.invalidate(false)",method:"get",response:null},publish:{url:"/{flow_id}/publish",method:"post",response:null}},o={send:{url:"/{number_id}/messages",method:"post",headers:{"Content-Type":"application/json"},request:{body:null},response:null},showTypingIndicator:{url:"/{number_id}/messages",method:"post",headers:{"Content-Type":"application/json"},request:{body:null},response:null}},i={updateEncryption:{url:"/{number_id}/whatsapp_business_encryption",method:"post",headers:{"Content-Type":"application/x-www-form-urlencoded"},request:{body:null}},registerNumber:{url:"/{number_id}/register",method:"post",headers:{"Content-Type":"application/json"},request:{body:null}}},c={fetch:{url:"/{media_id}",method:"get",request:{query:null},response:null}},l={flows:s,messages:o,waba:i,media:c},d=(e=>(e[e.e0AuthException=0]="e0AuthException",e[e.e3ApiMethod=3]="e3ApiMethod",e[e.e10PermissionDenied=10]="e10PermissionDenied",e[e.e190AccessTokenExpired=190]="e190AccessTokenExpired",e[e.e2xxApiPermission=200]="e2xxApiPermission",e[e.e368TemporarilyBlockedForPolicyViolations=368]="e368TemporarilyBlockedForPolicyViolations",e[e.e130497BusinessAccountRestrictedByCountry=130497]="e130497BusinessAccountRestrictedByCountry",e[e.e131031AccountLocked=131031]="e131031AccountLocked",e[e.e1ApiUnknown=1]="e1ApiUnknown",e[e.e2ApiService=2]="e2ApiService",e[e.e33ParameterValueNotValid=33]="e33ParameterValueNotValid",e[e.e100InvalidParameter=100]="e100InvalidParameter",e[e.e130472UserNumberPartOfExperiment=130472]="e130472UserNumberPartOfExperiment",e[e.e131000SomethingWentWrong=131e3]="e131000SomethingWentWrong",e[e.e131005AccessDenied=131005]="e131005AccessDenied",e[e.e131008RequiredParameterMissing=131008]="e131008RequiredParameterMissing",e[e.e131009ParameterValueNotValid=131009]="e131009ParameterValueNotValid",e[e.e131016ServiceUnavailable=131016]="e131016ServiceUnavailable",e[e.e131021RecipientCannotBeSender=131021]="e131021RecipientCannotBeSender",e[e.e131026MessageUndeliverable=131026]="e131026MessageUndeliverable",e[e.e131030RecipientNotAllowedInList=131030]="e131030RecipientNotAllowedInList",e[e.e131037DisplayNameApprovalNeeded=131037]="e131037DisplayNameApprovalNeeded",e[e.e131042BusinessEligibilityPaymentIssue=131042]="e131042BusinessEligibilityPaymentIssue",e[e.e131045IncorrectCertificate=131045]="e131045IncorrectCertificate",e[e.e131047ReEngagementMessage=131047]="e131047ReEngagementMessage",e[e.e131049MetaChoseNotToDeliver=131049]="e131049MetaChoseNotToDeliver",e[e.e131050UserStoppedMarketingMessages=131050]="e131050UserStoppedMarketingMessages",e[e.e131051UnsupportedMessageType=131051]="e131051UnsupportedMessageType",e[e.e131052MediaDownloadError=131052]="e131052MediaDownloadError",e[e.e131053MediaUploadError=131053]="e131053MediaUploadError",e[e.e131057AccountInMaintenanceMode=131057]="e131057AccountInMaintenanceMode",e[e.e132000TemplateParamCountMismatch=132e3]="e132000TemplateParamCountMismatch",e[e.e132001TemplateDoesNotExist=132001]="e132001TemplateDoesNotExist",e[e.e132005TemplateHydratedTextTooLong=132005]="e132005TemplateHydratedTextTooLong",e[e.e132007TemplateFormatCharacterPolicyViolated=132007]="e132007TemplateFormatCharacterPolicyViolated",e[e.e132012TemplateParameterFormatMismatch=132012]="e132012TemplateParameterFormatMismatch",e[e.e132015TemplateIsPaused=132015]="e132015TemplateIsPaused",e[e.e132016TemplateIsDisabled=132016]="e132016TemplateIsDisabled",e[e.e132068FlowIsBlocked=132068]="e132068FlowIsBlocked",e[e.e132069FlowIsThrottled=132069]="e132069FlowIsThrottled",e[e.e133000IncompleteDeregistration=133e3]="e133000IncompleteDeregistration",e[e.e133004ServerTemporarilyUnavailable=133004]="e133004ServerTemporarilyUnavailable",e[e.e133005TwoStepVerificationPinMismatch=133005]="e133005TwoStepVerificationPinMismatch",e[e.e133006PhoneNumberReVerificationNeeded=133006]="e133006PhoneNumberReVerificationNeeded",e[e.e133008TooManyTwoStepVerificationPinGuesses=133008]="e133008TooManyTwoStepVerificationPinGuesses",e[e.e133009TwoStepVerificationPinGuessedTooFast=133009]="e133009TwoStepVerificationPinGuessedTooFast",e[e.e133010PhoneNumberNotRegistered=133010]="e133010PhoneNumberNotRegistered",e[e.e133015PhoneNumberDeletionPending=133015]="e133015PhoneNumberDeletionPending",e[e.e134011PaymentsTermsNotAccepted=134011]="e134011PaymentsTermsNotAccepted",e[e.e135000GenericUserError=135e3]="e135000GenericUserError",e[e.e2593107SynchronizationRequestLimitExceeded=2593107]="e2593107SynchronizationRequestLimitExceeded",e[e.e2593108SynchronizationRequestOutsideTimeWindow=2593108]="e2593108SynchronizationRequestOutsideTimeWindow",e[e.e4ApiTooManyCalls=4]="e4ApiTooManyCalls",e[e.e80007RateLimitIssues=80007]="e80007RateLimitIssues",e[e.e130429RateLimitHit=130429]="e130429RateLimitHit",e[e.e131048SpamRateLimitHit=131048]="e131048SpamRateLimitHit",e[e.e131056BusinessConsumerPairRateLimitHit=131056]="e131056BusinessConsumerPairRateLimitHit",e[e.e133016AccountRegisterDeregisterRateLimitExceeded=133016]="e133016AccountRegisterDeregisterRateLimitExceeded",e))(d||{}),p={};r(p,{actions:()=>D,createContactUrl:()=>k,flows:()=>A,parsers:()=>L,security:()=>O,toGraphLanguageTag:()=>R,utils:()=>K});var u={};r(u,{create:()=>h,delete:()=>b,get:()=>v,getMany:()=>T,getPreview:()=>E,publish:()=>P,updateJson:()=>w,updateMetadata:()=>g});var m={};r(m,{settings:()=>y});var f={},y={setup:e=>{Object.assign(f,e)},get:e=>{const t=f[e]??process.env[e];if(!t)throw new Error(`Missing environment variable: ${e}`);return t}};async function _(e,t,a){const r=`https://graph.facebook.com/v${y.get("GRAPH_API_VERSION")}`,n={Authorization:`Bearer ${y.get("META_APP_ACCESS_TOKEN")}`};let s=e.url;const o={...n,...e.headers,...t.headers},i=Object.fromEntries(Object.entries(t.query??{}).filter((([,e])=>null!=e))),c=new URLSearchParams(i).toString();"/"===s[0]&&(s=s.slice(1)),s=`${r}/${s}`,c&&(s=`${s}?${c}`);const l={waba_id:y.get("WHATSAPP_ACCOUNT_ID"),number_id:y.get("WHATSAPP_NUMBER_ID"),...t.params};for(const e in l)s=s.replace(`{${e}}`,l[e]);const d=t.body;let p;if(d&&a?.asFormData){const e=new FormData;for(const t in d)e.append(t,d[t]);p=e}else d&&a?.asUrlEncoded?p=new URLSearchParams(d).toString():d&&(p=JSON.stringify(d));return await fetch(s,{method:e.method,headers:o,body:p}).then((e=>e.json())).then((e=>{if("error"in e)throw{response:{data:e.error}};return e})).catch((async t=>{if(!t.response)throw new Error(t.message,{cause:{method:e.method,url:s,body:p}});const a=t.response.data.message,r=t.response.data.error_data?.details||"",n=a.match(/^\(#\d+\)/),o=n?n[0]:"",i=a.replace(/^\(#\d+\)\s*/,""),c=r?r?.startsWith?.(i)?`${o} ${r}`:`${a}: ${r}`:a;t.response.data.message=c;const{message:l,...d}=t.response.data;throw new Error(l,{cause:d})}))}async function h(e){const t=l.flows.create,a=e.flow_json?JSON.stringify(e.flow_json,null,2):void 0;return await _(t,{body:{name:e.name,categories:e.categories,clone_flow_id:e.clone_flow_id,endpoint_uri:e.endpoint_uri,publish:e.publish,flow_json:a}})}async function g(e,t){const a=l.flows.updateMetadata;return await _(a,{body:t,params:{flow_id:e}})}async function w(e,t){const a=l.flows.updateJson,r={name:"flow.json",asset_type:"FLOW_JSON",file:new Blob([JSON.stringify(t,null,2)],{type:"application/json"})};return await _(a,{params:{flow_id:e},body:r},{asFormData:!0}).catch((e=>{const t=e?.error_user_msg??e?.cause?.error_user_msg;if(t?.startsWith?.("Flow JSON has been saved"))return{success:!0,validation_errors:[]};throw e}))}async function E(e,t){const a=l.flows.getPreview,{preview:r}=await _(a,{params:{flow_id:e}}),n=r.preview_url.replaceAll("\\",""),s=Object.entries(t??{}).reduce(((e,[t,a])=>(e[t]="flow_action_payload"===t?encodeURIComponent(JSON.stringify(a)):a.toString(),e)),{});return`${n}&${new URLSearchParams(s).toString()}`}async function b(e){const t=l.flows.delete;return await _(t,{params:{flow_id:e}})}async function v(e,t){const a=l.flows.read;return await _(a,{params:{flow_id:e},query:{fields:t?.fields.join(",")??""}})}async function T(){const e=l.flows.readMany;return await _(e,{})}async function P(e){const t=l.flows.publish;return await _(t,{params:{flow_id:e}})}var S=e=>e.split("?")[0],A={createToken:({chatId:e,flow_name:t,flow_parameters:a,flow_identifier:r})=>{const n=new URLSearchParams({chat_id:e});if(n.set("flow_identifier",r??crypto.randomUUID()),a)for(const[e,t]of Object.entries(a))n.set(e,t.toString());return`${t}?${decodeURIComponent(n.toString())}`},getName:S,destructureFlowToken:e=>{const t=S(e);let a=e.split("?")?.[1];"&"===a?.[0]&&(a=a.slice(1));const r=new URLSearchParams(a),n=r.get("chat_id")||r.get("chatId"),s=r.get("flow_identifier")||r.get("flowIdentifier"),o={};for(const[e,t]of r.entries())"chat_id"!==e&&"chatId"!==e&&"flow_identifier"!==e&&"flowIdentifier"!==e&&(o[e]=t);return{paramsString:a,flowName:t,chatId:n,flowIdentifier:s,flowParameters:o}}},N=(e=>(e.AR_SA="ar-SA",e.BN_BD="bn-BD",e.BN_IN="bn-IN",e.CS_CZ="cs-CZ",e.DA_DK="da-DK",e.DE_AT="de-AT",e.DE_CH="de-CH",e.DE_DE="de-DE",e.EL_GR="el-GR",e.EN_AU="en-AU",e.EN_CA="en-CA",e.EN_GB="en-GB",e.EN_IE="en-IE",e.EN_IN="en-IN",e.EN_NZ="en-NZ",e.EN_US="en-US",e.EN_ZA="en-ZA",e.ES_AR="es-AR",e.ES_CL="es-CL",e.ES_CO="es-CO",e.ES_ES="es-ES",e.ES_MX="es-MX",e.ES_US="es-US",e.FI_FI="fi-FI",e.FR_BE="fr-BE",e.FR_CA="fr-CA",e.FR_CH="fr-CH",e.FR_FR="fr-FR",e.HE_IL="he-IL",e.HI_IN="hi-IN",e.HU_HU="hu-HU",e.ID_ID="id-ID",e.IT_CH="it-CH",e.IT_IT="it-IT",e.JA_JP="ja-JP",e.KO_KR="ko-KR",e.NL_BE="nl-BE",e.NL_NL="nl-NL",e.NO_NO="no-NO",e.PL_PL="pl-PL",e.PT_BR="pt-BR",e.PT_PT="pt-PT",e.RO_RO="ro-RO",e.RU_RU="ru-RU",e.SK_SK="sk-SK",e.SV_SE="sv-SE",e.TA_IN="ta-IN",e.TA_LK="ta-LK",e.TH_TH="th-TH",e.TR_TR="tr-TR",e.ZH_CN="zh-CN",e.ZH_HK="zh-HK",e.ZH_TW="zh-TW",e))(N||{}),I={"ar-SA":"ar","bn-BD":"bn","bn-IN":"bn","cs-CZ":"cs","da-DK":"da","de-AT":"de","de-CH":"de","de-DE":"de","el-GR":"el","en-AU":"en","en-CA":"en","en-GB":"en_GB","en-IE":"en","en-IN":"en","en-NZ":"en","en-ZA":"en","en-US":"en_US","es-AR":"es_AR","es-CL":"es","es-CO":"es","es-ES":"es_ES","es-MX":"es_MX","es-US":"es","fi-FI":"fi","fr-BE":"fr","fr-CA":"fr","fr-CH":"fr","fr-FR":"fr","he-IL":"he","hi-IN":"hi","hu-HU":"hu","id-ID":"id","it-CH":"it","it-IT":"it","ja-JP":"ja","ko-KR":"ko","nl-BE":"nl","nl-NL":"nl","no-NO":"nb","pl-PL":"pl","pt-PT":"pt_PT","pt-BR":"pt_BR","ro-RO":"ro","ru-RU":"ru","sk-SK":"sk","sv-SE":"sv","ta-IN":"ta","ta-LK":"ta","th-TH":"th","tr-TR":"tr","zh-CN":"zh_CN","zh-HK":"zh_HK","zh-TW":"zh_TW"},R=e=>{const t=I[e];return void 0===t?"en":t},C=e=>{const t={to:e.to,messaging_product:"whatsapp",recipient_type:"individual"};return e.reply&&(t.context={message_id:e.reply}),e.showUrlPreviewImage&&(t.preview_url=e.showUrlPreviewImage),t},U={flow:e=>{if("flow"!==e.message.type)throw new Error("Invalid type");const{message:t,...a}=e,{flow:r,...n}=t,s=C(a);r.mode||(r.mode=y.get("WHATSAPP_FLOWS_MODE"));const o=A.createToken({chatId:s.to,flow_name:r.name,flow_parameters:r.parameters,flow_identifier:r.identifier});return{type:"interactive",...s,interactive:{...n,type:"flow",action:{name:"flow",parameters:{flow_name:r.name,flow_token:o,flow_message_version:"3",flow_cta:r.button,flow_action:r.action??"navigate",mode:r.mode,...r.payload&&{flow_action_payload:{...r.payload,screen:r.payload.screen}}}}}}},text:e=>{if("text"!==e.message.type)throw new Error("Invalid type");const{message:{text:t},...a}=e,r={type:"text",text:{body:t},...C(a)};return e.message.previewUrl&&r.text&&(r.text.preview_url=e.message.previewUrl),r},list:e=>{if("list"!==e.message.type)throw new Error("Invalid type");const{message:t,...a}=e,{list:r,type:n,...s}=t;return{...C(a),type:"interactive",interactive:{...s,type:n,action:r}}},button:e=>{if("button"!==e.message.type)throw new Error("Invalid type");const{message:t,...a}=e,{buttons:r,type:n,...s}=t;return{...C(a),type:"interactive",interactive:{...s,type:n,action:{buttons:r.map((e=>({reply:{id:e.id,title:e.text},type:"reply"})))}}}},template:e=>{if("template"!==e.message.type)throw new Error("Invalid type");const{message:{type:t,...a},...r}=e,n={...C(r),type:t,template:{...a,language:{code:R(a.language)}}};return n.template&&(n.template.namespace=process.env.WHATSAPP_MESSAGE_NAMESPACE),n},media:e=>{if("media"!==e.message.type)throw new Error("Invalid type");const{message:{type:t,...a},...r}=e,n=Object.entries(a)[0],[s,{ref:o,...i}]=n,c=i,l=C(r);return Number.isNaN(Number(o))?c.link=o:c.id=o,{type:s,[s]:c,...l}},contact:e=>{if("contact"!==e.message.type)throw new Error("Invalid type");const{message:{contacts:t},...a}=e;return{type:"contacts",contacts:t,...C(a)}}};function B(e){const{encrypted_aes_key:a,encrypted_flow_data:r,initial_vector:n}=e,s=t.default.createPrivateKey({key:y.get("WHATSAPP_ACCOUNT_ENCRYPTION_PRIVATE_KEY"),passphrase:y.get("WHATSAPP_ACCOUNT_ENCRYPTION_PASSPHRASE")}),o=t.default.privateDecrypt({key:s,padding:t.default.constants.RSA_PKCS1_OAEP_PADDING,oaepHash:"sha256"},Buffer.from(a,"base64")),i=Buffer.from(r,"base64"),c=Buffer.from(n,"base64"),l=i.subarray(0,-16),d=i.subarray(-16),p=t.default.createDecipheriv("aes-128-gcm",o,c);p.setAuthTag(d);const u=Buffer.concat([p.update(l),p.final()]).toString("utf-8");return{payload:JSON.parse(u),encryptionMetadata:{aesKeyBuffer:o,initialVectorBuffer:c}}}function M(e){const t=new URL(e.url).searchParams,a=t.get("hub.verify_token"),r=t.get("hub.challenge");return!(!a||!r)&&a===y.get("WHATSAPP_WEBHOOK_KEY")&&r}async function x(e,a){const r=e.headers.get("x-hub-signature-256");if(!r)return!1;const n=r.replace("sha256=",""),s=t.default.createHmac("sha256",y.get("WHATSAPP_WEBHOOK_KEY")).update(a,"utf-8").digest("hex");return!!t.default.timingSafeEqual(Buffer.from(n),Buffer.from(s))}var L={toGraph:{sendMessage:e=>U[e.message.type](e),flowResponse:(e,t)=>{if(!t.data)throw new Error("Missing data in flow response");if(!t.screen)throw new Error("Missing screen in flow response");if("SUCCESS"===t.screen){const a=t.data??{};t.data={extension_message_response:{params:{flow_token:e,...a}}}}return t}},toSDK:{webhook:async function(e){if("GET"===e.method){const t=M(e);if(!t)throw new Error("Invalid health check hub");return{type:"healthCheck",payload:t}}const t=await e.text(),a=JSON.parse(t);if("entry"in a){if(!x(e,t))throw new Error("Invalid application webhook signature");const n=a.entry.flatMap((e=>e.changes)).filter((e=>"messages"===e.field)).flatMap((e=>e.value.messages)).filter(Boolean).map((e=>function(e){const t=e.from,a={id:e.id,type:e.type,timestamp:e.timestamp,metadata:{forwarded:e.context?.forwarded,frequentlyForwarded:e.context?.frequently_forwarded}},r=function(e){if(!(e.audio||e.document||e.video||e.image))return null;const t=e.audio?.id??e.document?.id??e.image?.id??e.video?.id,a=e.audio?.mime_type??e.document?.mime_type??e.image?.mime_type??e.video?.mime_type,r=["audio","document","image","video"].find((t=>e[t])),n=e.image?.sha256??e.document?.sha256??e.video?.sha256;return{id:t,type:r,caption:e.document?.caption??e.image?.caption??e.video?.caption??null,mime_type:a,sha256:n,filename:e.document?.filename??e.video?.filename}}(e),n=function(e){return e.text?.body??e.button?.text??e.interactive?.button_reply?.title??e.interactive?.list_reply?.title??e.document?.caption??null}(e),s=function(e){return e.interactive?{...e.interactive?.button_reply||e.button?{button:{id:e.interactive?.button_reply?.id??null,title:e.button?.text??e.interactive.button_reply?.title,payload:e.button?.payload??null}}:{},...e.interactive.list_reply?{selectedOption:{id:e.interactive.list_reply.id,title:e.interactive.list_reply.title,description:e.interactive.list_reply.description}}:{},...e.interactive.nfm_reply?{flowResponse:JSON.parse(e.interactive.nfm_reply.response_json)}:{}}:null}(e);return{...a,...s&&{interaction:s},...r&&{media:r},text:n,chatId:t}}(e)));return{type:"application",payload:{messageReceived:(r=n,r.length?r:void 0)}}}var r;if("encrypted_flow_data"in a)return{type:"flowExchange",payload:{...B(a),pingResponse:{data:{status:"active"}}}};throw new Error("Unrecognized event")}}},D={messages:{send:async function(e){const t=L.toGraph.sendMessage(e),a=l.messages.send;return await _(a,{body:t})},showTypingIndicator:async function(e){const t=l.messages.showTypingIndicator;return await _(t,{body:{messaging_product:"whatsapp",status:"read",typing_indicator:{type:"text"},message_id:e.messageId}})}},media:{retrieve:async function({id:e}){const t=l.media.fetch;return await _(t,{query:{phone_number_id:y.get("WHATSAPP_NUMBER_ID")},params:{media_id:e}})}},flows:{...u},waba:{registerNumber:async function({dataRegion:e,pin:t}){const a=l.waba.registerNumber,r={messaging_product:"whatsapp",pin:t};return e&&(r.data_localization_region=e),await _(a,{body:r},{asUrlEncoded:!0})},encryption:{upload:async function(e){const t=l.waba.updateEncryption,a={business_public_key:e};return await _(t,{body:a},{asUrlEncoded:!0})}}}};async function H(e){return await fetch(e).then((async e=>{const t=await e.arrayBuffer();return Buffer.from(t)}))}var O={verifyHub:M,verifySignature:x,generateWabaEncryption:async function(){const e=t.default.randomUUID(),{publicKey:a,privateKey:r}=t.default.generateKeyPairSync("rsa",{modulusLength:2048,publicKeyEncoding:{type:"spki",format:"pem"},privateKeyEncoding:{type:"pkcs8",format:"pem",cipher:"aes-256-cbc",passphrase:e}});return{passphrase:e,publicKey:a,privateKey:r}},decryptFlowBody:B,encryptFlowResponse:function(e,a){const r=[];for(const e of Buffer.from(a.initialVectorBuffer).entries())r.push(~e[1]);const n=t.default.createCipheriv("aes-128-gcm",Buffer.from(a.aesKeyBuffer),Buffer.from(r));return Buffer.concat([n.update(JSON.stringify(e),"utf-8"),n.final(),n.getAuthTag()]).toString("base64")},decryptFlowMedia:async function(e,a="https://picsum.photos/seed/picsum/200"){let{cdn_url:r,encryption_metadata:n}=e;if("EXAMPLE_DATA__CDN_URL_WILL_COME_IN_THIS_FIELD"===r)return r=a,await H(r);const s=await H(r),{iv:o,encryption_key:i,hmac_key:c,encrypted_hash:l,plaintext_hash:d}=n,p={iv:Buffer.from(o,"base64"),encryption_key:Buffer.from(i,"base64"),hmac_key:Buffer.from(c,"base64")};if(t.default.createHash("sha256").update(s).digest("base64")!==l)throw new Error("Encrypted hash validation failed");const u=s.subarray(0,s.length-10),m=s.subarray(-10);if(!t.default.createHmac("sha256",p.hmac_key).update(Buffer.concat([p.iv,u])).digest().subarray(0,10).equals(m))throw new Error("HMAC validation failed");const f=t.default.createDecipheriv("aes-256-cbc",p.encryption_key,p.iv),y=f.update(u),_=Buffer.concat([y,f.final()]);if(t.default.createHash("sha256").update(_).digest("base64")!==d)throw new Error("Decrypted media hash validation failed");return _}},k=(e,t)=>{const a=new URL("/send","https://api.whatsapp.com/"),r=e.replace(/[()\-+ ]/g,"");return a.searchParams.append("phone",r),t&&a.searchParams.append("text",t),a.href},K={flows:A,createContactUrl:k},F=null;exports.BCP47LanguageTag=N,exports.WhatsappErrorCode=d,exports.actions=D,exports.createContactUrl=k,exports.createWhatsapp=()=>F||(F=(()=>{const{settings:e}=m,{actions:t,flows:a,parsers:r,security:s,utils:o,toGraphLanguageTag:i}=p,{endpoints:c}=n;return{settings:e,sdk:{actions:t,flows:a,parsers:r,security:s,utils:o,toGraphLanguageTag:i},graph:{endpoints:c}}})()),exports.endpoints=l,exports.flows=A,exports.flowsEndpoints=s,exports.mediaEndpoints=c,exports.messagesEndpoints=o,exports.parsers=L,exports.security=O,exports.toGraphLanguageTag=R,exports.utils=K,exports.wabaEndpoints=i;
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __exportAll = (all, no_symbols) => {
|
|
10
|
+
let target = {};
|
|
11
|
+
for (var name in all) __defProp(target, name, {
|
|
12
|
+
get: all[name],
|
|
13
|
+
enumerable: true
|
|
14
|
+
});
|
|
15
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
16
|
+
return target;
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
20
|
+
key = keys[i];
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
22
|
+
get: ((k) => from[k]).bind(null, key),
|
|
23
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
return to;
|
|
27
|
+
};
|
|
28
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
29
|
+
value: mod,
|
|
30
|
+
enumerable: true
|
|
31
|
+
}) : target, mod));
|
|
32
|
+
//#endregion
|
|
33
|
+
let node_crypto = require("node:crypto");
|
|
34
|
+
node_crypto = __toESM(node_crypto);
|
|
35
|
+
let _apostlejs_whatsapp_api = require("@apostlejs/whatsapp-api");
|
|
36
|
+
//#region src/models/chat/value-objects/chat-url.v-object.ts
|
|
37
|
+
var ChatURL = class {
|
|
38
|
+
constructor(phoneNumber, prefilled) {
|
|
39
|
+
this.phoneNumber = phoneNumber;
|
|
40
|
+
this.prefilled = prefilled;
|
|
41
|
+
}
|
|
42
|
+
toString() {
|
|
43
|
+
const url = new URL("/send", "https://api.whatsapp.com/");
|
|
44
|
+
const normalizedPhone = this.phoneNumber.replace(/[()\-+ ]/g, "");
|
|
45
|
+
url.searchParams.append("phone", normalizedPhone);
|
|
46
|
+
if (this.prefilled) url.searchParams.append("text", this.prefilled);
|
|
47
|
+
return url.href;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/models/flow/models/flow-token.model.ts
|
|
52
|
+
var FlowToken = class FlowToken {
|
|
53
|
+
chatId;
|
|
54
|
+
flowName;
|
|
55
|
+
flowParameters;
|
|
56
|
+
flowIdentifier;
|
|
57
|
+
constructor(args) {
|
|
58
|
+
this.chatId = args.chatId;
|
|
59
|
+
this.flowName = args.flowName;
|
|
60
|
+
this.flowParameters = args.flowParameters ?? {};
|
|
61
|
+
this.flowIdentifier = args.flowIdentifier ?? crypto.randomUUID();
|
|
62
|
+
}
|
|
63
|
+
static fromString(token) {
|
|
64
|
+
const flowName = token.split("?")[0];
|
|
65
|
+
let paramsString = token.split("?")?.[1];
|
|
66
|
+
if (paramsString?.[0] === "&") paramsString = paramsString.slice(1);
|
|
67
|
+
const params = new URLSearchParams(paramsString);
|
|
68
|
+
const chatId = params.get("chatId");
|
|
69
|
+
const flowIdentifier = params.get("flowIdentifier");
|
|
70
|
+
const flowParameters = {};
|
|
71
|
+
for (const [key, value] of params.entries()) if (key !== "chatId" && key !== "flowIdentifier") flowParameters[key] = value;
|
|
72
|
+
return new FlowToken({
|
|
73
|
+
flowName,
|
|
74
|
+
chatId,
|
|
75
|
+
flowIdentifier,
|
|
76
|
+
flowParameters
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
toString() {
|
|
80
|
+
const params = new URLSearchParams({ chatId: this.chatId });
|
|
81
|
+
params.set("flowIdentifier", this.flowIdentifier);
|
|
82
|
+
for (const [key, value] of Object.entries(this.flowParameters)) params.set(key, value.toString());
|
|
83
|
+
return `${this.flowName}?${decodeURIComponent(params.toString())}`;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/models/i18n/enums/language-tag.model.ts
|
|
88
|
+
let WhatsappLanguageTag = /* @__PURE__ */ function(WhatsappLanguageTag) {
|
|
89
|
+
WhatsappLanguageTag["AR_SA"] = "ar-SA";
|
|
90
|
+
WhatsappLanguageTag["BN_BD"] = "bn-BD";
|
|
91
|
+
WhatsappLanguageTag["BN_IN"] = "bn-IN";
|
|
92
|
+
WhatsappLanguageTag["CS_CZ"] = "cs-CZ";
|
|
93
|
+
WhatsappLanguageTag["DA_DK"] = "da-DK";
|
|
94
|
+
WhatsappLanguageTag["DE_AT"] = "de-AT";
|
|
95
|
+
WhatsappLanguageTag["DE_CH"] = "de-CH";
|
|
96
|
+
WhatsappLanguageTag["DE_DE"] = "de-DE";
|
|
97
|
+
WhatsappLanguageTag["EL_GR"] = "el-GR";
|
|
98
|
+
WhatsappLanguageTag["EN_AU"] = "en-AU";
|
|
99
|
+
WhatsappLanguageTag["EN_CA"] = "en-CA";
|
|
100
|
+
WhatsappLanguageTag["EN_GB"] = "en-GB";
|
|
101
|
+
WhatsappLanguageTag["EN_IE"] = "en-IE";
|
|
102
|
+
WhatsappLanguageTag["EN_IN"] = "en-IN";
|
|
103
|
+
WhatsappLanguageTag["EN_NZ"] = "en-NZ";
|
|
104
|
+
WhatsappLanguageTag["EN_US"] = "en-US";
|
|
105
|
+
WhatsappLanguageTag["EN_ZA"] = "en-ZA";
|
|
106
|
+
WhatsappLanguageTag["ES_AR"] = "es-AR";
|
|
107
|
+
WhatsappLanguageTag["ES_CL"] = "es-CL";
|
|
108
|
+
WhatsappLanguageTag["ES_CO"] = "es-CO";
|
|
109
|
+
WhatsappLanguageTag["ES_ES"] = "es-ES";
|
|
110
|
+
WhatsappLanguageTag["ES_MX"] = "es-MX";
|
|
111
|
+
WhatsappLanguageTag["ES_US"] = "es-US";
|
|
112
|
+
WhatsappLanguageTag["FI_FI"] = "fi-FI";
|
|
113
|
+
WhatsappLanguageTag["FR_BE"] = "fr-BE";
|
|
114
|
+
WhatsappLanguageTag["FR_CA"] = "fr-CA";
|
|
115
|
+
WhatsappLanguageTag["FR_CH"] = "fr-CH";
|
|
116
|
+
WhatsappLanguageTag["FR_FR"] = "fr-FR";
|
|
117
|
+
WhatsappLanguageTag["HE_IL"] = "he-IL";
|
|
118
|
+
WhatsappLanguageTag["HI_IN"] = "hi-IN";
|
|
119
|
+
WhatsappLanguageTag["HU_HU"] = "hu-HU";
|
|
120
|
+
WhatsappLanguageTag["ID_ID"] = "id-ID";
|
|
121
|
+
WhatsappLanguageTag["IT_CH"] = "it-CH";
|
|
122
|
+
WhatsappLanguageTag["IT_IT"] = "it-IT";
|
|
123
|
+
WhatsappLanguageTag["JA_JP"] = "ja-JP";
|
|
124
|
+
WhatsappLanguageTag["KO_KR"] = "ko-KR";
|
|
125
|
+
WhatsappLanguageTag["NL_BE"] = "nl-BE";
|
|
126
|
+
WhatsappLanguageTag["NL_NL"] = "nl-NL";
|
|
127
|
+
WhatsappLanguageTag["NO_NO"] = "no-NO";
|
|
128
|
+
WhatsappLanguageTag["PL_PL"] = "pl-PL";
|
|
129
|
+
WhatsappLanguageTag["PT_BR"] = "pt-BR";
|
|
130
|
+
WhatsappLanguageTag["PT_PT"] = "pt-PT";
|
|
131
|
+
WhatsappLanguageTag["RO_RO"] = "ro-RO";
|
|
132
|
+
WhatsappLanguageTag["RU_RU"] = "ru-RU";
|
|
133
|
+
WhatsappLanguageTag["SK_SK"] = "sk-SK";
|
|
134
|
+
WhatsappLanguageTag["SV_SE"] = "sv-SE";
|
|
135
|
+
WhatsappLanguageTag["TA_IN"] = "ta-IN";
|
|
136
|
+
WhatsappLanguageTag["TA_LK"] = "ta-LK";
|
|
137
|
+
WhatsappLanguageTag["TH_TH"] = "th-TH";
|
|
138
|
+
WhatsappLanguageTag["TR_TR"] = "tr-TR";
|
|
139
|
+
WhatsappLanguageTag["ZH_CN"] = "zh-CN";
|
|
140
|
+
WhatsappLanguageTag["ZH_HK"] = "zh-HK";
|
|
141
|
+
WhatsappLanguageTag["ZH_TW"] = "zh-TW";
|
|
142
|
+
return WhatsappLanguageTag;
|
|
143
|
+
}({});
|
|
144
|
+
//#endregion
|
|
145
|
+
//#region src/models/waba/models/waba-encryption.model.ts
|
|
146
|
+
function normalizePem(key) {
|
|
147
|
+
const normalized = key.replace(/\\n/g, "\n");
|
|
148
|
+
return normalized.endsWith("\n") ? normalized : `${normalized}\n`;
|
|
149
|
+
}
|
|
150
|
+
var WabaEncryption = class WabaEncryption {
|
|
151
|
+
passphrase;
|
|
152
|
+
privateKey;
|
|
153
|
+
publicKey;
|
|
154
|
+
constructor(args) {
|
|
155
|
+
this.passphrase = args.passphrase;
|
|
156
|
+
this.privateKey = normalizePem(args.privateKey);
|
|
157
|
+
if (args.publicKey) this.publicKey = normalizePem(args.publicKey);
|
|
158
|
+
}
|
|
159
|
+
static generate() {
|
|
160
|
+
const passphrase = node_crypto.default.randomUUID();
|
|
161
|
+
const { publicKey, privateKey } = node_crypto.default.generateKeyPairSync("rsa", {
|
|
162
|
+
modulusLength: 2048,
|
|
163
|
+
publicKeyEncoding: {
|
|
164
|
+
type: "spki",
|
|
165
|
+
format: "pem"
|
|
166
|
+
},
|
|
167
|
+
privateKeyEncoding: {
|
|
168
|
+
type: "pkcs8",
|
|
169
|
+
format: "pem",
|
|
170
|
+
cipher: "aes-256-cbc",
|
|
171
|
+
passphrase
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
return new WabaEncryption({
|
|
175
|
+
passphrase,
|
|
176
|
+
publicKey,
|
|
177
|
+
privateKey
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region src/context/context.ts
|
|
183
|
+
let ctx;
|
|
184
|
+
function setContext(context) {
|
|
185
|
+
ctx = context;
|
|
186
|
+
}
|
|
187
|
+
function getContext() {
|
|
188
|
+
if (!ctx) throw new Error("Whatsapp context is not initialized. Call createWhatsapp() first.");
|
|
189
|
+
return ctx;
|
|
190
|
+
}
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region src/features/flows/utils/sanitize-flow.ts
|
|
193
|
+
const removeStringTypeFromUpdateData = (flow) => flow.replace(/^\s*"type":\s*"string",?\n(?=\s*"const":\s*"update_data")/gm, "");
|
|
194
|
+
const lowercaseForbiddenKeys = (flow) => flow.replaceAll(/"(Then|Else|Cases)":/g, (_, key) => `"${key.toLowerCase()}":`);
|
|
195
|
+
const sanitizeFlow = (json) => {
|
|
196
|
+
let flow = json;
|
|
197
|
+
flow = removeStringTypeFromUpdateData(flow);
|
|
198
|
+
flow = lowercaseForbiddenKeys(flow);
|
|
199
|
+
return flow;
|
|
200
|
+
};
|
|
201
|
+
//#endregion
|
|
202
|
+
//#region src/features/flows/actions/create.ts
|
|
203
|
+
async function create(body) {
|
|
204
|
+
const { config, client } = getContext();
|
|
205
|
+
if (!config.whatsappAccountId) throw new Error("WABA ID is required to create a flow");
|
|
206
|
+
const flow_json = body.flow_json ? sanitizeFlow(JSON.stringify(body.flow_json, null, 2)) : void 0;
|
|
207
|
+
return await client.flows.createFlow(config.whatsappAccountId, {
|
|
208
|
+
name: body.name,
|
|
209
|
+
categories: body.categories,
|
|
210
|
+
clone_flow_id: body.clone_flow_id,
|
|
211
|
+
endpoint_uri: body.endpoint_uri,
|
|
212
|
+
publish: body.publish,
|
|
213
|
+
flow_json
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
//#endregion
|
|
217
|
+
//#region src/features/flows/actions/delete.ts
|
|
218
|
+
async function deleteFlow(flow_id) {
|
|
219
|
+
const { client } = getContext();
|
|
220
|
+
return await client.flows.deleteFlow(flow_id);
|
|
221
|
+
}
|
|
222
|
+
//#endregion
|
|
223
|
+
//#region src/features/flows/actions/get.ts
|
|
224
|
+
async function get(flow_id, query) {
|
|
225
|
+
const { client } = getContext();
|
|
226
|
+
return await client.flows.getFlow(flow_id, { fields: query?.fields.join(",") });
|
|
227
|
+
}
|
|
228
|
+
//#endregion
|
|
229
|
+
//#region src/features/flows/actions/get-many.ts
|
|
230
|
+
async function getMany() {
|
|
231
|
+
const { config, client } = getContext();
|
|
232
|
+
if (!config.whatsappAccountId) throw new Error("WABA ID is required to list flows");
|
|
233
|
+
return await client.flows.listFlows(config.whatsappAccountId);
|
|
234
|
+
}
|
|
235
|
+
//#endregion
|
|
236
|
+
//#region src/features/flows/actions/get-preview.ts
|
|
237
|
+
async function getPreview(flow_id, query) {
|
|
238
|
+
const { client } = getContext();
|
|
239
|
+
const previewUrl = (await client.flows.getFlowPreview(flow_id, { fields: "preview.invalidate(false)" })).preview.preview_url.replace(/\\/g, "");
|
|
240
|
+
if (!query) return previewUrl;
|
|
241
|
+
const urlParams = {};
|
|
242
|
+
urlParams.flow_token = new FlowToken({
|
|
243
|
+
chatId: query.recipientId,
|
|
244
|
+
flowName: query.name,
|
|
245
|
+
flowParameters: query.parameters
|
|
246
|
+
}).toString();
|
|
247
|
+
urlParams.flow_action = query.action;
|
|
248
|
+
if (query.payload) urlParams.flow_action_payload = encodeURIComponent(JSON.stringify(query.payload));
|
|
249
|
+
urlParams.phone_number = query.phoneNumber;
|
|
250
|
+
if (query.interactive !== void 0) urlParams.interactive = String(query.interactive);
|
|
251
|
+
return `${previewUrl}&${new URLSearchParams(urlParams).toString()}`;
|
|
252
|
+
}
|
|
253
|
+
//#endregion
|
|
254
|
+
//#region src/features/flows/actions/publish.ts
|
|
255
|
+
async function publish(flow_id) {
|
|
256
|
+
const { client } = getContext();
|
|
257
|
+
return await client.flows.publishFlow(flow_id);
|
|
258
|
+
}
|
|
259
|
+
//#endregion
|
|
260
|
+
//#region src/features/flows/actions/update-json.ts
|
|
261
|
+
async function updateJson(flow_id, json) {
|
|
262
|
+
const { config } = getContext();
|
|
263
|
+
const url = `${`https://graph.facebook.com/v${config.metaGraphApiVersion ?? "25.0"}`}/${flow_id}/assets`;
|
|
264
|
+
const blob = new Blob([sanitizeFlow(JSON.stringify(json, null, 2))], { type: "application/json" });
|
|
265
|
+
const form = new FormData();
|
|
266
|
+
form.append("name", "flow.json");
|
|
267
|
+
form.append("asset_type", "FLOW_JSON");
|
|
268
|
+
form.append("file", blob, "flow.json");
|
|
269
|
+
const response = await fetch(url, {
|
|
270
|
+
method: "POST",
|
|
271
|
+
headers: { Authorization: `Bearer ${config.metaAppAccessToken}` },
|
|
272
|
+
body: form
|
|
273
|
+
});
|
|
274
|
+
if (!response.ok) {
|
|
275
|
+
const errorBody = await response.text();
|
|
276
|
+
throw new Error(`updateFlowJson failed (${response.status}): ${errorBody}`);
|
|
277
|
+
}
|
|
278
|
+
return response.json();
|
|
279
|
+
}
|
|
280
|
+
//#endregion
|
|
281
|
+
//#region src/features/flows/actions/update-metadata.ts
|
|
282
|
+
async function updateMetadata(flow_id, metadata) {
|
|
283
|
+
const { client } = getContext();
|
|
284
|
+
return await client.flows.updateFlowMetadata(flow_id, {
|
|
285
|
+
application_id: metadata.applicationId,
|
|
286
|
+
name: metadata.name,
|
|
287
|
+
categories: metadata.categories,
|
|
288
|
+
endpoint_uri: metadata.endpointUri
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
//#endregion
|
|
292
|
+
//#region src/features/flows/crypto/decrypt-flow-body.ts
|
|
293
|
+
const decryptFlowBody = (encryptedBody) => {
|
|
294
|
+
const { encryption } = getContext();
|
|
295
|
+
if (!encryption) throw new Error("WABA encryption private key and passphrase are required to decrypt flow body");
|
|
296
|
+
const { encrypted_aes_key, encrypted_flow_data, initial_vector } = encryptedBody;
|
|
297
|
+
const privateKey = node_crypto.default.createPrivateKey({
|
|
298
|
+
key: encryption.privateKey,
|
|
299
|
+
passphrase: encryption.passphrase
|
|
300
|
+
});
|
|
301
|
+
const decryptedAesKey = node_crypto.default.privateDecrypt({
|
|
302
|
+
key: privateKey,
|
|
303
|
+
padding: node_crypto.default.constants.RSA_PKCS1_OAEP_PADDING,
|
|
304
|
+
oaepHash: "sha256"
|
|
305
|
+
}, Buffer.from(encrypted_aes_key, "base64"));
|
|
306
|
+
const flowDataBuffer = Buffer.from(encrypted_flow_data, "base64");
|
|
307
|
+
const initialVectorBuffer = Buffer.from(initial_vector, "base64");
|
|
308
|
+
const TAG_LENGTH = 16;
|
|
309
|
+
const encrypted_flow_data_body = flowDataBuffer.subarray(0, -TAG_LENGTH);
|
|
310
|
+
const encrypted_flow_data_tag = flowDataBuffer.subarray(-TAG_LENGTH);
|
|
311
|
+
const decipher = node_crypto.default.createDecipheriv("aes-128-gcm", decryptedAesKey, initialVectorBuffer);
|
|
312
|
+
decipher.setAuthTag(encrypted_flow_data_tag);
|
|
313
|
+
const decryptedJSONString = Buffer.concat([decipher.update(encrypted_flow_data_body), decipher.final()]).toString("utf-8");
|
|
314
|
+
return {
|
|
315
|
+
body: JSON.parse(decryptedJSONString),
|
|
316
|
+
encryptionMetadata: {
|
|
317
|
+
initialVectorBuffer,
|
|
318
|
+
aesKeyBuffer: decryptedAesKey
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
};
|
|
322
|
+
//#endregion
|
|
323
|
+
//#region src/features/flows/crypto/decrypt-flow-media.ts
|
|
324
|
+
async function fetchMedia(url) {
|
|
325
|
+
return await fetch(url).then(async (response) => {
|
|
326
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
327
|
+
return Buffer.from(arrayBuffer);
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
const decryptFlowMedia = async (media, defaultUrl = "https://picsum.photos/seed/picsum/200") => {
|
|
331
|
+
let { cdn_url, encryption_metadata } = media;
|
|
332
|
+
if (cdn_url === "EXAMPLE_DATA__CDN_URL_WILL_COME_IN_THIS_FIELD") {
|
|
333
|
+
cdn_url = defaultUrl;
|
|
334
|
+
return await fetchMedia(cdn_url);
|
|
335
|
+
}
|
|
336
|
+
const fileBuffer = await fetchMedia(cdn_url);
|
|
337
|
+
const { iv, encryption_key, hmac_key, encrypted_hash, plaintext_hash } = encryption_metadata;
|
|
338
|
+
const base64Metadata = {
|
|
339
|
+
iv: Buffer.from(iv, "base64"),
|
|
340
|
+
encryption_key: Buffer.from(encryption_key, "base64"),
|
|
341
|
+
hmac_key: Buffer.from(hmac_key, "base64")
|
|
342
|
+
};
|
|
343
|
+
if (node_crypto.default.createHash("sha256").update(fileBuffer).digest("base64") !== encrypted_hash) throw new Error("Encrypted hash validation failed");
|
|
344
|
+
const ciphertext = fileBuffer.subarray(0, fileBuffer.length - 10);
|
|
345
|
+
const hmac10 = fileBuffer.subarray(-10);
|
|
346
|
+
if (!node_crypto.default.createHmac("sha256", base64Metadata.hmac_key).update(Buffer.concat([base64Metadata.iv, ciphertext])).digest().subarray(0, 10).equals(hmac10)) throw new Error("HMAC validation failed");
|
|
347
|
+
const decipher = node_crypto.default.createDecipheriv("aes-256-cbc", base64Metadata.encryption_key, base64Metadata.iv);
|
|
348
|
+
const decrypted = decipher.update(ciphertext);
|
|
349
|
+
const decryptedMedia = Buffer.concat([decrypted, decipher.final()]);
|
|
350
|
+
if (node_crypto.default.createHash("sha256").update(decryptedMedia).digest("base64") !== plaintext_hash) throw new Error("Decrypted media hash validation failed");
|
|
351
|
+
return decryptedMedia;
|
|
352
|
+
};
|
|
353
|
+
//#endregion
|
|
354
|
+
//#region src/features/flows/crypto/encrypt-flow-response.ts
|
|
355
|
+
const encryptFlowResponse = (response, encryptionMetadata) => {
|
|
356
|
+
const flipped_iv = [];
|
|
357
|
+
for (const pair of Buffer.from(encryptionMetadata.initialVectorBuffer).entries()) flipped_iv.push(~pair[1]);
|
|
358
|
+
const cipher = node_crypto.default.createCipheriv("aes-128-gcm", Buffer.from(encryptionMetadata.aesKeyBuffer), Buffer.from(flipped_iv));
|
|
359
|
+
return Buffer.concat([
|
|
360
|
+
cipher.update(JSON.stringify(response), "utf-8"),
|
|
361
|
+
cipher.final(),
|
|
362
|
+
cipher.getAuthTag()
|
|
363
|
+
]).toString("base64");
|
|
364
|
+
};
|
|
365
|
+
//#endregion
|
|
366
|
+
//#region src/features/flows/adapters/flow-response.adapter.ts
|
|
367
|
+
const flowResponseAdapter = { toGraph(flow_token, response) {
|
|
368
|
+
if (!response.data) throw new Error("Missing data in flow response");
|
|
369
|
+
if (!response.screen) throw new Error("Missing screen in flow response");
|
|
370
|
+
if (response.screen === "SUCCESS") response.data = { extension_message_response: { params: {
|
|
371
|
+
flow_token,
|
|
372
|
+
...response.data ?? {}
|
|
373
|
+
} } };
|
|
374
|
+
return response;
|
|
375
|
+
} };
|
|
376
|
+
//#endregion
|
|
377
|
+
//#region src/features/flows/index.ts
|
|
378
|
+
var flows_exports = /* @__PURE__ */ __exportAll({
|
|
379
|
+
create: () => create,
|
|
380
|
+
decryptFlowBody: () => decryptFlowBody,
|
|
381
|
+
decryptFlowMedia: () => decryptFlowMedia,
|
|
382
|
+
deleteFlow: () => deleteFlow,
|
|
383
|
+
encryptFlowResponse: () => encryptFlowResponse,
|
|
384
|
+
flowResponseAdapter: () => flowResponseAdapter,
|
|
385
|
+
get: () => get,
|
|
386
|
+
getMany: () => getMany,
|
|
387
|
+
getPreview: () => getPreview,
|
|
388
|
+
publish: () => publish,
|
|
389
|
+
updateJson: () => updateJson,
|
|
390
|
+
updateMetadata: () => updateMetadata
|
|
391
|
+
});
|
|
392
|
+
//#endregion
|
|
393
|
+
//#region src/features/i18n/adapters/language-tag.adapter.ts
|
|
394
|
+
const languageTagMapping = {
|
|
395
|
+
[WhatsappLanguageTag.AR_SA]: "ar",
|
|
396
|
+
[WhatsappLanguageTag.BN_BD]: "bn",
|
|
397
|
+
[WhatsappLanguageTag.BN_IN]: "bn",
|
|
398
|
+
[WhatsappLanguageTag.CS_CZ]: "cs",
|
|
399
|
+
[WhatsappLanguageTag.DA_DK]: "da",
|
|
400
|
+
[WhatsappLanguageTag.DE_AT]: "de",
|
|
401
|
+
[WhatsappLanguageTag.DE_CH]: "de",
|
|
402
|
+
[WhatsappLanguageTag.DE_DE]: "de",
|
|
403
|
+
[WhatsappLanguageTag.EL_GR]: "el",
|
|
404
|
+
[WhatsappLanguageTag.EN_AU]: "en",
|
|
405
|
+
[WhatsappLanguageTag.EN_CA]: "en",
|
|
406
|
+
[WhatsappLanguageTag.EN_GB]: "en_GB",
|
|
407
|
+
[WhatsappLanguageTag.EN_IE]: "en",
|
|
408
|
+
[WhatsappLanguageTag.EN_IN]: "en",
|
|
409
|
+
[WhatsappLanguageTag.EN_NZ]: "en",
|
|
410
|
+
[WhatsappLanguageTag.EN_ZA]: "en",
|
|
411
|
+
[WhatsappLanguageTag.EN_US]: "en_US",
|
|
412
|
+
[WhatsappLanguageTag.ES_AR]: "es_AR",
|
|
413
|
+
[WhatsappLanguageTag.ES_CL]: "es",
|
|
414
|
+
[WhatsappLanguageTag.ES_CO]: "es",
|
|
415
|
+
[WhatsappLanguageTag.ES_ES]: "es_ES",
|
|
416
|
+
[WhatsappLanguageTag.ES_MX]: "es_MX",
|
|
417
|
+
[WhatsappLanguageTag.ES_US]: "es",
|
|
418
|
+
[WhatsappLanguageTag.FI_FI]: "fi",
|
|
419
|
+
[WhatsappLanguageTag.FR_BE]: "fr",
|
|
420
|
+
[WhatsappLanguageTag.FR_CA]: "fr",
|
|
421
|
+
[WhatsappLanguageTag.FR_CH]: "fr",
|
|
422
|
+
[WhatsappLanguageTag.FR_FR]: "fr",
|
|
423
|
+
[WhatsappLanguageTag.HE_IL]: "he",
|
|
424
|
+
[WhatsappLanguageTag.HI_IN]: "hi",
|
|
425
|
+
[WhatsappLanguageTag.HU_HU]: "hu",
|
|
426
|
+
[WhatsappLanguageTag.ID_ID]: "id",
|
|
427
|
+
[WhatsappLanguageTag.IT_CH]: "it",
|
|
428
|
+
[WhatsappLanguageTag.IT_IT]: "it",
|
|
429
|
+
[WhatsappLanguageTag.JA_JP]: "ja",
|
|
430
|
+
[WhatsappLanguageTag.KO_KR]: "ko",
|
|
431
|
+
[WhatsappLanguageTag.NL_BE]: "nl",
|
|
432
|
+
[WhatsappLanguageTag.NL_NL]: "nl",
|
|
433
|
+
[WhatsappLanguageTag.NO_NO]: "nb",
|
|
434
|
+
[WhatsappLanguageTag.PL_PL]: "pl",
|
|
435
|
+
[WhatsappLanguageTag.PT_PT]: "pt_PT",
|
|
436
|
+
[WhatsappLanguageTag.PT_BR]: "pt_BR",
|
|
437
|
+
[WhatsappLanguageTag.RO_RO]: "ro",
|
|
438
|
+
[WhatsappLanguageTag.RU_RU]: "ru",
|
|
439
|
+
[WhatsappLanguageTag.SK_SK]: "sk",
|
|
440
|
+
[WhatsappLanguageTag.SV_SE]: "sv",
|
|
441
|
+
[WhatsappLanguageTag.TA_IN]: "ta",
|
|
442
|
+
[WhatsappLanguageTag.TA_LK]: "ta",
|
|
443
|
+
[WhatsappLanguageTag.TH_TH]: "th",
|
|
444
|
+
[WhatsappLanguageTag.TR_TR]: "tr",
|
|
445
|
+
[WhatsappLanguageTag.ZH_CN]: "zh_CN",
|
|
446
|
+
[WhatsappLanguageTag.ZH_HK]: "zh_HK",
|
|
447
|
+
[WhatsappLanguageTag.ZH_TW]: "zh_TW"
|
|
448
|
+
};
|
|
449
|
+
const languageTagAdapter = { toGraph(languageTag) {
|
|
450
|
+
const waTag = languageTagMapping[languageTag];
|
|
451
|
+
if (waTag === void 0) return "en";
|
|
452
|
+
return waTag;
|
|
453
|
+
} };
|
|
454
|
+
//#endregion
|
|
455
|
+
//#region src/features/i18n/index.ts
|
|
456
|
+
var i18n_exports = /* @__PURE__ */ __exportAll({ languageTagAdapter: () => languageTagAdapter });
|
|
457
|
+
//#endregion
|
|
458
|
+
//#region src/features/messages/adapters/message.adapter.ts
|
|
459
|
+
const messageAdapter = { toGraph(message, chatId) {
|
|
460
|
+
const { config: { whatsappMessageNamespace } } = getContext();
|
|
461
|
+
switch (message.type) {
|
|
462
|
+
case "text": return {
|
|
463
|
+
type: "text",
|
|
464
|
+
text: {
|
|
465
|
+
body: message.text,
|
|
466
|
+
preview_url: message.previewUrl
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
case "flow": {
|
|
470
|
+
const { flow, type, ...interactiveBase } = message;
|
|
471
|
+
return {
|
|
472
|
+
type: "interactive",
|
|
473
|
+
interactive: {
|
|
474
|
+
...interactiveBase,
|
|
475
|
+
type: "flow",
|
|
476
|
+
action: {
|
|
477
|
+
name: "flow",
|
|
478
|
+
parameters: {
|
|
479
|
+
flow_token: new FlowToken({
|
|
480
|
+
chatId,
|
|
481
|
+
flowName: flow.name,
|
|
482
|
+
flowIdentifier: flow.identifier,
|
|
483
|
+
flowParameters: flow.parameters
|
|
484
|
+
}).toString(),
|
|
485
|
+
flow_message_version: "3",
|
|
486
|
+
flow_cta: flow.button ?? "",
|
|
487
|
+
flow_action: flow.action ?? "navigate",
|
|
488
|
+
mode: flow.mode ?? "draft",
|
|
489
|
+
...flow.name && { flow_name: flow.name },
|
|
490
|
+
...flow.payload && { flow_action_payload: {
|
|
491
|
+
screen: flow.payload.screen,
|
|
492
|
+
data: {
|
|
493
|
+
__type__: "data",
|
|
494
|
+
...flow.payload.data
|
|
495
|
+
}
|
|
496
|
+
} }
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
case "list": {
|
|
503
|
+
const { list, type, ...interactiveBase } = message;
|
|
504
|
+
return {
|
|
505
|
+
type: "interactive",
|
|
506
|
+
interactive: {
|
|
507
|
+
...interactiveBase,
|
|
508
|
+
type: "list",
|
|
509
|
+
action: list
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
case "button": {
|
|
514
|
+
const { buttons, type, ...interactiveBase } = message;
|
|
515
|
+
return {
|
|
516
|
+
type: "interactive",
|
|
517
|
+
interactive: {
|
|
518
|
+
...interactiveBase,
|
|
519
|
+
type: "button",
|
|
520
|
+
action: { buttons: buttons.map((b) => ({
|
|
521
|
+
type: "reply",
|
|
522
|
+
reply: {
|
|
523
|
+
id: b.id,
|
|
524
|
+
title: b.text
|
|
525
|
+
}
|
|
526
|
+
})) }
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
case "template": {
|
|
531
|
+
const { type, language, ...rest } = message;
|
|
532
|
+
return {
|
|
533
|
+
type: "template",
|
|
534
|
+
template: {
|
|
535
|
+
...rest,
|
|
536
|
+
namespace: whatsappMessageNamespace,
|
|
537
|
+
language: { code: languageTagAdapter.toGraph(language) }
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
case "media": {
|
|
542
|
+
const { type, ...mediaFields } = message;
|
|
543
|
+
const [mediaType, mediaData] = Object.entries(mediaFields)[0];
|
|
544
|
+
const { ref, ...rest } = mediaData;
|
|
545
|
+
const resolvedMedia = !Number.isNaN(Number(ref)) ? {
|
|
546
|
+
id: ref,
|
|
547
|
+
...rest
|
|
548
|
+
} : {
|
|
549
|
+
link: ref,
|
|
550
|
+
...rest
|
|
551
|
+
};
|
|
552
|
+
return {
|
|
553
|
+
type: mediaType,
|
|
554
|
+
[mediaType]: resolvedMedia
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
case "contact": return {
|
|
558
|
+
type: "contacts",
|
|
559
|
+
contacts: message.contacts
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
} };
|
|
563
|
+
//#endregion
|
|
564
|
+
//#region src/features/messages/actions/send.ts
|
|
565
|
+
async function send(body) {
|
|
566
|
+
const { config, client } = getContext();
|
|
567
|
+
if (!config.whatsappNumberId) throw new Error("WABA Number Id is required to run send");
|
|
568
|
+
const fragment = messageAdapter.toGraph(body.message, body.to);
|
|
569
|
+
return await client.messages.sendMessage(config.whatsappNumberId, {
|
|
570
|
+
messaging_product: "whatsapp",
|
|
571
|
+
to: body.to,
|
|
572
|
+
recipient_type: "individual",
|
|
573
|
+
...body.reply && { context: { message_id: body.reply } },
|
|
574
|
+
...body.showUrlPreviewImage && { preview_url: body.showUrlPreviewImage },
|
|
575
|
+
...body.metadata && { biz_opaque_callback_data: body.metadata },
|
|
576
|
+
...fragment
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
//#endregion
|
|
580
|
+
//#region src/features/messages/actions/show-typing-indicator.ts
|
|
581
|
+
async function showTypingIndicator(params) {
|
|
582
|
+
const { config, client } = getContext();
|
|
583
|
+
if (!config.whatsappNumberId) throw new Error("WABA Number Id is required to run send");
|
|
584
|
+
return await client.messages.showTypingIndicator(config.whatsappNumberId, {
|
|
585
|
+
messaging_product: "whatsapp",
|
|
586
|
+
status: "read",
|
|
587
|
+
message_id: params.messageId,
|
|
588
|
+
typing_indicator: { type: "text" }
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
//#endregion
|
|
592
|
+
//#region src/features/messages/index.ts
|
|
593
|
+
var messages_exports = /* @__PURE__ */ __exportAll({
|
|
594
|
+
messageAdapter: () => messageAdapter,
|
|
595
|
+
send: () => send,
|
|
596
|
+
showTypingIndicator: () => showTypingIndicator
|
|
597
|
+
});
|
|
598
|
+
//#endregion
|
|
599
|
+
//#region src/features/waba/actions/register-number.ts
|
|
600
|
+
async function registerNumber({ pin, dataRegion }) {
|
|
601
|
+
const { config, client } = getContext();
|
|
602
|
+
if (!config.whatsappNumberId) throw new Error("WABA Number Id is required to run send");
|
|
603
|
+
return await client.waba.registerNumber(config.whatsappNumberId, {
|
|
604
|
+
messaging_product: "whatsapp",
|
|
605
|
+
pin,
|
|
606
|
+
...dataRegion && { data_localization_region: dataRegion }
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
//#endregion
|
|
610
|
+
//#region src/features/waba/actions/upload-encryption.ts
|
|
611
|
+
async function uploadEncryption(publicKey) {
|
|
612
|
+
const { config, client } = getContext();
|
|
613
|
+
if (!config.whatsappNumberId) throw new Error("WABA Number Id is required to run send");
|
|
614
|
+
return await client.waba.uploadEncryptionKey(config.whatsappNumberId, { business_public_key: publicKey });
|
|
615
|
+
}
|
|
616
|
+
//#endregion
|
|
617
|
+
//#region src/features/waba/index.ts
|
|
618
|
+
var waba_exports = /* @__PURE__ */ __exportAll({
|
|
619
|
+
registerNumber: () => registerNumber,
|
|
620
|
+
uploadEncryption: () => uploadEncryption
|
|
621
|
+
});
|
|
622
|
+
//#endregion
|
|
623
|
+
//#region src/features/webhook/adapters/message-received.adapter.ts
|
|
624
|
+
function textParser(message) {
|
|
625
|
+
return message.text?.body ?? message.button?.text ?? message.interactive?.button_reply?.title ?? message.interactive?.list_reply?.title ?? message.document?.caption ?? null;
|
|
626
|
+
}
|
|
627
|
+
function mediaParser(message) {
|
|
628
|
+
if (!(message.audio || message.document || message.video || message.image)) return void 0;
|
|
629
|
+
const id = message.audio?.id ?? message.document?.id ?? message.image?.id ?? message.video?.id;
|
|
630
|
+
const mime_type = message.audio?.mime_type ?? message.document?.mime_type ?? message.image?.mime_type ?? message.video?.mime_type;
|
|
631
|
+
const type = [
|
|
632
|
+
"audio",
|
|
633
|
+
"document",
|
|
634
|
+
"image",
|
|
635
|
+
"video"
|
|
636
|
+
].find((k) => message[k]);
|
|
637
|
+
const sha256 = message.image?.sha256 ?? message.document?.sha256 ?? message.video?.sha256;
|
|
638
|
+
return {
|
|
639
|
+
id,
|
|
640
|
+
type,
|
|
641
|
+
caption: message.document?.caption ?? message.image?.caption ?? message.video?.caption ?? null,
|
|
642
|
+
mime_type,
|
|
643
|
+
sha256,
|
|
644
|
+
filename: message.document?.filename ?? message.video?.filename
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
function interactionParser(message) {
|
|
648
|
+
if (!message.interactive) return void 0;
|
|
649
|
+
const button = message.interactive.button_reply || message.button ? { button: {
|
|
650
|
+
id: message.interactive.button_reply?.id ?? null,
|
|
651
|
+
title: message.button?.text ?? message.interactive.button_reply?.title,
|
|
652
|
+
payload: message.button?.payload ?? null
|
|
653
|
+
} } : {};
|
|
654
|
+
const selectedOption = message.interactive.list_reply ? { selectedOption: {
|
|
655
|
+
id: message.interactive.list_reply.id,
|
|
656
|
+
title: message.interactive.list_reply.title,
|
|
657
|
+
description: message.interactive.list_reply.description
|
|
658
|
+
} } : {};
|
|
659
|
+
const flowResponse = message.interactive.nfm_reply ? { flowResponse: JSON.parse(message.interactive.nfm_reply.response_json) } : {};
|
|
660
|
+
return {
|
|
661
|
+
...button,
|
|
662
|
+
...selectedOption,
|
|
663
|
+
...flowResponse
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
const messageReceivedAdapter = { fromGraph(message) {
|
|
667
|
+
return {
|
|
668
|
+
id: message.id,
|
|
669
|
+
type: message.type,
|
|
670
|
+
timestamp: message.timestamp,
|
|
671
|
+
metadata: {
|
|
672
|
+
forwarded: message.context?.forwarded,
|
|
673
|
+
frequentlyForwarded: message.context?.frequently_forwarded
|
|
674
|
+
},
|
|
675
|
+
chatId: message.from,
|
|
676
|
+
text: textParser(message),
|
|
677
|
+
reply: message.context?.id,
|
|
678
|
+
media: mediaParser(message),
|
|
679
|
+
interaction: interactionParser(message)
|
|
680
|
+
};
|
|
681
|
+
} };
|
|
682
|
+
//#endregion
|
|
683
|
+
//#region src/features/webhook/guards/guard-hub.ts
|
|
684
|
+
const guardHub = (request) => {
|
|
685
|
+
const { config } = getContext();
|
|
686
|
+
if (!config.whatsappWebhookToken) throw new Error("Webhook token is required to verify signature");
|
|
687
|
+
const params = new URL(request.url).searchParams;
|
|
688
|
+
const untrustedToken = params.get("hub.verify_token");
|
|
689
|
+
const challenge = params.get("hub.challenge");
|
|
690
|
+
if (!untrustedToken || !challenge) return false;
|
|
691
|
+
if (untrustedToken !== config.whatsappWebhookToken) return false;
|
|
692
|
+
return challenge;
|
|
693
|
+
};
|
|
694
|
+
//#endregion
|
|
695
|
+
//#region src/features/webhook/guards/guard-signature.ts
|
|
696
|
+
async function guardSignature(request, rawBody) {
|
|
697
|
+
const { config } = getContext();
|
|
698
|
+
if (!config.metaAppSecretKey) throw new Error("Meta App Secret Key is required for guarding signature.");
|
|
699
|
+
const headerSignature = request.headers.get("x-hub-signature-256");
|
|
700
|
+
if (!headerSignature) return false;
|
|
701
|
+
const untrustedSignature = headerSignature.replace("sha256=", "");
|
|
702
|
+
const trustedSignature = node_crypto.default.createHmac("sha256", config.metaAppSecretKey).update(rawBody, "utf-8").digest("hex");
|
|
703
|
+
return node_crypto.default.timingSafeEqual(Buffer.from(untrustedSignature), Buffer.from(trustedSignature));
|
|
704
|
+
}
|
|
705
|
+
//#endregion
|
|
706
|
+
//#region src/features/webhook/webhook.ts
|
|
707
|
+
async function webhook(request) {
|
|
708
|
+
if (request.method === "GET") {
|
|
709
|
+
const challenge = guardHub(request);
|
|
710
|
+
if (!challenge) throw new Error("Invalid health check hub");
|
|
711
|
+
return { health: challenge };
|
|
712
|
+
}
|
|
713
|
+
const rawBody = await request.text();
|
|
714
|
+
const body = JSON.parse(rawBody);
|
|
715
|
+
if ("entry" in body) {
|
|
716
|
+
if (!await guardSignature(request, rawBody)) throw new Error("Invalid application webhook signature");
|
|
717
|
+
return { message: {
|
|
718
|
+
received: body.entry.flatMap((entry) => entry.changes).filter((change) => change.field === "messages").flatMap((change) => change.value.messages ?? []).map((message) => messageReceivedAdapter.fromGraph(message)),
|
|
719
|
+
status: void 0
|
|
720
|
+
} };
|
|
721
|
+
}
|
|
722
|
+
if ("encrypted_flow_data" in body) {
|
|
723
|
+
const { body: payload, encryptionMetadata } = decryptFlowBody(body);
|
|
724
|
+
return { flow: {
|
|
725
|
+
payload,
|
|
726
|
+
pingResponse: { data: { status: "active" } },
|
|
727
|
+
encryptionMetadata
|
|
728
|
+
} };
|
|
729
|
+
}
|
|
730
|
+
throw new Error("Unrecognized webhook event");
|
|
731
|
+
}
|
|
732
|
+
//#endregion
|
|
733
|
+
//#region src/features/webhook/index.ts
|
|
734
|
+
var webhook_exports = /* @__PURE__ */ __exportAll({
|
|
735
|
+
guardHub: () => guardHub,
|
|
736
|
+
guardSignature: () => guardSignature,
|
|
737
|
+
messageReceivedAdapter: () => messageReceivedAdapter,
|
|
738
|
+
webhook: () => webhook
|
|
739
|
+
});
|
|
740
|
+
//#endregion
|
|
741
|
+
//#region src/client/normalize-pem-key.ts
|
|
742
|
+
function normalizePemKey(key) {
|
|
743
|
+
if (!key) return key;
|
|
744
|
+
return key.replace(/\\\n/g, "\n");
|
|
745
|
+
}
|
|
746
|
+
//#endregion
|
|
747
|
+
//#region src/client/client.ts
|
|
748
|
+
const createWhatsapp = (config) => {
|
|
749
|
+
config = {
|
|
750
|
+
metaAppAccessToken: process.env.META_APP_ACCESS_TOKEN,
|
|
751
|
+
metaAppSecretKey: process.env.META_APP_SECRET_KEY,
|
|
752
|
+
metaGraphApiVersion: process.env.META_GRAPH_API_VERSION ?? "25.0",
|
|
753
|
+
whatsappWebhookToken: process.env.WHATSAPP_WEBHOOK_TOKEN,
|
|
754
|
+
whatsappAccountEncryptionPassphrase: process.env.WHATSAPP_ACCOUNT_ENCRYPTION_PASSPHRASE,
|
|
755
|
+
whatsappAccountEncryptionPrivateKey: process.env.WHATSAPP_ACCOUNT_ENCRYPTION_PRIVATE_KEY,
|
|
756
|
+
whatsappAccountId: process.env.WHATSAPP_ACCOUNT_ID,
|
|
757
|
+
whatsappFlowsMode: process.env.WHATSAPP_FLOWS_MODE,
|
|
758
|
+
whatsappMessageNamespace: process.env.WHATSAPP_MESSAGE_NAMESPACE,
|
|
759
|
+
whatsappNumberId: process.env.WHATSAPP_NUBMER_ID,
|
|
760
|
+
...config
|
|
761
|
+
};
|
|
762
|
+
config.whatsappAccountEncryptionPrivateKey = normalizePemKey(config.whatsappAccountEncryptionPrivateKey);
|
|
763
|
+
const client = new _apostlejs_whatsapp_api.WhatsappApiClient({
|
|
764
|
+
accessToken: config.metaAppAccessToken,
|
|
765
|
+
baseUrl: `https://graph.facebook.com/v${config.metaGraphApiVersion}`
|
|
766
|
+
});
|
|
767
|
+
let encryption;
|
|
768
|
+
if (config.whatsappAccountEncryptionPassphrase && config.whatsappAccountEncryptionPrivateKey) encryption = new WabaEncryption({
|
|
769
|
+
passphrase: config.whatsappAccountEncryptionPassphrase,
|
|
770
|
+
privateKey: config.whatsappAccountEncryptionPrivateKey
|
|
771
|
+
});
|
|
772
|
+
setContext({
|
|
773
|
+
config,
|
|
774
|
+
client,
|
|
775
|
+
encryption
|
|
776
|
+
});
|
|
777
|
+
return {
|
|
778
|
+
flows: flows_exports,
|
|
779
|
+
i18n: i18n_exports,
|
|
780
|
+
messages: messages_exports,
|
|
781
|
+
waba: waba_exports,
|
|
782
|
+
webhook: webhook_exports
|
|
783
|
+
};
|
|
784
|
+
};
|
|
785
|
+
//#endregion
|
|
786
|
+
exports.ChatURL = ChatURL;
|
|
787
|
+
exports.FlowToken = FlowToken;
|
|
788
|
+
exports.WabaEncryption = WabaEncryption;
|
|
789
|
+
exports.WhatsappLanguageTag = WhatsappLanguageTag;
|
|
790
|
+
exports.createWhatsapp = createWhatsapp;
|
|
791
|
+
exports.normalizePemKey = normalizePemKey;
|