Custom channel not working

hi. i keep getting this error every time i try to run my rasa chatbot. this was a working code and i got the errors when i tried sudo apt-get update/ upgrade.

after examining it again, i tried running my code using the default telegram channel provided by rasa and it worked. so i figured the problem was with my custom channel or the way i connect it to my rasa chatbot? thank you so much!

here is my credentials file:

rest:

addons.custom_telegram.TelegramInput: access_token: “xxx” verify: “xxx” webhook_url: “https://xxx.ngrok.io/webhooks/custom/webhook

here is my custom channel file:

import logging from copy import deepcopy from sanic import Blueprint, response from sanic.request import Request from sanic.response import HTTPResponse from telebot import TeleBot from telebot.apihelper import ApiTelegramException from telebot.types import ( InlineKeyboardButton, Update, InlineKeyboardMarkup, KeyboardButton, ReplyKeyboardMarkup, Message, ) from typing import Dict, Text, Any, List, Optional, Callable, Awaitable from telegram_bot_pagination import InlineKeyboardPaginator

from rasa.core.channels.channel import InputChannel, UserMessage, OutputChannel from rasa.shared.constants import INTENT_MESSAGE_PREFIX from rasa.shared.core.constants import USER_INTENT_RESTART from rasa.shared.exceptions import RasaException

logger = logging.getLogger(name)

class TelegramOutput(TeleBot, OutputChannel): “”“Output channel for Telegram.”""

# skipcq: PYL-W0236
@classmethod
def name(cls) -> Text:
    return "custom"

def __init__(self, access_token: Optional[Text]) -> None:
    super().__init__(access_token)


async def send_response(self, recipient_id: Text, message: Dict[Text, Any]) -> None:
    """Send a message to the client."""

    if message.get("quick_replies"):
        await self.send_quick_replies(
            recipient_id,
            message.pop("text"),
            message.pop("quick_replies"),
            **message
        )
    elif message.get("buttons"):
        await self.send_text_with_buttons(
            recipient_id, message.pop("text"), message.pop("buttons"), **message
        )
    elif message.get("list_pages"):
        await self.send_paginate(recipient_id, message.pop("list_pages"), **message)
    elif message.get("text"):
        await self.send_text_message(recipient_id, message.pop("text"), **message)

    if message.get("custom"):
        await self.send_custom_json(recipient_id, message.pop("custom"), **message)

    # if there is an image we handle it separately as an attachment
    if message.get("image"):
        await self.send_image_url(recipient_id, message.pop("image"), **message)

    if message.get("attachment"):
        await self.send_attachment(
            recipient_id, message.pop("attachment"), **message
        )

    if message.get("elements"):
        await self.send_elements(recipient_id, message.pop("elements"), **message)
    
    

async def send_paginate(
    self, recipient_id: Text, list_pages: List,data: Text,metadata,message: Text, page=1, **kwargs: Any
) -> None:

    data+="#"

    if data in message:

        page = int(message.split('#')[1])
        data += '{page}'

        paginator = InlineKeyboardPaginator(
            len(list_pages),
            current_page=page,
            data_pattern=data
        )

        self.edit_message_text(
            chat_id=recipient_id,
            message_id=metadata["callback_query"]["message"]["message_id"],
            text=list_pages[page - 1],
            reply_markup=paginator.markup,
            parse_mode='Markdown'
        )

    else:
        data += '{page}'
        paginator = InlineKeyboardPaginator(
            len(list_pages),
            current_page=page,
            data_pattern=data
        )

        self.send_message(
            recipient_id,
            list_pages[page-1],
            reply_markup=paginator.markup,
            parse_mode='Markdown'
        )

async def send_text_message(
    self, recipient_id: Text, text: Text, **kwargs: Any
) -> None:
    for message_part in text.strip().split("\n\n"):
        self.send_message(recipient_id, message_part)

async def send_image_url(
    self, recipient_id: Text, image: Text, **kwargs: Any
) -> None:
    self.send_photo(recipient_id, image)

