Jacks_Soft Echo
Problem: If my Skype phone number is included in Google Voice and I am not online when the call is made. Skype goes right to voice mail, as does Google Voice.
Solution: Remove Skype from Google Voice when you are offline.
But of course, it sounds so easy when you put it like that. In fact, I thought I would be able to do this in a single day. Here I am 5 days later, finally writing these words. The biggest problem I cam across though, was with a python library. But thats for another post.
Considering Google Voice is still beta, you can consider this a hack. This is probably not the best way to go about doing this but it has been working for me for 12 hours already.
So here is the script....
from xml.etree import ElementTree
from StringIO import StringIO
import simplejson
user = 'example@gmail.com'
passwd = 'example'
source = 'gvoice-skype script'
account_type = 'GOOGLE'
skype_uid = 'example'
phone_name = 'Skype'
post_content = 'application/x-www-form-urlencoded'
skype_url = 'http://mystatus.skype.com/%s'
auth_url = 'https://www.google.com/voice/account/signin?auth=%s'
login_url = 'https://www.google.com/accounts/ClientLogin'
phones_url = 'https://www.google.com/voice/settings/tab/phones'
forwarding_url = 'https://www.google.com/voice/settings/editDefaultForwarding'
OFFLINE = 0
ONLINE = 1
OTHER = 2
statusHash = {
-305107275330832706:ONLINE,
-2234680221637025580:OFFLINE,
-2821272954437439161:OTHER, #away
4330642765452267998:OTHER, #not available
1274729031316133580:OTHER, #do not disturbe
7654804797299624632:OTHER, #skype me!
}
webConnection = httplib2.Http()
webConnection.follow_redirects = False
gsession = re.compile('(?P<key>S)=(?P<val>grandcentral=[A-Za-z0-9-_]*)')
gvtoken = re.compile('(?P<key>gv)=(?P<val>[A-Za-z0-9-_]*)')
sidtoken = re.compile('(?P<key>SID)=(?P<val>[A-Za-z0-9-_]*)')
authtoken = re.compile('(?P<key>Auth)=(?P<val>[A-Za-z0-9-_]*)')
def main():
#testRequest()
sstatus = skypeOnlineStatus()
tokens = mainAuth()
combineTokens(tokens, reAuth(tokens))
phoneid, gstatus = getPhoneId(phone_name, tokens)
print('Phone id: %s, Enabled: %s' % (phoneid, gstatus))
# If sykpe is doing something else, just set it to online
simpleStatus = sstatus
if simpleStatus == OTHER:
simpleStatus = ONLINE
# Only set if needed
#if gstatus != simpleStatus:
setPhone(phoneid, simpleStatus, tokens)
#else:
#print('No change needed')
def skypeOnlineStatus():
response, body = webConnection.request(skype_url % skype_uid)
imgHash = hash(body)
if imgHash in statusHash:
return statusHash[imgHash]
else:
print('Unknown skype hash')
return OTHER
def mainAuth():
"""Parimary auth, expecting 'Auth' and 'SID' tokens"""
loginStr = urllib.urlencode({'Passwd':passwd, 'source':source, 'Email':user, 'accountType':account_type, 'service':'grandcentral'})
response, body = webConnection.request(login_url, 'POST', loginStr, {'Content-Type':post_content, 'user-agent':source})
tokens = cookieMonster(body)
if 'set-cookie' in response:
allCookies = response['set-cookie']
return tokens
def reAuth(tokens):
"""Another auth, expecting 'gv' and 'grandcentral' tokens"""
morecookies = {}
if 'Auth' in tokens:
response, body = webConnection.request(auth_url % tokens['Auth'], 'GET', None, {'Cookie':cookieJar(tokens), 'user-agent':source})
if 'set-cookie' in response:
morecookies = cookieMonster(response['set-cookie'])
return morecookies
def getPhoneId(phoneName, tokens):
"""Find the id of the phone you wanna toggle. idk if it will ever change, but just in case."""
print('Cookie: %s' % cookieJar(tokens))
response, body = webConnection.request(phones_url,'GET', None, {'Cookie':cookieJar(tokens), 'user-agent':source})
if response.status == 200:
resultingXml = ElementTree.fromstring(body)
phonedata = resultingXml.find('./json')
if phonedata is not None:
phoneObj = simplejson.load(StringIO(phonedata.text))
for phone in phoneObj['phones'].itervalues():
if phone['name'] == phoneName:
return (phone['id'], phone['active'])
def setPhone(id, status, tokens):
"""Set the phone status"""
#Not sure what _rnr_se is but it will 500 without it
postData = 'phoneId=%s&enabled=%s' % (id, status)
postData += '&_rnr_se=bVaWimKRDR3b4AaLlykfAW2zqsk%3D'
response, body = webConnection.request(forwarding_url, 'POST', postData, {'Cookie':cookieJar(tokens),'Content-type':post_content, 'user-agent':source})
if response.status == 200:
print('Phone set to: %s' % status)
else:
print('Set failed')
def cookieMonster(cookies):
"""Parse select key, value pairs out of given string using re"""
cookieMatches = {}
if cookies:
searches = (gsession, gvtoken, sidtoken, authtoken)
for search in searches:
reResult = search.search(cookies)
if reResult:
key = reResult.group('key')
val = reResult.group('val')
cookieMatches[key] = val
else:
print('No cookies?')
return cookieMatches
def cookieJar(tokens):
"""Convert token dict into cookie format"""
jar = ''
for token in tokens:
jar += token + '=' + tokens[token] + '; '
return jar
def combineTokens(origional, addition):
"""Convience function to combine dicts"""
while addition:
addKey, addVal = addition.popitem()
if addKey in origional:
if origional[addKey] != addVal:
origional[addKey] = addVal
else:
origional[addKey] = addVal
if __name__ == "__main__":
main()
Personally, I run it as a cron at 10 minute intervals on one of my servers. I don't know if anyone else will find this useful but here it is.
