Forums

Building Telegram bot Using pyTelegramBotAPI and Flask

Hello. I'm followed your example building a simple Telegram bot. It's working fine.

Than i'd like to make a bot using pyTelegramBotAPI. And it doesn't work — i don't have answers from my bot. I tried different exemples, not only using pyTelegramBotAPI (e.g. this one) all of them don't work.

So, my question is how to make working something like that code (from this example):

@app.route(WEBHOOK_URL_PATH, methods=['POST'])
def webhook():
if flask.request.headers.get('content-type') == 'application/json':
    json_string = flask.request.get_data().decode('utf-8')
    update = telebot.types.Update.de_json(json_string)
    bot.process_new_updates([update])
    return ''
else:
    flask.abort(403)
@bot.message_handler(commands=['help', 'start'])
def send_welcome(message):
bot.reply_to(message,
             ("Hi there, I am EchoBot.\n"
              "I am here to echo your kind words back to you."))

using Flask in PythonAnywhere?

Is it possible?

Thanks.

It should be possible -- it looks like pyTelegramBotAPI uses requests, which will pick up all of the proxy-related stuff needed to run in a PythonAnywhere free account. Is your web app receiving webhook messages? You should be able to see from the access log (linked from the "Web" tab).

E.g.

from flask import Flask, request
import telebot

secret = "YOURSECRET"
bot = telebot.TeleBot('YOURTOKEN')
bot.remove_webhook()
bot.set_webhook(url="https://YOURURL{}".format(secret))

app = Flask(__name__)
@app.route('/{}'.format(secret), methods=["POST"])
def lololo():
update = request.get_json()
if "message" in update:
    text = update["message"]["text"]
    chat_id = update["message"]["chat"]["id"]
    bot.send_message(chat_id, "you said '{}'".format(text))
return "ok"

This example works just fine in PythonAnywhere, pyTelegramBotAPI uses requests and without any proxy-related stuff needed to run in a PythonAnywhere free account.

The earlier example has following error:

149.154.167.203 - - [11/Feb/2017:19:50:04 +0000] "POST /YOURSECRET HTTP/1.1" 500 1202 "-" "-" "149.154.167.203"

Now, i do have following:

149.154.167.203 - - [12/Feb/2017:01:03:18 +0000] "POST /YOURSECRET HTTP/1.1" 200 1 "-" "-" "149.154.167.203"

but i still don't have any answer from my bot.

deleted

It looks like your code is executing correctly. Perhaps you could put some print statements in the webhook view that will show what route it's following through the code, and see if that tells you anything? They'll wind up in the server or the error log.

Hi. I have some news :). I've learned and deployed my telegram bot to Heroku. It works fine there!

So, i also made some print statements in the app's code.

What's wrong in my opinion?

port = int(os.environ.get("PORT", 5000))

app.run(host="0.0.0.0", port = port)

That's not working, it's something with host and port.

Any suggestions?

Thanks.

P.S. I've read about proxy in free accounts, but your example building a simple Telegram bot works fine without proxy part of code, also i tried before another code with proxy part and telepot library and it does not work either.

You shouldn't use app.run (instead pass the app to your wsgi.py) and also you don't need to specify a random port.

See here

+1 to what @bfg says. If your Flask app was doing app.run at the end, then that would explain why it didn't work -- that's why the example you linked to originally doesn't do that.

Yea, i've read this couple of times. Actually my code is:

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

So, it does not help — i have no answers from the bot.

Any others suggestions?

Also i tried

if __name__ == '__main__':
     app.run(host="0.0.0.0") 
(Without: "port = int(os.environ.get("PORT", 5000))" )

The same result — does not work. To be more specific, this part of code:

@app.route('/{}'.format(constants.token), methods=["POST"])
def getMessage():
  bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))])

Very odd! What do you see if you put some logging print statements in your view? It would be interesting to see if you're getting the results you expect from telebot.types.Update.de_json(request.stream.read().decode("utf-8"))

Sorry, i don't understand exactly where to put print statements, could you be more precise, please?

E.g. i have one like this:

@app.route('/{}'.format(constants.token), methods=["POST"])
def getMessage():
     bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))])
     print('030TESTING MESSAGE')

and i have the following server log:

2017-02-23 17:02:29 01TESTING MESSAGE
2017-02-23 17:02:29 02TESTING MESSAGE
2017-02-23 17:02:29 03TESTING MESSAGE
2017-02-23 17:02:29 04TESTING MESSAGE
2017-02-23 17:02:29 05TESTING MESSAGE
2017-02-23 17:02:29 06TESTING MESSAGE
2017-02-23 17:02:29 WSGI app 0 (mountpoint='') ready in 2 seconds on interpreter 0x1ee1310 pid: 9286 (default app)
2017-02-23 17:02:29 *** uWSGI is running in multiple interpreter mode ***
2017-02-23 17:02:29 spawned uWSGI master process (pid: 9286)
2017-02-23 17:02:29 spawned uWSGI worker 1 (pid: 9290, cores: 1)
2017-02-23 17:02:29 metrics collector thread started
2017-02-23 17:02:29 spawned 2 offload threads for uWSGI worker 1
2017-02-23 17:02:30 announcing my loyalty to the Emperor...
2017-02-23 17:03:20 030TESTING MESSAGE

What I mean is, what do you see if you log the results of some of those commands. For example:

@app.route('/{}'.format(constants.token), methods=["POST"])
def getMessage():
    new_updates = [telebot.types.Update.de_json(request.stream.read().decode("utf-8"))]
    bot.process_new_updates(new_updates)
    print('030TESTING MESSAGE: {!r}'.format(new_updates))

The result is below:

2017-02-23 17:29:14 01TESTING MESSAGE
2017-02-23 17:29:14 02TESTING MESSAGE
2017-02-23 17:29:14 03TESTING MESSAGE
2017-02-23 17:29:14 04TESTING MESSAGE
2017-02-23 17:29:14 05TESTING MESSAGE
2017-02-23 17:29:14 06TESTING MESSAGE
2017-02-23 17:29:14 WSGI app 0 (mountpoint='') ready in 1 seconds on interpreter 0x1937310 pid: 9737 (default app)
2017-02-23 17:29:14 *** uWSGI is running in multiple interpreter mode ***
2017-02-23 17:29:14 spawned uWSGI master process (pid: 9737)
2017-02-23 17:29:14 spawned uWSGI worker 1 (pid: 9742, cores: 1)
2017-02-23 17:29:14 metrics collector thread started
2017-02-23 17:29:14 spawned 2 offload threads for uWSGI worker 1
2017-02-23 17:29:16 announcing my loyalty to the Emperor...
2017-02-23 17:29:25 030TESTING MESSAGE: [<telebot.types.Update object at 0x7f00dfba00f0>]
2017-02-23 17:29:27 030TESTING MESSAGE: [<telebot.types.Update object at 0x7f00dfba0e48>]
2017-02-23 17:29:32 030TESTING MESSAGE: [<telebot.types.Update object at 0x7f00de9fb278>]

OK, so we know that it's definitely getting updates. But for some reason its process_new_updates method isn't doing anything.

Having had a look at the pyTelegramBotAPI module, it looks like it's using the Python logging module, so perhaps you can try telling it to log with debug information. That would mean adding this code to the start of your Flask app:

from telebot import logger as telebot_logger
import logging

telebot_logger.setLevel(logging.DEBUG)

Maybe then it will print out some more useful information.

Thanks, the sever log is below:

2017-02-23 18:28:48 WSGI app 0 (mountpoint='') ready in 2 seconds on interpreter 0x1141310 pid: 11124 (default app)
2017-02-23 18:28:48 *** uWSGI is running in multiple interpreter mode ***
2017-02-23 18:28:48 spawned uWSGI master process (pid: 11124)
2017-02-23 18:28:48 spawned uWSGI worker 1 (pid: 11131, cores: 1)
2017-02-23 18:28:48 metrics collector thread started
2017-02-23 18:28:48 spawned 2 offload threads for uWSGI worker 1
2017-02-23 18:28:50 announcing my loyalty to the Emperor...
2017-02-23 18:28:54 2017-02-23 18:28:54,093 (__init__.py:164 MainThread) DEBUG - TeleBot: "Received 1 new updates"
2017-02-23 18:28:54 030TESTING MESSAGE

Error log:

2017-02-23 17:35:38,898 :ValueError: View function did not return a response
2017-02-23 17:36:29,631 :[2017-02-23 17:36:29,627] ERROR in app: Exception on /(MY TOKEN number, the same as .format(constants.token) ) [POST]
2017-02-23 17:36:29,631 :Traceback (most recent call last):
2017-02-23 17:36:29,631 :  File "/home/rubledollar/mysite/env/lib/python3.5/site-packages/flask/app.py", line 1982, in wsgi_app
2017-02-23 17:36:29,631 :    response = self.full_dispatch_request()
2017-02-23 17:36:29,631 :  File "/home/rubledollar/mysite/env/lib/python3.5/site-packages/flask/app.py", line 1615, in full_dispatch_request
2017-02-23 17:36:29,632 :    return self.finalize_request(rv)
2017-02-23 17:36:29,632 :  File "/home/rubledollar/mysite/env/lib/python3.5/site-packages/flask/app.py", line 1630, in finalize_request
2017-02-23 17:36:29,632 :    response = self.make_response(rv)
2017-02-23 17:36:29,632 :  File "/home/rubledollar/mysite/env/lib/python3.5/site-packages/flask/app.py", line 1725, in make_response
2017-02-23 17:36:29,632 :    raise ValueError('View function did not return a response')
2017-02-23 17:36:29,632 :ValueError: View function did not return a response
2017-02-23 18:28:54,093 :Received 1 new updates

Does your getMessage view return a response?

Do you mean if i have some response from my bot? No, the bot keeps silent, no answer.

No. He was asking if the getMessage that you wrote actually has the code to return a response.

As i can see, the answer is yes; the code to return is e.g. something like this:

@bot.message_handler(commands=['start'])
def start(message):
    bot.reply_to(message, 'Hello, ' + message.from_user.first_name)

Actually, as i wrote, i have the same code in Heroku, and it works fine. The only difference among them i've mentioned before.

P.S. As i realize the error log says that it's something with Flask library. I checked, and it has the same version as in Heroku.

That code doesn't return an HTTP response -- you'd need a return statement at the end for that.

Still, if it works on Heroku then that's probably not the problem. The only difference between the systems that I can think of is that free accounts on PythonAnywhere have to access the outside Internet using a proxy -- but as I said back at the start of this thread, the module uses requests, which should handle that transparently. This GitHub issue, given that it was closed as "not a real problem" also suggests that it should be fine, because the environment variables mentioned there are set in the PythonAnywhere environment.

However -- just as a test to see if we can rule the proxy thing in or out, I've temporarily switched off the proxy requirement for your account. Could you reload your web app on the "Web" tab, and see if it works now?

Thanks a lot. I see, the code i'm using is below (it has return statement):

def getMessage():
    bot.process_new_updates([telebot.types.Update.de_json(request.stream.read().decode("utf-8"))])
    print('030TESTING MESSAGE')
    return "!", 200

I've reloaded my "Web" — the same result (no answer).

log:

2017-02-24 20:04:57 2017-02-24 20:04:57,576 (__init__.py:164 MainThread) DEBUG - TeleBot: "Received 1 new updates"
2017-02-24 20:04:57 030TESTING MESSAGE
2017-02-24 20:05:01 2017-02-24 20:05:01,102 (__init__.py:164 MainThread) DEBUG - TeleBot: "Received 1 new updates"
2017-02-24 20:05:01 030TESTING MESSAGE
2017-02-24 20:05:05 2017-02-24 20:05:05,941 (__init__.py:164 MainThread) DEBUG - TeleBot: "Received 1 new updates"
2017-02-24 20:05:05 030TESTING MESSAGE
2017-02-24 20:05:13 2017-02-24 20:05:13,868 (__init__.py:164 MainThread) DEBUG - TeleBot: "Received 1 new updates"
2017-02-24 20:05:13 030TESTING MESSAGE

I have new Flask installation and Python 3.4 (The API is tested with).

Maybe it helps:

2017-02-26 17:46:30 2017-02-26 17:46:30,059 (__init__.py:164 MainThread) DEBUG - TeleBot: "Received 1 new updates"
2017-02-26 17:46:30 Sun Feb 26 17:46:30 2017 - SIGPIPE: writing to a closed pipe/socket/fd (probably the client disconnected) on request /TOKEN (ip 10.0.0.14) !!!
2017-02-26 17:46:30 Sun Feb 26 17:46:30 2017 - uwsgi_response_write_headers_do(): Broken pipe [core/writer.c line 229] during POST /TOKEN (10.0.0.14)
2017-02-26 17:46:30 RuntimeError: generator ignored GeneratorExit