async def send_text_with_buttons(
    self,
    recipient_id: Text,
    text: Text,
    buttons: List[Dict[Text, Any]],
    button_type: Optional[Text] = "inline",
    **kwargs: Any,
) -> None:
    """Sends a message with keyboard.
    For more information: https://core.telegram.org/bots#keyboards
    :button_type inline: horizontal inline keyboard
    :button_type vertical: vertical inline keyboard
    :button_type reply: reply keyboard
    """
    if button_type == "inline":
        reply_markup = InlineKeyboardMarkup()
        button_list = [
            InlineKeyboardButton(s["title"], callback_data=s["payload"])
            for s in buttons
        ]
        reply_markup.row(*button_list)

    elif button_type == "vertical":
        reply_markup = InlineKeyboardMarkup()
        [
            reply_markup.row(
                InlineKeyboardButton(s["title"], callback_data=s["payload"])
            )
            for s in buttons
        ]

    elif button_type == "reply":
        reply_markup = ReplyKeyboardMarkup(
            resize_keyboard=False, one_time_keyboard=True
        )
        # drop button_type from button_list
        button_list = [b for b in buttons if b.get("title")]
        for idx, button in enumerate(buttons):
            if isinstance(button, list):
                reply_markup.add(KeyboardButton(s["title"]) for s in button)
            else:
                reply_markup.add(KeyboardButton(button["title"]))
    else:
        logger.error(
            "Trying to send text with buttons for unknown "
            "button type {}".format(button_type)
        )
        return

    self.send_message(recipient_id, text, reply_markup=reply_markup)

async def send_custom_json(
    self, recipient_id: Text, json_message: Dict[Text, Any], **kwargs: Any
) -> None:
    json_message = deepcopy(json_message)

    recipient_id = json_message.pop("chat_id", recipient_id)

    send_functions = {
        ("text",): "send_message",
        ("photo",): "send_photo",
        ("audio",): "send_audio",
        ("document",): "send_document",
        ("sticker",): "send_sticker",
        ("video",): "send_video",
        ("video_note",): "send_video_note",
        ("animation",): "send_animation",
        ("voice",): "send_voice",
        ("media",): "send_media_group",
        ("latitude", "longitude", "title", "address"): "send_venue",
        ("latitude", "longitude"): "send_location",
        ("phone_number", "first_name"): "send_contact",
        ("game_short_name",): "send_game",
        ("action",): "send_chat_action",
        (
            "title",
            "decription",
            "payload",
            "provider_token",
            "start_parameter",
            "currency",
            "prices",
        ): "send_invoice",
    }

    for params in send_functions.keys():
        if all(json_message.get(p) is not None for p in params):
            args = [json_message.pop(p) for p in params]
            api_call = getattr(self, send_functions[params])
            api_call(recipient_id, *args, **json_message)

class TelegramInput(InputChannel): “”“Telegram input channel”""

@classmethod
def name(cls) -> Text:
    return "custom"

@classmethod
def from_credentials(cls, credentials: Optional[Dict[Text, Any]]) -> InputChannel:
    if not credentials:
        cls.raise_missing_credentials_exception()

    return cls(
        credentials.get("access_token"),
        credentials.get("verify"),
        credentials.get("webhook_url"),
    )

def __init__(
    self,
    access_token: Optional[Text],
    verify: Optional[Text],
    webhook_url: Optional[Text],
    debug_mode: bool = True,
) -> None:
    self.access_token = access_token
    self.verify = verify
    self.webhook_url = webhook_url
    self.debug_mode = debug_mode

@staticmethod
def _is_location(message: Message) -> bool:
    return message.location is not None

@staticmethod
def _is_user_message(message: Message) -> bool:
    return message.text is not None

@staticmethod
def _is_button(message: Update) -> bool:
    return message.callback_query is not None

