emailhunter 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +2 -0
- data/README.md +210 -16
- data/Rakefile +2 -0
- data/bin/console +1 -0
- data/emailhunter.gemspec +4 -2
- data/lib/email_hunter/account.rb +3 -3
- data/lib/email_hunter/api.rb +67 -6
- data/lib/email_hunter/campaigns.rb +139 -0
- data/lib/email_hunter/combined_enrichment.rb +54 -0
- data/lib/email_hunter/company.rb +7 -7
- data/lib/email_hunter/company_enrichment.rb +61 -0
- data/lib/email_hunter/count.rb +3 -3
- data/lib/email_hunter/discover.rb +80 -0
- data/lib/email_hunter/exist.rb +3 -3
- data/lib/email_hunter/finder.rb +3 -3
- data/lib/email_hunter/lead_enrichment.rb +69 -0
- data/lib/email_hunter/leads.rb +129 -0
- data/lib/email_hunter/people.rb +7 -7
- data/lib/email_hunter/search.rb +4 -4
- data/lib/email_hunter/verify.rb +3 -3
- data/lib/email_hunter/version.rb +1 -1
- data/lib/emailhunter.rb +2 -0
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2006e973eb996d6b4285c772c1af115e0d96091c403fff927381233f4ebeed94
|
|
4
|
+
data.tar.gz: 51433f5603cbb512baedb0342eb83f7655505ae4a4ee5f4deac18d317d8861ae
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 71a8eebcf7e1c0452a08d0d8ef4c79d980b1d48dbe0d898dab096b8012267dc2d7e7d4ade75ea861635dfa78690522b409993a60f065d3901299f85de161715f
|
|
7
|
+
data.tar.gz: 27c55a270224f1a9bc513c54903d0e5c16598bc939270dd331422dea13487c2fb733f5cb90f47efd83dd4628180730cf3baf9b1f90ff6ecff9bc1d2871c5103f
|
data/Gemfile
CHANGED
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# EmailHunter
|
|
2
2
|
|
|
3
|
-
A lightweight Ruby wrapper around Hunter (formerly Email Hunter) API, providing direct access to email search, verification, and company insights.
|
|
3
|
+
A lightweight Ruby wrapper around [Hunter.io](https://hunter.io/) (formerly Email Hunter) API, providing direct access to email search, verification, and company insights.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -33,7 +33,20 @@ Your API key can be generated in your [Hunter dashboard](https://hunter.io).
|
|
|
33
33
|
|
|
34
34
|
## Features
|
|
35
35
|
|
|
36
|
+
**Core APIs:**
|
|
37
|
+
|
|
38
|
+
1. **Domain Search API** - Retrieve all email addresses associated with a domain
|
|
39
|
+
2. **Email Verification API** - Check the deliverability of an email address
|
|
40
|
+
3. **Email Finder API** - Find the most likely email using name and domain
|
|
41
|
+
4. **Count API** - Get the number of email addresses for a domain (FREE)
|
|
42
|
+
5. **Account Information API** - Retrieve details about your Hunter account
|
|
43
|
+
6. **Company Information API** - Get company details using a domain name
|
|
44
|
+
7. **People Search API** - Find key individuals associated with a company
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
36
48
|
### 1. Domain Search API
|
|
49
|
+
|
|
37
50
|
Retrieve all email addresses associated with a given domain.
|
|
38
51
|
|
|
39
52
|
```ruby
|
|
@@ -41,6 +54,7 @@ result = email_hunter.search('stripe.com')
|
|
|
41
54
|
```
|
|
42
55
|
|
|
43
56
|
#### Response Fields:
|
|
57
|
+
|
|
44
58
|
```ruby
|
|
45
59
|
result.fetch(:meta)
|
|
46
60
|
result.fetch(:webmail)
|
|
@@ -50,6 +64,7 @@ result.fetch(:domain)
|
|
|
50
64
|
```
|
|
51
65
|
|
|
52
66
|
### 2. Email Verification API
|
|
67
|
+
|
|
53
68
|
Check the deliverability of an email address.
|
|
54
69
|
|
|
55
70
|
```ruby
|
|
@@ -57,6 +72,7 @@ result = email_hunter.verify('bonjour@firmapi.com')
|
|
|
57
72
|
```
|
|
58
73
|
|
|
59
74
|
#### Response Fields:
|
|
75
|
+
|
|
60
76
|
```ruby
|
|
61
77
|
result.fetch(:result)
|
|
62
78
|
result.fetch(:score)
|
|
@@ -72,6 +88,7 @@ result.fetch(:meta)
|
|
|
72
88
|
```
|
|
73
89
|
|
|
74
90
|
### 3. Email Finder API
|
|
91
|
+
|
|
75
92
|
Guess the most likely email of a person using their first name, last name, and domain.
|
|
76
93
|
|
|
77
94
|
```ruby
|
|
@@ -79,6 +96,7 @@ result = email_hunter.finder('gmail.com', 'Davide', 'Santangelo')
|
|
|
79
96
|
```
|
|
80
97
|
|
|
81
98
|
#### Response Fields:
|
|
99
|
+
|
|
82
100
|
```ruby
|
|
83
101
|
result.fetch(:email)
|
|
84
102
|
result.fetch(:score)
|
|
@@ -88,6 +106,7 @@ result.fetch(:meta)
|
|
|
88
106
|
```
|
|
89
107
|
|
|
90
108
|
### 4. Count API
|
|
109
|
+
|
|
91
110
|
Retrieve the number of email addresses associated with a domain (FREE API call).
|
|
92
111
|
|
|
93
112
|
```ruby
|
|
@@ -95,12 +114,14 @@ result = email_hunter.count('gmail.com')
|
|
|
95
114
|
```
|
|
96
115
|
|
|
97
116
|
#### Response Fields:
|
|
117
|
+
|
|
98
118
|
```ruby
|
|
99
119
|
result.fetch(:data)
|
|
100
120
|
result.fetch(:meta)
|
|
101
121
|
```
|
|
102
122
|
|
|
103
123
|
### 5. Company Information API (New Feature)
|
|
124
|
+
|
|
104
125
|
Retrieve company details using a domain name.
|
|
105
126
|
|
|
106
127
|
```ruby
|
|
@@ -108,6 +129,7 @@ result = email_hunter.company('stripe.com')
|
|
|
108
129
|
```
|
|
109
130
|
|
|
110
131
|
#### Response Fields:
|
|
132
|
+
|
|
111
133
|
```ruby
|
|
112
134
|
result.fetch(:name)
|
|
113
135
|
result.fetch(:industry)
|
|
@@ -117,6 +139,7 @@ result.fetch(:meta)
|
|
|
117
139
|
```
|
|
118
140
|
|
|
119
141
|
### 6. People Search API (New Feature)
|
|
142
|
+
|
|
120
143
|
Retrieve key individuals associated with a company based on a domain name.
|
|
121
144
|
|
|
122
145
|
```ruby
|
|
@@ -124,6 +147,7 @@ result = email_hunter.people('stripe.com')
|
|
|
124
147
|
```
|
|
125
148
|
|
|
126
149
|
#### Response Fields:
|
|
150
|
+
|
|
127
151
|
```ruby
|
|
128
152
|
result.fetch(:employees)
|
|
129
153
|
result.fetch(:position)
|
|
@@ -131,7 +155,177 @@ result.fetch(:email)
|
|
|
131
155
|
result.fetch(:meta)
|
|
132
156
|
```
|
|
133
157
|
|
|
134
|
-
### 7.
|
|
158
|
+
### 7. Discover API (New in v2.0.0)
|
|
159
|
+
|
|
160
|
+
Search for companies using natural language queries.
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
result = email_hunter.discover('US-based Software companies', limit: 10)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Response Fields:
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
result.data # Array of companies
|
|
170
|
+
result.meta.fetch(:results)
|
|
171
|
+
result.meta.fetch(:limit)
|
|
172
|
+
result.meta.fetch(:offset)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### 8. Leads Management API (New in v2.0.0)
|
|
176
|
+
|
|
177
|
+
Manage your leads stored in Hunter.
|
|
178
|
+
|
|
179
|
+
#### List Leads:
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
result = email_hunter.leads(limit: 20, offset: 0)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### Create Lead:
|
|
186
|
+
|
|
187
|
+
```ruby
|
|
188
|
+
lead_data = {
|
|
189
|
+
email: 'john@example.com',
|
|
190
|
+
first_name: 'John',
|
|
191
|
+
last_name: 'Doe',
|
|
192
|
+
company: 'Example Inc',
|
|
193
|
+
position: 'CEO'
|
|
194
|
+
}
|
|
195
|
+
result = email_hunter.lead_create(lead_data)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
#### Update Lead:
|
|
199
|
+
|
|
200
|
+
```ruby
|
|
201
|
+
update_data = { position: 'CTO' }
|
|
202
|
+
result = email_hunter.lead_update(lead_id, update_data)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
#### Delete Lead:
|
|
206
|
+
|
|
207
|
+
```ruby
|
|
208
|
+
success = email_hunter.lead_delete(lead_id)
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### Response Fields:
|
|
212
|
+
|
|
213
|
+
```ruby
|
|
214
|
+
result.data.leads # Array of lead objects
|
|
215
|
+
result.data.leads.first.email
|
|
216
|
+
result.data.leads.first.company
|
|
217
|
+
result.meta.fetch(:count)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### 9. Campaigns Management API (New in v2.0.0)
|
|
221
|
+
|
|
222
|
+
Manage your email campaigns and recipients.
|
|
223
|
+
|
|
224
|
+
#### List Campaigns:
|
|
225
|
+
|
|
226
|
+
```ruby
|
|
227
|
+
result = email_hunter.campaigns(limit: 20)
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
#### Get Campaign Recipients:
|
|
231
|
+
|
|
232
|
+
```ruby
|
|
233
|
+
result = email_hunter.campaign_recipients(campaign_id, limit: 20)
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
#### Add Recipient to Campaign:
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
recipient_data = {
|
|
240
|
+
email: 'john@example.com',
|
|
241
|
+
first_name: 'John',
|
|
242
|
+
last_name: 'Doe'
|
|
243
|
+
}
|
|
244
|
+
result = email_hunter.campaign_add_recipient(campaign_id, recipient_data)
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
#### Remove Recipient from Campaign:
|
|
248
|
+
|
|
249
|
+
```ruby
|
|
250
|
+
success = email_hunter.campaign_delete_recipient(campaign_id, 'john@example.com')
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
#### Response Fields:
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
result.data.campaigns # Array of campaign objects
|
|
257
|
+
result.data.recipients # Array of recipient objects
|
|
258
|
+
result.meta.fetch(:limit)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### 10. Lead Enrichment API (New in v2.0.0)
|
|
262
|
+
|
|
263
|
+
Enrich person data with 100+ attributes using email or LinkedIn.
|
|
264
|
+
|
|
265
|
+
```ruby
|
|
266
|
+
# Using email
|
|
267
|
+
result = email_hunter.lead_enrichment(email: 'matt@hunter.io')
|
|
268
|
+
|
|
269
|
+
# Using LinkedIn
|
|
270
|
+
result = email_hunter.lead_enrichment(linkedin: 'matttharp')
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
#### Response Fields:
|
|
274
|
+
|
|
275
|
+
```ruby
|
|
276
|
+
result.data.name.fullName
|
|
277
|
+
result.data.email
|
|
278
|
+
result.data.location
|
|
279
|
+
result.data.timeZone
|
|
280
|
+
result.data.employment.domain
|
|
281
|
+
result.data.employment.title
|
|
282
|
+
result.data.employment.name
|
|
283
|
+
result.data.twitter.handle
|
|
284
|
+
result.data.linkedin.handle
|
|
285
|
+
result.meta.fetch(:email)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### 11. Company Enrichment API (New in v2.0.0)
|
|
289
|
+
|
|
290
|
+
Enrich company data with detailed firmographic information.
|
|
291
|
+
|
|
292
|
+
```ruby
|
|
293
|
+
result = email_hunter.company_enrichment('stripe.com')
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
#### Response Fields:
|
|
297
|
+
|
|
298
|
+
```ruby
|
|
299
|
+
result.data.name
|
|
300
|
+
result.data.description
|
|
301
|
+
result.data.foundedYear
|
|
302
|
+
result.data.location
|
|
303
|
+
result.data.category.industry
|
|
304
|
+
result.data.metrics.employees
|
|
305
|
+
result.data.tech # Array of technologies used
|
|
306
|
+
result.data.site.emailAddresses
|
|
307
|
+
result.meta.fetch(:domain)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### 12. Combined Enrichment API (New in v2.0.0)
|
|
311
|
+
|
|
312
|
+
Get both lead and company data in a single call.
|
|
313
|
+
|
|
314
|
+
```ruby
|
|
315
|
+
result = email_hunter.combined_enrichment(email: 'patrick@stripe.com')
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
#### Response Fields:
|
|
319
|
+
|
|
320
|
+
```ruby
|
|
321
|
+
result.lead.data # Lead enrichment data
|
|
322
|
+
result.company.data # Company enrichment data
|
|
323
|
+
result.meta.email
|
|
324
|
+
result.meta.domain
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### 13. Account Information API
|
|
328
|
+
|
|
135
329
|
Retrieve details about your Hunter account.
|
|
136
330
|
|
|
137
331
|
```ruby
|
|
@@ -139,21 +333,22 @@ result = email_hunter.account
|
|
|
139
333
|
```
|
|
140
334
|
|
|
141
335
|
#### Response Example:
|
|
336
|
+
|
|
142
337
|
```json
|
|
143
338
|
{
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
339
|
+
"data": {
|
|
340
|
+
"first_name": "Davide",
|
|
341
|
+
"last_name": "Santangelo",
|
|
342
|
+
"email": "davide.santangelo@gmail.com",
|
|
343
|
+
"plan_name": "Free",
|
|
344
|
+
"plan_level": 0,
|
|
345
|
+
"reset_date": "2025-06-29",
|
|
346
|
+
"team_id": 349,
|
|
347
|
+
"calls": {
|
|
348
|
+
"used": 4,
|
|
349
|
+
"available": 50
|
|
350
|
+
}
|
|
351
|
+
}
|
|
157
352
|
}
|
|
158
353
|
```
|
|
159
354
|
|
|
@@ -168,4 +363,3 @@ The EmailHunter gem is released under the [MIT License](https://opensource.org/l
|
|
|
168
363
|
3. Commit your changes (`git commit -am 'Add new feature'`)
|
|
169
364
|
4. Push to the branch (`git push origin my-new-feature`)
|
|
170
365
|
5. Create a new Pull Request
|
|
171
|
-
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
data/emailhunter.gemspec
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# Use require_relative to load the version file
|
|
2
4
|
require_relative 'lib/email_hunter/version'
|
|
3
5
|
|
|
@@ -14,11 +16,11 @@ Gem::Specification.new do |spec|
|
|
|
14
16
|
email discovery capabilities into your sales and marketing workflows.
|
|
15
17
|
DESC
|
|
16
18
|
spec.license = 'MIT'
|
|
17
|
-
|
|
19
|
+
|
|
18
20
|
# Adding additional metadata improves discoverability
|
|
19
21
|
spec.homepage = 'https://github.com/davidesantangelo/emailhunter'
|
|
20
22
|
spec.metadata = {
|
|
21
|
-
|
|
23
|
+
'source_code_uri' => 'https://github.com/davidesantangelo/emailhunter'
|
|
22
24
|
}
|
|
23
25
|
|
|
24
26
|
# Automatically include all version-controlled files except tests and specs
|
data/lib/email_hunter/account.rb
CHANGED
|
@@ -6,7 +6,7 @@ require 'json'
|
|
|
6
6
|
module EmailHunter
|
|
7
7
|
class Account
|
|
8
8
|
API_URL = 'https://api.hunter.io/v2/account'
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
attr_reader :result, :first_name, :last_name, :email, :plan_name, :plan_level, :reset_date, :team_id, :calls,
|
|
11
11
|
:requests
|
|
12
12
|
|
|
@@ -28,10 +28,10 @@ module EmailHunter
|
|
|
28
28
|
|
|
29
29
|
def parse_response(response)
|
|
30
30
|
return nil unless response.success?
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
data = JSON.parse(response.body, symbolize_names: true)
|
|
33
33
|
return nil if data.empty?
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
Struct.new(*data.keys).new(*data.values)
|
|
36
36
|
end
|
|
37
37
|
end
|
data/lib/email_hunter/api.rb
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
require 'uri'
|
|
4
4
|
|
|
5
|
-
%w[exist search finder verify count account company people
|
|
5
|
+
%w[exist search finder verify count account company people discover leads campaigns lead_enrichment company_enrichment
|
|
6
|
+
combined_enrichment].each do |file|
|
|
6
7
|
require_relative file
|
|
7
8
|
end
|
|
8
9
|
|
|
@@ -44,7 +45,7 @@ module EmailHunter
|
|
|
44
45
|
def account
|
|
45
46
|
execute_hunt(Account)
|
|
46
47
|
end
|
|
47
|
-
|
|
48
|
+
|
|
48
49
|
# Company Information API
|
|
49
50
|
def company(domain)
|
|
50
51
|
execute_hunt(Company, domain)
|
|
@@ -55,15 +56,75 @@ module EmailHunter
|
|
|
55
56
|
execute_hunt(People, domain)
|
|
56
57
|
end
|
|
57
58
|
|
|
59
|
+
# Discover API - Search companies using natural language
|
|
60
|
+
def discover(query, params = {})
|
|
61
|
+
execute_hunt(Discover, query, params)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Leads API - List leads
|
|
65
|
+
def leads(params = {})
|
|
66
|
+
Leads.new(key, action: :list, params: params).hunt
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Leads API - Create lead
|
|
70
|
+
def lead_create(data)
|
|
71
|
+
Leads.new(key, action: :create, data: data).hunt
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Leads API - Update lead
|
|
75
|
+
def lead_update(id, data)
|
|
76
|
+
Leads.new(key, action: :update, lead_id: id, data: data).hunt
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Leads API - Delete lead
|
|
80
|
+
def lead_delete(id)
|
|
81
|
+
Leads.new(key, action: :delete, lead_id: id).hunt
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Campaigns API - List campaigns
|
|
85
|
+
def campaigns(params = {})
|
|
86
|
+
Campaigns.new(key, action: :list, params: params).hunt
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Campaigns API - Get campaign recipients
|
|
90
|
+
def campaign_recipients(campaign_id, params = {})
|
|
91
|
+
Campaigns.new(key, action: :recipients, campaign_id: campaign_id, params: params).hunt
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Campaigns API - Add recipient to campaign
|
|
95
|
+
def campaign_add_recipient(campaign_id, data)
|
|
96
|
+
Campaigns.new(key, action: :add_recipient, campaign_id: campaign_id, data: data).hunt
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Campaigns API - Delete recipient from campaign
|
|
100
|
+
def campaign_delete_recipient(campaign_id, email)
|
|
101
|
+
Campaigns.new(key, action: :delete_recipient, campaign_id: campaign_id, data: { email: email }).hunt
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Lead Enrichment API - Enrich person data
|
|
105
|
+
def lead_enrichment(email: nil, linkedin: nil)
|
|
106
|
+
LeadEnrichment.new(key, email: email, linkedin: linkedin).hunt
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Company Enrichment API - Enrich company data
|
|
110
|
+
def company_enrichment(domain)
|
|
111
|
+
CompanyEnrichment.new(domain, key).hunt
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Combined Enrichment API - Enrich both lead and company data
|
|
115
|
+
def combined_enrichment(email: nil, linkedin: nil)
|
|
116
|
+
CombinedEnrichment.new(key, email: email, linkedin: linkedin).hunt
|
|
117
|
+
end
|
|
118
|
+
|
|
58
119
|
private
|
|
59
120
|
|
|
60
121
|
# Helper method to reduce repetition
|
|
61
122
|
def execute_hunt(klass, *args)
|
|
62
123
|
case klass.name.split('::').last
|
|
63
|
-
when 'Search'
|
|
64
|
-
# Search
|
|
65
|
-
|
|
66
|
-
klass.new(
|
|
124
|
+
when 'Search', 'Discover'
|
|
125
|
+
# Search and Discover expect (value, key, params)
|
|
126
|
+
value, params = args
|
|
127
|
+
klass.new(value, key, params || {}).hunt
|
|
67
128
|
when 'Count'
|
|
68
129
|
# Count doesn't need key
|
|
69
130
|
domain = args.first
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'ostruct'
|
|
6
|
+
|
|
7
|
+
module EmailHunter
|
|
8
|
+
class Campaigns
|
|
9
|
+
API_URL = 'https://api.hunter.io/v2/campaigns'
|
|
10
|
+
|
|
11
|
+
attr_reader :key, :params, :action, :campaign_id, :data
|
|
12
|
+
|
|
13
|
+
def initialize(key, action: :list, campaign_id: nil, data: nil, params: {})
|
|
14
|
+
@key = key
|
|
15
|
+
@action = action
|
|
16
|
+
@campaign_id = campaign_id
|
|
17
|
+
@data = data
|
|
18
|
+
@params = params
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def hunt
|
|
22
|
+
case action
|
|
23
|
+
when :list
|
|
24
|
+
list_campaigns
|
|
25
|
+
when :recipients
|
|
26
|
+
list_recipients
|
|
27
|
+
when :add_recipient
|
|
28
|
+
add_recipient
|
|
29
|
+
when :delete_recipient
|
|
30
|
+
delete_recipient
|
|
31
|
+
else
|
|
32
|
+
raise ArgumentError, "Unknown action: #{action}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def list_campaigns
|
|
39
|
+
response_data = fetch_campaigns_data
|
|
40
|
+
return nil if response_data.empty?
|
|
41
|
+
|
|
42
|
+
# Convert to OpenStruct for easy access
|
|
43
|
+
result = OpenStruct.new(response_data)
|
|
44
|
+
|
|
45
|
+
# Convert campaigns array to OpenStruct objects if it exists
|
|
46
|
+
if result.data.is_a?(Hash) && result.data[:campaigns].is_a?(Array)
|
|
47
|
+
campaigns_array = result.data[:campaigns].map { |c| OpenStruct.new(c) }
|
|
48
|
+
result.data = OpenStruct.new(result.data.merge(campaigns: campaigns_array))
|
|
49
|
+
elsif result.data.is_a?(OpenStruct) && result.data.campaigns.is_a?(Array)
|
|
50
|
+
result.data.campaigns = result.data.campaigns.map { |c| OpenStruct.new(c) }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
result
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def list_recipients
|
|
57
|
+
response_data = fetch_recipients_data
|
|
58
|
+
return nil if response_data.empty?
|
|
59
|
+
|
|
60
|
+
result = OpenStruct.new(response_data)
|
|
61
|
+
|
|
62
|
+
# Convert recipients array to OpenStruct objects if it exists
|
|
63
|
+
if result.data.is_a?(Hash) && result.data[:recipients].is_a?(Array)
|
|
64
|
+
recipients_array = result.data[:recipients].map { |r| OpenStruct.new(r) }
|
|
65
|
+
result.data = OpenStruct.new(result.data.merge(recipients: recipients_array))
|
|
66
|
+
elsif result.data.is_a?(OpenStruct) && result.data.recipients.is_a?(Array)
|
|
67
|
+
result.data.recipients = result.data.recipients.map { |r| OpenStruct.new(r) }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
result
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def add_recipient
|
|
74
|
+
response_data = post_recipient_data
|
|
75
|
+
return nil if response_data.empty?
|
|
76
|
+
|
|
77
|
+
OpenStruct.new(response_data)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def delete_recipient
|
|
81
|
+
response = delete_recipient_data
|
|
82
|
+
response.success?
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def fetch_campaigns_data
|
|
86
|
+
connection = Faraday.new
|
|
87
|
+
request_params = {
|
|
88
|
+
api_key: key,
|
|
89
|
+
limit: params.fetch(:limit, 20),
|
|
90
|
+
offset: params.fetch(:offset, 0)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
response = connection.get(API_URL, request_params)
|
|
94
|
+
|
|
95
|
+
return {} unless response.success?
|
|
96
|
+
|
|
97
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def fetch_recipients_data
|
|
101
|
+
connection = Faraday.new
|
|
102
|
+
url = "#{API_URL}/#{campaign_id}/recipients"
|
|
103
|
+
request_params = {
|
|
104
|
+
api_key: key,
|
|
105
|
+
limit: params.fetch(:limit, 20),
|
|
106
|
+
offset: params.fetch(:offset, 0)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
response = connection.get(url, request_params)
|
|
110
|
+
|
|
111
|
+
return {} unless response.success?
|
|
112
|
+
|
|
113
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def post_recipient_data
|
|
117
|
+
connection = Faraday.new
|
|
118
|
+
url = "#{API_URL}/#{campaign_id}/recipients"
|
|
119
|
+
request_data = data.merge(api_key: key)
|
|
120
|
+
|
|
121
|
+
response = connection.post(url) do |req|
|
|
122
|
+
req.headers['Content-Type'] = 'application/json'
|
|
123
|
+
req.body = request_data.to_json
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
return {} unless response.success?
|
|
127
|
+
|
|
128
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def delete_recipient_data
|
|
132
|
+
connection = Faraday.new
|
|
133
|
+
email = data[:email]
|
|
134
|
+
url = "#{API_URL}/#{campaign_id}/recipients/#{email}"
|
|
135
|
+
|
|
136
|
+
connection.delete(url, api_key: key)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ostruct'
|
|
4
|
+
require_relative 'lead_enrichment'
|
|
5
|
+
require_relative 'company_enrichment'
|
|
6
|
+
|
|
7
|
+
module EmailHunter
|
|
8
|
+
class CombinedEnrichment
|
|
9
|
+
attr_reader :key, :email, :linkedin
|
|
10
|
+
|
|
11
|
+
def initialize(key, email: nil, linkedin: nil)
|
|
12
|
+
@key = key
|
|
13
|
+
@email = email
|
|
14
|
+
@linkedin = linkedin
|
|
15
|
+
|
|
16
|
+
raise ArgumentError, 'Either email or linkedin must be provided' if email.nil? && linkedin.nil?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def hunt
|
|
20
|
+
# First get lead enrichment data
|
|
21
|
+
lead_data = LeadEnrichment.new(key, email: email, linkedin: linkedin).hunt
|
|
22
|
+
return nil if lead_data.nil?
|
|
23
|
+
|
|
24
|
+
# Extract domain from employment if available
|
|
25
|
+
domain = lead_data.data.employment&.domain
|
|
26
|
+
return lead_data if domain.nil?
|
|
27
|
+
|
|
28
|
+
# Get company enrichment data
|
|
29
|
+
company_data = CompanyEnrichment.new(domain, key).hunt
|
|
30
|
+
|
|
31
|
+
# Combine the data
|
|
32
|
+
OpenStruct.new(
|
|
33
|
+
lead: lead_data,
|
|
34
|
+
company: company_data,
|
|
35
|
+
meta: OpenStruct.new(
|
|
36
|
+
email: email,
|
|
37
|
+
linkedin: linkedin,
|
|
38
|
+
domain: domain
|
|
39
|
+
)
|
|
40
|
+
)
|
|
41
|
+
rescue StandardError => e
|
|
42
|
+
# If company enrichment fails, just return lead data
|
|
43
|
+
OpenStruct.new(
|
|
44
|
+
lead: lead_data,
|
|
45
|
+
company: nil,
|
|
46
|
+
meta: OpenStruct.new(
|
|
47
|
+
email: email,
|
|
48
|
+
linkedin: linkedin,
|
|
49
|
+
error: e.message
|
|
50
|
+
)
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/email_hunter/company.rb
CHANGED
|
@@ -7,7 +7,7 @@ require 'ostruct'
|
|
|
7
7
|
module EmailHunter
|
|
8
8
|
class Company
|
|
9
9
|
API_URL = 'https://api.hunter.io/v2/companies/find'
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
attr_reader :domain, :key
|
|
12
12
|
|
|
13
13
|
def initialize(domain, key)
|
|
@@ -18,19 +18,19 @@ module EmailHunter
|
|
|
18
18
|
def hunt
|
|
19
19
|
response_data = fetch_company_data
|
|
20
20
|
return nil if response_data.empty?
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
# Convert nested data structure to a Struct
|
|
23
23
|
data = response_data[:data]
|
|
24
24
|
meta = response_data[:meta]
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
result = OpenStruct.new(
|
|
27
27
|
data: OpenStruct.new(data),
|
|
28
28
|
meta: OpenStruct.new(meta)
|
|
29
29
|
)
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
# Recursively convert nested hashes to OpenStructs for deeper access
|
|
32
32
|
convert_hash_to_struct(result.data, data)
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
result
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -40,7 +40,7 @@ module EmailHunter
|
|
|
40
40
|
@fetch_company_data ||= begin
|
|
41
41
|
connection = Faraday.new
|
|
42
42
|
response = connection.get(API_URL, domain: domain, api_key: key)
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
return {} unless response.success?
|
|
45
45
|
|
|
46
46
|
JSON.parse(response.body, symbolize_names: true)
|
|
@@ -58,4 +58,4 @@ module EmailHunter
|
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
|
-
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'ostruct'
|
|
6
|
+
|
|
7
|
+
module EmailHunter
|
|
8
|
+
class CompanyEnrichment
|
|
9
|
+
API_URL = 'https://api.hunter.io/v2/companies/find'
|
|
10
|
+
|
|
11
|
+
attr_reader :domain, :key
|
|
12
|
+
|
|
13
|
+
def initialize(domain, key)
|
|
14
|
+
@domain = domain
|
|
15
|
+
@key = key
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def hunt
|
|
19
|
+
response_data = fetch_enrichment_data
|
|
20
|
+
return nil if response_data.empty?
|
|
21
|
+
|
|
22
|
+
# Convert nested data structure to OpenStruct
|
|
23
|
+
data = response_data[:data]
|
|
24
|
+
meta = response_data[:meta]
|
|
25
|
+
|
|
26
|
+
result = OpenStruct.new(
|
|
27
|
+
data: OpenStruct.new(data),
|
|
28
|
+
meta: OpenStruct.new(meta)
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# Recursively convert nested hashes to OpenStructs for deeper access
|
|
32
|
+
convert_hash_to_struct(result.data, data)
|
|
33
|
+
|
|
34
|
+
result
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def fetch_enrichment_data
|
|
40
|
+
@fetch_enrichment_data ||= begin
|
|
41
|
+
connection = Faraday.new
|
|
42
|
+
response = connection.get(API_URL, domain: domain, api_key: key)
|
|
43
|
+
|
|
44
|
+
return {} unless response.success?
|
|
45
|
+
|
|
46
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def convert_hash_to_struct(struct, hash)
|
|
51
|
+
hash.each do |key, value|
|
|
52
|
+
if value.is_a?(Hash)
|
|
53
|
+
struct[key] = OpenStruct.new(value)
|
|
54
|
+
convert_hash_to_struct(struct[key], value)
|
|
55
|
+
elsif value.is_a?(Array) && value.first.is_a?(Hash)
|
|
56
|
+
struct[key] = value.map { |item| OpenStruct.new(item) }
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
data/lib/email_hunter/count.rb
CHANGED
|
@@ -6,7 +6,7 @@ require 'json'
|
|
|
6
6
|
module EmailHunter
|
|
7
7
|
class Count
|
|
8
8
|
API_URL = 'https://api.hunter.io/v2/email-count'
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
attr_reader :domain
|
|
11
11
|
|
|
12
12
|
def initialize(domain)
|
|
@@ -16,7 +16,7 @@ module EmailHunter
|
|
|
16
16
|
def hunt
|
|
17
17
|
response_data = fetch_count_data
|
|
18
18
|
return nil if response_data.empty?
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
Struct.new(*response_data.keys).new(*response_data.values)
|
|
21
21
|
end
|
|
22
22
|
|
|
@@ -26,7 +26,7 @@ module EmailHunter
|
|
|
26
26
|
@fetch_count_data ||= begin
|
|
27
27
|
connection = Faraday.new
|
|
28
28
|
response = connection.get(API_URL, domain: domain)
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
return {} unless response.success?
|
|
31
31
|
|
|
32
32
|
JSON.parse(response.body, symbolize_names: true)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'json'
|
|
5
|
+
|
|
6
|
+
module EmailHunter
|
|
7
|
+
class Discover
|
|
8
|
+
API_URL = 'https://api.hunter.io/v2/discover'
|
|
9
|
+
|
|
10
|
+
attr_reader :query, :key, :params
|
|
11
|
+
|
|
12
|
+
def initialize(query, key, params = {})
|
|
13
|
+
@query = query
|
|
14
|
+
@key = key
|
|
15
|
+
@params = params
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def hunt
|
|
19
|
+
response_data = fetch_discover_data
|
|
20
|
+
return nil if response_data.empty?
|
|
21
|
+
|
|
22
|
+
# Convert to OpenStruct for easy access
|
|
23
|
+
result = OpenStruct.new(response_data)
|
|
24
|
+
|
|
25
|
+
# Recursively convert nested hashes to OpenStructs
|
|
26
|
+
convert_hash_to_struct(result, response_data)
|
|
27
|
+
|
|
28
|
+
result
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def convert_hash_to_struct(struct, hash)
|
|
34
|
+
hash.each do |key, value|
|
|
35
|
+
if value.is_a?(Hash)
|
|
36
|
+
struct[key] = OpenStruct.new(value)
|
|
37
|
+
convert_hash_to_struct(struct[key], value)
|
|
38
|
+
elsif value.is_a?(Array) && value.first.is_a?(Hash)
|
|
39
|
+
struct[key] = value.map { |item| OpenStruct.new(item) }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def limit
|
|
45
|
+
params.fetch(:limit, 100).to_i
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def offset
|
|
49
|
+
params.fetch(:offset, 0).to_i
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def filters
|
|
53
|
+
params.fetch(:filters, {})
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def fetch_discover_data
|
|
57
|
+
@fetch_discover_data ||= begin
|
|
58
|
+
connection = Faraday.new
|
|
59
|
+
request_data = {
|
|
60
|
+
query: query,
|
|
61
|
+
api_key: key,
|
|
62
|
+
limit: limit,
|
|
63
|
+
offset: offset
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# Add filters if provided
|
|
67
|
+
request_data[:filters] = filters unless filters.empty?
|
|
68
|
+
|
|
69
|
+
response = connection.post(API_URL) do |req|
|
|
70
|
+
req.headers['Content-Type'] = 'application/json'
|
|
71
|
+
req.body = request_data.to_json
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
return {} unless response.success?
|
|
75
|
+
|
|
76
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
data/lib/email_hunter/exist.rb
CHANGED
|
@@ -6,7 +6,7 @@ require 'json'
|
|
|
6
6
|
module EmailHunter
|
|
7
7
|
class Exist
|
|
8
8
|
API_URL = 'https://api.emailhunter.co/v1/exist'
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
attr_reader :email, :key
|
|
11
11
|
|
|
12
12
|
def initialize(email, key)
|
|
@@ -17,7 +17,7 @@ module EmailHunter
|
|
|
17
17
|
def hunt
|
|
18
18
|
response_data = fetch_exist_data
|
|
19
19
|
return nil if response_data.empty?
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
Struct.new(*response_data.keys).new(*response_data.values)
|
|
22
22
|
end
|
|
23
23
|
|
|
@@ -27,7 +27,7 @@ module EmailHunter
|
|
|
27
27
|
@fetch_exist_data ||= begin
|
|
28
28
|
connection = Faraday.new
|
|
29
29
|
response = connection.get(API_URL, email: email, api_key: key)
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
return {} unless response.success?
|
|
32
32
|
|
|
33
33
|
JSON.parse(response.body, symbolize_names: true)
|
data/lib/email_hunter/finder.rb
CHANGED
|
@@ -6,7 +6,7 @@ require 'json'
|
|
|
6
6
|
module EmailHunter
|
|
7
7
|
class Finder
|
|
8
8
|
API_FINDER_URL = 'https://api.hunter.io/v2/email-finder'
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
attr_reader :email, :score, :sources, :domain, :meta, :key, :first_name, :last_name
|
|
11
11
|
|
|
12
12
|
def initialize(domain, first_name, last_name, key)
|
|
@@ -23,7 +23,7 @@ module EmailHunter
|
|
|
23
23
|
def data
|
|
24
24
|
@data ||= begin
|
|
25
25
|
response = fetch_finder_data
|
|
26
|
-
|
|
26
|
+
|
|
27
27
|
return {} unless response.success?
|
|
28
28
|
|
|
29
29
|
JSON.parse(response.body, symbolize_names: true)
|
|
@@ -40,7 +40,7 @@ module EmailHunter
|
|
|
40
40
|
last_name: last_name,
|
|
41
41
|
api_key: key
|
|
42
42
|
}
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
connection.get(API_FINDER_URL, params)
|
|
45
45
|
end
|
|
46
46
|
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'ostruct'
|
|
6
|
+
|
|
7
|
+
module EmailHunter
|
|
8
|
+
class LeadEnrichment
|
|
9
|
+
API_URL = 'https://api.hunter.io/v2/people/find'
|
|
10
|
+
|
|
11
|
+
attr_reader :key, :email, :linkedin
|
|
12
|
+
|
|
13
|
+
def initialize(key, email: nil, linkedin: nil)
|
|
14
|
+
@key = key
|
|
15
|
+
@email = email
|
|
16
|
+
@linkedin = linkedin
|
|
17
|
+
|
|
18
|
+
raise ArgumentError, 'Either email or linkedin must be provided' if email.nil? && linkedin.nil?
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def hunt
|
|
22
|
+
response_data = fetch_enrichment_data
|
|
23
|
+
return nil if response_data.empty?
|
|
24
|
+
|
|
25
|
+
# Convert nested data structure to OpenStruct
|
|
26
|
+
data = response_data[:data]
|
|
27
|
+
meta = response_data[:meta]
|
|
28
|
+
|
|
29
|
+
result = OpenStruct.new(
|
|
30
|
+
data: OpenStruct.new(data),
|
|
31
|
+
meta: OpenStruct.new(meta)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Recursively convert nested hashes to OpenStructs for deeper access
|
|
35
|
+
convert_hash_to_struct(result.data, data)
|
|
36
|
+
|
|
37
|
+
result
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def fetch_enrichment_data
|
|
43
|
+
@fetch_enrichment_data ||= begin
|
|
44
|
+
connection = Faraday.new
|
|
45
|
+
request_params = { api_key: key }
|
|
46
|
+
|
|
47
|
+
request_params[:email] = email if email
|
|
48
|
+
request_params[:linkedin_handle] = linkedin if linkedin
|
|
49
|
+
|
|
50
|
+
response = connection.get(API_URL, request_params)
|
|
51
|
+
|
|
52
|
+
return {} unless response.success?
|
|
53
|
+
|
|
54
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def convert_hash_to_struct(struct, hash)
|
|
59
|
+
hash.each do |key, value|
|
|
60
|
+
if value.is_a?(Hash)
|
|
61
|
+
struct[key] = OpenStruct.new(value)
|
|
62
|
+
convert_hash_to_struct(struct[key], value)
|
|
63
|
+
elsif value.is_a?(Array) && value.first.is_a?(Hash)
|
|
64
|
+
struct[key] = value.map { |item| OpenStruct.new(item) }
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'faraday'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'ostruct'
|
|
6
|
+
|
|
7
|
+
module EmailHunter
|
|
8
|
+
class Leads
|
|
9
|
+
API_URL = 'https://api.hunter.io/v2/leads'
|
|
10
|
+
|
|
11
|
+
attr_reader :key, :params, :action, :lead_id, :data
|
|
12
|
+
|
|
13
|
+
def initialize(key, action: :list, lead_id: nil, data: nil, params: {})
|
|
14
|
+
@key = key
|
|
15
|
+
@action = action
|
|
16
|
+
@lead_id = lead_id
|
|
17
|
+
@data = data
|
|
18
|
+
@params = params
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def hunt
|
|
22
|
+
case action
|
|
23
|
+
when :list
|
|
24
|
+
list_leads
|
|
25
|
+
when :create
|
|
26
|
+
create_lead
|
|
27
|
+
when :update
|
|
28
|
+
update_lead
|
|
29
|
+
when :delete
|
|
30
|
+
delete_lead
|
|
31
|
+
else
|
|
32
|
+
raise ArgumentError, "Unknown action: #{action}"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def list_leads
|
|
39
|
+
response_data = fetch_leads_data
|
|
40
|
+
return nil if response_data.empty?
|
|
41
|
+
|
|
42
|
+
# Convert nested data structure to OpenStruct
|
|
43
|
+
result = OpenStruct.new(
|
|
44
|
+
data: OpenStruct.new(response_data[:data] || {}),
|
|
45
|
+
meta: OpenStruct.new(response_data[:meta] || {})
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Convert leads array to OpenStruct objects
|
|
49
|
+
result.data.leads = result.data.leads.map { |lead| OpenStruct.new(lead) } if result.data.leads.is_a?(Array)
|
|
50
|
+
|
|
51
|
+
result
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def create_lead
|
|
55
|
+
response_data = post_lead_data
|
|
56
|
+
return nil if response_data.empty?
|
|
57
|
+
|
|
58
|
+
OpenStruct.new(response_data)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def update_lead
|
|
62
|
+
response_data = put_lead_data
|
|
63
|
+
return nil if response_data.empty?
|
|
64
|
+
|
|
65
|
+
OpenStruct.new(response_data)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def delete_lead
|
|
69
|
+
response = delete_lead_data
|
|
70
|
+
response.success?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def fetch_leads_data
|
|
74
|
+
@fetch_leads_data ||= begin
|
|
75
|
+
connection = Faraday.new
|
|
76
|
+
request_params = {
|
|
77
|
+
api_key: key,
|
|
78
|
+
limit: params.fetch(:limit, 20),
|
|
79
|
+
offset: params.fetch(:offset, 0)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# Add optional filters
|
|
83
|
+
request_params[:leads_list_id] = params[:leads_list_id] if params[:leads_list_id]
|
|
84
|
+
|
|
85
|
+
response = connection.get(API_URL, request_params)
|
|
86
|
+
|
|
87
|
+
return {} unless response.success?
|
|
88
|
+
|
|
89
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def post_lead_data
|
|
94
|
+
connection = Faraday.new
|
|
95
|
+
request_data = data.merge(api_key: key)
|
|
96
|
+
|
|
97
|
+
response = connection.post(API_URL) do |req|
|
|
98
|
+
req.headers['Content-Type'] = 'application/json'
|
|
99
|
+
req.body = request_data.to_json
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
return {} unless response.success?
|
|
103
|
+
|
|
104
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def put_lead_data
|
|
108
|
+
connection = Faraday.new
|
|
109
|
+
url = "#{API_URL}/#{lead_id}"
|
|
110
|
+
request_data = data.merge(api_key: key)
|
|
111
|
+
|
|
112
|
+
response = connection.put(url) do |req|
|
|
113
|
+
req.headers['Content-Type'] = 'application/json'
|
|
114
|
+
req.body = request_data.to_json
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
return {} unless response.success?
|
|
118
|
+
|
|
119
|
+
JSON.parse(response.body, symbolize_names: true)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def delete_lead_data
|
|
123
|
+
connection = Faraday.new
|
|
124
|
+
url = "#{API_URL}/#{lead_id}"
|
|
125
|
+
|
|
126
|
+
connection.delete(url, api_key: key)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
data/lib/email_hunter/people.rb
CHANGED
|
@@ -7,7 +7,7 @@ require 'ostruct'
|
|
|
7
7
|
module EmailHunter
|
|
8
8
|
class People
|
|
9
9
|
API_URL = 'https://api.hunter.io/v2/people/find'
|
|
10
|
-
|
|
10
|
+
|
|
11
11
|
attr_reader :email, :key
|
|
12
12
|
|
|
13
13
|
def initialize(email, key)
|
|
@@ -18,19 +18,19 @@ module EmailHunter
|
|
|
18
18
|
def hunt
|
|
19
19
|
response_data = fetch_people_data
|
|
20
20
|
return nil if response_data.empty?
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
# Convert nested data structure to a Struct
|
|
23
23
|
data = response_data[:data]
|
|
24
24
|
meta = response_data[:meta]
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
result = OpenStruct.new(
|
|
27
27
|
data: OpenStruct.new(data),
|
|
28
28
|
meta: OpenStruct.new(meta)
|
|
29
29
|
)
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
# Recursively convert nested hashes to OpenStructs for deeper access
|
|
32
32
|
convert_hash_to_struct(result.data, data)
|
|
33
|
-
|
|
33
|
+
|
|
34
34
|
result
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -40,7 +40,7 @@ module EmailHunter
|
|
|
40
40
|
@fetch_people_data ||= begin
|
|
41
41
|
connection = Faraday.new
|
|
42
42
|
response = connection.get(API_URL, email: email, api_key: key)
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
return {} unless response.success?
|
|
45
45
|
|
|
46
46
|
JSON.parse(response.body, symbolize_names: true)
|
|
@@ -58,4 +58,4 @@ module EmailHunter
|
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
|
-
end
|
|
61
|
+
end
|
data/lib/email_hunter/search.rb
CHANGED
|
@@ -6,7 +6,7 @@ require 'json'
|
|
|
6
6
|
module EmailHunter
|
|
7
7
|
class Search
|
|
8
8
|
API_URL = 'https://api.hunter.io/v2/domain-search'
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
attr_reader :domain, :key, :params
|
|
11
11
|
|
|
12
12
|
def initialize(domain, key, params = {})
|
|
@@ -18,7 +18,7 @@ module EmailHunter
|
|
|
18
18
|
def hunt
|
|
19
19
|
response_data = fetch_search_data
|
|
20
20
|
return nil if response_data.empty?
|
|
21
|
-
|
|
21
|
+
|
|
22
22
|
Struct.new(*response_data.keys).new(*response_data.values)
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -41,10 +41,10 @@ module EmailHunter
|
|
|
41
41
|
offset: offset,
|
|
42
42
|
limit: limit
|
|
43
43
|
}
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
request_params[:type] = params[:type] if params[:type]
|
|
46
46
|
response = connection.get(API_URL, request_params)
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
return {} unless response.success?
|
|
49
49
|
|
|
50
50
|
JSON.parse(response.body, symbolize_names: true)
|
data/lib/email_hunter/verify.rb
CHANGED
|
@@ -6,7 +6,7 @@ require 'json'
|
|
|
6
6
|
module EmailHunter
|
|
7
7
|
class Verify
|
|
8
8
|
API_URL = 'https://api.hunter.io/v2/email-verifier'
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
attr_reader :email, :key
|
|
11
11
|
|
|
12
12
|
def initialize(email, key)
|
|
@@ -17,7 +17,7 @@ module EmailHunter
|
|
|
17
17
|
def hunt
|
|
18
18
|
response_data = fetch_verify_data
|
|
19
19
|
return nil if response_data.empty?
|
|
20
|
-
|
|
20
|
+
|
|
21
21
|
Struct.new(*response_data.keys).new(*response_data.values)
|
|
22
22
|
end
|
|
23
23
|
|
|
@@ -27,7 +27,7 @@ module EmailHunter
|
|
|
27
27
|
@fetch_verify_data ||= begin
|
|
28
28
|
connection = Faraday.new
|
|
29
29
|
response = connection.get(API_URL, email: email, api_key: key)
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
return {} unless response.success?
|
|
32
32
|
|
|
33
33
|
JSON.parse(response.body, symbolize_names: true)
|
data/lib/email_hunter/version.rb
CHANGED
data/lib/emailhunter.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: emailhunter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Davide Santangelo
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-11-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bundler
|
|
@@ -132,10 +132,16 @@ files:
|
|
|
132
132
|
- lib/email_hunter.rb
|
|
133
133
|
- lib/email_hunter/account.rb
|
|
134
134
|
- lib/email_hunter/api.rb
|
|
135
|
+
- lib/email_hunter/campaigns.rb
|
|
136
|
+
- lib/email_hunter/combined_enrichment.rb
|
|
135
137
|
- lib/email_hunter/company.rb
|
|
138
|
+
- lib/email_hunter/company_enrichment.rb
|
|
136
139
|
- lib/email_hunter/count.rb
|
|
140
|
+
- lib/email_hunter/discover.rb
|
|
137
141
|
- lib/email_hunter/exist.rb
|
|
138
142
|
- lib/email_hunter/finder.rb
|
|
143
|
+
- lib/email_hunter/lead_enrichment.rb
|
|
144
|
+
- lib/email_hunter/leads.rb
|
|
139
145
|
- lib/email_hunter/people.rb
|
|
140
146
|
- lib/email_hunter/search.rb
|
|
141
147
|
- lib/email_hunter/verify.rb
|