It uses oauth to authenticate with twitter before opening a twitter user stream, the program then waits for data to appear, when it does, it uses google translate to create an mp3 file which is then stream by mplayer and hey presto, the Pi talks the tweet!
Its not really a finished program, more, a proof of concept, but I thought it might be useful for other people who want to use twitter in their projects and need a starting point. There is also a great tutorial about consuming twitter streams using python, where I got some of the code for this program
There are a few pre-requisites to getting this to work.
Create a twitter app
You need to create a twitter app using your twitter id, you can do this by visiting dev.twitter.com, signing on, and clicking create app; if you are having problems see a previous blog post of mine, automatically posting updates to twitter, which has some in-depth instructions.
Install python-oauth
I used leah's python oauth module to authenticate with twitter.
Install distribute
If you have never installed python modules before you are going to need to install the python setup tools, module, distribute, see blog post, python - installing modules, for info on how to do this.
Install git-core
In order to get the code from github you need to install git-core tools.
sudo apt-get install git-core
Get the code from git
git clone https://github.com/leah/python-oauth.git
Install the module
cd python-oauth
sudo python setup.py install
sudo python setup.py install
Install pycurl
pycurl is used to connect to the twitter streams.
sudo apt-get install python-pycurl
Install mplayer
mplayer is used to output the audio stream.
sudo apt-get install mplayer
Create talking twitter client program
nano ttc.py
Cut and paste the program into it and save.
#!/usr/bin/env python
# An experimental talking twitter client for the Raspberry Pi
# written in Python, by Martin O'Hanlon
# www.stuffaboutcode.com
from oauth.oauth import OAuthRequest, OAuthSignatureMethod_HMAC_SHA1
from hashlib import md5
import json, time, random, math, urllib, urllib2, pycurl, subprocess, sys
# twitter oauth keys, get your from dev.twitter.com
CONSUMER_KEY = 'consumer key'
CONSUMER_SECRET = 'consumer secret'
ACCESS_TOKEN = 'access token'
ACCESS_TOKEN_SECRET = 'access token secret'
# function to download a file from a url, used for testing
def downloadFile(url, fileName):
fp = open(fileName, "wb")
curl = pycurl.Curl()
curl.setopt(pycurl.URL, url)
curl.setopt(pycurl.WRITEDATA, fp)
curl.perform()
curl.close()
fp.close()
# returns the appropriate google speech url for a particular phrase
def getGoogleSpeechURL(phrase):
googleTranslateURL = "http://translate.google.com/translate_tts?tl=en&"
parameters = {'q': phrase}
data = urllib.urlencode(parameters)
googleTranslateURL = "%s%s" % (googleTranslateURL,data)
return googleTranslateURL
# function to download an mp3 file for a particular phrase, used for testing
def downloadSpeechFromText(phrase, fileName):
googleSpeechURL = getGoogleSpeechURL(phrase)
print googleSpeechURL
downloadFile(googleSpeechURL, fileName)
# output phrase to audio using mplayer
def speakSpeechFromText(phrase):
googleSpeechURL = getGoogleSpeechURL(phrase)
subprocess.call(["mplayer",googleSpeechURL], shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# class for managing tokens
class Token(object):
def __init__(self,key,secret):
self.key = key
self.secret = secret
def _generate_nonce(self):
random_number = ''.join(str(random.randint(0, 9)) for i in range(40))
m = md5(str(time.time()) + str(random_number))
return m.hexdigest()
# talking twitter client
class TalkingTwitterStreamClient:
def __init__(self, streamURL):
self.streamURL = streamURL
self.buffer = ""
self.conn = pycurl.Curl()
self.conn.setopt(pycurl.URL, self.streamURL)
self.conn.setopt(pycurl.WRITEFUNCTION, self.on_receive)
self.conn.perform()
def on_receive(self, data):
sys.stdout.write(".")
self.buffer += data
if data.endswith("\n") and self.buffer.strip():
content = json.loads(self.buffer)
self.buffer = ""
#debug - output json from buffer
#print content
if "friends" in content:
self.friends = content["friends"]
if "text" in content:
print u"{0[user][name]}: {0[text]}".format(content).encode('utf-8')
speakSpeechFromText(u"A tweet from {0[user][name]}".format(content))
#downloadSpeechFromText(u"A tweet from {0[user][name]}".format(content), "./tweet.mp3")
speakSpeechFromText(u"{0[text]}".format(content))
#downloadSpeechFromText(u"{0[text]}".format(content), "./tweet.mp3")
# get the url needed to open the twitter user stream, including signature after authentication
def getTwitterUserStreamURL():
STREAM_URL = "https://userstream.twitter.com/2/user.json"
access_token = Token(ACCESS_TOKEN,ACCESS_TOKEN_SECRET)
consumer = Token(CONSUMER_KEY,CONSUMER_SECRET)
parameters = {
'oauth_consumer_key': CONSUMER_KEY,
'oauth_token': access_token.key,
'oauth_signature_method': 'HMAC-SHA1',
'oauth_timestamp': str(int(time.time())),
'oauth_nonce': access_token._generate_nonce(),
'oauth_version': '1.0',
}
oauth_request = OAuthRequest.from_token_and_callback(access_token,
http_url=STREAM_URL,
parameters=parameters)
signature_method = OAuthSignatureMethod_HMAC_SHA1()
signature = signature_method.build_signature(oauth_request, consumer, access_token)
parameters['oauth_signature'] = signature
data = urllib.urlencode(parameters)
return "%s?%s" % (STREAM_URL,data)
# Run Talking Twitter Client
client = TalkingTwitterStreamClient(getTwitterUserStreamURL())
#some useful debug commands, comment out running the client and uncomment the command
#get twitter stream url, including oauth signature
#print getTwitterUserStreamURL()
#download a speech file from google
#downloadSpeechFromText("hello, how are you today", "./downloadedFile.mp3")
#output phrase to audio
#speakSpeechFromText("hello, how are you today")
#start talking twitter client
# An experimental talking twitter client for the Raspberry Pi
# written in Python, by Martin O'Hanlon
# www.stuffaboutcode.com
from oauth.oauth import OAuthRequest, OAuthSignatureMethod_HMAC_SHA1
from hashlib import md5
import json, time, random, math, urllib, urllib2, pycurl, subprocess, sys
# twitter oauth keys, get your from dev.twitter.com
CONSUMER_KEY = 'consumer key'
CONSUMER_SECRET = 'consumer secret'
ACCESS_TOKEN = 'access token'
ACCESS_TOKEN_SECRET = 'access token secret'
# function to download a file from a url, used for testing
def downloadFile(url, fileName):
fp = open(fileName, "wb")
curl = pycurl.Curl()
curl.setopt(pycurl.URL, url)
curl.setopt(pycurl.WRITEDATA, fp)
curl.perform()
curl.close()
fp.close()
# returns the appropriate google speech url for a particular phrase
def getGoogleSpeechURL(phrase):
googleTranslateURL = "http://translate.google.com/translate_tts?tl=en&"
parameters = {'q': phrase}
data = urllib.urlencode(parameters)
googleTranslateURL = "%s%s" % (googleTranslateURL,data)
return googleTranslateURL
# function to download an mp3 file for a particular phrase, used for testing
def downloadSpeechFromText(phrase, fileName):
googleSpeechURL = getGoogleSpeechURL(phrase)
print googleSpeechURL
downloadFile(googleSpeechURL, fileName)
# output phrase to audio using mplayer
def speakSpeechFromText(phrase):
googleSpeechURL = getGoogleSpeechURL(phrase)
subprocess.call(["mplayer",googleSpeechURL], shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
# class for managing tokens
class Token(object):
def __init__(self,key,secret):
self.key = key
self.secret = secret
def _generate_nonce(self):
random_number = ''.join(str(random.randint(0, 9)) for i in range(40))
m = md5(str(time.time()) + str(random_number))
return m.hexdigest()
# talking twitter client
class TalkingTwitterStreamClient:
def __init__(self, streamURL):
self.streamURL = streamURL
self.buffer = ""
self.conn = pycurl.Curl()
self.conn.setopt(pycurl.URL, self.streamURL)
self.conn.setopt(pycurl.WRITEFUNCTION, self.on_receive)
self.conn.perform()
def on_receive(self, data):
sys.stdout.write(".")
self.buffer += data
if data.endswith("\n") and self.buffer.strip():
content = json.loads(self.buffer)
self.buffer = ""
#debug - output json from buffer
#print content
if "friends" in content:
self.friends = content["friends"]
if "text" in content:
print u"{0[user][name]}: {0[text]}".format(content).encode('utf-8')
speakSpeechFromText(u"A tweet from {0[user][name]}".format(content))
#downloadSpeechFromText(u"A tweet from {0[user][name]}".format(content), "./tweet.mp3")
speakSpeechFromText(u"{0[text]}".format(content))
#downloadSpeechFromText(u"{0[text]}".format(content), "./tweet.mp3")
# get the url needed to open the twitter user stream, including signature after authentication
def getTwitterUserStreamURL():
STREAM_URL = "https://userstream.twitter.com/2/user.json"
access_token = Token(ACCESS_TOKEN,ACCESS_TOKEN_SECRET)
consumer = Token(CONSUMER_KEY,CONSUMER_SECRET)
parameters = {
'oauth_consumer_key': CONSUMER_KEY,
'oauth_token': access_token.key,
'oauth_signature_method': 'HMAC-SHA1',
'oauth_timestamp': str(int(time.time())),
'oauth_nonce': access_token._generate_nonce(),
'oauth_version': '1.0',
}
oauth_request = OAuthRequest.from_token_and_callback(access_token,
http_url=STREAM_URL,
parameters=parameters)
signature_method = OAuthSignatureMethod_HMAC_SHA1()
signature = signature_method.build_signature(oauth_request, consumer, access_token)
parameters['oauth_signature'] = signature
data = urllib.urlencode(parameters)
return "%s?%s" % (STREAM_URL,data)
# Run Talking Twitter Client
client = TalkingTwitterStreamClient(getTwitterUserStreamURL())
#some useful debug commands, comment out running the client and uncomment the command
#get twitter stream url, including oauth signature
#print getTwitterUserStreamURL()
#download a speech file from google
#downloadSpeechFromText("hello, how are you today", "./downloadedFile.mp3")
#output phrase to audio
#speakSpeechFromText("hello, how are you today")
#start talking twitter client
python ttc.py
Press CTRL C to exit and wait a few moments, this is for pycurl to give up the connection.
Limitations (found so far!)
Google translate seems to have a maximum length of string it will create an mp3 for, I found in testing that some tweets weren't output and it was because google didn't return an mp3 because it was too long.
There is no mechanism for recovering from errors, such as a the stream going down, it just falls over!
Hey Martin,
ReplyDeletei found your great tutorial and i am happy to have it nearly working.
but sadly i get an error when i start the ttc.py
Here is the output which isnt very instructive for me:
------------------------------------------------
.Traceback (most recent call last):
File "ttc.py", line 70, in on_receive
content = json.loads(self.buffer)
File "/usr/lib/python2.7/json/__init__.py", line 326, in loads
return _default_decoder.decode(s)
File "/usr/lib/python2.7/json/decoder.py", line 365, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python2.7/json/decoder.py", line 383, in raw_decode
raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded
Traceback (most recent call last):
File "ttc.py", line 112, in
client = TalkingTwitterStreamClient(getTwitterUserStreamURL())
File "ttc.py", line 64, in __init__
self.conn.perform()
pycurl.error: (23, 'Failed writing body (0 != 100)')
------------------------------------------------
Do you have an idea where the problem could be?
greetings
It sounds like you arent receiving a valid return from twitter. Have you correctly setup the application in twitter?
Deleteon twitter i think i have done it right but the problem seems to be that i didn't download and put into the right directory the essential twitterizer and json files as you described in the other blog post:http://www.stuffaboutcode.com/2012/04/automatically-posting-updates-to.html
Deletei think i am going to find another solution because this seems to be too heavy for me :D
i just want to let my raspi read the latest posts on my twitter account.
So i thougt i could use the twitter api to get the new posts, put them into a text file or something and let the simple text-to-speech tool "festival" or "flite" read that.
gonna work on this, after i got an usb sound card to avoid the bad noise at the analogue output on the pi.
thanks for your reply
greetings
Ok, well if you need any additional help, just ask. You shouldn't need either the json or twitterizer dll's, they are part of an entirely different solution which I built for Microsoft .net and Windows.
Deletethanks for your kindness, i just got it working with use of the still available rss links and a few lines of python code and python-feedparser.
Deletecombinated with my local siriproxy (also running on the same pi) its pretty lazy to ask siri about new tweets and let my raspberry answer :D
iLove it
maybe i am going to publish it on one of my old and long-time-not-updated or a new blog ;)
keep up yours! and very impressive how fast and kind you answer to your comments ;)
greetz
Pete
Hello
ReplyDeleteI am working on my RPi and i can send tweets. A question i have is HOW TO RECIEVE TWEETS AND HOW TO READ THE TWEET? We are doing a project where we are trying to connect the RPi to the Traffic light and we are change the signal by sending a tweet i.e RED/GREEN/YELLOW. I need the coding to read make RPi read the tweets and put hi/low voltage signal to the output pin, resulting in the signal changing.
Also is there any other way we can do this…. like can i do by sending e-mails to raspberry pi that are readable and it will enable the output pin to HIGH.. please e-mail me at sahil_verma@live.ca
I will look forward for your answer.
Thank You for your help
Regards
Sahil Verma
The code above gives you what you need to wait for a tweet to arrive, read it when it does and then react to it. You just need to create your code to output to the gpio based on the content of the tweet.
ReplyDeleteIf you looking for a more indepth description than that, sorry! Unless of course your paying then lets do business! ;-)
I can get live tweets on my terminal but there is no audio to it?? Where did I go wrong?
ReplyDeleteDont know, did you plug a speaker in? Try putting some headphones straight into the audio jack.
ReplyDeleteHi Martin!
ReplyDeleteThanks for sharing this project! I am about to give it a try but I would like twitter to search for tweets with certain hashtags, any idea of how that would be implemated in your code?
Best regards,
Jacob
Check out the streaming api's on twitter https://dev.twitter.com/docs/streaming-apis
DeleteThanks, that looks like a good resource. I am though having problem with the original code of yours, when I try to run the program I get:
ReplyDelete" File "ttc.py", line 19
fp = open(fileName, "wb")
Syntax error: invalid syntax."
I´m trying to get this to work to be used in a furniture that will talk through twitter about the place it´s in.
Jacob
Did you cut and paste the code from a browser? This can sometimes introduce errors with odd characters. Perhaps try again?
DeleteHi Martin, I am creating a twitter application where your tutorial sounds really useful. After following the steps, I get this error:
ReplyDelete.Traceback (most recent call last):
File "ttc.py", line 70, in on_receive
content = json.loads(self.buffer)
File "/usr/lib/python2.7/json/__init__.py", line 326, in loads
return _default_decoder.decode(s)
File "/usr/lib/python2.7/json/decoder.py", line 365, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python2.7/json/decoder.py", line 383, in raw_decode
raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded
Traceback (most recent call last):
File "ttc.py", line 112, in
client = TalkingTwitterStreamClient(getTwitterUserStreamURL())
File "ttc.py", line 64, in __init__
self.conn.perform()
pycurl.error: (23, 'Failed writing body (0 != 1529)')
You suggested Peter to check if the twitter application is setup properly. I did check twice, but have no clue what is missing. Your answer would be helpful.
Thanks.
Yesha
Help it runs fine for a little bit then crashes is there any advice you could give me to fix this
ReplyDeleteTraceback (most recent call last):
File "ttc.py", line 82, in on_receive
speakSpeechFromText(u"{0[text]}".format(content))
File "ttc.py", line 42, in speakSpeechFromText
googleSpeechURL = getGoogleSpeechURL(phrase)
File "ttc.py", line 30, in getGoogleSpeechURL
data = urllib.urlencode(parameters)
File "/usr/lib/python2.7/urllib.py", line 1312, in urlencode
v = quote_plus(str(v))
UnicodeEncodeError: 'ascii' codec can't encode character u'\u2026' in position 139: ordinal not in range(128)
Traceback (most recent call last):
File "ttc.py", line 112, in
client = TalkingTwitterStreamClient(getTwitterUserStreamURL())
File "ttc.py", line 64, in __init__
self.conn.perform()
Picking Up The Tweets But Just Not Playing Audio
ReplyDeleteAn errors or just no sound? If there are no errors I would put my money on not the right output being selected. Are you using a speaker into the headphone jack or hdmi? Use 'sudo raspi-config', and Advanced, to pick the right audio output.
DeleteThanks for your help:
DeleteI Manage To Get Sound From The Internet Fine, When I Do CTRL+C it comes up with an error from translate. It is the same problem that RaiDevansh had.
hey martin,
ReplyDeletewhen copy and pasting your code are these the only things that need changing?
CONSUMER_KEY = 'consumer key'
CONSUMER_SECRET = 'consumer secret'
ACCESS_TOKEN = 'access token'
ACCESS_TOKEN_SECRET = 'access token secret'
This comment has been removed by the author.
ReplyDelete