-
Notifications
You must be signed in to change notification settings - Fork 4
/
testapp.py
executable file
·254 lines (216 loc) · 8.44 KB
/
testapp.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# request parsing libaries
import json
# system libraries
import os
# database related libraries
import urllib.parse
import uuid
import psycopg2
# flask libraries
from flask import Flask, request, make_response, jsonify
# Bot libraries
from fbmq import Template, fbmq
# wellness libaries
from packages.wellness.health import health_resources, health_concern_msg
# dining
from packages.dining.open_hall_finder import dininghallisOpen_msg
# from dining.menu_scraper import dining_hall_menu_msg
from packages.dining.dining import dining_events_msg
from packages.dining.dining import dining_hall_food_request_msg
from packages.dining.dining import dining_hall_menu_msg
# academic
from packages.academic.library_hours import libraries_msg
from packages.academic.academic_calendar import calendar_msg
from packages.academic.printers import printers_msg
# housing
from packages.housing.cutv_channels import tv_network_msg
from packages.housing.laundry import open_machines_msg
# offcampus
from packages.offcampus.broadway import broadway_rush_msg
from packages.offcampus.food_recommendations import offcampus_dining_request_msg
from packages.offcampus.mta import mta_subway_info_msg
from packages.offcampus.food_hours import offcampus_dining_hours_msg
# clubs
from packages.clubs.news import news_msg
from packages.clubs.clubs import clubs_msg
# etc
from packages.etc.memes import get_meme_msg
from packages.etc.wisdomsearch import wisdom_search
from packages.etc.weather import weather_msg
# internal libraries
from packages.internal.postbacks import intro_reply, health_reply
from packages.internal.postbacks import subscriptions_reply
from packages.internal.postbacks import current_features_msg
# density
from packages.density.density import density_msg
MAX_MESSAGE_LENGTH = 160 # Pretty sure that max message length is 160 for dialogflow
app = Flask(__name__)
app.config.update(
SECRET_KEY=os.environ['SECRET_KEY']
)
app.config["DEBUG"] = True
# Dictionary of all module interface functions.
Msg_Fn_Dict = {
'clubs': clubs_msg,
'printers': printers_msg,
'mta_subway_info': mta_subway_info_msg,
'campus_news_updates': news_msg,
'tv_network': tv_network_msg,
'libraries': libraries_msg,
'dining_events': dining_events_msg,
'dininghallisOpen': dininghallisOpen_msg,
'offcampus_dining_request': offcampus_dining_request_msg,
'offcampus_dining_hours': offcampus_dining_hours_msg,
'broadway_rush': broadway_rush_msg,
'calendar': calendar_msg,
'dining_hall_menu': dining_hall_menu_msg,
'dining_hall_food_request': dining_hall_food_request_msg,
'weather': weather_msg,
'current_features': current_features_msg,
'health_concern': health_concern_msg,
'web.search': wisdom_search,
'meme': get_meme_msg,
'laundry': open_machines_msg,
'density': density_msg
}
#################
def chunkify(msg):
""" Break message into chunks that are below
Dialogflow's max character limit.
This uses a generator comprehension to be efficient.
"""
return (msg[0+i:MAX_MESSAGE_LENGTH+i] for i in range(0, len(msg), MAX_MESSAGE_LENGTH))
def get_generic_or_msg(intent, result):
""" The master method. This method takes in the
intent and the result dict structure
and calls the proper interface method. """
return Msg_Fn_Dict[intent](result)
def add_string_response(msg, response):
"""
If the response from the package functions is a string,
then we want to parse it to make sure that it doesn't
exceed the character limit, and then add that to the
response message.
"""
for chunk in chunkify(msg):
message = { "text": { "text": [ chunk ] } }
response['fulfillmentMessages'].append(message)
def add_template_list_response(tlist, response):
"""
If the response from the package function is a Template.List,
then we want to parse the container and then add its
information to the response message.
"""
for element in tlist.payload['elements']:
add_generic_element(element, response)
def add_generic_element(element, response):
"""
A Template.GenericElement is the type that is contained in a
Template.List; we want to make sure that we get the info from this
data structure properly so that we can emulate how Facebook
would send a response back to a user.
"""
message = { "platform": "ACTIONS_ON_GOOGLE",
"card" : {
"title": element.title,
"subtitle": element.subtitle,
"imageUri": element.image_url,
"buttons": format_buttons(element.buttons)
}
}
print(element)
response['fulfillmentMessages'].append(message)
def format_buttons(buttons):
"""
In the typical Facebook response message, it sends back pressable
buttons. We don't really care that much about that for the
testing(for now). We just want to be sure that we're sending pictures/text
properly. Returns a list.
"""
if not buttons: return []
formatted_buttons = []
for button in buttons:
postback = button.url if button.type == 'web_url' else button.payload
button_object = {
"text": button.title,
"type": button.type,
"postback": postback
}
formatted_buttons.append(button_object)
return formatted_buttons
def init_dialogflow_response():
""" Typical dialogflow response template for v1 """
resp = { "fulfillmentText": "",
"fulfillmentMessages" : []
}
return resp
# Quick hack in order to simulate the class that v2 would return
class v2_query:
def __init__(self, queryResult):
self.parameters = queryResult['parameters']
self.intent = queryResult['intent']
###############################################
urllib.parse.uses_netloc.append("postgres")
url = urllib.parse.urlparse(os.environ['DATABASE_URL'])
conn = psycopg2.connect(
database=url.path[1:],
user=url.username,
password=url.password,
host=url.hostname,
port=url.port
)
cur = conn.cursor()
@app.route('/webhook', methods=['GET'])
def validate():
if request.args.get(
'hub.mode',
'') == 'subscribe' and request.args.get(
'hub.verify_token',
'') == os.environ['VERIFY_TOKEN']:
print("Validating webhook")
return request.args.get('hub.challenge', '')
return "Failed validation. Make sure the validation tokens match."
@app.route('/webhook', methods=['POST'])
def message_handler():
req = request.get_json(force=True) # Get the post request, get it in json format
res = '' # Response to the post query
intentName = '' # intent Name
result = None # Dialogflow's default response to the request
response = init_dialogflow_response() # POST request response (what we will send back to query)
# Get the intentName from the post request
# We don't Necessarily need to use get(), but an Attribute Error
# is more intuitive than a KeyError since we're working
# with dict() objects derived from JSON
text = {}
try:
result = v2_query(req.get('queryResult'))
intentInfo = result.intent
intentName = intentInfo.get('displayName')
except AttributeError as e :
print(json.dumps(req, indent=4, sort_keys=True))
print(str(e))
return 'json error'
#Check the intent name
if intentName in Msg_Fn_Dict:
webhook_resp = get_generic_or_msg(intentName, result)
print(type(webhook_resp))
if isinstance(webhook_resp, Template.List):
print('We got a template list response!')
add_template_list_response(webhook_resp, response)
elif isinstance(webhook_resp, str):
print('We got a string list response!')
add_string_response(webhook_resp, response)
else:
print("What the hell HAPPENED!")
print("Type of the returned response %s" % (type(webhook_resp)))
else:
message = { "text": { "text": [
"Interesting... I don't really know how to respond to that." ]
},
"platform": "ACTIONS_ON_GOOGLE"
}
response['fulfillmentMessages'].append(message)
#Return the value of the response
return make_response(jsonify(response))
if __name__ == "__main__":
app.run()