How to send message to Viber bot with Python?
Asked Answered
C

2

10

I have following HTTPS server:

from flask import Flask, request, Response
from viberbot import Api
from viberbot.api.bot_configuration import BotConfiguration
from viberbot.api.messages import VideoMessage
from viberbot.api.messages.text_message import TextMessage
import logging

from viberbot.api.viber_requests import ViberConversationStartedRequest
from viberbot.api.viber_requests import ViberFailedRequest
from viberbot.api.viber_requests import ViberMessageRequest
from viberbot.api.viber_requests import ViberSubscribedRequest
from viberbot.api.viber_requests import ViberUnsubscribedRequest

logger = logging.getLogger(__name__)
app = Flask(__name__)
viber = Api(BotConfiguration(
    name='PythonSampleBot',
    avatar='http://www.clker.com/cliparts/3/m/v/Y/E/V/small-red-apple-hi.png',
    auth_token='xxx-xxx-xxx'
))


@app.route('/', methods=['POST'])
def incoming():
    logger.debug("received request. post data: {0}".format(request.get_data()))
    # every viber message is signed, you can verify the signature using this method
    if not viber.verify_signature(request.get_data(), request.headers.get('X-Viber-Content-Signature')):
        return Response(status=403)

    # this library supplies a simple way to receive a request object
    viber_request = viber.parse_request(request.get_data())

    if isinstance(viber_request, ViberMessageRequest):
        message = viber_request.message
        # lets echo back
        viber.send_messages(viber_request.sender.id, [
            message
        ])
    elif isinstance(viber_request, ViberSubscribedRequest):
        viber.send_messages(viber_request.get_user.id, [
            TextMessage(text="thanks for subscribing!")
        ])
    elif isinstance(viber_request, ViberFailedRequest):
        logger.warn(
            "client failed receiving message. failure: {0}".format(viber_request))

    return Response(status=200)


def set_webhook(viber_bot):
    viber_bot.set_webhook('https://xxx.xxx.xxx.xxx:4443')
    logging.info("Web hook has been set")


if __name__ == "__main__":
    context = ('certificate.pem', 'key.pem')
    app.run(host='0.0.0.0', port=4443, debug=True, ssl_context=context)

and trying to send message:

import json
import requests

webhook_url = 'https://xxx.xxx.xxx.xxx:4443'

data = {
    "receiver": "xxx-xxx-xxx",
    "type": "text",
    "text": "Hello world!"
}

response = requests.post(
    webhook_url, data=json.dumps(data),
    headers={'Content-Type': 'application/json'},
    verify='E:\\Docs\\learn_py\\viberbot\\certificate.pem'
)
if response.status_code != 200:
    raise ValueError(
        'Request returned an error %s, the response is:\n%s'
        % (response.status_code, response.text)
    )

I'm getting 403 error

ValueError: Request returned an error 403, the response is:

UPDATE:

The 403 comes from:

if not viber.verify_signature(request.get_data(), request.headers.get('X-Viber-Content-Signature')):
        return Response(status=403)
Commonable answered 5/3, 2019 at 7:51 Comment(2)
Please confirm that your webhook sending code is merely for test purposes. If that's the case, I'll provide the answer shortly.Severin
I just want to send "Hello World" message to the Viber bot, for the really first step only. Then I'll try to do something more complexCommonable
S
7

You are getting the 403 error for 2 reasons. To simulate a webhook request from Viber, you must send the X-Viber-Content-Signature header. Also this value must be a SHA256 hash computed using the auth token and the webhook payload as described in their API docs under Callbacks.

I believe you have 2 choices here. If you want to just verify that your code receives the webhook properly, you can just comment out the verify_signature() lines temporarily. Validation of webhook requests is not required by Viber (or any webhook source). Typically a developer would assume that a library like the one provided by Viber tests their code properly, so usually there is no need to test their functionality again. You could also consider mocking the function, as that is very simple in this case.

