Class: Contacts::Yahoo

Inherits:
Object
  • Object
show all
Defined in:
lib/contacts/yahoo.rb

Overview

How I can fetch Yahoo Contacts?

To gain access to a Yahoo user’s data in the Yahoo Address Book Service, a third-party developer first must ask the owner for permission. You must do that through Yahoo Browser Based Authentication (BBAuth).

This library give you access to Yahoo BBAuth and Yahoo Address Book API. Just follow the steps below and be happy!

Registering your app

First of all, follow the steps in this page to register your app. If you need some help with that form, you can get it here. Just two tips: inside Required access scopes in that registration form, choose Yahoo! Address Book with Read Only access. Inside Authentication method choose Browser Based Authentication.

Configuring your Yahoo YAML

After registering your app, you will have an application id and a shared secret. Use their values to fill in the config/contacts.yml file.

Authenticating your user and fetching his contacts

yahoo = Contacts::Yahoo.new
auth_url = yahoo.get_authentication_url

Use that auth_url to redirect your user to Yahoo BBAuth. He will authenticate there and Yahoo will redirect to your application entrypoint URL (that you provided while registering your app with Yahoo). You have to get the path of that redirect, let’s call it path (if you’re using Rails, you can get it through request.request_uri, in the context of an action inside ActionController)

Now, to fetch his contacts, just do this:

contacts = wl.contacts(path)
#-> [ ['Fitzgerald', '[email protected]', '[email protected]'],
      ['William Paginate', '[email protected]'], ...
    ]

– This class has two responsibilities:

  1. Access the Yahoo Address Book API through Delegated Authentication

  2. Import contacts from Yahoo Mail and deliver it inside an Array

Constant Summary collapse

AUTH_DOMAIN =
"https://api.login.yahoo.com"
AUTH_PATH =
"/WSLogin/V1/wslogin?appid=#appid"
CREDENTIAL_PATH =
"/WSLogin/V1/wspwtoken_login?appid=#appid&ts=#ts&token=#token"
ADDRESS_BOOK_DOMAIN =
"address.yahooapis.com"
ADDRESS_BOOK_PATH =
"/v1/searchContacts?format=json&fields=all&appid=#appid&WSSID=#wssid"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config_file) ⇒ Yahoo

Initialize a new Yahoo object.

Paramaters

  • config_file <String>

    The contacts YAML config file name

– You can check an example of a config file inside config/ directory



71
72
73
74
75
# File 'lib/contacts/yahoo.rb', line 71

def initialize(config_file)
  confs = YAML.load_file(config_file)['yahoo']
  @appid = confs['appid']
  @secret = confs['secret']
end

Instance Attribute Details

#appidObject (readonly)

Returns the value of attribute appid.



62
63
64
# File 'lib/contacts/yahoo.rb', line 62

def appid
  @appid
end

Returns the value of attribute cookie.



62
63
64
# File 'lib/contacts/yahoo.rb', line 62

def cookie
  @cookie
end

#secretObject (readonly)

Returns the value of attribute secret.



62
63
64
# File 'lib/contacts/yahoo.rb', line 62

def secret
  @secret
end

#tokenObject (readonly)

Returns the value of attribute token.



62
63
64
# File 'lib/contacts/yahoo.rb', line 62

def token
  @token
end

#wssidObject (readonly)

Returns the value of attribute wssid.



62
63
64
# File 'lib/contacts/yahoo.rb', line 62

def wssid
  @wssid
end

Class Method Details

.parse_contacts(json) ⇒ Object

This method parses the JSON contacts document and returns an array contaning all the user’s contacts.

Parameters

  • json <String>

    A String of user’s contacts in JSON format

“fields”: [

  {
      "type": "phone",
      "data": "808 123 1234",
      "home": true,
  },
  {
      "type": "email",
      "data": "[email protected]",
  },

  {
      "type": "otherid",
      "data": "[email protected]",
      "msn":  true,
  }
]


231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/contacts/yahoo.rb', line 231

def self.parse_contacts(json)
  contacts = []
  people = JSON.parse(json)
  people['contacts'].each do |contact|
    name = nil
    email = nil
    firstname = nil
    lastname = nil
    
    contact_fields=Yahoo.array_to_hash contact['fields']

    emails    = (contact_fields['email'] || []).collect {|e| e['data']}
    ims       = (contact_fields['otherid'] || []).collect { |im| get_type_value(im) }
    phones    = (contact_fields['phone'] || []).collect { |phone| get_type_value(phone) }
    addresses = (contact_fields['address'] || []).collect do |address| 
      type=get_type(address)
      type = {"home" => "home", "work" => "work"}[type.downcase] || "other"
      value = [address['street'], address['city'], address['state'], address['zip'], address['country']].compact.join(", ")
      {"type" => type, "value" => value}
    end
    
    name_field=(contact_fields['name'] || [])
    
    # if name is blank, try go for the yahoo id, and if that's blank too, ignore the record altogether (probably a mailing list)
    if (name_field.empty?)
      if contact_fields['yahooid']
        name = contact_fields['yahooid'][0]['data']
      else
        next
      end
    else
      name_field = name_field[0]
      name = "#{name_field['first']} #{name_field['last']}"
      name.strip!
      lastname = name_field['last']
      firstname = name_field['first']
    end
    
    yahoo_contact            = Contact.new(nil, name, nil, firstname, lastname)
    yahoo_contact.emails     = emails
    yahoo_contact.ims        = ims
    yahoo_contact.phones     = phones
    yahoo_contact.addresses  = addresses
    yahoo_contact.service_id = contact['cid']

    contacts.push yahoo_contact
  end
  contacts
