I have been running locally, without host param nor port param and it has been working well without a webhook (very unintuitive, how is just bot token enough?).
I decided to put my bot on a server. I've created a python3 file and I've now:
created the webhook on ipv4 port 8443
{"ok":true,"result":true,"description":"Webhook is already set"}
edited the lines in main:
context=('/path_to/cert.pem', '/path_to/privkey.pem')
app.run(host="xxx.xx.xxx.xxx",port="8443", ssl_context=context, debug=False)
This gives output of
* Serving Flask app 'xx'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on https://xxx.xx.xxx.xxx:8443
Press CTRL+C to quit
Bot is unresponsive.
I've used the following command and it didn't help:
sudo ufw allow 8443
I've tried running with:
context=('/path_to/cert.pem', '/path_to/privkey.pem')
app.run(host="0.0.0.0",port="8443", ssl_context=context, debug=False)
And the output is:
* Serving Flask app 'xx'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on https://xxx.0.0.x:8443
* Running on https://xxx.xx.xxx.xxx:8443
Press CTRL+C to quit
Bot is unresponsive...
Edit: bot code:
from flask import Flask
from flask import request
from flask import Response
import requests
TOKEN = "..."
app = Flask(__name__)
def parse_message(message):
print("message-->",message)
chat_id = message['message']['chat']['id']
txt = message['message']['text']
print("chat_id-->", chat_id)
print("txt-->", txt)
return chat_id,txt
def tel_send_message(chat_id, text):
url = f'https://api.telegram.org/bot{TOKEN}/sendMessage'
payload = {
'chat_id': chat_id,
'text': text
}
r = requests.post(url,json=payload)
return r
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
msg = request.get_json()
chat_id,txt = parse_message(msg)
if txt == "hi":
tel_send_message(chat_id,"Hello!!")
else:
tel_send_message(chat_id,'from webhook')
return Response('ok', status=200)
else:
return "<h1>Welcome!</h1>"
if __name__ == '__main__':
context=('path/cert.pem', 'path/privkey.pem')
app.run(host="xxx.xx.xxx.xxx",port="8443", ssl_context=context,debug=False)
Related
I'm using waitress server to deploy the flask app for production. I'm also using flask's socketio along with the eventlet server which requires its own app run.
Currently only serving app on waitress:
serve(app, host='0.0.0.0', port=8080)
How do I include the socket.run command for running the socket server?
socketio.run(app)
My code:
This snippet sets up server for the flask socketio on which it is to be run and in the if name part I serve the app on waitress if in prod mode.
app.py
import eventlet
async_mode = None
if async_mode is None:
try:
async_mode = 'eventlet'
except ImportError:
pass
if async_mode is None:
async_mode = 'threading'
print('async_mode is ' + async_mode)
if async_mode == 'eventlet':
eventlet.monkey_patch()
socketio = socketIO(app,cors_allowed_origins='*',async_mode=async_mode)
if __name__=='__main__':
if env_mode=='dev':
app.run(host='0.0.0.0', port=8080)
elif env_mode=='prod':
serve(app, host='0.0.0.0', port=8080)
I am using telegram bot api (telebot), flask and gunicorn.
When I use command python app.py everything is work fine but when I use python wsgi.py flask is stated on http://127.0.0.1:5000/ and bot doesn't answer and if I am using gunicorn --bind 0.0.0.0:8443 wsgi:app webhook is setting but telegram bot doesn't answer. I tried to add app.run from app.py to wsgi.py but it doesn't work
app.py
import logging
import time
import flask
import telebot
API_TOKEN = '111111111:token_telegram'
WEBHOOK_HOST = 'droplet ip'
WEBHOOK_PORT = 8443 # 443, 80, 88 or 8443 (port need to be 'open')
WEBHOOK_LISTEN = '0.0.0.0' # In some VPS you may need to put here the IP addr
WEBHOOK_SSL_CERT = 'webhook_cert.pem' # Path to the ssl certificate
WEBHOOK_SSL_PRIV = 'webhook_pkey.pem' # Path to the ssl private key
WEBHOOK_URL_BASE = "https://%s:%s" % (WEBHOOK_HOST, WEBHOOK_PORT)
WEBHOOK_URL_PATH = "/%s/" % (API_TOKEN)
logger = telebot.logger
telebot.logger.setLevel(logging.DEBUG)
bot = telebot.TeleBot(API_TOKEN)
app = flask.Flask(__name__)
# Empty webserver index, return nothing, just http 200
#app.route('/', methods=['GET', 'HEAD'])
def index():
return ''
# Process webhook calls
#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)
# Handle all other messages
#bot.message_handler(func=lambda message: True, content_types=['text'])
def echo_message(message):
bot.reply_to(message, message.text)
# Remove webhook, it fails sometimes the set if there is a previous webhook
bot.remove_webhook()
#
time.sleep(1)
# Set webhook
bot.set_webhook(url=WEBHOOK_URL_BASE + WEBHOOK_URL_PATH,
certificate=open(WEBHOOK_SSL_CERT, 'r'))
if __name__ == "__main__":
# Start flask server
app.run(host=WEBHOOK_LISTEN,
port=WEBHOOK_PORT,
ssl_context=(WEBHOOK_SSL_CERT, WEBHOOK_SSL_PRIV),
debug=True)
wsgi.py
from app import app
if __name__ == "__main__":
app.run()
It's a bad practice to show python webservers to the world. It's not secure.
Good practice - using a reverse proxy, e.g. nginx
Chain
So the chain shoud be:
api.telegram.org -> your_domain -> your_nginx -> your_webserver -> your_app
SSL
Your ssl certificates shoud be checked on nginx level. On success just pass request to your webserver (flask or something else). It's also a tip about how to use infitity amount of bots on one host/port :)
How to configure nginx reverse proxy - you can find in startoverflow or google.
Telegram Webhooks
telebot is so complicated for using webhooks.
Try to use aiogram example. It's pretty simple:
from aiogram import Bot, Dispatcher, executor
from aiogram.types import Message
WEBHOOK_HOST = 'https://your.domain'
WEBHOOK_PATH = '/path/to/api'
bot = Bot('BOT:TOKEN')
dp = Dispatcher(bot)
#dp.message_handler()
async def echo(message: Message):
return message.answer(message.text)
async def on_startup(dp: Dispatcher):
await bot.set_webhook(f"{WEBHOOK_HOST}{WEBHOOK_PATH}")
if __name__ == '__main__':
executor.start_webhook(dispatcher=dp, on_startup=on_startup,
webhook_path=WEBHOOK_PATH, host='localhost', port=3000)
app.run(host=0.0.0.0, port=5000, debug=True)
Change port number and debug arguments based on your case.
from flask import Flask
from flask_apscheduler import APScheduler
class Config(object):
JOBS = [
{
'id': 'job5',
'func': 'f_s_api.view:job1',
'trigger': 'interval',
'seconds': 50
}
]
SCHEDULER_API_ENABLED = True
def job1():
print('job add')
if __name__ == '__main__':
app = Flask(__name__)
app.config.from_object(Config())
scheduler = APScheduler()
scheduler.init_app(app)
scheduler.start()
app.run(debug= True , port= 8080)
output
Serving Flask app "view" (lazy loading)
Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
Debug mode: on
Running on http://127.0.0.1:8080/ (Press CTRL+C to quit)
Restarting with stat
Debugger is active!
Debugger PIN: 135-565-985
job add
ob add
run the flask and open the postman and http://localhost:5000/scheduler/jobs this flask apscheduler api url for add job in the form of post request then in body-->row select text type as JSON and the send the request.
I created a flask application that consists of MQTT client which simultaneously subscribe data from esp32 and store it in database.
from flask import Flask, redirect, render_template, request, session, abort,make_response,jsonify, flash, url_for
import paho.mqtt.client as mqtt
import json
import config
import db_access
#mqtt code
def on_message(client, userdata, message):
topic = message.topic
print("line 12 - topic checkpoint - ",topic)
msgDecode=str(message.payload.decode("utf-8","ignore"))
msgJson=json.loads(msgDecode) #decode json data
print("line 15 - json checkpoint - ",type(msgJson))
# deviceID = msgJson["DeviceID"]
# currentCounter = msgJson["Counter"]
# status = msgJson["Status"]
db_access.updateStatus(msgJson["DeviceID"],msgJson["Status"])
app = Flask(__name__,template_folder='templates')
'''Web portal routes'''
#app.route('/device/switch', methods=['POST'])
def switch():
#parameter parsing
deviceID = request.args.get('deviceID')
status = request.args.get('status')
statusMap = {"on":1,"off":0}
#MQTT publish
mqtt_msg = json.dumps({"deviceID":int(deviceID),"status":statusMap[status]})
client.publish(config.MQTT_STATUS_CHANGE_TOPIC,mqtt_msg)
time_over_flag = 0
loop_Counter = 0
while status != db_access.getDeviceStatus(deviceID):
time.sleep(2)
loop_Counter+=1
if loop_Counter ==2:
time_over_flag = 1
break
if time_over_flag:
return make_response(jsonify({"statusChange":False}))
else:
return make_response(jsonify({"statusChange":True}))
if __name__ == "__main__":
db_access.createUserTable()
db_access.insertUserData()
db_access.createDeviceTable()
db_access.insertDeviceData()
print("creating new instance")
client = mqtt.Client("server") #create new instance
client.on_message=on_message #attach function to callback
print("connecting to broker")
client.connect(config.MQTT_BROKER_ADDRESS)
client.loop_start()
print("Subscribing to topic","esp/#")
client.subscribe("esp/#")
app.run(debug=True, use_reloader=False)
This is code in ____init____.py
db_access.py is consist of database operations and config.py consist of configurations.
Will this work in apache?
also, I have no previous experience with WSGI
The problem with launching your code (as included) with a WSGI server, is the part in that last if block, only gets run when you execute that file directly with the python command.
To make this work, I'd try moving that block of code to the top of your file, around here:
app = Flask(__name__,template_folder='templates')
db_access.createUserTable()
db_access.insertUserData()
db_access.createDeviceTable()
db_access.insertDeviceData()
print("creating new instance")
client = mqtt.Client("server") #create new instance
client.on_message=on_message #attach function to callback
print("connecting to broker")
client.connect(config.MQTT_BROKER_ADDRESS)
client.loop_start()
print("Subscribing to topic","esp/#")
client.subscribe("esp/#")
I'd also rename that __init__.py file, to something like server.py as the init file isn't meant to be heavy.
Once you've done that, run it with the development server again and test that it works as expected.
Then install a WSGI server like gunicorn into your virtual enviornment:
pip install gunicorn
And launch the app with gunicorn (this command should work, assuming you renamed your file to server.py):
gunicorn --bind '0.0.0.0:5000' server:app
Then test again.
The localhost is not running and I am getting an OS:Error of 'No route to host' error when I run the flask app.
I've tried adding this:
app.run(host='0.0.0.0')
but it does not work.Also I have tried changing the port from 5000 to 4996 and various other ports but still I am facing the same issue .
Here is my complete code:
from flask import Flask
from flask_mail import Mail,Message
app = Flask(__name__)
app.config['DEBUG']=True
app.config['TESTING']=False
app.config['MAIL_SERVER']='smtp.gmail.com'
app.config['MAIL_PORT']=456
app.config['MAIL_USE_SSL']=True
#app.config['MAIL_DEBUG']=
app.config['MAIL_USERNAME']='trvt1234#gmail.com'
app.config['MAIL_PASSWORD']='#insert password here'
app.config['MAIL_DEFAULT_SENDER']='trvt1234#gmail.com'
app.config['MAIL_MAX_EMAILS']=None
#app.config['MAIL_SUPRESS_SEND']=
app.config['MAIL_ASCII_ATTACHMENTS']=False
email = Mail(app)
#app.route('/')
def mail():
message = Message('Hello',recipients=['trvt1234#gmail.com'])
email.send(message)
return('message sent successfully')
if __name__ == '__main__':
app.run(host='0.0.0.0')
I am a newbie to Flask and was figuring out how should I go on about this problem.
I think you have the wrong MAIL_PORT - it should be 465 not 456.