2017-02-26 17:46:30,059 :Received 1 new updates
2017-02-26 17:46:30,061 :OSError: write error
2017-02-26 17:46:30,061 :Error running WSGI application
2017-02-26 17:46:30,062 :GeneratorExit

Is it taking a long time for the telegram api to return? could the client-side request be timing out? that would explain the probably the client disconnected message...

No, as i know telegram api works fine, without time out in ordinary situation.

As i wrote, i have the same code in Heroku, and it works fine. I actually, don't know what could i see on my side.

Maybe some of you knows an working example of telegram bot with webhook on pythonanywhere?

Right. I just worked out what's going on with your telegram bot. You have a free account. Free accounts only have one web worker. That worker is busy processing your request, so when the webhook comes in, there's no worker to process the webhook.

To test this, I'll temporarily bump up the number of workers available to your web app so you can see whether that helps. You will need to reload your web app to get the extra worker.

Thanks a lot. I reloaded the web app, but nothing unfortunately changed. (no answer from the bot)

Can I take a look at your code? We can see it from our admin interface, but we always ask for permission first.

It would be great. Thanks.

Thanks! OK, I've spotted something interesting. The library you're using is threaded by default, and threads are disabled for PythonAnywhere websites. What happens if you disable threads? There's a constructor parameter for that, so you just need to change

bot = telebot.TeleBot(constants.token)

...to...

bot = telebot.TeleBot(constants.token, threaded=False)

I'm so grateful to you, the bot works now. Thank you very much, Giles.

p.s. It might be helpful in FAQ.

That's great! Thanks for your patience with this.

Could we just check one more thing -- that it still works if I switch off the paid features we enabled on your account to see if the free account restrictions were causing the problem. All you need to do is reload the web app on the "Web" tab and check that it still works. I'm 99% sure it will, but it would be good to be sure.

Done. The bot still works. Thanks again.

Brilliant -- many thanks!

I spent whole day today trying to figure out the same thing=) Luckily, I came across this page. This topic was very helpful! Thank you, guys!

:-D

Can you send final code?

Not really. We were helping the user to debug what was happening to their code. If you have similar code, you can apply the lessons from the thread to make sure that yours works, too.

I encountered the same problem, so I disabled threading, but after that a TypeError raised:

TypeError: init() got an unexpected keyword argument 'threaded'

Here's my code:

enter code here
from flask import Flask, request
import telepot
import urllib3
import telebot

proxy_url = "http://proxy.server:3128"
telepot.api._pools = {
    'default': urllib3.ProxyManager(proxy_url=proxy_url, num_pools=3, maxsize=10, retries=False, timeout=30),
}
telepot.api._onetime_pool_spec = (urllib3.ProxyManager, dict(proxy_url=proxy_url, num_pools=1, maxsize=1, 
retries=False, timeout=30))

secret = "12"
bot = telebot.TeleBot('token', threaded=False)
bot.setWebhook("https://ganduras3.pythonanywhere.com/{}".format(secret), max_connections=100000)

app = Flask(__name__)

@app.route('/{}'.format(secret), methods=["POST"])
def telegram_webhook():
    update = request.get_json()
    if "message" in update:
        text = update["message"]["text"]
        chat_id = update["message"]["chat"]["id"]
        if text == '/start':
            bot.sendMEssage(chat_id, 'cool')
        bot.sendMessage(chat_id, "From the web: you said '{}'".format(text))
    return "OK"

sounds like telebot.TeleBot('token', threaded=False) is wrong for your version of telebot. Double check wha tyour version is and what the correct way to disable threading is by looking at telebot's online documentation?

Dear PythonAnyWhere staff, please could you take a look at my code? I have read this page 3 times in a loop (spent two days examining the code and searching for the bug), changed threaded to False, but its still not answering. I would be grateful. Thanks. :)

Hi there -- we can certainly look at your code, but it would help to know a bit more about the problem you're having first. What is the bot not doing that it should do, or doing that it should not do? Are you getting any errors in the error log?

Hello thanks for fast reply but I forgot to tell that 5 minutes after posting my question I have solved my problem. The problem was caused by not putting a '/' between host url and secret key.

Cool. Glad you have a solution.

thx for the solution