possible_email 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +17 -0
- data/.rspec +3 -0
- data/.travis.yml +6 -0
- data/Gemfile +2 -0
- data/Guardfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +161 -0
- data/Rakefile +13 -0
- data/bin/possible_email +6 -0
- data/lib/possible_email.rb +56 -0
- data/lib/possible_email/cli.rb +33 -0
- data/lib/possible_email/error.rb +3 -0
- data/lib/possible_email/patterns.rb +38 -0
- data/lib/possible_email/permutator.rb +64 -0
- data/lib/possible_email/profile/image.rb +11 -0
- data/lib/possible_email/profile/membership.rb +12 -0
- data/lib/possible_email/profile/occupation.rb +10 -0
- data/lib/possible_email/profile/phone.rb +5 -0
- data/lib/possible_email/profile/profile.rb +58 -0
- data/lib/possible_email/rapportive_requester.rb +49 -0
- data/lib/possible_email/response.rb +26 -0
- data/lib/possible_email/response_getter.rb +37 -0
- data/lib/possible_email/version.rb +3 -0
- data/possible_email.gemspec +35 -0
- data/spec/PossibleEmail/possible_email/permutator_spec.rb +83 -0
- data/spec/PossibleEmail/possible_email/profile/image_spec.rb +20 -0
- data/spec/PossibleEmail/possible_email/profile/membership_spec.rb +26 -0
- data/spec/PossibleEmail/possible_email/profile/occupation_spec.rb +15 -0
- data/spec/PossibleEmail/possible_email/profile/phone_spec.rb +0 -0
- data/spec/PossibleEmail/possible_email/profile/profile_spec.rb +100 -0
- data/spec/PossibleEmail/possible_email/rapportive_requester_spec.rb +67 -0
- data/spec/PossibleEmail/possible_email/response_getter_spec.rb +50 -0
- data/spec/PossibleEmail/possible_email/response_spec.rb +22 -0
- data/spec/PossibleEmail/possible_email_spec.rb +92 -0
- data/spec/fixtures/rapportive_example_data.json +509 -0
- data/spec/spec_helper.rb +12 -0
- metadata +274 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e7c59b28497e97a707ccef97030b2ba5c71f5902
|
4
|
+
data.tar.gz: 7545a313b3364563cbad881f5bc3620ecaaccb7c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d4a1ecc7e3e67f6ffa3b35a2b424319877a61e9bfde31b3907769a860412f2de3f4a12065be4e952b9fb26a4bbfc5524a1e8301a4b22ae14459adfea05145d1a
|
7
|
+
data.tar.gz: 2c4657ce20186cf5d03416e02347257af0b127b2d49aa5015280233c65a4c9dedd8bffc420ddf7638bc21e2b746058810929834f37a6ce7991f48d2231e23d11
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
guard :bundler do
|
2
|
+
watch('Gemfile')
|
3
|
+
watch(/^.+\.gemspec/)
|
4
|
+
end
|
5
|
+
|
6
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
7
|
+
watch(%r{^spec/.+_spec\.rb$})
|
8
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
9
|
+
watch('spec/spec_helper.rb') { "spec" }
|
10
|
+
end
|
11
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Patrick Perey
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,161 @@
|
|
1
|
+
PossibleEmail
|
2
|
+
=========
|
3
|
+
A Ruby Gem to find someone's possible email address using their first name, last name, and domain.
|
4
|
+
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/possible_email.svg)](http://badge.fury.io/rb/possible_email)
|
6
|
+
[![Build Status](https://travis-ci.org/the4dpatrick/possible-email.svg?branch=master)](https://travis-ci.org/the4dpatrick/possible-email)
|
7
|
+
[![Coverage Status](https://coveralls.io/repos/the4dpatrick/possible-email/badge.png?branch=master)](https://coveralls.io/r/the4dpatrick/possible-email?branch=master)
|
8
|
+
[![Code Climate](https://codeclimate.com/github/the4dpatrick/possible-email/badges/gpa.svg)](https://codeclimate.com/github/the4dpatrick/possible-email)
|
9
|
+
|
10
|
+
More information in my [blog post](http://patrickperey.com/possible-email "blog post") at [PatrickPerey.com](http://patrickperey.com "Patrick Perey Blog")
|
11
|
+
|
12
|
+
Installation
|
13
|
+
------------
|
14
|
+
Install the gem:
|
15
|
+
|
16
|
+
```
|
17
|
+
$ gem install possible_email
|
18
|
+
```
|
19
|
+
|
20
|
+
Basic Usage
|
21
|
+
-----
|
22
|
+
|
23
|
+
### Command Line Interface
|
24
|
+
|
25
|
+
|
26
|
+
Enter target's first name, last name, and possible domain name into the terminal using `search`
|
27
|
+
|
28
|
+
```
|
29
|
+
$ possible_email search first_name last_name domain
|
30
|
+
```
|
31
|
+
|
32
|
+
Not sure about the domain name? Just add multiple domains at the end
|
33
|
+
|
34
|
+
```
|
35
|
+
$ possible_email search first_name last_name gmail.com, yahoo.com, live.com
|
36
|
+
```
|
37
|
+
|
38
|
+
|
39
|
+
Just want to Confirm a single email address? Use `find_profile`
|
40
|
+
|
41
|
+
```
|
42
|
+
$ possible_email find_profile test@example.com
|
43
|
+
```
|
44
|
+
Multiple email address? Same `find_profile`
|
45
|
+
|
46
|
+
```
|
47
|
+
$ possible_email find_profile test@example.com test1@example.com
|
48
|
+
```
|
49
|
+
### Ruby
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
require 'possible_email'
|
53
|
+
|
54
|
+
profiles = PossibleEmail.search('Kevin', 'Rose', 'gmail.com')
|
55
|
+
profiles #=> "#<PossibleEmail::Response>"
|
56
|
+
first_profile = profiles.first
|
57
|
+
|
58
|
+
first_profile.name #=> 'Kevin Rose'
|
59
|
+
first_profile.email #=> 'kevinrose@gmail.com'
|
60
|
+
first_profile.location #=> 'San Francisco Bay Area'
|
61
|
+
```
|
62
|
+
Documentation
|
63
|
+
-------------
|
64
|
+
### PossibleEmail
|
65
|
+
|
66
|
+
Available methods:
|
67
|
+
|
68
|
+
**search(first_name, last_name, *domain)**
|
69
|
+
|
70
|
+
Accepts three arguments `first_name`, `last_name`, and `domain`. PossibleEmail will use these three arguments to generate possible email addresses based on common email address patterns. PossibleEmail would then attempt to verify and return an email profile for each email address.
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
# Single domain name
|
74
|
+
PossibleEmail.search('bob', 'jones', 'gmail.com')
|
75
|
+
|
76
|
+
# Multiple domain names as Strings
|
77
|
+
PossibleEmail.search('bob', 'jones', 'gmail.com', 'yahoo.com')
|
78
|
+
|
79
|
+
# Multiple domain names as an Array
|
80
|
+
domains = ['gmail.com', 'yahoo.com', 'live.com']
|
81
|
+
PossibleEmail.search('bob', 'jones', domains)
|
82
|
+
```
|
83
|
+
|
84
|
+
**find_profile(*emails)**
|
85
|
+
|
86
|
+
Accepts a list of email address string arguments or an array. Instead of generating email addresses based on name arguments, PossibleEmail would attempt to verify and return an email profile for each email addresses passed into the method.
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
# Comma-splitted email arguments
|
90
|
+
PossibleEmail.find_profile('test@example.com', 'test1@example.com')
|
91
|
+
|
92
|
+
# Array of emails
|
93
|
+
PossibleEmail.find_profile(['test@example.com', 'test1@example.com'])
|
94
|
+
```
|
95
|
+
|
96
|
+
Both methods return a `PossibleEmail::Response` object. `PossibleEmail::Response` includes the `Enumerable` module, so all the methods you need to iterate through the profiles are available. The only exception when neither the `search` or `find_profile` is when there is only one profile within the response. In this case, the method returns the single Profile.
|
97
|
+
|
98
|
+
### Profile
|
99
|
+
|
100
|
+
Class for the associated data connnected with a specific email address.
|
101
|
+
|
102
|
+
`Profile` attribute list:
|
103
|
+
|
104
|
+
* `email` - Returns the profile's email address
|
105
|
+
* `name` - Full name
|
106
|
+
* `first_name` - First name
|
107
|
+
* `last_name` - Last name
|
108
|
+
* `friendly_name` - First name or named used to address this person
|
109
|
+
* `location` - Location
|
110
|
+
* `headline` - Short blurb about person
|
111
|
+
* `success` - Type of response returned back from Rapportive API
|
112
|
+
* `occupations` - Array of Occupation objects
|
113
|
+
* `memberships` - Array of social network Membership objects
|
114
|
+
* `images` - Array of Image objects
|
115
|
+
|
116
|
+
### Occupation
|
117
|
+
|
118
|
+
Class for person's jobs.
|
119
|
+
|
120
|
+
`Occupation` attribute list:
|
121
|
+
|
122
|
+
* `job_title` - Job title
|
123
|
+
* `company` - Company
|
124
|
+
|
125
|
+
### Membership
|
126
|
+
|
127
|
+
Class for Social Network Accounts
|
128
|
+
|
129
|
+
`Membership` attribute list:
|
130
|
+
|
131
|
+
* `profile_url` - URL to person's website membership
|
132
|
+
* `profile_id` - Website profile ID
|
133
|
+
* `username` - Username
|
134
|
+
* `site_name` - Name of the website membership
|
135
|
+
|
136
|
+
### Image
|
137
|
+
|
138
|
+
Class for images associated with email profile.
|
139
|
+
|
140
|
+
`Image` attribute list:
|
141
|
+
|
142
|
+
* `url` - Image url
|
143
|
+
* `service` - Where the image is located
|
144
|
+
* `url_proxied` - Rapportive image proxied URL
|
145
|
+
|
146
|
+
Notes
|
147
|
+
-----
|
148
|
+
* With great power, comes great responsibly
|
149
|
+
* Wrapper around the undocumented Rapportive API.
|
150
|
+
* Valid results may be hidden due to API's limitations
|
151
|
+
* Send Bitcoin `18fZ6muNmBrtENMZhnAjUw8eEsytmY8mZJ`
|
152
|
+
|
153
|
+
|
154
|
+
Contributing
|
155
|
+
------------
|
156
|
+
|
157
|
+
1. Fork it ( http://github.com/the4dpatrick/possible-email )
|
158
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
159
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
160
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
161
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
desc "Run all specs"
|
7
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
8
|
+
t.rspec_opts = ['--color', '--format', 'nested']
|
9
|
+
t.pattern = 'spec/**/*_spec.rb'
|
10
|
+
t.verbose = false
|
11
|
+
end
|
12
|
+
|
13
|
+
task :default => :spec
|
data/bin/possible_email
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'possible_email/error'
|
2
|
+
require 'possible_email/version'
|
3
|
+
require 'possible_email/permutator'
|
4
|
+
require 'possible_email/rapportive_requester'
|
5
|
+
|
6
|
+
require 'httpi'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
HTTPI.log = false
|
10
|
+
|
11
|
+
EMAIL_REGEX = /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i
|
12
|
+
DOMAIN_REGEX = /^[^@]([\w\-]+\.)+([\w]{2,})$/
|
13
|
+
NAME_REGEX = /^\b[a-zA-Z]+\b$/
|
14
|
+
|
15
|
+
module PossibleEmail
|
16
|
+
class InvalidNameFormat < ArgumentError; end
|
17
|
+
class InvalidEmailFormat < ArgumentError; end
|
18
|
+
|
19
|
+
module_function
|
20
|
+
|
21
|
+
def search(first_name, last_name, *domain)
|
22
|
+
assign_instance_variables first_name, last_name, domain
|
23
|
+
|
24
|
+
fail InvalidNameFormat, "Name arguments were not formatted correctly #{[@first_name, @last_name, *@domain].inspect}" unless valid_names?
|
25
|
+
|
26
|
+
permutations = Permutator.call(@first_name, @last_name, @domain)
|
27
|
+
RapportiveRequester.request(permutations)
|
28
|
+
end
|
29
|
+
|
30
|
+
def find_profile(*emails)
|
31
|
+
@emails = emails.flatten
|
32
|
+
|
33
|
+
fail InvalidEmailFormat, "Email arguments were not formatted correctly #{@emails.inspect}" if invalid_emails?
|
34
|
+
|
35
|
+
RapportiveRequester.request(@emails)
|
36
|
+
end
|
37
|
+
|
38
|
+
private_class_method
|
39
|
+
|
40
|
+
def assign_instance_variables(first_name, last_name, domain)
|
41
|
+
@first_name = first_name
|
42
|
+
@last_name = last_name
|
43
|
+
@domain = domain.flatten
|
44
|
+
end
|
45
|
+
|
46
|
+
def invalid_emails?
|
47
|
+
@emails.any? { |email| email !~ EMAIL_REGEX }
|
48
|
+
end
|
49
|
+
|
50
|
+
def valid_names?
|
51
|
+
valid_domain = @domain.all? { |d| d =~ DOMAIN_REGEX }
|
52
|
+
valid_names = [@first_name, @last_name].all? { |n| n =~ NAME_REGEX }
|
53
|
+
|
54
|
+
valid_domain && valid_names
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module PossibleEmail
|
4
|
+
class CLI < Thor
|
5
|
+
# possible_email search
|
6
|
+
|
7
|
+
desc 'search', 'Search for valid emails using first name, last name, and domain name'
|
8
|
+
long_desc <<-LONGDESC
|
9
|
+
Takes in a first name, last name, and domain name.
|
10
|
+
|
11
|
+
Domain name can be a single domain like 'gmail.com', or multiple domain names like 'gmail.com', 'yahoo.com'
|
12
|
+
|
13
|
+
Prints out all of the found email profiles for valid email addresses with associated information
|
14
|
+
LONGDESC
|
15
|
+
|
16
|
+
def search(first_name, last_name, *domain)
|
17
|
+
profiles = PossibleEmail.search(first_name, last_name, domain)
|
18
|
+
puts profiles
|
19
|
+
end
|
20
|
+
|
21
|
+
# possible_email find_profile
|
22
|
+
|
23
|
+
desc 'find_profile', 'return a profile for one or more email addresses'
|
24
|
+
long_desc <<-LONGDESC
|
25
|
+
Prints out all of the found email profiles for valid email addresses with associated information
|
26
|
+
LONGDESC
|
27
|
+
|
28
|
+
def find_profile(*emails)
|
29
|
+
profiles = PossibleEmail.find_profile(emails)
|
30
|
+
puts profiles
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
PATTERNS = <<PATTERNS
|
2
|
+
{first_name}@{domain}
|
3
|
+
{last_name}@{domain}
|
4
|
+
{first_initial}@{domain}
|
5
|
+
{last_initial}@{domain}
|
6
|
+
{first_name}{last_name}@{domain}
|
7
|
+
{first_name}.{last_name}@{domain}
|
8
|
+
{first_initial}{last_name}@{domain}
|
9
|
+
{first_initial}.{last_name}@{domain}
|
10
|
+
{first_name}{last_initial}@{domain}
|
11
|
+
{first_name}.{last_initial}@{domain}
|
12
|
+
{first_initial}{last_initial}@{domain}
|
13
|
+
{first_initial}.{last_initial}@{domain}
|
14
|
+
{last_name}{first_name}@{domain}
|
15
|
+
{last_name}.{first_name}@{domain}
|
16
|
+
{last_name}{first_initial}@{domain}
|
17
|
+
{last_name}.{first_initial}@{domain}
|
18
|
+
{last_initial}{first_name}@{domain}
|
19
|
+
{last_initial}.{first_name}@{domain}
|
20
|
+
{last_initial}{first_initial}@{domain}
|
21
|
+
{last_initial}.{first_initial}@{domain}
|
22
|
+
{first_name}-{last_name}@{domain}
|
23
|
+
{first_initial}-{last_name}@{domain}
|
24
|
+
{first_name}-{last_initial}@{domain}
|
25
|
+
{first_initial}-{last_initial}@{domain}
|
26
|
+
{last_name}-{first_name}@{domain}
|
27
|
+
{last_name}-{first_initial}@{domain}
|
28
|
+
{last_initial}-{first_name}@{domain}
|
29
|
+
{last_initial}-{first_initial}@{domain}
|
30
|
+
{first_name}_{last_name}@{domain}
|
31
|
+
{first_initial}_{last_name}@{domain}
|
32
|
+
{first_name}_{last_initial}@{domain}
|
33
|
+
{first_initial}_{last_initial}@{domain}
|
34
|
+
{last_name}_{first_name}@{domain}
|
35
|
+
{last_name}_{first_initial}@{domain}
|
36
|
+
{last_initial}_{first_name}@{domain}
|
37
|
+
{last_initial}_{first_initial}@{domain}
|
38
|
+
PATTERNS
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'possible_email/patterns'
|
2
|
+
|
3
|
+
module PossibleEmail
|
4
|
+
class Permutator
|
5
|
+
def self.call(first_name, last_name, *domain)
|
6
|
+
new(first_name, last_name, domain).call
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(first_name, last_name, domain)
|
10
|
+
@first_name = first_name
|
11
|
+
@last_name = last_name
|
12
|
+
@domain = domain.flatten # HACK
|
13
|
+
@first_initial = first_name.chars.first
|
14
|
+
@last_initial = last_name.chars.first
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
permutations = create_all_permutations
|
19
|
+
permutations
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def create_all_permutations
|
25
|
+
@domain.reduce([]) do |permutations, domain|
|
26
|
+
substitute_pattern_placeholders_with_correct_values(domain)
|
27
|
+
|
28
|
+
separate_each_permutation(permutations)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def substitute_pattern_placeholders_with_correct_values(domain)
|
33
|
+
# reset after each domain iteration
|
34
|
+
@permutation_patterns = PATTERNS.dup
|
35
|
+
|
36
|
+
substitute_every_pattern_placeholder_except_domain
|
37
|
+
|
38
|
+
@permutation_patterns.gsub!('{domain}', domain)
|
39
|
+
end
|
40
|
+
|
41
|
+
def substitute_every_pattern_placeholder_except_domain
|
42
|
+
all_instance_variables_except_domain.each do |var|
|
43
|
+
@permutation_patterns.gsub!(placeholder(var), value_of(var))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def all_instance_variables_except_domain
|
48
|
+
instance_variables.dup - [:@domain]
|
49
|
+
end
|
50
|
+
|
51
|
+
def placeholder(var)
|
52
|
+
"{#{var.to_s[1..-1]}}"
|
53
|
+
end
|
54
|
+
|
55
|
+
def value_of(var)
|
56
|
+
instance_variable_get("@#{var[1..-1]}".to_sym)
|
57
|
+
end
|
58
|
+
|
59
|
+
def separate_each_permutation(permutations)
|
60
|
+
permutations << @permutation_patterns.split("\n")
|
61
|
+
permutations.flatten
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|