7

I'm trying to develop a simple bot, that can retrieve photo from user and then do several operations with media file. I'm using telebot (https://github.com/eternnoir/pyTelegramBotAPI) for design.

As far as I can see from the wiki, I can divide income messages by content_type using special handlers.

However, when I wrote such simple method:

#main.py
@bot.message_handler(content_types= ["photo"])
def verifyUser(message):
    print ("Got photo")
    percent = userFace.verify(message.photo, config.photoToCompare)
    bot.send_message(message.chat.id, "Percentage: " + str(percent))

def getData(json_string):
    updates = telebot.types.Update.de_json(json_string)
    bot.process_new_updates([updates])


#server.py
app = Flask(__name__)

@app.route("/", methods=["POST", "GET"])
def hello():
    json_string = request.get_data()
    getData(json_string)
    print("....")
    print(request.data)
    return 'test is runing'

if __name__ == '__main__':
    app.run(host='0.0.0.0')

I get such mistake which I can't classify whether I'm doing something wrong or there is a problem with API

obj = cls.check_json(json_type)
File "/usr/local/lib/python2.7/dist-packages/telebot/types.py", line 77, in check_json
return json.loads(json_type)
File "/usr/lib/python2.7/json/__init__.py", line 339, in loads
return _default_decoder.decode(s)
File "/usr/lib/python2.7/json/decoder.py", line 364, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python2.7/json/decoder.py", line 382, in raw_decode
raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded

I'm new to bot design, so I don't know, whether I'm following the right way. I will be glad to hear the common practice of working with photo media files.

Tibebes. M
  • 6,940
  • 5
  • 15
  • 36
dand1
  • 371
  • 2
  • 8
  • 22
  • How do you want to get updates (new messages) on your bot - by polling or by webhook? Do you know what is this about? P.S. Your problem for now is not about photos but getting those updates – Andrew Che Mar 14 '17 at 21:03
  • @АндрейЧереваткин hm, I'm using webhooks. Yes, it was a mistake to name function like that, but I don't use pooling as a telebot method. It is just a problem of naming. – dand1 Mar 14 '17 at 21:25
  • https://gist.github.com/skgsergio/161ea0029a9c8c4fb871 here are examples on how webhook servers should be written, including flask. I noticed that there is a check that the request's content-type is application/json before sending that JSON to telebot's de_json method – Andrew Che Mar 14 '17 at 21:30
  • Oh, and did you know that Telegram webhooks work on https only? This means that you need an SSL certificate (self-signed will do). I just noticed that you didn't attach the cert when doing app.run – Andrew Che Mar 14 '17 at 21:56
  • @АндрейЧереваткин Actually, I used particularly that example. Moreover, I should notice, that there is one more method with `@bot.message_handler(content_types=["text"])` in my main.py, that is repeating user's messages and it is working fine. According to examples, I don't understand, why should I set webHook in code, if I can do it once through browser. As for SSL I'm using Let's Encrypt on my web server. – dand1 Mar 15 '17 at 08:59
  • Well, at least you should put that check for Content-Type in getData function and return 403 if it's not JSON like in the example. I think this will save you from ValueError: No JSON object could be decoded because anybody can send anything to your server and it doesn't have to be JSON. Non-JSON requests will always raise that error with your current code. – Andrew Che Mar 15 '17 at 09:05

2 Answers2

13

In case you want to download the photo you can use this piece of code:

@bot.message_handler(content_types=['photo'])
def photo(message):
    print 'message.photo =', message.photo
    fileID = message.photo[-1].file_id
    print 'fileID =', fileID
    file_info = bot.get_file(fileID)
    print 'file.file_path =', file_info.file_path
    downloaded_file = bot.download_file(file_info.file_path)

    with open("image.jpg", 'wb') as new_file:
        new_file.write(downloaded_file)

After running this code,a file named "image.jpg" will appear next to the script which is the photo you were asking.

Omid N
  • 947
  • 2
  • 11
  • 24
10

So if anyone ever has problems with receiving photos on a Telegram bot, here's the code that can retreive a relative file path to the photo:

# -*- coding: utf-8 -*-

import logging
import flask
import telebot
import sys
import re
import json
import decorator
from subprocess import Popen, PIPE

def externalIP():
    return Popen('wget http://ipinfo.io/ip -qO -', shell=True, stdout=PIPE).stdout.read()[:-1]

TELEBOT_TOKEN = '<token>'
WEBHOOK_HOST = externalIP()
WEBHOOK_PORT = 8443
WEBHOOK_LISTEN = '0.0.0.0'
WEBHOOK_SSL_CERT = 'server.crt'
WEBHOOK_SSL_PRIV = 'server.key'
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/%s/" % (TELEBOT_TOKEN)
bot = telebot.TeleBot(TELEBOT_TOKEN)
app = flask.Flask(__name__)

@decorator.decorator
def errLog(func, *args, **kwargs):
    result = None
    try:
        result = func(*args, **kwargs)
    except Exception as e:
        print e.__repr__()
    return result


@app.route('/', methods=['GET', 'HEAD'])
def index():
    return 'Hello world!'


@app.route(WEBHOOK_URL_PATH, methods=['POST'])
def webhook():
    if flask.request.headers.get('content-type') == 'application/json':
        json_string = flask.request.get_data()
        update = telebot.types.Update.de_json(json_string)
        bot.process_new_messages([update.message])
        return ''
    else:
        flask.abort(403)


@bot.message_handler(content_types=['text'])
def echo(message):
    bot.send_message(message.chat.id, message.text)


@errLog
def processPhotoMessage(message):
    print 'message.photo =', message.photo
    fileID = message.photo[-1].file_id
    print 'fileID =', fileID
    file = bot.get_file(fileID)
    print 'file.file_path =', file.file_path



@bot.message_handler(content_types=['photo'])
def photo(message):
    processPhotoMessage(message)


def main():
    global data
    bot.remove_webhook()
    bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH,
                    certificate=open(WEBHOOK_SSL_CERT, 'r'))
    app.run(host=WEBHOOK_LISTEN,
            port=8443,
            ssl_context=(WEBHOOK_SSL_CERT, WEBHOOK_SSL_PRIV),
            debug=False)

if __name__ == '__main__':
    main()

Use this template

https://api.telegram.org/file/bot<token>/<file_path>

To get the full photo URL

Andrew Che
  • 928
  • 7
  • 20