Skip to content

Commit

Permalink
Merge pull request #15 from ineshbose/updates
Browse files Browse the repository at this point in the history
Scheduler Update to Master
  • Loading branch information
ineshbose authored Aug 25, 2020
2 parents 1dce6b9 + f4f2b29 commit 5ada8de
Show file tree
Hide file tree
Showing 27 changed files with 623 additions and 185 deletions.
27 changes: 18 additions & 9 deletions boyd_bot/__init__.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
# flake8: noqa

import os
import logging
from flask import Flask, Blueprint


app = Flask(__name__)
app.logger.setLevel(logging.DEBUG)
app.logger.setLevel(logging.INFO)

app_url = os.environ.get("APP_URL", "http://127.0.0.1")
app_url = os.environ.get("APP_URL", "http://127.0.0.1:5000")
app.config["SECRET_KEY"] = os.environ.get("FLASK_KEY")
app.config["DEBUG"] = "127.0.0.1" in app_url

from . import _config

app.logger.handlers[0].setFormatter(logging.Formatter(app.config["LOG"]["FORMAT"]))
blueprint = Blueprint("boyd_bot", __name__, template_folder="templates")

from . import views
from .forms import *
from .forms import RegisterForm

webhook_token = os.environ.get("VERIFY_TOKEN")
wb_arg_name = os.environ.get("WB_ARG_NAME")
Expand Down Expand Up @@ -51,23 +55,28 @@
platform = Platform(platform_token=os.environ.get("PLATFORM_TOKEN"))


from .services.scheduler import Scheduler

if app.config["FEATURES"]["SCHEDULER"]:
scheduler = Scheduler()
scheduler.run()


def log(message):
app.logger.info(message)


from .app import *
from .app import webhook, new_user_registration, app

app.register_blueprint(blueprint, url_prefix=app.config["URL_ROOT"])


@app.after_request
def secure_http_header(response):
response.headers[
"Strict-Transport-Security"
] = "max-age=31536000; includeSubDomains"
response.headers["Content-Security-Policy"] = "default-src * 'unsafe-inline'"
response.headers["Strict-Transport-Security"] = "max-age=31536000"
response.headers["Content-Security-Policy"] = "default-src 'self' *"
response.headers["X-Frame-Options"] = "SAMEORIGIN"
response.headers["X-Content-Type-Options"] = "nosniff"
response.headers["Referrer-Policy"] = "same-origin"
response.headers["Feature-Policy"] = "none"
response.headers["Feature-Policy"] = "geolocation 'none'"
return response
83 changes: 76 additions & 7 deletions boyd_bot/_config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from . import app
# flake8: noqa
# fmt: off

from . import app, app_url


# URL root for the app
Expand All @@ -11,17 +14,83 @@
}


# Features you can switch on/off
app.config["FEATURES"] = {

# read https://github.com/ineshbose/boyd_bot_messenger/issues/8
"ONE_TIME_USE": True,

# Works for chatbots without platform user-accounts for demos
"DEMO": True,

# read https://github.com/ineshbose/boyd_bot_messenger/issues/3
"SCHEDULER": True,

}


# Simple string messages that can be replaced
app.config["MSG"] = {

"NEW_USER": lambda name, reg_id: (
f"Hey there, {name}! "
"I'm Boyd Bot - your university chatbot, here to make things easier. "
f"To get started, register here: {app_url}/register/{reg_id}"
),

"NOT_REG": lambda reg_id: (
"It doesn't seem like you've registered yet.\n"
f"Register here: {app_url}/register/{reg_id}"
),

"ERROR_LOGIN": lambda reg_id: (
"Whoops! Something went wrong; maybe your login details changed?\n"
f"Register here: {app_url}/register/{reg_id}"
),

"REG_ACKNOWLEDGE": "Alrighty! We can get started. :D",
"SUCCESS_MSG": "Login successful! You can now close this page and chat to the bot.",
"ONE_TIME_DONE": "You were logged out and since we don't have your credentials, you'll have to register again!",

"SUCCESS_MSG": (
"Login successful! "
"You can now close this page and chat to the bot."
),

"ONE_TIME_DONE": (
"You were logged out and since we don't have your credentials, "
"you'll have to register again!"
),

"ERROR_MSG": "I'm sorry, something went wrong understanding that. :(",

}