def blueprint(
    self, on_new_message: Callable[[UserMessage], Awaitable[Any]]
) -> Blueprint:
    telegram_webhook = Blueprint("telegram_webhook", __name__)
    out_channel = self.get_output_channel()

    @telegram_webhook.route("/", methods=["GET"])
    async def health(_: Request) -> HTTPResponse:
        return response.json({"status": "ok"})

    @telegram_webhook.route("/set_webhook", methods=["GET", "POST"])
    async def set_webhook(_: Request) -> HTTPResponse:
        s = out_channel.setWebhook(self.webhook_url)
        if s:
            logger.info("Webhook Setup Successful")
            return response.text("Webhook setup successful")
        else:
            logger.warning("Webhook Setup Failed")
            return response.text("Invalid webhook")

    @telegram_webhook.route("/webhook", methods=["GET", "POST"])
    async def message(request: Request) -> Any:
        if request.method == "POST":

            request_dict = request.json
            update = Update.de_json(request_dict)
            if not out_channel.get_me().username == self.verify:
                logger.debug("Invalid access token, check it matches Telegram")
                return response.text("failed")

            if self._is_button(update):
                msg = update.callback_query.message
                text = update.callback_query.data
            else:
                msg = update.message
                if self._is_user_message(msg):
                    text = msg.text.replace("/bot", "")
                elif self._is_location(msg):
                    text = '{{"lng":{0}, "lat":{1}}}'.format(
                        msg.location.longitude, msg.location.latitude
                    )
                else:
                    return response.text("success")
            sender_id = msg.chat.id
            metadata = self.get_metadata(request)
            try:
                if text == (INTENT_MESSAGE_PREFIX + USER_INTENT_RESTART):
                    await on_new_message(
                        UserMessage(
                            text,
                            out_channel,
                            sender_id,
                            input_channel=self.name(),
                            metadata=metadata,
                        )
                    )
                    await on_new_message(
                        UserMessage(
                            "/start",
                            out_channel,
                            sender_id,
                            input_channel=self.name(),
                            metadata=metadata,
                        )
                    )
                else:
                    await on_new_message(
                        UserMessage(
                            text,
                            out_channel,
                            sender_id,
                            input_channel=self.name(),
                            metadata=metadata,
                        )
                    )
            except Exception as e:
                logger.error(f"Exception when trying to handle message.{e}")
                logger.debug(e, exc_info=True)
                if self.debug_mode:
                    raise
                pass

            return response.text("success")

    return telegram_webhook

def get_output_channel(self) -> TelegramOutput:
    """Loads the telegram channel."""
    channel = TelegramOutput(self.access_token)
    try:
        channel.set_webhook(url=self.webhook_url)
    except ApiTelegramException as error:
        raise RasaException(
            "Failed to set channel webhook: " + str(error)
        ) from error

    return channel
    
def get_metadata(self, request: Request) -> Optional[Dict[Text, Any]]:
    metadata=request.json
    return metadata

this is the error i got:

