@unavatar/core 3.7.62 → 3.7.64

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.
package/README.md CHANGED
@@ -14,10 +14,12 @@
14
14
  - [DuckDuckGo](#duckduckgo)
15
15
  - [GitHub](#github)
16
16
  - [GitLab](#gitlab)
17
+ - [LinkedIn](#linkedin)
17
18
  - [Google](#google)
18
19
  - [Gravatar](#gravatar)
19
20
  - [Instagram](#instagram)
20
21
  - [Microlink](#microlink)
22
+ - [Mastodon](#mastodon)
21
23
  - [OnlyFans](#onlyfans)
22
24
  - [OpenStreetMap](#openstreetmap)
23
25
  - [Patreon](#patreon)
@@ -43,13 +45,13 @@ Welcome to **unavatar.io**, the ultimate avatar service that offers everything y
43
45
 
44
46
  - **Versatile**: A wide range of platforms and services including [TikTok](#tiktok), [Instagram](#instagram), [YouTube](#youtube), [X/Twitter](#xtwitter), [Gravatar](#gravatar), etc., meaning you can rule all of them just querying against unavatar.
45
47
 
46
- - **Speed**: Designed to be fast and efficient, all requests are being cached and delivered +200 global datacenters, allowing you to consume avatars instantly, counting more than 20 millions requests per month.
48
+ - **Speed**: Designed to be fast and efficient with a 97% cache hit rate, serving 24.3 TB of data across 522M requests.
47
49
 
48
50
  - **Optimize**: All the images are not only compressed on-the-fly to reduce their size and save bandwith, but also optimized to maintain a high-quality ratio. They are ready for immediate use, enhancing the overall optimization of your website or application.
49
51
 
50
52
  - **Integration**: The service seamlessly incorporates into your current applications or websites with ease. We offer straightforward documentation and comprehensive support to ensure a quick and effortless onboarding experience.
51
53
 
52
- It's proudly powered by [microlink.io](https://microlink.io), the headless browser API that handles all the heavy lifting behind the scenes to ensure your avatars are always ready.
54
+ It's proudly powered by [microlink.io](https://microlink.io/), the headless browser API that handles all the heavy lifting behind the scenes to ensure your avatars are always ready.
53
55
 
54
56
  ## Quick start
55
57
 
@@ -65,8 +67,8 @@ Use the `/:provider/:key` format for all lookups. You can read more about availa
65
67
 
66
68
  ### TTL
67
69
 
68
- Type: `number`|`string`<br/>
69
- Default: `'24h'`<br/>
70
+ Type: `number` or `string`<br>
71
+ Default: `'24h'`<br>
70
72
  Range: from `'1h'` to `'28d'`
71
73
 
72
74
  It determines the maximum quantity of time an avatar is considered fresh.
@@ -79,7 +81,7 @@ The same resource will continue to be used until reach TTL expiration. After tha
79
81
 
80
82
  ### Fallback
81
83
 
82
- Type: `string`|`boolean`
84
+ Type: `string` or `boolean`
83
85
 
84
86
  When it can't be possible to get a user avatar, a fallback image is returned instead, and it can be personalized to fit better with your website or application style.
85
87
 
@@ -113,7 +115,7 @@ e.g., [unavatar.io/github/kikobeats?json](https://unavatar.io/github/kikobeats?j
113
115
 
114
116
  ## Pricing
115
117
 
116
- The service is **FREE** for everyone, no registration required, with a daily rate limit of **50 requests** per IP address.
118
+ The service is **FREE** for everyone, no registration required, with a daily rate limit of **50 requests** per IP address.
117
119
 
118
120
  For preventing abusive usage, the service has associated a daily rate limit based on requests IP address.
119
121
 
@@ -123,25 +125,23 @@ You can verify for your rate limit state checking the following headers in the r
123
125
  - `x-rate-limit-remaining`: The number of requests remaining in the current rate limit window.
124
126
  - `x-rate-limit-reset`: The time at which the current rate limit window resets in UTC epoch seconds.
125
127
 
126
- For higher usage, the **[PRO](https://unavatar.io/checkout)** plan is a usage-based plan billed monthly that removes rate limits and unlocks custom TTL.
128
+ For higher usage, the plan is a usage-based plan billed monthly that removes rate limits and unlocks custom TTL.
127
129
 
128
- Every request has a cost in tokens (**$0.001 per token**) based on the proxy tier needed to resolve the avatar:
130
+ Every request has a cost in tokens (**\$0.001 per token**) based on the proxy tier needed to resolve the avatar:
129
131
 
130
- | Proxy tier | Tokens | Cost |
131
- | ----------- | :----: | :----: |
132
- | Origin | 1 | $0.001 |
133
- | Datacenter | +2 | $0.003 |
134
- | Residential | +4 | $0.007 |
132
+ | Proxy tier | Tokens | Cost |
133
+ |-------------|--------|---------|
134
+ | Origin | 1 | \$0.001 |
135
+ | Datacenter | +2 | \$0.003 |
136
+ | Residential | +4 | \$0.007 |
135
137
 
136
138
  The proxy tier used is returned in the `x-proxy-tier` response header, and the total cost in the `x-unavatar-cost` header.
137
139
 
138
- ```bash
139
- $ curl -I -H "x-api-key: YOUR_API_KEY" https://unavatar.io/instagram/kikobeats
140
+ $ curl -I -H "x-api-key: YOUR_API_KEY" https://unavatar.io/instagram/kikobeats
140
141
 
141
- x-pricing-tier: pro
142
- x-proxy-tier: origin
143
- x-unavatar-cost: 1
144
- ```
142
+ x-pricing-tier: pro
143
+ x-proxy-tier: origin
144
+ x-unavatar-cost: 1
145
145
 
146
146
  To upgrade, visit [unavatar.io/checkout](https://unavatar.io/checkout). After completing the payment, you'll receive an API key.
147
147
 
@@ -149,19 +149,19 @@ To upgrade, visit [unavatar.io/checkout](https://unavatar.io/checkout). After co
149
149
 
150
150
  ### Apple Music
151
151
 
152
- It resolves user avatar against **music.apple.com**.
152
+ Get artwork for any Apple Music artist, album, or song. Search by name or look up directly by numeric Apple Music ID.
153
153
 
154
154
  e.g., [unavatar.io/apple-music/daft%20punk](https://unavatar.io/apple-music/daft%20punk)
155
155
 
156
- The endpoint supports explictiy type as part of the input.
156
+ The endpoint supports explicit type as part of the input.
157
157
 
158
158
  If explicit type is not provided, it searches `artist` and `song` (in that order).
159
159
 
160
- Available types:
160
+ Available URI format inputs:
161
161
 
162
162
  - artist
163
- - by artist name: [unavatar.io/apple-music/artist:daft%20punk](https://unavatar.io/apple-music/artist:daft%20punk)
164
- - by numeric artist ID: [unavatar.io/apple-music/artist:5468295](https://unavatar.io/apple-music/artist:5468295)
163
+ - by artist name: [unavatar.io/apple-music/artist:daft%20punk](https://unavatar.io/apple-music/artist:daft%20punk)
164
+ - by numeric artist ID: [unavatar.io/apple-music/artist:5468295](https://unavatar.io/apple-music/artist:5468295)
165
165
  - album
166
166
  - by album name: [unavatar.io/apple-music/album:discovery](https://unavatar.io/apple-music/album:discovery)
167
167
  - by album ID: [unavatar.io/apple-music/album:78691923](https://unavatar.io/apple-music/album:78691923)
@@ -171,108 +171,155 @@ Available types:
171
171
 
172
172
  ### Bluesky
173
173
 
174
- It resolves user avatar against **bsky.app**.
174
+ Get any Bluesky user's profile picture by their handle. Domain-style handles are supported.
175
175
 
176
- e.g., [unavatar.io/bluesky/pfrazee.com](https://unavatar.io/bluesky/pfrazee.com)
176
+ Available inputs:
177
+
178
+ - User handle, e.g., [unavatar.io/bluesky/pfrazee.com](https://unavatar.io/bluesky/pfrazee.com)
179
+ - Domain handle, e.g., [unavatar.io/bluesky/bsky.app](https://unavatar.io/bluesky/bsky.app)
177
180
 
178
181
  ### DeviantArt
179
182
 
180
- It resolves user avatar against **deviantart.com**.
183
+ Get any DeviantArt user's profile picture by their username.
184
+
185
+ Available inputs:
181
186
 
182
- e.g., [unavatar.io/deviantart/spyed](https://unavatar.io/deviantart/spyed)
187
+ - Username, e.g., [unavatar.io/deviantart/spyed](https://unavatar.io/deviantart/spyed)
183
188
 
184
189
  ### Dribbble
185
190
 
186
- It resolves user avatar against **dribbble.com**.
191
+ Get any Dribbble designer's profile picture by their username.
187
192
 
188
- e.g., [unavatar.io/dribbble/omidnikrah](https://unavatar.io/dribbble/omidnikrah)
193
+ Available inputs:
194
+
195
+ - Username, e.g., [unavatar.io/dribbble/omidnikrah](https://unavatar.io/dribbble/omidnikrah)
189
196
 
190
197
  ### DuckDuckGo
191
198
 
192
- It resolves user avatar using **duckduckgo.com**.
199
+ Get the favicon or logo for any domain via DuckDuckGo's icon service. Useful as a fallback when a domain doesn't expose its favicon directly.
193
200
 
194
- e.g., [unavatar.io/duckduckgo/gummibeer.dev](https://unavatar.io/duckduckgo/gummibeer.dev)
201
+ Available inputs:
202
+
203
+ - Domain, e.g., [unavatar.io/duckduckgo/gummibeer.dev](https://unavatar.io/duckduckgo/gummibeer.dev)
195
204
 
196
205
  ### GitHub
197
206
 
198
- It resolves user avatar against **github.com**.
207
+ Get any GitHub user or organization's profile picture by their username.
208
+
209
+ Available inputs:
199
210
 
200
- e.g., [unavatar.io/github/mdo](https://unavatar.io/github/mdo)
211
+ - User, e.g., [unavatar.io/github/mdo](https://unavatar.io/github/mdo)
212
+ - Organization, e.g., [unavatar.io/github/vercel](https://unavatar.io/github/vercel)
201
213
 
202
214
  ### GitLab
203
215
 
204
- It resolves user avatar against **gitlab.com**.
216
+ Get any GitLab user or group's profile picture by their username.
217
+
218
+ Available inputs:
219
+
220
+ - User, e.g., [unavatar.io/gitlab/sytses](https://unavatar.io/gitlab/sytses)
221
+ - Group, e.g., [unavatar.io/gitlab/inkscape](https://unavatar.io/gitlab/inkscape)
205
222
 
206
- e.g., [unavatar.io/gitlab/inkscape](https://unavatar.io/gitlab/inkscape)
223
+ ### LinkedIn
224
+
225
+ Get any LinkedIn user's profile picture by their public profile slug.
226
+
227
+ Available inputs:
228
+
229
+ - Profile slug, e.g., [unavatar.io/linkedin/kikobeats](https://unavatar.io/linkedin/kikobeats)
207
230
 
208
231
  ### Google
209
232
 
210
- It resolves user avatar using **google.com**.
233
+ Get the favicon or logo for any domain using Google's favicon service.
211
234
 
212
- e.g., [unavatar.io/google/netflix.com](https://unavatar.io/google/netflix.com)
235
+ Available inputs:
236
+
237
+ - Domain, e.g., [unavatar.io/google/netflix.com](https://unavatar.io/google/netflix.com)
213
238
 
214
239
  ### Gravatar
215
240
 
216
- It resolves user avatar against **gravatar.com**.
241
+ Get any user's avatar by their email address via Gravatar. The most widely used global avatar service — if your users have a Gravatar set up, this is the fastest way to retrieve it.
217
242
 
218
- e.g., [unavatar.io/gravatar/hello@microlink.io](https://unavatar.io/gravatar/hello@microlink.io)
243
+ Available inputs:
244
+
245
+ - Email address, e.g., [unavatar.io/gravatar/hello@microlink.io](https://unavatar.io/gravatar/hello@microlink.io)
219
246
 
220
247
  ### Instagram
221
248
 
222
- It resolves user avatar against **instagram.com**.
249
+ Get any Instagram user's profile picture by their username. No authentication or API tokens needed — just pass the username.
250
+
251
+ Available inputs:
223
252
 
224
- e.g., [unavatar.io/instagram/willsmith](https://unavatar.io/instagram/willsmith)
253
+ - Username, e.g., [unavatar.io/instagram/willsmith](https://unavatar.io/instagram/willsmith)
225
254
 
226
255
  ### Microlink
227
256
 
228
- It resolves user avatar using **microlink.io**.
257
+ Extract the logo or representative image from any URL. The page is rendered and the best available image is selected — useful for getting brand logos from any website.
258
+
259
+ Available inputs:
260
+
261
+ - Domain, e.g., [unavatar.io/microlink/microlink.io](https://unavatar.io/microlink/microlink.io)
229
262
 
230
- e.g., [unavatar.io/microlink/microlink.io](https://unavatar.io/microlink/microlink.io)
263
+ ### Mastodon
264
+
265
+ Get any Mastodon user's profile picture from any instance using the public account lookup API. Pass the handle as `user@server` so the account resolves on the correct home instance.
266
+
267
+ Available inputs:
268
+
269
+ - user@server, e.g., [unavatar.io/mastodon/kpwags@hachyderm.io](https://unavatar.io/mastodon/kpwags@hachyderm.io)
231
270
 
232
271
  ### OnlyFans
233
272
 
234
- It resolves user avatar using **onlyfans.com**.
273
+ Get any OnlyFans creator's profile picture by their username.
235
274
 
236
- e.g., [unavatar.io/onlyfans/amandaribas](https://unavatar.io/onlyfans/amandaribas)
275
+ Available inputs:
276
+
277
+ - Username, e.g., [unavatar.io/onlyfans/amandaribas](https://unavatar.io/onlyfans/amandaribas)
237
278
 
238
279
  ### OpenStreetMap
239
280
 
240
- It resolves user avatar using **openstreetmap.org**.
281
+ Get any OpenStreetMap contributor's profile picture. Accepts either a numeric user ID or a username.
241
282
 
242
- The input accepts:
283
+ Available inputs:
243
284
 
244
285
  - Numeric user ID, e.g., [unavatar.io/openstreetmap/98672](https://unavatar.io/openstreetmap/98672)
245
- - Username e.g., [unavatar.io/openstreetmap/Terence%20Eden](https://unavatar.io/openstreetmap/Terence%20Eden)
286
+ - Username, e.g., [unavatar.io/openstreetmap/Terence%20Eden](https://unavatar.io/openstreetmap/Terence%20Eden)
246
287
 
247
288
  ### Patreon
248
289
 
249
- It resolves user avatar against **patreon.com**.
290
+ Get any Patreon creator's profile picture by their username.
250
291
 
251
- e.g., [unavatar.io/patreon/kikobeats](https://unavatar.io/patreon/kikobeats)
292
+ Available inputs:
293
+
294
+ - Username, e.g., [unavatar.io/patreon/kikobeats](https://unavatar.io/patreon/kikobeats)
252
295
 
253
296
  ### Reddit
254
297
 
255
- It resolves user avatar against **reddit.com**.
298
+ Get any Reddit user's avatar by their username.
299
+
300
+ Available inputs:
256
301
 
257
- e.g., [unavatar.io/reddit/kikobeats](https://unavatar.io/reddit/kikobeats)
302
+ - Username, e.g., [unavatar.io/reddit/kikobeats](https://unavatar.io/reddit/kikobeats)
258
303
 
259
304
  ### SoundCloud
260
305
 
261
- It resolves user avatar against **soundcloud.com**.
306
+ Get any SoundCloud artist's profile picture by their username.
307
+
308
+ Available inputs:
262
309
 
263
- e.g., [unavatar.io/soundcloud/gorillaz](https://unavatar.io/soundcloud/gorillaz)
310
+ - Username, e.g., [unavatar.io/soundcloud/gorillaz](https://unavatar.io/soundcloud/gorillaz)
264
311
 
265
312
  ### Spotify
266
313
 
267
- It resolves user avatar against **open.spotify.com**.
314
+ Get artwork for any Spotify entity — users, artists, albums, playlists, shows, episodes, or tracks. Look up by username or Spotify ID.
268
315
 
269
316
  e.g., [unavatar.io/spotify/kikobeats](https://unavatar.io/spotify/kikobeats)
270
317
 
271
- The endpoint supports explictiy type as part of the input.
318
+ The endpoint supports explicit type as part of the input.
272
319
 
273
320
  If explicit type is not provided, it defaults to `user`.
274
321
 
275
- Available types:
322
+ Available URI format inputs:
276
323
 
277
324
  - `user`: [unavatar.io/spotify/kikobeats](https://unavatar.io/spotify/kikobeats)
278
325
  - `artist`: [unavatar.io/spotify/artist:6sFIWsNpZYqbRiDnNOkZCA](https://unavatar.io/spotify/artist:6sFIWsNpZYqbRiDnNOkZCA)
@@ -284,40 +331,52 @@ Available types:
284
331
 
285
332
  ### Substack
286
333
 
287
- It resolves user avatar against **substack.com**.
334
+ Get any Substack author's profile picture by their publication username.
288
335
 
289
- e.g., [unavatar.io/substack/bankless](https://unavatar.io/substack/bankless)
336
+ Available inputs:
337
+
338
+ - Publication username, e.g., [unavatar.io/substack/bankless](https://unavatar.io/substack/bankless)
290
339
 
291
340
  ### Telegram
292
341
 
293
- It resolves user avatar against **telegram.com**.
342
+ Get any Telegram user's profile picture by their username.
343
+
344
+ Available inputs:
294
345
 
295
- e.g., [unavatar.io/telegram/drsdavidsoft](https://unavatar.io/telegram/drsdavidsoft)
346
+ - Username, e.g., [unavatar.io/telegram/drsdavidsoft](https://unavatar.io/telegram/drsdavidsoft)
296
347
 
297
348
  ### TikTok
298
349
 
299
- It resolves user avatar against **tiktok.com**.
350
+ Get any TikTok user's profile picture by their username. No authentication or API tokens needed — just pass the username.
300
351
 
301
- e.g., [unavatar.io/tiktok/carlosazaustre](https://unavatar.io/tiktok/carlosazaustre)
352
+ Available inputs:
353
+
354
+ - Username, e.g., [unavatar.io/tiktok/carlosazaustre](https://unavatar.io/tiktok/carlosazaustre)
302
355
 
303
356
  ### Twitch
304
357
 
305
- It resolves user avatar against **twitch.tv**.
358
+ Get any Twitch streamer's profile picture by their username.
306
359
 
307
- e.g., [unavatar.io/twitch/midudev](https://unavatar.io/twitch/midudev)
360
+ Available inputs:
361
+
362
+ - Username, e.g., [unavatar.io/twitch/midudev](https://unavatar.io/twitch/midudev)
308
363
 
309
364
  ### Vimeo
310
365
 
311
- It resolves user avatar against **vimeo.com**.
366
+ Get any Vimeo user's profile picture by their username.
367
+
368
+ Available inputs:
312
369
 
313
- e.g., [unavatar.io/vimeo/staff](https://unavatar.io/vimeo/staff)
370
+ - Username, e.g., [unavatar.io/vimeo/staff](https://unavatar.io/vimeo/staff)
314
371
 
315
372
  ### WhatsApp
316
373
 
317
- It resolves user avatar against **whatsapp.com**.
374
+ Get the profile picture for a WhatsApp phone number, channel, chat, or group.
318
375
 
319
376
  The input supports a URI format `type:id`. When no type is provided, it defaults to `phone`.
320
377
 
378
+ Available URI format inputs:
379
+
321
380
  - `phone` (default): [unavatar.io/whatsapp/34612345678](https://unavatar.io/whatsapp/34612345678)
322
381
  - `channel`: [unavatar.io/whatsapp/channel:0029VaABC1234abcDEF56789](https://unavatar.io/whatsapp/channel:0029VaABC1234abcDEF56789)
323
382
  - `chat`: [unavatar.io/whatsapp/chat:ABC1234DEFghi](https://unavatar.io/whatsapp/chat:ABC1234DEFghi)
@@ -325,13 +384,15 @@ The input supports a URI format `type:id`. When no type is provided, it defaults
325
384
 
326
385
  ### X/Twitter
327
386
 
328
- It resolves user avatar against **x.com**.
387
+ Get any X (formerly Twitter) user's profile picture by their username.
388
+
389
+ Available inputs:
329
390
 
330
- e.g., [unavatar.io/x/kikobeats](https://unavatar.io/x/kikobeats)
391
+ - Username, e.g., [unavatar.io/x/kikobeats](https://unavatar.io/x/kikobeats)
331
392
 
332
393
  ### YouTube
333
394
 
334
- It resolves user avatar against **youtube.com**.
395
+ Get any YouTube channel's thumbnail by their handle, legacy username, or channel ID.
335
396
 
336
397
  e.g., [unavatar.io/youtube/casey](https://unavatar.io/youtube/casey)
337
398
 
@@ -352,29 +413,29 @@ However, you can get a [json](#json) as response payload.
352
413
 
353
414
  When an endpoint returns JSON, the shape is predictable so you can parse it reliably in your app:
354
415
 
355
- | Field | Type | Present in | Description |
356
- | --------- | -------------- | ----------------------------- | ------------------------------------------------ |
357
- | `status` | `string` | all JSON responses | One of: `success`, `fail`, `error`. |
358
- | `message` | `string` | all JSON responses | Human-readable summary for display/logging. |
359
- | `data` | `object` | `success` | Response payload for successful requests. |
360
- | `code` | `string` | `fail`, `error` | Stable machine-readable error code. |
361
- | `more` | `string (URL)` | most `fail`/`error` responses | Documentation URL with troubleshooting details. |
362
- | `report` | `string` | some `error` responses | Support contact channel (for example `mailto:`). |
416
+ | Field | Type | Present in | Description |
417
+ |----|----|----|----|
418
+ | `status` | `string` | all JSON responses | One of: `success`, `fail`, `error`. |
419
+ | `message` | `string` | all JSON responses | Human-readable summary for display/logging. |
420
+ | `data` | `object` | `success` | Response payload for successful requests. |
421
+ | `code` | `string` | `fail`, `error` | Stable machine-readable error code. |
422
+ | `more` | `string (URL)` | most `fail`/`error` responses | Documentation URL with troubleshooting details. |
423
+ | `report` | `string` | some `error` responses | Support contact channel (for example `mailto:`). |
363
424
 
364
425
  ## Response Headers
365
426
 
366
427
  These headers help you understand pricing, limits, and request diagnostics.
367
428
 
368
- | Header | Purpose |
369
- | ------------------------ | --------------------------------------------------------- |
370
- | `x-pricing-tier` | `free` or `pro` — the plan used for this request |
371
- | `x-timestamp` | Server timestamp when request was received |
372
- | `x-unavatar-cost` | Token cost of the request (avatar routes only) |
373
- | `x-proxy-tier` | Proxy tier used: `origin`, `datacenter`, or `residential` |
374
- | `x-rate-limit-limit` | Maximum requests allowed per window (free tier only) |
375
- | `x-rate-limit-remaining` | Remaining requests in current window (free tier only) |
376
- | `x-rate-limit-reset` | UTC epoch seconds when window resets (free tier only) |
377
- | `retry-after` | Seconds until rate limit resets (only on 429 responses) |
429
+ | Header | Purpose |
430
+ |----|----|
431
+ | `x-pricing-tier` | `free` or `pro` — the plan used for this request |
432
+ | `x-timestamp` | Server timestamp when request was received |
433
+ | `x-unavatar-cost` | Token cost of the request (avatar routes only) |
434
+ | `x-proxy-tier` | Proxy tier used: `origin`, `datacenter`, or `residential` |
435
+ | `x-rate-limit-limit` | Maximum requests allowed per window (free tier only) |
436
+ | `x-rate-limit-remaining` | Remaining requests in current window (free tier only) |
437
+ | `x-rate-limit-reset` | UTC epoch seconds when window resets (free tier only) |
438
+ | `retry-after` | Seconds until rate limit resets (only on 429 responses) |
378
439
 
379
440
  ## Response Errors
380
441
 
@@ -389,7 +450,7 @@ Expected errors are known operational cases returned with stable codes.
389
450
  - `report` (when present) indicates how to contact support for server errors.
390
451
 
391
452
  | HTTP | Code | Typical trigger |
392
- | ---- | -------------------- | ------------------------------------------- |
453
+ |------|----------------------|---------------------------------------------|
393
454
  | 400 | `ESESSIONID` | Missing `session_id` in `/checkout/success` |
394
455
  | 400 | `ESESSION` | Checkout session not paid or not found |
395
456
  | 400 | `ESIGNATURE` | Missing `stripe-signature` header |
@@ -413,4 +474,4 @@ Expected errors are known operational cases returned with stable codes.
413
474
 
414
475
  ## Contact
415
476
 
416
- If you have any suggestion or bug to report, please contact to ust mailing to hello@unavatar.io.
477
+ If you have any suggestion or bug to report, please contact to ust mailing to [hello@unavatar.io](mailto:hello@unavatar.io).
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@unavatar/core",
3
3
  "description": "Get unified user avatar from social networks, including Instagram, SoundCloud, Telegram, Twitter, YouTube & more.",
4
4
  "homepage": "https://unavatar.io",
5
- "version": "3.7.62",
5
+ "version": "3.7.64",
6
6
  "main": "src/index.js",
7
7
  "exports": {
8
8
  ".": "./src/index.js",
@@ -34,6 +34,10 @@
34
34
  "name": "Alexander Schlindwein",
35
35
  "email": "alexander.schlindwein@hotmail.de"
36
36
  },
37
+ {
38
+ "name": "Wes Bos",
39
+ "email": "wesbos@gmail.com"
40
+ },
37
41
  {
38
42
  "name": "Zadkiel",
39
43
  "email": "hello@zadkiel.fr"
package/src/index.js CHANGED
@@ -15,6 +15,7 @@ module.exports = ({ constants: userConstants, redis, onFetchHTML } = {}) => {
15
15
  const { createMultiCache, createRedisCache } = require('./util/keyv')({ ...constants, redis })
16
16
  const cache = require('./util/cache')({ createMultiCache, createRedisCache })
17
17
  const cacheableLookup = require('./util/cacheable-lookup')({ ...constants, cache: cache.dnsCache })
18
+ const isReservedIp = require('./util/is-reserved-ip')({ cacheableLookup })
18
19
  const got = require('./util/got')({ cacheableLookup })
19
20
  const reachableUrl = require('./util/reachable-url')({ got, pingCache: cache.pingCache })
20
21
  const createBrowser = require('./util/browserless')(constants)
@@ -25,7 +26,14 @@ module.exports = ({ constants: userConstants, redis, onFetchHTML } = {}) => {
25
26
  onFetchHTML
26
27
  })
27
28
 
28
- const providerCtx = { constants, createHtmlProvider, getOgImage, got, itunesSearchCache: cache.itunesSearchCache }
29
+ const providerCtx = {
30
+ constants,
31
+ createHtmlProvider,
32
+ getOgImage,
33
+ got,
34
+ isReservedIp,
35
+ itunesSearchCache: cache.itunesSearchCache
36
+ }
29
37
  const { providers, providersBy } = require('./providers')(providerCtx)
30
38
 
31
39
  const { auto, getInputType, getAvatar } = require('./avatar/auto')({
@@ -10,6 +10,8 @@ const providersBy = {
10
10
  'github',
11
11
  'gitlab',
12
12
  'instagram',
13
+ 'linkedin',
14
+ 'mastodon',
13
15
  'onlyfans',
14
16
  'openstreetmap',
15
17
  'patreon',
@@ -39,6 +41,8 @@ module.exports = ctx => {
39
41
  google: require('./google')(ctx),
40
42
  gravatar: require('./gravatar')(ctx),
41
43
  instagram: require('./instagram')(ctx),
44
+ linkedin: require('./linkedin')(ctx),
45
+ mastodon: require('./mastodon')(ctx),
42
46
  microlink: require('./microlink')(ctx),
43
47
  onlyfans: require('./onlyfans')(ctx),
44
48
  openstreetmap: require('./openstreetmap')(ctx),
@@ -0,0 +1,8 @@
1
+ 'use strict'
2
+
3
+ module.exports = ({ createHtmlProvider, getOgImage }) =>
4
+ createHtmlProvider({
5
+ name: 'linkedin',
6
+ url: input => `https://www.linkedin.com/in/${input}`,
7
+ getter: getOgImage
8
+ })
@@ -0,0 +1,58 @@
1
+ 'use strict'
2
+
3
+ const isValidServer = server => {
4
+ try {
5
+ const url = new URL(`https://${server}`)
6
+ return (
7
+ Boolean(url.hostname) &&
8
+ url.username === '' &&
9
+ url.password === '' &&
10
+ url.pathname === '/' &&
11
+ url.search === '' &&
12
+ url.hash === '' &&
13
+ url.host.toLowerCase() === server.toLowerCase()
14
+ )
15
+ } catch {
16
+ return false
17
+ }
18
+ }
19
+
20
+ const parseMastodonInput = input => {
21
+ if (typeof input !== 'string') return null
22
+
23
+ const cleaned = input.startsWith('@') ? input.slice(1) : input
24
+ const parts = cleaned.split('@')
25
+ if (parts.length !== 2) return null
26
+
27
+ const [username, server] = parts
28
+ if (!username || !server) return null
29
+ if (!isValidServer(server)) return null
30
+
31
+ return {
32
+ username,
33
+ server
34
+ }
35
+ }
36
+
37
+ module.exports = ({ got, isReservedIp }) => {
38
+ const mastodon = async function ({ input }) {
39
+ const parsed = parseMastodonInput(input)
40
+ if (!parsed) return undefined
41
+
42
+ const { username, server } = parsed
43
+
44
+ if (await isReservedIp(server)) return undefined
45
+
46
+ const { body } = await got(
47
+ `https://${server}/api/v1/accounts/lookup?acct=${encodeURIComponent(
48
+ username
49
+ )}`,
50
+ { responseType: 'json' }
51
+ )
52
+
53
+ return body?.avatar
54
+ }
55
+
56
+ mastodon.parseMastodonInput = parseMastodonInput
57
+ return mastodon
58
+ }
@@ -0,0 +1,23 @@
1
+ 'use strict'
2
+
3
+ const ip = require('ipaddr.js')
4
+
5
+ module.exports = ({ cacheableLookup }) => {
6
+ const getIpAddress = async hostname => {
7
+ if (ip.IPv4.isIPv4(hostname)) return hostname
8
+ if (
9
+ hostname.startsWith('[') &&
10
+ hostname.endsWith(']') &&
11
+ ip.IPv6.isIPv6(hostname.slice(1, -1))
12
+ ) {
13
+ return hostname.slice(1, -1)
14
+ }
15
+ const { address } = await cacheableLookup.lookupAsync(hostname)
16
+ return address
17
+ }
18
+
19
+ return async hostname => {
20
+ const ipAddress = await getIpAddress(hostname)
21
+ return ip.process(ipAddress).range() !== 'unicast'
22
+ }
23
+ }