If you really want to test Viber's signature validation, then you will need to implement the 2 reasons I mentioned first. Here's basically what you need to do in your test webhook sending code. Note that I only included the new code you need below, please merge into your other test code.

import json
import hmac
import hashlib

# Compute SHA256 hex digest signature using auth token and payload.
auth_token = 'xxx-xxx-xxx'
signature = hmac.new(
    key=auth_token.encode('ascii'),
    msg=data.encode('ascii'),
    digestmod=hashlib.sha256
).hexdigest()

# Send test webhook request with computed signature in header.
response = requests.post(
    webhook_url,
    data=json.dumps(data),
    headers={
        'X-Viber-Content-Signature': signature,
        'Content-Type': 'application/json'
    },
    verify='E:\\Docs\\learn_py\\viberbot\\certificate.pem'
)

Note that @tukan pointed out the _calculate_message_signature() function in the viber-bot-python repo, which shows how the signature is computed.

Severin answered 7/3, 2019 at 16:20 Comment(0)
M
7

Edited due to update. You are getting an error on verify_signature.

The definition of verify_signature:

def verify_signature(self, request_data, signature):
    return signature == self._calculate_message_signature(request_data)

You are sending there a request.headers.get('X-Viber-Content-Signature') as a `signature. So the solution for you is to check the result of __calculate_message_signature(request_data)

requiest_data = request.get_data() in your case.

The definition of _calculate_message_signature is:

    def _calculate_message_signature(self, message):
        return hmac.new(
            bytes(self._bot_configuration.auth_token.encode('ascii')),
            msg=message,
            digestmod=hashlib.sha256)\
.hexdigest()

I would check your auth_token which is used in self._bot_configuration.auth_token.encode('ascii'). Does it include non-ascii characters? If yes then you have the reason. (as example)

Try to compare the result of:

hmac.new(bytes(self._bot_configuration.auth_token.encode('ascii')),
                msg=request.get_data(),
                digestmod=hashlib.sha256).hexdigest()

to:

request.headers.get('X-Viber-Content-Signature') which is different and that is why you are getting forbidden message.

Malan answered 7/3, 2019 at 9:41 Comment(4)
This 403 comes from: if not viber.verify_signature(request.get_data(), request.headers.get('X-Viber-Content-Signature')): return Response(status=403)Commonable
@DmitrijKultasev So you have wrong signature isn't that so?Malan
yes, because I do not know how to send proper request.Commonable
@DmitrijKultasev I see. I have updated the answer due to your update question. Please see if that will helpMalan
S
7

You are getting the 403 error for 2 reasons. To simulate a webhook request from Viber, you must send the X-Viber-Content-Signature header. Also this value must be a SHA256 hash computed using the auth token and the webhook payload as described in their API docs under Callbacks.

I believe you have 2 choices here. If you want to just verify that your code receives the webhook properly, you can just comment out the verify_signature() lines temporarily. Validation of webhook requests is not required by Viber (or any webhook source). Typically a developer would assume that a library like the one provided by Viber tests their code properly, so usually there is no need to test their functionality again. You could also consider mocking the function, as that is very simple in this case.

If you really want to test Viber's signature validation, then you will need to implement the 2 reasons I mentioned first. Here's basically what you need to do in your test webhook sending code. Note that I only included the new code you need below, please merge into your other test code.

import json
import hmac
import hashlib

# Compute SHA256 hex digest signature using auth token and payload.
auth_token = 'xxx-xxx-xxx'
signature = hmac.new(
    key=auth_token.encode('ascii'),
    msg=data.encode('ascii'),
    digestmod=hashlib.sha256
).hexdigest()

# Send test webhook request with computed signature in header.
response = requests.post(
    webhook_url,
    data=json.dumps(data),
    headers={
        'X-Viber-Content-Signature': signature,
        'Content-Type': 'application/json'
    },
    verify='E:\\Docs\\learn_py\\viberbot\\certificate.pem'
)

Note that @tukan pointed out the _calculate_message_signature() function in the viber-bot-python repo, which shows how the signature is computed.

Severin answered 7/3, 2019 at 16:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.