# Features you can switch on/off
app.config["FEATURES"] = {
"ONE_TIME_USE": True, # On-going issue (read https://github.com/ineshbose/boyd_bot_messenger/issues/8)
"DEMO": True, # Works for chatbots without platform user-accounts (eg Dialogflow Web Demo / Embedded)
# Simple logging messages that can be replaced
app.config["LOG"] = {

"FORMAT": (
"\033[94m[%(asctime)s] %(levelname)s in %(name)s:"
"\033[96m %(message)s\033[0m"
),

"INVALID_USER": lambda sender_id: f"{sender_id} is not a valid user",

"USER_AUTH": lambda uid, login_result: (
f"{uid} undergoing registration. Result: {login_result}"
),

"RELOGIN": lambda uid: f"{uid} logging in again.",

"AUTH_FAIL": lambda uid, login_result: (
f"{uid} failed to log in. Result: {login_result}"
),

"ERROR": lambda e_name, e_msg, uid, r_data: (
f"Exception ({e_name}) thrown: {e_msg}. {uid} requested '{r_data}'."
),

}

# fmt: on

config = app.config
81 changes: 41 additions & 40 deletions boyd_bot/app.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from flask import request, redirect, render_template, url_for, abort
from . import *
from . import app, blueprint, RegisterForm, webhook_token, wb_arg_name, log
from . import timetable, guard, db, parser, platform
from ._config import config


@blueprint.route("/webhook", methods=["GET", "POST"])
Expand All @@ -8,7 +10,10 @@ def webhook():
if request.method == "GET":
return redirect(url_for(".index"))

if not guard.sanitized([request.headers, request.args], wb_arg_name, webhook_token):
if not (
guard.sanitized(request.headers, wb_arg_name, webhook_token)
or guard.sanitized(request.args, wb_arg_name, webhook_token)
):
abort(403)

request_data = request.get_json()
Expand All @@ -18,26 +23,22 @@ def webhook():
response = user_gateway(request_data, sender_id)

elif db.check_in_reg(sender_id):
response = (
"It doesn't seem like you've registered yet.\n"
"Register here: {}/register/{}"
).format(app_url, db.get_reg_id(sender_id))
response = config["MSG"]["NOT_REG"](db.get_data(sender_id)["reg_id"])

else:
user_data = platform.get_user_data(sender_id)
if (
not sender_id
or ("error" in user_data and platform_user)
or not (platform_user or app.config["FEATURES"]["DEMO"])
or not (platform_user or config["FEATURES"]["DEMO"])
):
log("{} is not a valid user".format(sender_id))
log(config["LOG"]["INVALID_USER"](sender_id))
abort(401)

reg_id = db.insert_in_reg(sender_id, platform_user)
response = (
"Hey there, {}! I'm Boyd Bot - your university chatbot, here to make things easier. "
"To get started, register here: {}/register/{}"
).format(user_data.get("first_name", "new person"), app_url, reg_id)
response = config["MSG"]["NEW_USER"](
user_data.get("first_name", "new person"), reg_id
)

return platform.reply(response)

Expand All @@ -48,8 +49,9 @@ def new_user_registration(reg_id):
if request.method == "GET":
return (
render_template(
app.config["TEMPLATES"]["REG_FORM"],
form=RegisterForm(reg_id=reg_id, remember=db.get_reg_id_result(reg_id)),
config["TEMPLATES"]["REG_FORM"],
allow_remember=db.get_data(reg_id)["platform_user"],
form=RegisterForm(reg_id=reg_id),
)
if db.get_data(reg_id)
else abort(404)
Expand All @@ -66,69 +68,68 @@ def new_user_registration(reg_id):
reg_id = request.form.get("reg_id")
uni_id = request.form.get("uni_id")
uni_pw = request.form.get("uni_pw")

subscribe = request.form.get("subscribe")

remember = (
request.form.get("remember")
if app.config["FEATURES"]["ONE_TIME_USE"]
else db.get_reg_id_result(reg_id)
if config["FEATURES"]["ONE_TIME_USE"]
else db.get_data(reg_id)["platform_user"]
)

uid = db.get_user_id(reg_id)
uid = db.get_data(reg_id)["user_id"]
login_result = timetable.login(uid, uni_id, uni_pw)
log("{} undergoing registration. Result: {}".format(uid, login_result))
log(config["LOG"]["USER_AUTH"](uid, login_result))

if not login_result[0]:
return render_template(
app.config["TEMPLATES"]["REG_FORM"],
form=RegisterForm(reg_id=reg_id, remember=remember),
config["TEMPLATES"]["REG_FORM"],
allow_remember=db.get_data(reg_id)["platform_user"],
form=RegisterForm(reg_id=reg_id),
message=login_result[1],
)

db.delete_data(uid)
db.delete_data(reg_id)
user_details = {"uni_id": uni_id, "uni_pw": uni_pw} if remember else {}
user_details = (
{"uni_id": uni_id, "uni_pw": uni_pw, "subscribe": subscribe}
if remember
else {}
)
db.insert_data(uid, **user_details)
platform.send_message(uid, app.config["MSG"]["REG_ACKNOWLEDGE"])
platform.send_message(uid, config["MSG"]["REG_ACKNOWLEDGE"])

return render_template(app.config["TEMPLATES"]["REG_FORM"], success=True)
return render_template(config["TEMPLATES"]["REG_FORM"], success=True)


def user_gateway(request_data, uid):

try:
user_data = db.get_data(uid)

if not timetable.check_loggedIn(user_data["_id"]):
if not timetable.check_loggedIn(uid):

log("{} logging in again.".format(uid))
user_data = db.get_data(uid)
log(config["LOG"]["RELOGIN"](uid))

if not guard.sanitized(user_data, ["uni_id", "uni_pw"]):
db.delete_data(uid)
return app.config["MSG"]["ONE_TIME_DONE"]
return config["MSG"]["ONE_TIME_DONE"]

login_result = timetable.login(
user_data["_id"], user_data["uni_id"], user_data["uni_pw"]
)

if not login_result[0]:

log("{} failed to log in. Result: {}".format(uid, login_result))
log(config["LOG"]["AUTH_FAIL"](uid, login_result))
db.delete_data(uid)
reg_id = db.insert_in_reg(uid)
reg_id = db.insert_in_reg(*platform.get_id(request_data))

return (
"Whoops! Something went wrong; maybe your login details changed?\n"
"Register here: {}/register/{}"
).format(app_url, reg_id)
return config["MSG"]["ERROR_LOGIN"](reg_id)

message = parser.parse(request_data, uid)

except Exception as e:
log(
"Exception ({}) thrown: {}. {} requested '{}'.".format(
type(e).__name__, e, uid, request_data
)
)
message = app.config["MSG"]["ERROR_MSG"]
log(config["LOG"]["ERROR"](type(e).__name__, e, uid, request_data))
message = config["MSG"]["ERROR_MSG"]

return message
4 changes: 3 additions & 1 deletion boyd_bot/forms.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from flask_wtf import FlaskForm
from wtforms.validators import DataRequired
from wtforms import StringField, PasswordField, SubmitField, HiddenField, BooleanField
from wtforms import StringField, PasswordField
from wtforms import SubmitField, HiddenField, BooleanField


class RegisterForm(FlaskForm):
reg_id = HiddenField("reg_id")
uni_id = StringField("University ID", validators=[DataRequired()])
uni_pw = PasswordField("Password", validators=[DataRequired()])
remember = BooleanField("Remember Me", default=True)
subscribe = BooleanField("Notify Me About My Schedule", default=False)
submit = SubmitField("Login")
Loading

0 comments on commit 5ada8de

Please sign in to comment.