It's the Google Voice API for node.js. Except there is no official "Google Voice API", so node-google-voice is also the only (javascript) Google Voice API. It allows you to
- place calls
- send SMS's
- schedule calls and SMS's from inside node or with Google Calendar
- access & manipulate GV data
First install node-google-voice in the usual manner for node:
npm install google-voice
See the npm page for information on installing npm, the Node Package Manager.
node-google-voice depends on:
- googleclientlogin - used for authentication for Google Voice and Google Calendar
- xml2js - used for extracting JSON & HTML data from Google Voice XML responses
- jsdom - used for extracting SMS messages from Google Voice HTML responses
npm should take care of dependencies, but in case it fails to do so, try installing these modules (and their dependencies) independently.
node-google-voice has only been tested in Node 0.4.7. Theoretically, it should work fine in older versions, as long as:
- those versions are supported by the dependencies and
- the particular Node version's
https
is not much different from v0.4.7's. This is the only major core Node module used by node-google-voice.
Google Voice client instances are made by calling voiceClient = new require('google-voice').Client(options)
where
where options
is an Object with the following properties:
email
(String) - your Google Voice login emailpassword
(String) - your Google Voice passwordrnr_se
(String)-
This last item is a unique identifier for each Google Voice account. You can get it by logging into Google Voice web front-end and running the following javascript bookmarklet in the browser window:
javascript:alert('Your rnr_se is:\n\n'+_gcData._rnr_se);
-
You only have to do this once, because the
_rnr_se
doesn't change. (...at least it hasn't changed for me since I have become aware of it. If something doesn't work in your GV.Client, first check that your_rnr_se
hasn't changed.)
-
var GV = require('google-voice');
var voiceClient = new GV.Client({
email: '[email protected]',
password: 'password',
rnr_se: '_rnr_se from Google Voice web page'
});
In the examples below:
-
textMessage
(String) is the SMS to send -
outgoingNumber
(String) is the number to which the forwardingNumber will be connected to or the number(s) to which an SMS will be sent. For SMS's, outgoingNumber can be an Array of Strings, to send the SMS to multiple phone numbers. -
forwardingNumber
(String) is one of the forwarding numbers set up in your GV account. This is the number that will ring on YOUR end. -
phoneType
(Integer) is the phone type of the forwardingNumber set up in your GV account. It can be one of the following values:- 1 - Home
- 2 - Mobile
- 3 - Work
- 7 - Gizmo
-
body
is either:- an Object formed from the JSON response from Google Voice (typically something like
{ ok: true, data: { code: 0 } }
or{ ok: false, error: 'Cannot complete call.' }
or{ ok: false, data: { code: 20 } }
- a String containing the HTML response from Google Voice (for cases when the body of the response doesn't contain JSON)
The body object/string can change as Google makes changes to how Google Voice works. You can attempt to map the different codes to different events, but this is unreliable due to the undocumented and unofficial nature of the GV 'api'.
- an Object formed from the JSON response from Google Voice (typically something like
-
response
(http.ClientResponse) is an instance of Node's http.ClientResponse. This is the given response for that particular request. It is provided for cases where you would like to get more information about what went wrong (or right!) and act on it.
voiceClient.placeCall(outgoingNumber,forwardingNumber,phoneType,function(body,response){
console.log(body);
});
or
voiceClient.placeCall(outgoingNumber,forwardingNumber,phoneType);
voiceClient.sendSMS(outgoingNumber,textMessage,function(body,response){
console.log(body);
});
or
voiceClient.sendSMS(outgoingNumber,textMessage);
voiceClient.sendSMS([outgoingNumber1,outgoingNumber2],textMessage,function(body,response){
console.log(body);
});
Note that the callbacks
are optional.
Calls and SMSs can be scheduled to take place in the future. The GV.Client instance contains a schedule
object that is populated with event details when events are scheduled successfully. After the events execute (i.e a call is made or an SMS is sent), that event will be removed from the schedule object.
The name of each event object in the schedule
object is the ISO String representation of the Date of the event (using Date.toISOString()
). So, for the example events below, set to take place on 12/25/2011 at 8:00 AM, voiceClient.schedule
will contain the object voiceClient.schedule['2011-12-25T13:00:00.000Z']
.
Each scheduled event ( voiceClient.schedule[ISOdateString]
) will contain at least the following properties:
type
(String): will be either'call'
or'sms'
, indicating the event typetimer
(timeoutId): the timer object created by thesetTimeout()
that schedules the event
The other properties of voiceClient.schedule[ISOdateString]
will be event-specific items such as outgoingNumber
, forwardingNumber
, text
, etc...
Events are scheduled with voiceClient.scheduler(type,date,...,eventCallback,scheduleCallback)
where
type
(String) is either'sms'
or'call'
date
(Array or Date) is the time of the event. Events can be scheduled using an array of the form [YEAR,MONTH,DAY,HOUR (24-hr format),MINUTE] or with a Date object....
represents the normal variables associated with the event (such asoutgoingNumber
,textMessage
, etc...see above)eventCallback
(Function) is of the formfunction(body,response), where
bodyand
response` are the same Objects as described earlier in the Preliminaries section. This callback is called AT THE TIME OF THE EVENT.scheduleCallback
(Function) is of the formfunction(schedulingID, scheduledEvent)
. This callback is called after the event has been successfully SCHEDULED. The parameters are:schedulingID
(String) is the ISO string representation of the date of the event.sheduledEvent
(Object) is the event object fromvoiceClient.schedule
corresponding to the scheduled event.
NOTE: If the date & time of an event is before the current system time, the event will not be scheduled. The scheduling request will fail silently. (I plan to change this soon so that the scheduleCallback
will be notified of a failed scheduling request.)
NOTE: Only one event can be scheduled for a particular time, regardless of event type. If the date & time of an event you are trying to add to the schedule matches the date & time of another event already on the schedule, the new event will silently replace the old event. The old event will be silently unscheduled. (TODO: provide a notification to scheduleCallback
about the schedule change.)
Example: Schedule a call for 12/25/2011 at 8:00 AM using an array of the form [YEAR,MONTH,DAY,HOUR,MINUTE] to represent the date:
voiceClient.scheduler('call',[2011,12,25,8,00],outgoingNumber,forwardingNumber,phoneType,
function(body,response){
console.log(body);
},
function(schedulingID, evt){
console.log('scheduled '+evt.type+' to '+evt.outgoingNumber+' on '+new Date(Date.parse(schedulingID)));
});
voiceClient.scheduler('call',new Date(2011,11,25,8,00),outgoingNumber,forwardingNumber,phoneType,
function(body,response){
console.log(body);
});
Example: Schedule an sms to be sent on 12/25/2011 at 8:00 AM using an array of the form [YEAR,MONTH,DAY,HOUR,MINUTE] to represent the date:
voiceClient.scheduler('sms',[2011,12,25,8,00],outgoingNumber,'Merry Christmas!',null,
function(schedulingID, evt){
console.log('scheduled '+evt.type+' to '+evt.outgoingNumber+' on '+ new Date(Date.parse(schedulingID)));
});
Example: Schedule an sms to be sent on 12/25/2011 at 10:00 PM using a Date object to represent the date:
voiceClient.scheduler('sms',new Date(2011,11,25,22,00),outgoingNumber,'Hope you had a wonderful Christmas!');
Note that all of the above requests are valid: you can include both callbacks, just one of the callbacks, or no callbacks.
Schedule calls from your Google Calendars with: voiceClient.scheduleCallsFromCalendar(callLabel,forwardingNumber,phoneType,eventCallback,scheduleCallback)
where
callLabel
(String) is the string used in Calendar event title/details in the formatcallLabel=outgoingNumber
whereoutgoingNumber
is the number you will be connected to.forwardingNumber
(String) is the number on YOUR end that will ringphoneType
(Integer) is the phone type offorwardingNumber
(see above)eventCallback
(Function(body,response)) is the callback called at the time of the event (see above)scheduleCallback
(Function(scheduleID,evt)) is the callback called when the event is scheduled (see above)
This searches your Google Calendars for events with callLabel
(String) in the event title or event description, and schedules calls for the outgoingNumber
at that event time.
The format in the event title/details should be: callLabel=outgoingNumber
. Note the absence of spaces in that string.
For example, if callLabel='GVCall'
, then the event title or description in Google Calendar can contain GVCall=18005551212
to schedule a call to 18005551212.
NOTE: If the callLabel
is found in both the event title and description, the one in the title will be used.
Use case: Using this inside setInterval()
is an easy way to periodically add new events to the schedule as they are added in Google Calendar.
voiceClient.scheduleCallsFromCalendar(callLabel,forwardingNumber,phoneType,
function(body,response){
console.log(body);
},
function(schedulingID, evt){
console.log('scheduled '+evt.type+' to '+evt.outgoingNumber+' on '+ new Date(Date.parse(schedulingID)));
});
or
voiceClient.scheduleCallsFromCalendar(callLabel,forwardingNumber,phoneType,
function(body,response){
console.log(body);
});
or
voiceClient.scheduleCallsFromCalendar(callLabel,forwardingNumber,phoneType,null,
function(schedulingID, evt){
console.log('scheduled '+evt.type+' to '+evt.outgoingNumber+' on '+ new Date(Date.parse(schedulingID)));
});
or
voiceClient.scheduleCallsFromCalendar(callLabel,forwardingNumber,phoneType);
Note that all of the above requests are valid: you can include both callbacks, just one of the callbacks, or no callbacks.
To remove one event from the schedule, call voiceClient.unscheduler(date)
where date
is the dateTime of the event and is one of the following types:
- Array (in the format discussed above)
- Date
- String (in the ISO format)
voiceClient.unscheduler(date)
returns true
if an event was unscheduled, false
if not (it may be false
simply because no event was scheduled at that time).
voiceClient.unscheduler([2011,12,25,8,00]);
or
voiceClient.unscheduler(new Date(2011,11,25,8,00));
or
voiceClient.unscheduler('2011-12-25T13:00:00.000Z');
To unschedule all scheduled events, use voiceClient.unscheduleAll(callback)
. The callback
is optional. This was added in v0.0.2
.
voiceClient.unscheduleAll(function(){
console.log('The schedule has been cleared.');
})
All data requests are of the following form: voiceClient.get(request,limit,callback)
where:
-
request
(String or Object) is either:- one of the following self-explanatory Strings:
'history' 'inbox' 'spam' 'trash' 'starred' 'sms' 'voicemail' 'placed' 'missed' 'received' 'recorded'
- or a query object:
{query: 'searchString'}
This last form retrieves messages that match the given
searchString
in some way. The search function is entirely implemented by Google Voice, so the search results are the same as would be returned by searching from the Google Voice web interface. -
limit
(Integer) limits the number of returned messages to a certain number, ordered by time. Solimit=1
will return the most recent message of the given request andlimit=10
will return the 10 most recent messages. Iflimit = -1
, ALL messages will be returned (can be slow for very large message lists). -
callback
(Function) is of the formfunction(error,messages)
wheremessages
is an array of message objects. Each message object is formed from the JSON response from Google Voice; the format is therefore subject to change. At the time of this writing, an example message looked like this:{ id: 'someStringIdentifier', phoneNumber: '+18005551212', displayNumber: '(800) 555-1212', startTime: '1305138033000', displayStartDateTime: '5/11/11 2:20 PM', displayStartTime: '2:20 PM', relativeStartTime: '3 weeks ago', note: '', isRead: true, isSpam: false, isTrash: false, star: false, labels: [ 'missed', 'all' ], type: 0, children: '' }
SMS messages are grouped under one message ID by Google Voice. In order to present all text messages in an SMS thread, an extra processing step occurs for SMS messages which attaches two properties to the message object:
-
lastText
(String) is the most recent SMS in the thread -
thread
(Array) is the collection of text messages in the SMS thread. Each item in this Array is a DOM element (made with jsdom) that has three children, corresponding to thetime
,from
, andtext
of the SMS. A convenience method is provided to extract this information from text message DOM elements:voiceClient.parseSMS(param,msgDomElement)
This returns the requested parameter of the text message where
param
(String) is one of 'time', 'from', or 'text'msgDomElement
is the DOM element from the thread Array
voiceClient.get('missed',function(err,msgs){
if(err){ console.log('error on request: '+err); return; }
console.log('missed call from ' + msgs[0].phoneNumber + ' at ' + msgs[0].displayStartDateTime);
});
voiceClient.get('sms',-1,function(err,msgs){
if(err){ console.log('error on request: '+err); return; }
console.log(msgs.length + ' SMSs found.');
});
voiceClient.get('inbox',10,function(err,msgs){
if(err){ console.log('error on request: '+err); return; }
for(var i=0; i<msgs.length; i++){
console.log(msgs[i]);
}
});
voiceClient.get('sms',1,function(err,msgs){
if(err){ console.log('error on request: '+err); return; }
console.log('latest SMS thread:');
for(var i=0;i<msgs[0].thread.length;i++){
var currentMsg = msgs[0].thread[i];
console.log(voiceClient.parseSMS('time',currentMsg)+' '+voiceClient.parseSMS('from',currentMsg)+voiceClient.parseSMS('text',currentMsg) );
}
});
voiceClient.get({search: 'mom'},-1,function(err,msgs){
if(err){ console.log('error on request: '+err); return; }
console.log(msgs.length + ' messages found.');
});
Every time a voiceClient.get()
request is made, the voice client's unreadCounts
property is updated with the most current information from Google Voice. At the time of this writing, an example voiceClient.unreadCounts
object looked like this:
{ all: 3,
inbox: 3,
missed: 0,
placed: 0,
received: 0,
recorded: 0,
sms: 1,
starred: 0,
trash: 4,
unread: 3,
voicemail: 2
}
All data manipulation requests are of the following form: voiceClient.set(param,messageID,callback)
where:
-
param
(String) is one of the following Strings:'markRead' 'markUnread' 'archive' 'unarchive' 'star' 'unstar' 'deleteForever' 'toggleTrash' //calling this on a message will move it to the inbox if it is in the trash OR will move it to the trash if it is somewhere else
-
messageID
(String or Array) is the String/Array of unique Google Voice message id(s). This ID can be had from the message objects returned byvoiceClient.get()
(discussed earlier) -
callback
(Function) is of the form function(body, response) wherebody
andresponse
are described above in the Preliminaries section
voiceClient.set('star',messageID,function(body,response){
console.log(body);
})
voiceClient.set('archive',[messageID1,messageID2,messageID3],function(body,response){
console.log(body);
})
- Detect removed scheduling events in Google Calendar
- Set message notes
- Schedule SMS's with Google Calendar
- Get and set Google Voice settings
- Retrieve contacts
- Download/stream voicemail MP3s
What? This project is UNLICENSED .
Google does not have an official Google Voice API. Therefore, the nature of the requests and returned data can change without notice. It is unlikely to change often or soon, but I will make all efforts to keep up with the most current implementation. If you have any issues, please give me a shout, and I'll do my best to address them. I have not trained as a developer (I code only as a hobby), so I'm also open to any constructive criticism on best coding practices and the like.
Enjoy!