0

I've developed a WhatsApp webhook using AWS Lambda function in Typescript. When I test locally, everything works fine. When I deploy the application, everything works fine as soon as the deployment is finished. Hours after deployment, when you write to the webhook, it may or may not respond. You never know when the webhook will reply. Sometimes you write and it responds hours later. And when I open Cloudwatch console, I can't find the errors.

Here's my serverless.yml

service: scenario
frameworkVersion: '3'

provider:
    name: aws
    runtime: nodejs18.x
    versionFunctions: false
    stage: "dev"
    region: eu-central-1
    httpApi:
        cors: true
    deploymentBucket:
        name: dba-chatbot-bucket

functions:
    webhook:
        handler: app/handler.Webhook
        timeout: 30
        events:
            - httpApi:
                path: /webhook
                method: get
            - httpApi:
                path: /webhook
                method: post

plugins:
    - serverless-offline
    - serverless-plugin-typescript
    - serverless-deployment-bucket

custom:
    serverless-offline:
        noPrependStageInUrl: true
        httpPort: 4000

Here's my handler.ts

import middy from "@middy/core";
import jsonBodyParser from "@middy/http-json-body-parser";
import { APIGatewayProxyEventV2 } from "aws-lambda";
import { container } from "tsyringe";
import "../utility";
import { CompanyChatsService } from "../service/company-chats-service";

const companyChatsService = container.resolve(CompanyChatsService);

export const Webhook = middy((event: APIGatewayProxyEventV2) => {
    const httpMethod = event.requestContext.http.method.toLowerCase();
    if (httpMethod === "post") {
        return companyChatsService.sendMessage(event);
    } else if (httpMethod === "get") {
        return companyChatsService.getMessage(event);
    } else {
        return companyChatsService.ResponseWithError(event);
    }
}).use(jsonBodyParser());

Here's my utility whatsapp-method.ts

export type WAResponseModel = {
    phone_number_id: string;
    phone_number: string;
    name: string;
    type: string;
    data: any;
    id: string;
};

export const getWhatsappResponse = async (body: any): Promise<WAResponseModel|boolean> => {
  if (body.object) {


      if (
          body.entry &&
          body.entry[0].changes &&
          body.entry[0].changes[0] &&
          body.entry[0].changes[0].value.messages &&
          body.entry[0].changes[0].value.messages[0]
      ) {
          const waResponse: WAResponseModel = {
              phone_number_id: body.entry[0].changes[0].value.metadata.phone_number_id,
              phone_number: body.entry[0].changes[0].value.messages[0].from,
              name: body.entry[0].changes[0].value.contacts[0].profile.name,
              type: body.entry[0].changes[0].value.messages[0].type,
              data: body.entry[0].changes[0].value.messages[0],
              id: body.entry[0].changes[0].value.messages[0].id
          };
          
          
          return waResponse;
      }
      return false;
  }
  return false;
};

Here's my service company-chats-service.ts, which receives get and post requests

import { APIGatewayProxyEventV2 } from "aws-lambda";
import { autoInjectable } from "tsyringe";
import { ErrorResponse } from "../utility/response";
import { getWhatsappResponse } from "../utility/whatsapp-method";
import { SendWAButtonModel, SendWAListModel, SendWATextModel, WAResponseModel } from "../models/whatsapp-message-type";
import { CredentialsRepository } from "../repository/credentials-repository";
import axios from "axios";

@autoInjectable()
export class CompanyChatsService {

    constructor(
        private credentialsRepository: CredentialsRepository,

    async ResponseWithError(event: APIGatewayProxyEventV2) {
        return ErrorResponse(404, "request method is not supported!");
    }

    async getMessage(event: APIGatewayProxyEventV2) {
        try {
            const queryParams = event.queryStringParameters;

            const mode = queryParams["hub.mode"];
            const verify_token = queryParams["hub.verify_token"];
            const challenge = queryParams["hub.challenge"];

            const credentials = await this.credentialsRepository.getByVerifyToken(verify_token)

            if (mode && verify_token) {
                if (mode === "subscribe" && verify_token === credentials.verify_token) {
                  console.log("WEBHOOK_VERIFIED");
                  return {
                    statusCode: 200,
                    body: challenge,
                  };
                } else {
                  return {
                    statusCode: 403,
                  };
                }
            }
            return {
                statusCode: 403,
            };
        } catch (error) {
            console.log(error);
            return ErrorResponse(500, error);
        }
    }

    async sendMessage(event: APIGatewayProxyEventV2) {
        try {
            const body = event.body;
            
            let data: SendWATextModel|SendWAButtonModel|SendWAListModel;
            if (await getWhatsappResponse(body)) {
                const waResponse = await getWhatsappResponse(body) as WAResponseModel;
                const credentials = await this.credentialsRepository.getByPhoneNumber(waResponse.phone_number_id);
                const token = credentials.token;

                const { status } = await axios({
                    method: "POST",
                    url:
                        "https://graph.facebook.com/v17.0/" +
                        waResponse.phone_number_id +
                        "/messages?access_token=" +
                        token,
                    data: {
                        messaging_product: "whatsapp",
                        to: waResponse.phone_number,
                        type: "text",
                        text: {
                            body: `Hi *${waResponse.name}*,\nThe chatbot is currently in development mode.\n_Thanks_`,
                        }
                    },
                    headers: { "Content-Type": "application/json" },
                });

                if (status === 200) {
                    return {
                        statusCode:200
                    }
                } else {
                    return {
                        statusCode: 403
                    }
                }
            } else {
                return {
                    statusCode: 403
                };
            }
        } catch (error) {
            console.log(error);
            return {
                statusCode: 500
            };
        }
    }
    
}

I don't know what's blocking the webhook from time to time.

I've opened Cloudwatch on AWS but I can't find any errors. enter image description here

0