Encountered an exception while running action ‘action_intro’.Bot will continue, but the actions events are lost. Please check the logs of your action server for more information. Traceback (most recent call last): File “/home/ndn/rvenv/lib/python3.8/site-packages/rasa/core/processor.py”, line 772, in _run_action events = await action.run( File “/home/ndn/rvenv/lib/python3.8/site-packages/rasa/core/actions/action.py”, line 685, in run response = await self.action_endpoint.request( File “/home/ndn/rvenv/lib/python3.8/site-packages/rasa/utils/endpoints.py”, line 146, in request async with session.request( File “/home/ndn/rvenv/lib/python3.8/site-packages/aiohttp/client.py”, line 1117, in aenter self._resp = await self._coro File “/home/ndn/rvenv/lib/python3.8/site-packages/aiohttp/client.py”, line 390, in _request data = payload.JsonPayload(json, dumps=self._json_serialize) File “/home/ndn/rvenv/lib/python3.8/site-packages/aiohttp/payload.py”, line 381, in init dumps(value).encode(encoding), File “/usr/lib/python3.8/json/init.py”, line 231, in dumps return _default_encoder.encode(obj) File “/usr/lib/python3.8/json/encoder.py”, line 199, in encode chunks = self.iterencode(o, _one_shot=True) File “/usr/lib/python3.8/json/encoder.py”, line 257, in iterencode return _iterencode(o, 0) File “/usr/lib/python3.8/json/encoder.py”, line 179, in default raise TypeError(f’Object of type {o.class.name} ’ TypeError: Object of type Message is not JSON serializable 2021-07-18 15:17:52 ERROR rasa.core.tracker_store - Error happened when trying to save conversation tracker to ‘InMemoryTrackerStore’. Falling back to use the ‘InMemoryTrackerStore’. Please investigate the following error: Object of type Message is not JSON serializable. 2021-07-18 15:17:52 ERROR addons.custom_telegram - Exception when trying to handle message.Object of type Message is not JSON serializable Exception occurred while handling uri: ‘http://xxx.ngrok.io/webhooks/custom/webhook’ Traceback (most recent call last): File “/home/ndn/rvenv/lib/python3.8/site-packages/rasa/core/tracker_store.py”, line 1232, in save self._tracker_store.save(tracker) File “/home/ndn/rvenv/lib/python3.8/site-packages/rasa/core/tracker_store.py”, line 314, in save serialised = InMemoryTrackerStore.serialise_tracker(tracker) File “/home/ndn/rvenv/lib/python3.8/site-packages/rasa/core/tracker_store.py”, line 262, in serialise_tracker return json.dumps(dialogue.as_dict()) File “/usr/lib/python3.8/json/init.py”, line 231, in dumps return _default_encoder.encode(obj) File “/usr/lib/python3.8/json/encoder.py”, line 199, in encode chunks = self.iterencode(o, _one_shot=True) File “/usr/lib/python3.8/json/encoder.py”, line 257, in iterencode return _iterencode(o, 0) File “/usr/lib/python3.8/json/encoder.py”, line 179, in default raise TypeError(f’Object of type {o.class.name} ’ TypeError: Object of type Message is not JSON serializable

During handling of the above exception, another exception occurred:

Traceback (most recent call last): File “/home/ndn/rvenv/lib/python3.8/site-packages/sanic/app.py”, line 931, in handle_request response = await response File “/home/ndn/Desktop/TESTBOT/addons/custom_telegram.py”, line 332, in message await on_new_message( File “/home/ndn/rvenv/lib/python3.8/site-packages/rasa/core/channels/channel.py”, line 89, in handler await app.agent.handle_message(message) File “/home/ndn/rvenv/lib/python3.8/site-packages/rasa/core/agent.py”, line 577, in handle_message return await processor.handle_message(message) File “/home/ndn/rvenv/lib/python3.8/site-packages/rasa/core/processor.py”, line 111, in handle_message self._save_tracker(tracker) File “/home/ndn/rvenv/lib/python3.8/site-packages/rasa/core/processor.py”, line 901, in _save_tracker self.tracker_store.save(tracker) File “/home/ndn/rvenv/lib/python3.8/site-packages/rasa/core/tracker_store.py”, line 1235, in save self.fallback_tracker_store.save(tracker) File “/home/ndn/rvenv/lib/python3.8/site-packages/rasa/core/tracker_store.py”, line 314, in save serialised = InMemoryTrackerStore.serialise_tracker(tracker) File “/home/ndn/rvenv/lib/python3.8/site-packages/rasa/core/tracker_store.py”, line 262, in serialise_tracker return json.dumps(dialogue.as_dict()) File “/usr/lib/python3.8/json/init.py”, line 231, in dumps return _default_encoder.encode(obj) File “/usr/lib/python3.8/json/encoder.py”, line 199, in encode chunks = self.iterencode(o, _one_shot=True) File “/usr/lib/python3.8/json/encoder.py”, line 257, in iterencode return _iterencode(o, 0) File “/usr/lib/python3.8/json/encoder.py”, line 179, in default raise TypeError(f’Object of type {o.class.name} ’ TypeError: Object of type Message is not JSON serializable

Hi @ccloud

Have you registered your custom action py file('action_intro’) into domain.yml.

Have you started your actions server.

If yes, what are the error logs that you are seeing in your action server logs. Could you please share them…as well. Thanks

Best Regards, Ravi

thanks for replying, ravi!

yes i have it in my domain file, and i started the actions server. im sorry, but how do i save action server logs? thank u so much