skip to Main Content

This code is from here
I have the following code for a telegram bot which i am building:

import pandas as pd
from pandas import datetime
from pandas import DataFrame as df
import matplotlib
from pandas_datareader import data as web
import matplotlib.pyplot as plt
import datetime
import requests 

from bottle import (  
    run, post, response, request as bottle_request
)

BOT_URL = 'https://api.telegram.org/bot128secretns/'  
def get_chat_id(data):  
    """
    Method to extract chat id from telegram request.
    """
    chat_id = data['message']['chat']['id']

    return chat_id

def get_message(data):  
    """
    Method to extract message id from telegram request.
    """
    message_text = data['message']['text']

    return message_text

def send_message(prepared_data):  
    """
    Prepared data should be json which includes at least `chat_id` and `text`
    """ 
    message_url = BOT_URL + 'sendMessage'
    requests.post(message_url, json=prepared_data)   

def get_ticker(text):  
    stock = f'^GSPC'
    start = datetime.date(2000,1,1)
    end = datetime.date.today()
    data = web.DataReader(stock, 'yahoo',start, end)
    plot = data.plot(y='Open') 
    return plot


def prepare_data_for_answer(data):  
    answer = get_ticker(get_message(data))

    json_data = {
        "chat_id": get_chat_id(data),
        "text": answer,
    }

    return json_data

@post('/')
def main():  
    data = bottle_request.json

    answer_data = prepare_data_for_answer(data)
    send_message(answer_data)  # <--- function for sending answer

    return response  # status 200 OK by default

if __name__ == '__main__':  
    run(host='localhost', port=8080, debug=True)

When i run this code i am getting the following error:

TypeError: Object of type AxesSubplot is not JSON serializable

What this code is suppose to do is take ticker symbols from telegram app and return its chart back.

I know this is because json does not handle images.
What can i do to resolve it?

3

Answers


  1. You cannot send a matplotlib figure directly. You will need to convert it to bytes and then send it as a multipart message.

    data.plot will return a matplotlib.axes.Axes object. You can save convert the figure to bytes like this

    import StringIO
    img = StringIO.StringIO()
    plot.fig.savefig(img, format='png')
    img.seek(0)
    

    yukuku/telebot has some good code on how to send the image as a message. Check this line here.

    Login or Signup to reply.
  2. Sorry, I’m a bit late to the party. Here is a possible solution below, though I didn’t test it. Hope it works or at least gives you a way to go about solving the issue 🙂

    import datetime
    from io import BytesIO
    
    import requests
    from pandas_datareader import data as web
    from bottle import (
        run, post, response, request as bottle_request
    )
    
    BOT_URL = 'https://api.telegram.org/bot128secretns/'
    
    def get_chat_id(data):
        """
        Method to extract chat id from telegram request.
        """
        chat_id = data['message']['chat']['id']
    
        return chat_id
    
    def get_message(data):
        """
        Method to extract message id from telegram request.
        """
        message_text = data['message']['text']
    
        return message_text
    
    def send_photo(prepared_data):
        """
        Prepared data should be json which includes at least `chat_id` and `plot_file`
        """
        data = {'chat_id': prepared_data['chat_id']}
        files = {'photo': prepared_data['plot_file']}
    
        requests.post(BOT_URL + 'sendPhoto', json=data, files=files)
    
    def get_ticker(text):
        stock = f'^GSPC'
        start = datetime.date(2000,1,1)
        end = datetime.date.today()
        data = web.DataReader(stock, 'yahoo',start, end)
        plot = data.plot(y='Open')
    
        return plot
    
    def prepare_data_for_answer(data):
        plot = get_ticker(get_message(data))
    
        # Write the plot Figure to a file-like bytes object:
        plot_file = BytesIO()
        fig = plot.get_figure()
        fig.savefig(plot_file, format='png')
        plot_file.seek(0)
    
        prepared_data = {
            "chat_id": get_chat_id(data),
            "plot_file": plot_file,
        }
    
        return prepared_data
    
    @post('/')
    def main():
        data = bottle_request.json
    
        answer_data = prepare_data_for_answer(data)
        send_photo(answer_data)  # <--- function for sending answer
    
        return response  # status 200 OK by default
    
    if __name__ == '__main__':
        run(host='localhost', port=8080, debug=True)
    

    The idea is not to send a message using the sendMessage Telegram API endpoint, but to send a photo file by using the sendPhoto endpoint. Here, we use savefig call in the prepare_data_for_answer function body to convert AxesSubplot instance, that we get as a return value from the get_ticker function, to a file-like BytesIO object, which we then send as a photo to Telegram using send_photo function (previously named as send_message).

    Login or Signup to reply.
  3. You may use bob-telegram-tools

    from bob_telegram_tools.bot 
    import TelegramBot
    import matplotlib.pyplot as plt
    
    
    token = '<your_token>'
    user_id = int('<your_chat_id>')
    bot = TelegramBot(token, user_id)
    
    plt.plot([1, 2, 3, 4])
    plt.ylabel('some numbers')
    
    bot.send_plot(plt)
    
    # This method delete the generetad image
    bot.clean_tmp_dir()
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search