end

Instance Method Details

#access_address_book_apiObject

This method accesses the Yahoo Address Book API and retrieves the user’s contacts in JSON.



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/contacts/yahoo.rb', line 192

def access_address_book_api
  http = http = Net::HTTP.new(ADDRESS_BOOK_DOMAIN, 80)

  response = nil
  http.start do |http|
     path = ADDRESS_BOOK_PATH.clone
     path.sub!(/#appid/, @appid)
     path.sub!(/#wssid/, @wssid)

     request = Net::HTTP::Get.new(path, {'Cookie' => @cookie})
     response = http.request(request)
  end

  response.body
end

#access_user_credentialsObject

This method accesses Yahoo to retrieve the user’s credentials.



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/contacts/yahoo.rb', line 145

def access_user_credentials
  url = get_credential_url()
  uri = URI.parse(url)

  http = http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true

  response = nil
  http.start do |http|
     request = Net::HTTP::Get.new("#{uri.path}?#{uri.query}")
     response = http.request(request)
  end

  return response.body
end

#contacts(token) ⇒ Object

This method return the user’s contacts inside an Array in the following format:

[ 
  ['Brad Fitzgerald', '[email protected]'],
  [nil, '[email protected]'],
  ['William Paginate', '[email protected]']  ...
]

Paramaters

  • path <String>

    The path of the redirect request that Yahoo sent to you

after authenticating the user



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/contacts/yahoo.rb', line 105

def contacts(token)
  begin
    if token.is_a?(YahooToken)
      @token = token.token
    else
      validate_signature(token)
    end
    credentials = access_user_credentials()
    parse_credentials(credentials)
    contacts_json = access_address_book_api()
    Yahoo.parse_contacts(contacts_json)
  rescue Exception => e
    "Error #{e.class}: #{e.message}."
  end
end

#get_authentication_url(appdata = nil, send_userhash = 1) ⇒ Object

Yahoo Address Book API need to authenticate the user that is giving you access to his contacts. To do that, you must give him a URL. This method generates that URL. The user must access that URL, and after he has done authentication, hi will be redirected to your application.



82
83
84
85
86
87
88
89
90
# File 'lib/contacts/yahoo.rb', line 82

def get_authentication_url(appdata= nil, send_userhash=1)
  path = AUTH_PATH.clone
  path.sub!(/#appid/, @appid)
  path<< "&appdata=#{appdata}" unless appdata.nil?
  path<< "&send_userhash=#{send_userhash}" unless send_userhash.nil?
  path<< "&ts=#{Time.now.utc.to_i.to_s}"
  signature = MD5.hexdigest(path + @secret)
  "#{AUTH_DOMAIN}#{path}&sig=#{signature}"
end

#get_credential_urlObject

This method generates the URL that you must access to get user’s credentials.



164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/contacts/yahoo.rb', line 164

def get_credential_url
  path = CREDENTIAL_PATH.clone
  path.sub!(/#appid/, @appid)

  path.sub!(/#token/, @token)

  timestamp = Time.now.utc.to_i
  path.sub!(/#ts/, timestamp.to_s)

  signature = MD5.hexdigest(path + @secret)
  return AUTH_DOMAIN + "#{path}&sig=#{signature}"
end

#parse_credentials(xml) ⇒ Object

This method parses the user’s credentials to generate the WSSID and Coookie that are needed to give you access to user’s address book.

Paramaters

  • xml <String>

    A String containing the user’s credentials



183
184
185
186
187
# File 'lib/contacts/yahoo.rb', line 183

def parse_credentials(xml)
  doc = Hpricot::XML(xml)
  @wssid = doc.at('/BBAuthTokenLoginResponse/Success/WSSID').inner_text.strip
  @cookie = doc.at('/BBAuthTokenLoginResponse/Success/Cookie').inner_text.strip
end

#validate_signature(path) ⇒ Object

This method processes and validates the redirect request that Yahoo send to you. Validation is done to verify that the request was really made by Yahoo. Processing is done to get the token.

Paramaters

  • path <String>

    The path of the redirect request that Yahoo sent to you

after authenticating the user



129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/contacts/yahoo.rb', line 129

def validate_signature(path)
  path.match(/^(.+)&sig=(\w{32})$/)
  path_without_sig = $1
  sig = $2

  if sig == MD5.hexdigest(path_without_sig + @secret)
    path.match(/token=(.+?)&/)
    @token = $1
    true
  else
    raise 'Signature not valid. This request may not have been sent from Yahoo.'
  end
end