I want to text a twilio number and start a series of questions for a user. If it is their first time texting, a new "Caller" should be created. If they have played before, I'd like to look up the "last_question", we asked them and ask them the appropriate question. My code below yields no SMS response and a Twilio error "HTTP retrieval failure."
In models.py I have
class Caller(models.Model):
body = models.CharField(max_length=200)
from_number = models.CharField(max_length=20)
last_question = models.CharField(max_length=2, default="0")
def __unicode__(self):
return self.body
In views.py
def hello_there(request):
body = request.REQUEST.get('Body', None)
from_number = request.REQUEST.get('From', None)
try:
caller = Caller.objects.get(from_number = from_number)
except Caller.DoesNotExist:
caller = None
if caller:
if caller.last_question == "0":
if body == "Password":
message = "Welcome to the game. What is 3 + 4?"
caller.last_question = "1"
else:
message = "What is the password?"
else:
message = "you broke me"
else:
new_caller = Caller(body=body, from_number=from_number, last_question="0")
new_caller.save()
message = "New user created"
resp = twilio.twiml.Reponse()
resp.sms(message)
return HttpResponse(str(resp))
Twilio employee here - the issue could be because you're not providing a csrf_exempt decorator around this view. Django will trigger a security error because it is receiving a HTTP POST request from twilio.com. Django will not accept any HTTP POST request without a csrf token unless you make it exempt.
Have you thought about using the django-twilio package for Django? It will make your life much easier when developing with twilio. This is what your view will look like with django-twilio:
from django_twilio.decorators import twilio_view
#twilio_view
def hello_there(request):
body = request.REQUEST.get('Body', None)
from_number = request.REQUEST.get('From', None)
try:
caller = Caller.objects.get(from_number=from_number)
except Caller.DoesNotExist:
caller = None
if caller:
if caller.last_question == "0":
if body == "Password":
message = "Welcome to the game. What is 3 + 4?"
caller.last_question = "1"
else:
message = "What is the password?"
else:
message = "you broke me"
else:
new_caller = Caller(body=body, from_number=from_number, last_question="0")
new_caller.save()
message = "New user created"
resp = twilio.twiml.Reponse()
resp.sms(message)
return resp
The twilio_view decorator will provide csrf exemption as well as ensuring all your requests are genuine and from twilio.com.
Check out the installation instructions to get started.
Related
Few days ago i made a telegram bot with python using api from https://core.telegram.org/bots/api.
All the code works perfectly until i realize that the GetUpdates api didn't record the joined notification for the new user in my group
My plan is to made a welcome message to new user based on those record. The group not allowing user to send message (broadcast group) and right now i confused that i can't get any single record related to the new user joined, so then the welcome message didn't work perfectly
This is my function for getUpdates api and the welcome message
def get_updates(self, offset=0, timeout=50):
method = 'getUpdates'
params = {'timeout': timeout, 'offset': offset}
resp = requests.get(self.api_url + method, params)
result_json = resp.json()['result']
return result_json
def welcome_msg(self, item):
chat_id = item["message"]["chat"]["id"]
user_id = item["message"]["new_chat_member"]["id"]
if "username" in item["message"]["new_chat_member"]:
user_name = item["message"]["new_chat_member"].get("username", user_id)
else:
user_name = item["message"]["new_chat_member"].get("first_name", user_id)
welcom_msg = "HI WELCOME TO THIS GROUP"
to_url3 = 'https://api.telegram.org/bot{}/sendMessage?chat_id={}&text={}&parse_mode=HTML&disable_web_page_preview=True'.format(token, chat_id, welcom_msg)
resp3 = requests.get(to_url3)
And after that the welcome message should be like this
new_offset = 1
all_updates = get_updates(new_offset)
if len(all_updates) > 0:
for current_update in all_updates:
first_update_id = current_update['update_id']
if old_id < first_update_id:
old_id = int(current_update['update_id'])
try:
if "new_chat_member" in current_update['message']:
welcome_msg(current_update)
chat_id = current_update["message"]["chat"]["id"]
message_id = current_update["message"]["message_id"]
new_offset = first_update_id + 1
except:
pass
Does the restriction of the group (member can't send message) made the getUpdates API couldn't work and the welcome message didn't showing up???
Go to botfather, bot settings and set privacy mode off for this bot, It should work
I'm trying to have a FORGOT PASSWORD? functionality in my website, I'm simply generating a token with a user id and then I send the form where they can update their password along with this token, but the problem is that I can't actually get that token after I click on the link, I get none, can someone tell me what I'm missing exactly? here's what I've done so far:
# generates the token with the user id
def get_reset_password_token(user_id, expires_in=1800):
return jwt.encode({'reset_password': user_id, 'exp': time() + expires_in},
app.config['SECRET_KEY'], algorithm='HS256').decode('utf-8')
# verfies the token recovered from the link
def verify_reset_password_token(token):
try:
id = jwt.decode(token, app.config['SECRET_KEY'],algorithms=['HS256'])['reset_password']
except:
print("token was invalid.")
return None
return print("all good")
# sends email to the user containing reset password link
def send_password_reset_email(user_id, user_mail):
token = get_reset_password_token(user_id)
msg = Message('[El-menu] Password Reset Request', sender ='noreply#demo.com', recipients =[user_mail])
msg.body = f'''To reset your password, visit the following link:
{url_for('reset_password', token=token , _external=True)}
If you didn't make this request, ignore this email and no changes will be madeSincerely,
Sincerely,
El-menu Team
'''
mail.send(msg)
return print("email sent with token " + token +" to email " + user_mail, file=sys.stderr)
#app.route("/reset_password_request", methods = ["GET", "POST"])
def reset_password_request():
if request.method == "POST":
email = request.form.get("email")
# checks for valid mail
if not email:
error = "you must submit a valid email"
return render_template("reset_password_request.html", error = error)
# performs a query to find information about given email, whether it's in the system or not?
user = db.execute("SELECT * FROM users WHERE email= :email", email = email)
if user:
user_mail = user[0]['email']
user_id = user[0]['id']
send_password_reset_email(user_id, user_mail)
password_reset = "Password reset link has been sent to your email!"
return render_template("login.html", password_reset = password_reset)
else:
return render_template("reset_password_request.html")
#app.route('/reset_password/<token>', methods = ["GET", "POST"])
def reset_password(token):
if request.method == "POST":
print("trying to reset password now..", file=sys.stderr)
print(token, file=sys.stderr)
user_id = verify_reset_password_token(token)
print(user_id, file=sys.stderr)
if not user_id:
print("token is none", file=sys.stderr)
return redirect(url_for('index'))
print(user_id, file=sys.stderr)
password = request.form.get("password")
confirmation = request.form.get("confirmation")
# Ensure password was submitted
if not password:
error = "You must provide a password"
return render_template("reset_password.html", error = error)
# Ensure confirmation was submitted
elif not confirmation:
error = "You must provide a password confirmation"
return render_template("reset_password.html", error = error)
reg = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[A-Za-z\d#$!#%*?&]{8,}$"
# compiling regex
pat = re.compile(reg)
# searching regex
mat = re.search(pat, password)
# validating conditions
if not mat:
error = "Password is weak"
return render_template("reset_password.html", error = error)
# Ensure Passwords match
if password != confirmation:
error = "Password and confirmation must match!"
return render_template("reset_password.html", error = error)
# Hash password
hashed_password = generate_password_hash(password)
db.execute("UPDATE users SET hash = :hash WHERE id = :id", hash = hashed_password, id = user_id)
return redirect("/")
else:
return render_template("reset_password.html")
I guess that's because the link you give {url_for('reset_password', token=token , _external=True)} in send_password_request_email makes a GET request so the user gets in reset_password and directly gets returned return render_template("reset_password.html")
You need to use render_template in order to render an email body that understands what token is.
So the first step would be to create an email template for the body of the email. The next step would be to use render_template and pass the token in.
Example from one of my Flask apps:
message = Message(
subject=render_template("email/reset_subject.txt"),
recipients=[user.email]
)
salt = current_app.config.get("SECURITY_PASSWORD_SALT", b"")
message.html = render_template(
"email/reset.html",
token=generate_code(user.email, salt=salt)
)
mail.send(message)
I have created a fb bot which simply echoes the user input.
I want it to echo the user inputs continuously and stop when the user types in "bye".
How do I do that? Please help out.
Code :
```import os
import sys
import json
import requests
import time
from flask import Flask, request
app = Flask(__name__)
#app.route('/', methods=['GET'])
def verify():
if request.args.get("hub.mode") == "subscribe" and request.args.get("hub.challenge"):
if not request.args.get("hub.verify_token") == os.environ["VERIFY_TOKEN"]:
return "Verification token mismatch", 403
return request.args["hub.challenge"], 200
return "Hello world", 200
#app.route('/', methods=['POST'])
def webhook():
data = request.get_json()
if data["object"] == "page":
for entry in data["entry"]:
for messaging_event in entry["messaging"]:
if messaging_event.get("message"): # someone sent us a message
sender_id = messaging_event["sender"]["id"]
recipient_id = messaging_event["recipient"]["id"]
message_text = messaging_event["message"]["text"]
reply = "Received : " + message_text
if "bye" in message_text.lower():
reply = "Good-bye"
send_message(sender_id, reply)
return "ok", 200
def send_message(recipient_id, message_text):
log("sending message to {recipient}: {text}".format(recipient=recipient_id, text=message_text))
params = {
"access_token": os.environ["PAGE_ACCESS_TOKEN"]
}
headers = {
"Content-Type": "application/json"
}
data = json.dumps({
"recipient": {
"id": recipient_id
},
"message": {
"text": message_text
}
})
r = requests.post("https://graph.facebook.com/v2.6/me/messages", params=params, headers=headers, data=data)
if r.status_code != 200:
log(r.status_code)
log(r.text)
if __name__ == '__main__':
app.run(debug=True) ```
How about return without send_message(sender_id, reply) function? Like this
if "bye" in message_text.lower():
return "bye", 200
This code is very similar to yours, except the fact that I used class based view
To break the loop add fbid of the user in a file or a DB after he/she says bye.
Everytime a message is posted check fbid to see whether user had already said "bye". If yes return empty response
def post_facebook_message(fbid, recevied_message):
"""
takes a user fb id and posts the bot message as response
"""
post_message_url = 'https://graph.facebook.com/v2.6/me/messages?access_token='+PAGE_ACCESS_TOKEN
response_msg = json.dumps({"recipient":{"id":fbid}, "message":{"text":recevied_message}})
status = requests.post(post_message_url, headers={"Content-Type": "application/json"},data=response_msg)
print status.json()
class FBBotView(generic.View):
#this is to verify your FB account with your program
#use ythe same verify token used while registering the bot
#method_decorator(csrf_exempt)
def get(self, request, *args, **kwargs):
if self.request.GET['hub.verify_token'] == VERIFY_TOKEN:
return HttpResponse(self.request.GET['hub.challenge'])
else:
return HttpResponse('Error, invalid token')
#method_decorator(csrf_exempt)
def dispatch(self, request, *args, **kwargs):
return generic.View.dispatch(self, request, *args, **kwargs)
# Post function to handle Facebook messages
def post(self, request, *args, **kwargs):
# Converts the text payload into a python dictionary
incoming_message = json.loads(self.request.body.decode('utf-8'))
# Facebook recommends going through every entry since they might send
# multiple messages in a single call during high load
for entry in incoming_message['entry']:
for message in entry['messaging']:
# Check to make sure the received call is a message call
# This might be delivery, optin, postback for other events
if 'message' in message:
if recieved_message.lower() == "bye":
#sends an empty response
message = "Good bye"
post_facebook_message(message['sender']['id'],message)
#this line is important because many times FB checks validity of webhook.
return HttpResponse()
I am setting up a Flask-RESTful service and have user authentication working. The method that I'm using is:
def generate_auth_token(username, expiration=600):
gen_serial = Serializer(secret_key, expires_in=expiration)
return gen_serial.dumps({'username': username})
I pass the token to the user as follows:
class token(Resource):
decorators = [auth.login_required]
def post(self):
username = g.user
return_token = generate_auth_token(username)
return {'token':return_token.decode()}, 200
And the token is then verified as such so that it does not need to be stored server side:
def verify_auth_token(auth_token):
serial = Serializer(secret_key)
try:
data = serial.loads(auth_token)
except SignatureExpired:
return None
except BadSignature:
return None
serial_user = data['username']
return serial_user
This seems to work well, however I am unsure how to logout the user before the expiration is expired without storing the token serverside. My thought was to pass back a garbage token when the user elects to logout, but I don't think this is an elegant or secure solution.
Any tips would be really helpful!
Rather than a garbage token simply encode no data:
def generate_auth_token(username=None, expiration=600):
gen_serial = Serializer(secret_key, expires_in=expiration)
data = {'username': username} if username is not None else {}
return gen_serial.dumps(data)
Then you can have an invalidate endpoint that requires a login and returns a token without a username:
def invalidate(self):
return_token = generate_auth_token()
return {'token':return_token.decode()}, 200
At that point, you can just handle the possibly missing username field:
def verify_auth_token(auth_token):
serial = Serializer(secret_key)
try:
data = serial.loads(auth_token)
except SignatureExpired:
return None
except BadSignature:
return None
serial_user = data.get('username')
return serial_user
I have the following Handlers
First the user calls this Handler and gets redirected to Facebook:
class LoginFacebookHandler(BasicHandler):
def get(self):
user = self.auth.get_user_by_session()
if not user:
h = hashlib.new('sha512')
h.update(str(datetime.now())+"abc")
nonce = h.hexdigest()
logging.info("hash "+str(nonce))
memcache.set(str(nonce), True, 8600)
#facebook_uri = "https://www.facebook.com/dialog/oauth?client_id=%s&redirect_uri=%s&state=%s&scope=%s" % ("20773", "http://upstrackapp.appspot.com/f", str(nonce), "email")
data = {"client_id": 20773, "redirect_uri": "http://***.appspot.com/f", "state": str(nonce), "scope": "email"}
facebook_uri = "https://www.facebook.com/dialog/oauth?%s" % (urllib.urlencode(data))
self.redirect(facebook_uri)
After he authorized my app facebook redirects to the redirect URI (Handler):
class CreateUserFacebookHandler(BasicHandler):
def get(self):
state = self.request.get('state')
code = self.request.get('code')
logging.info("state "+state)
logging.info("code "+code)
if len(code) > 3 and len(state) > 3:
cached_state = memcache.get(str(state))
logging.info("cached_state "+str(cached_state))
if cached_state:
#memcache.delete(str(state))
data = { "client_id": 20773, "redirect_uri": "http://***.appspot.com/f", "client_secret": "7f587", "code": str(code)}
graph_url = "https://graph.facebook.com/oauth/access_token?%s" % (urllib.urlencode(data))
logging.info("grph url "+graph_url)
result = urlfetch.fetch(url=graph_url, method=urlfetch.GET)
if result.status_code == 200:
fb_response = urlparse.parse_qs(result.content)
access_token = fb_response["access_token"][0]
token_expires = fb_response["expires"][0]
logging.info("access token "+str(access_token))
logging.info("token expires "+str(token_expires))
if access_token:
api_data = { "access_token": str(access_token)}
api_url = "https://graph.facebook.com/me?%s" % (urllib.urlencode(api_data))
logging.info("api url "+api_url)
api_result = urlfetch.fetch(url=api_url, method=urlfetch.GET)
if api_result.status_code == 200:
api_content = json.loads(api_result.content)
user_id = str(api_content["id"])
email = str(api_content["email"])
logging.info("user id "+str(user_id))
logging.info("email "+str(email))
h = hashlib.new('sha512')
h.update(str(user_id)+"abc")
password = h.hexdigest()
expire_data = datetime.now() + timedelta(seconds=int(token_expires))
user = self.auth.store.user_model.create_user(email, password_raw=password, access_token=access_token, token_expires=expire_data, fb_id=user_id)
else:
self.response.write.out.write("error contacting the graph api")
else:
self.response.out.write("access token not long enough")
else:
self.response.out.write("error while contacting facebook server")
else:
self.response.out.write("error no cached state")
else:
self.response.out.write("error too short")
Mostly this works until the code tries to retrieve an access_token and I end up getting "error while contacting....".
The funny thing is, that I log all URLs, states etc. so I go into my Logs, copy&paste the URL that urlfetch tried to open (fb api->access_token) paste it into my browser and voilĂ I get my access_token + expires.
The same thing happens sometimes when the code tries to fetch the user information from the graph (graph/me).
The key problem is not facebook.
It is the AppEngine deployment process.
I always tested changes in the code live, not local, since the OAuth wouldn't properly work.
So the deployment -> flush casche -> flush database process seemed to have a certain delay causing artifacts to remain, which confused the code.
So if you have to test such things like OAuth live, I'd recommend deploying the changes as a new version of the app and after deployment you shall delete all data that could act as artifacts in the new version.