appwrite / templates

Templates for Appwrite Functions ⚡ī¸đŸŒŠī¸
https://appwrite.io
MIT License
103 stars 80 forks source link

🚀 Feature: Add WhatsApp with Vonage for PHP #206

Closed gewenyu99 closed 8 months ago

gewenyu99 commented 9 months ago

Appwrite Functions allow you to extend and customize your Appwrite server functionality by executing your custom code. 🤩 You can learn more at our official Appwrite Functions docs.

Your task is to implement the WhatsApp with Vonage function in PHP. You can look at the existing Appwrite Functions Templates in another coding language.

Your function should behave and be implemented similarly to existing WhatsApp with Vonage Node.js template.

Tasks summary:

If you need any help, contact us on our Discord server.

Are you ready to work on this issue? 🤔 Let us know, and we will assign it to you 😊

Happy coding!

SoNiC-HeRE commented 9 months ago

I'm interested in working on this issue, can i be assigned pls?

loks0n commented 9 months ago

Hi @SoNiC-HeRE, I've assigned this issue to you! Thanks your interest in contributing to Appwrite! Happy Hacktoberfest 🎃

Notes:

SoNiC-HeRE commented 9 months ago

Hi @SoNiC-HeRE, I've assigned this issue to you! Thanks your interest in contributing to Appwrite! Happy Hacktoberfest 🎃

Notes:

* Please update us with your progress every 3 days, so that we know that you are working on it.

* Join us on Discord - https://appwrite.io/discord to chat about Hacktoberfest and Appwrite!

Thank you; I'll keep the notes in mind.

SoNiC-HeRE commented 9 months ago

Progress:

To do:

monhelnog commented 9 months ago

I would like to work on this issue

Haimantika commented 9 months ago

I would like to work on this issue

Hi, we are assigning issues on a first-come, first-serve basis, if @SoNiC-HeRE decides to drop off, we will assign it to you.

SoNiC-HeRE commented 9 months ago

require(__DIR__ . '/../vendor/autoload.php');
require(__DIR__ . '/utils.php');
use Firebase\JWT\JWT; // Import JWT from Firebase\JWT namespace
use GuzzleHttp\Client;

// Load environment variables from .env file
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();

return function ($context) {

    if ($context->req->method === 'GET') {
        return $context->res->send(get_static_file('index.html'), 200, [
            'Content-Type' => 'text/html; charset=utf-8',
        ]);
    }

    // Obtain and decode the JWT token
    $headers = $context->req->headers;
    $token = explode(" ", ($headers["authorization"] ?? ""))[1] ?? "";

    try {
        $decoded = JWT::decode($token, $_ENV["VONAGE_API_SIGNATURE_SECRET"], ["HS256"]);
    } catch (Exception $e) {
        return $context->res->json(["ok" => false, "error" => $e->getMessage()], 401);
    }

    if (hash("sha256", $context->req->rawBody) !== $decoded->payload_hash) {
        return $context->res->json(["ok" => false, "error" => "Payload hash mismatch."], 401);
    }

    // Check for required fields in the request body
    $body = json_decode($context->req->rawBody, true);
    try {
        throw_if_missing($body, ["from", "text"]);
    } catch (Exception $e) {
        return $context->res->json(["ok" => false, "error" => $e->getMessage()], 400);
    }

    // Prepare and send the WhatsApp message
    $headers = [
        "Content-Type" => "application/json",
        "Accept" => "application/json",
    ];

    $data = [
        "from" => $_ENV["VONAGE_WHATSAPP_NUMBER"],
        "to" => $body["from"],
        "message_type" => "text",
        "text" => 'Hi there! You sent me: ' . $body["text"],
        "channel" => "whatsapp",
    ];

    $client = new Client();
    $url = "https://messages-sandbox.nexmo.com/v1/messages";

    try {
        $response = $client->post($url, [
            "headers" => $headers,
            "json" => $data,
            "auth" => [
                $_ENV["VONAGE_API_KEY"],
                $_ENV["VONAGE_API_SECRET"]
            ],
        ]);

        if ($response->getStatusCode() === 200) {
            $result = json_decode($response->getBody(), true);
            return $context->res->json(["ok" => true]);
        } else {
            return $context->error("Error " . $response->getBody());
        }
    } catch (Exception $e) {
        return $context->res->json(["ok" => false], 500);
    }
};
?>

I'm not sure what potentially am i doing wrong here since I'm getting no errors while deploying the page and when i try to send message (from a whitelisted and approved number) to the sandbox account i get no response

