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
|
import urllib2
import re
import random
from bs4 import BeautifulSoup
import app_utils
from semantic.numbers import NumberService
WORDS = ["HACKER", "NEWS", "YES", "NO", "FIRST", "SECOND", "THIRD"]
URL = 'http://news.ycombinator.com'
class HNStory:
def __init__(self, title, URL):
self.title = title
self.URL = URL
def getTopStories(maxResults=None):
"""
Returns the top headlines from Hacker News.
Arguments:
maxResults -- if provided, returns a random sample of size maxResults
"""
hdr = {'User-Agent': 'Mozilla/5.0'}
req = urllib2.Request(URL, headers=hdr)
page = urllib2.urlopen(req).read()
soup = BeautifulSoup(page)
matches = soup.findAll('td', class_="title")
matches = [m.a for m in matches if m.a and m.text != u'More']
matches = [HNStory(m.text, m['href']) for m in matches]
if maxResults:
num_stories = min(maxResults, len(matches))
return random.sample(matches, num_stories)
return matches
def handle(text, mic, profile):
"""
Responds to user-input, typically speech text, with a sample of
Hacker News's top headlines, sending them to the user over email
if desired.
Arguments:
text -- user-input, typically transcribed speech
mic -- used to interact with the user (for both input and output)
profile -- contains information related to the user (e.g., phone number)
"""
mic.say("Pulling up some stories.")
stories = getTopStories(maxResults=3)
all_titles = '... '.join(str(idx + 1) + ") " +
story.title for idx, story in enumerate(stories))
def handleResponse(text):
def extractOrdinals(text):
output = []
service = NumberService()
for w in text.split():
if w in service.__ordinals__:
output.append(service.__ordinals__[w])
return [service.parse(w) for w in output]
chosen_articles = extractOrdinals(text)
send_all = chosen_articles is [] and app_utils.isPositive(text)
if send_all or chosen_articles:
mic.say("Sure, just give me a moment")
if profile['prefers_email']:
body = "<ul>"
def formatArticle(article):
tiny_url = app_utils.generateTinyURL(article.URL)
if profile['prefers_email']:
return "<li><a href=\'%s\'>%s</a></li>" % (tiny_url,
article.title)
else:
return article.title + " -- " + tiny_url
for idx, article in enumerate(stories):
if send_all or (idx + 1) in chosen_articles:
article_link = formatArticle(article)
if profile['prefers_email']:
body += article_link
else:
if not app_utils.emailUser(profile, SUBJECT="", BODY=article_link):
mic.say(
"I'm having trouble sending you these articles. Please make sure that your phone number and carrier are correct on the dashboard.")
return
# if prefers email, we send once, at the end
if profile['prefers_email']:
body += "</ul>"
if not app_utils.emailUser(profile, SUBJECT="From the Front Page of Hacker News", BODY=body):
mic.say(
"I'm having trouble sending you these articles. Please make sure that your phone number and carrier are correct on the dashboard.")
return
mic.say("All done.")
else:
mic.say("OK I will not send any articles")
if profile['phone_number']:
mic.say("Here are some front-page articles. " +
all_titles + ". Would you like me to send you these? If so, which?")
handleResponse(mic.activeListen())
else:
mic.say(
"Here are some front-page articles. " + all_titles)
def isValid(text):
"""
Returns True if the input is related to Hacker News.
Arguments:
text -- user-input, typically transcribed speech
"""
return bool(re.search(r'\b(hack(er)?|HN)\b', text, re.IGNORECASE))
|