-
Notifications
You must be signed in to change notification settings - Fork 6
/
app.py
116 lines (95 loc) · 3.3 KB
/
app.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
import bottle
from bottle import request, post
from pyicloud import PyiCloudService
from pyicloud.exceptions import PyiCloudFailedLoginException
import difflib
import requests
from users import USERS
# It doesn't allow us to take extra time to process so who cares
send_progressive = False
def get_safe(dic, *keys):
"""
Safely traverse through dictionary chains
:param dict dic:
:param str keys:
:return:
"""
no_d = dict()
for key in keys:
dic = dic.get(key, no_d)
if dic is no_d:
return None
return dic
def response(msg):
return dict(
version='1.0',
response=dict(
outputSpeech=dict(
type='PlainText',
playBehavior='REPLACE_ENQUEUED',
text=msg,
),
shouldEndSession=True
)
)
def notify_user_phones(user, request):
if not user:
return response('I don\'t know who you are.')
user = user.lower()
found_user = difflib.get_close_matches(user, USERS.keys(), n=1)
if len(found_user) == 0:
return response('I don\'t know who {} is.'.format(user))
if send_progressive:
# Send a progressive notice that we're going to look up their
# account. This part is slow so Alexa might time out otherwise.
requests.post(
'https://api.amazonalexa.com/v1/directives',
json=dict(
header=dict(
requestId=get_safe(request, 'request', 'requestId')
),
directive=dict(
type='VoicePlayer.Speak',
speech='Looking for {}\'s devices to call.'.format(user)
)
),
headers=dict(
Authorization='Bearer {}'.format(
get_safe(request, 'context', 'System', 'apiAccessToken'))
))
user = found_user[0]
email, passwd = USERS[user]
api = PyiCloudService(email, passwd)
phones = [d for d in api.devices if d.content['deviceClass'] == 'iPhone']
if len(phones) < 1:
return response('Sorry, I couldn\'t find any iPhones for '
'{}.'.format(user))
for p in phones:
try:
p.play_sound()
except Exception:
pass
return response('Calling {}\'s iPhone'.format(user))
@post('/')
def find_iphone():
request_type = get_safe(request.json, 'request', 'type')
if request_type == 'SessionEndedRequest':
# Just eat these
return
intent = get_safe(request.json, 'request', 'intent')
if request_type != 'IntentRequest' or not intent:
return response('I could not understand your request.')
if intent.get('name') == 'FindIphone':
user = get_safe(intent, 'slots', 'User', 'value')
try:
return notify_user_phones(user, request.json)
except PyiCloudFailedLoginException:
return response('Invalid icloud email or password for '
'{}'.format(user))
except Exception as e:
return response('Whoops, something broke: {}'.format(e))
return response('No idea what you asked for. '
'Try saying, Find My iPhone, John.')
# run bottle
# do not remove the application assignment (wsgi won't work)
application = bottle.default_app()