loks0n commented 8 months ago

I'm not sure what potentially am i doing wrong here since I'm getting no errors while deploying the page and when i try to send message (from a whitelisted and approved number) to the sandbox account i get no response

Do you see anything helpful in the execution logs? Is Vonage calling your function? Try to add logging to understand how far in the execution flow the function is reaching

SoNiC-HeRE commented 8 months ago

I'm not sure what potentially am i doing wrong here since I'm getting no errors while deploying the page and when i try to send message (from a whitelisted and approved number) to the sandbox account i get no response

Do you see anything helpful in the execution logs? Is Vonage calling your function? Try to add logging to understand how far in the execution flow the function is reaching

Ah,yes I tried logging as well, in the execution tab my GET requests are successfully completed while POST requests are failing with an error An internal curl error has occurred within the executor! Error Msg: Operation timed out\nError Code: 500

Further I tried using cURL for making POST requests as well but nothing seems to work.

Should i open a draft pr for this?

SoNiC-HeRE commented 8 months ago

I'm not sure what potentially am i doing wrong here since I'm getting no errors while deploying the page and when i try to send message (from a whitelisted and approved number) to the sandbox account i get no response

Do you see anything helpful in the execution logs? Is Vonage calling your function? Try to add logging to understand how far in the execution flow the function is reaching

I've updated the code as

<?php

require(__DIR__ . '/../vendor/autoload.php');
require(__DIR__ . '/utils.php');

use \Firebase\JWT\JWT;
use GuzzleHttp\Client;

return function ($context) {
    $context->log('Authentication');

    throw_if_missing($_ENV, [
        'VONAGE_API_KEY',
        'VONAGE_API_SECRET',
        'VONAGE_API_SIGNATURE_SECRET',
        'VONAGE_WHATSAPP_NUMBER',
    ]);

    if ($context->req->method === 'GET') {
        return $context->res->send(get_static_file('index.html'), 200, [
            'Content-Type' => 'text/html; charset=utf-8',
        ]);
    }

    $body = $context->req->body;
    $headers = $context->req->headers;
    $token = (isset($headers["Authorization"])) ? explode(" ", $headers["Authorization"])[1] : "";

    try {
        $decoded = JWT::decode($token, $_ENV['VONAGE_API_SIGNATURE_SECRET'], ['HS256']);
        $context->log($decoded);
    } catch (Exception $e) {
        return $context->res->json([
          'ok'=> false,
          'error'=> $e->getMessage(),
        ]);
    }

    if (hash('sha256', $context->req->bodyRaw) !== $decoded['payload_hash']) {
        return $context->res->json([
            'ok' => false,
            'error' => 'Payload hash mismatch'
        ]);
    }

    try {
        throwIfMissing($context->req->body, ['from', 'text']);
    } catch (Exception $e) {
        return $context->res->json(["ok" => false, "error" => $e->getMessage()], 400);
    }

    $vonageApiKey = $_ENV['VONAGE_API_KEY'];
    $vonageAccountSecret = $_ENV['VONAGE_API_SECRET'];

    $basicAuthToken = base64_encode("$vonageApiKey:$vonageAccountSecret");
    $headers = [
        'Content-Type: application/json',
        'Accept: application/json',
        'Authorization: Basic ' . $basicAuthToken,
    ];

    $data = [
        'from' => $_ENV['VONAGE_WHATSAPP_NUMBER'],
        'to' => $context->req->body['from'],
        'message-type' => 'text',
        'text' => 'Hi there! You sent me: ' . $context->req->body['text'],
        'channel' => 'whatsapp',
    ];
    $client = new Client();
    $url = "https://messages-sandbox.nexmo.com/v1/messages";

    try {
        $response = $client->post($url, [
            "headers" => $headers,
            "json" => $data,
        ]);

        if ($response->getStatusCode() === 200) {
            $result = json_decode($response->getBody(), true);
            return $context->res->json(["ok" => true]);
        } else {
            $context->error("Error: " . $response->getBody());
            return $context->res->json(["ok" => false, "error" => "Internal server error"], 500);
        }
    } catch (Exception $e) {
        $context->error("Error: " . $e->getMessage());
        return $context->res->json(["ok" => false, "error" => "Internal server error"], 500);
    }
}

In the executions tab my GET requests are working where as POST requests are failing with following error: An internal curl error has occurred within the executor! Error Msg: Operation timed out\nError Code: 500 I increased the timeout and the error changed to: Operation timed out after 30001 milliseconds with 0 bytes received with status code 0\nError Code: 0

Issue Resolved âœ