@unavatar/core 3.44.0 → 3.45.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.
- package/README.md +65 -0
- package/package.json +1 -1
- package/src/providers/bilibili.js +27 -0
- package/src/providers/douban.js +15 -0
- package/src/providers/gitee.js +22 -0
- package/src/providers/index.js +19 -2
- package/src/providers/juejin.js +26 -0
- package/src/providers/netease-music.js +26 -0
- package/src/providers/qq.js +13 -0
- package/src/providers/tieba.js +36 -0
- package/src/providers/weibo.js +36 -0
- package/src/providers/zhihu.js +28 -0
package/README.md
CHANGED
|
@@ -19,7 +19,9 @@
|
|
|
19
19
|
- [Providers](#providers)
|
|
20
20
|
- [Apple Music](#apple-music)
|
|
21
21
|
- [Apple Store](#apple-store)
|
|
22
|
+
- [Baidu Tieba](#baidu-tieba)
|
|
22
23
|
- [Behance](#behance)
|
|
24
|
+
- [Bilibili](#bilibili)
|
|
23
25
|
- [Bluesky](#bluesky)
|
|
24
26
|
- [Buy Me a Coffee](#buy-me-a-coffee)
|
|
25
27
|
- [CodePen](#codepen)
|
|
@@ -30,11 +32,13 @@
|
|
|
30
32
|
- [Discord](#discord)
|
|
31
33
|
- [Docker Hub](#docker-hub)
|
|
32
34
|
- [Domain](#domain)
|
|
35
|
+
- [Douban](#douban)
|
|
33
36
|
- [Dribbble](#dribbble)
|
|
34
37
|
- [DuckDuckGo](#duckduckgo)
|
|
35
38
|
- [Email](#email)
|
|
36
39
|
- [Facebook](#facebook)
|
|
37
40
|
- [Flickr](#flickr)
|
|
41
|
+
- [Gitee](#gitee)
|
|
38
42
|
- [GitHub](#github)
|
|
39
43
|
- [GitLab](#gitlab)
|
|
40
44
|
- [Google](#google)
|
|
@@ -43,11 +47,13 @@
|
|
|
43
47
|
- [Hevy](#hevy)
|
|
44
48
|
- [Hugging Face](#hugging-face)
|
|
45
49
|
- [Instagram](#instagram)
|
|
50
|
+
- [Juejin](#juejin)
|
|
46
51
|
- [Ko-fi](#ko-fi)
|
|
47
52
|
- [LinkedIn](#linkedin)
|
|
48
53
|
- [Mastodon](#mastodon)
|
|
49
54
|
- [Medium](#medium)
|
|
50
55
|
- [Microlink](#microlink)
|
|
56
|
+
- [NetEase Music](#netease-music)
|
|
51
57
|
- [npm](#npm)
|
|
52
58
|
- [OnlyFans](#onlyfans)
|
|
53
59
|
- [OpenStreetMap](#openstreetmap)
|
|
@@ -57,6 +63,7 @@
|
|
|
57
63
|
- [Printables](#printables)
|
|
58
64
|
- [Product Hunt](#product-hunt)
|
|
59
65
|
- [PSN Profiles](#psn-profiles)
|
|
66
|
+
- [QQ](#qq)
|
|
60
67
|
- [Raycast](#raycast)
|
|
61
68
|
- [Reddit](#reddit)
|
|
62
69
|
- [Snapchat](#snapchat)
|
|
@@ -74,10 +81,12 @@
|
|
|
74
81
|
- [Tumblr](#tumblr)
|
|
75
82
|
- [Twitch](#twitch)
|
|
76
83
|
- [Vimeo](#vimeo)
|
|
84
|
+
- [Weibo](#weibo)
|
|
77
85
|
- [WhatsApp](#whatsapp)
|
|
78
86
|
- [X/Twitter](#xtwitter)
|
|
79
87
|
- [Xbox Gamertag](#xbox-gamertag)
|
|
80
88
|
- [YouTube](#youtube)
|
|
89
|
+
- [Zhihu](#zhihu)
|
|
81
90
|
- [Response Format](#response-format)
|
|
82
91
|
- [Response Headers](#response-headers)
|
|
83
92
|
- [Response Errors](#response-errors)
|
|
@@ -558,12 +567,24 @@ Available inputs:
|
|
|
558
567
|
- `id` (default): [unavatar.io/apple-store/id:529479190](https://unavatar.io/apple-store/id:529479190) or [unavatar.io/apple-store/id:6474323148@es](https://unavatar.io/apple-store/id:6474323148@es)
|
|
559
568
|
- `name`: [unavatar.io/apple-store/name:pokemon%20go](https://unavatar.io/apple-store/name:pokemon%20go)
|
|
560
569
|
|
|
570
|
+
### Baidu Tieba
|
|
571
|
+
|
|
572
|
+
Get any Baidu Tieba user's profile picture by their username.
|
|
573
|
+
|
|
574
|
+
e.g., [unavatar.io/tieba/Mojang](https://unavatar.io/tieba/Mojang)
|
|
575
|
+
|
|
561
576
|
### Behance
|
|
562
577
|
|
|
563
578
|
Get any Behance user's profile picture by their username.
|
|
564
579
|
|
|
565
580
|
e.g., [unavatar.io/behance/vitormatosinhos](https://unavatar.io/behance/vitormatosinhos)
|
|
566
581
|
|
|
582
|
+
### Bilibili
|
|
583
|
+
|
|
584
|
+
Get any Bilibili user's profile picture by their numeric uid.
|
|
585
|
+
|
|
586
|
+
e.g., [unavatar.io/bilibili/2](https://unavatar.io/bilibili/2)
|
|
587
|
+
|
|
567
588
|
### Bluesky
|
|
568
589
|
|
|
569
590
|
Get any Bluesky user's profile picture by their handle. Domain-style handles are supported.
|
|
@@ -639,6 +660,12 @@ Retrieve logos and favicons from any hostname with a single URL. Domain-shaped p
|
|
|
639
660
|
|
|
640
661
|
e.g., [unavatar.io/domain/microlink.io](https://unavatar.io/domain/microlink.io)
|
|
641
662
|
|
|
663
|
+
### Douban
|
|
664
|
+
|
|
665
|
+
Get any Douban user's profile picture by their username or numeric ID.
|
|
666
|
+
|
|
667
|
+
e.g., [unavatar.io/douban/ahbei](https://unavatar.io/douban/ahbei)
|
|
668
|
+
|
|
642
669
|
### Dribbble
|
|
643
670
|
|
|
644
671
|
Get any Dribbble designer's profile picture by their username.
|
|
@@ -695,6 +722,12 @@ Available inputs:
|
|
|
695
722
|
- `user` (default): [unavatar.io/flickr/user:elizabethgadd](https://unavatar.io/flickr/user:elizabethgadd)
|
|
696
723
|
- `group`: [unavatar.io/flickr/group:allthingsearthy](https://unavatar.io/flickr/group:allthingsearthy)
|
|
697
724
|
|
|
725
|
+
### Gitee
|
|
726
|
+
|
|
727
|
+
Get any Gitee user or organization's profile picture by their username.
|
|
728
|
+
|
|
729
|
+
e.g., [unavatar.io/gitee/y_project](https://unavatar.io/gitee/y_project)
|
|
730
|
+
|
|
698
731
|
### GitHub
|
|
699
732
|
|
|
700
733
|
Get any GitHub user or organization's profile picture by username, or resolve an avatar from a public email via GitHub search when the address matches a profile or commit history.
|
|
@@ -768,6 +801,12 @@ Get any Instagram user's profile picture by their username. No authentication or
|
|
|
768
801
|
|
|
769
802
|
e.g., [unavatar.io/instagram/willsmith](https://unavatar.io/instagram/willsmith)
|
|
770
803
|
|
|
804
|
+
### Juejin
|
|
805
|
+
|
|
806
|
+
Get any Juejin user's profile picture by their numeric user ID.
|
|
807
|
+
|
|
808
|
+
e.g., [unavatar.io/juejin/1556564194374926](https://unavatar.io/juejin/1556564194374926)
|
|
809
|
+
|
|
771
810
|
### Ko-fi
|
|
772
811
|
|
|
773
812
|
Get any Ko-fi page's profile picture by the creator username.
|
|
@@ -803,6 +842,12 @@ Extract the logo or representative image from any URL. The page is rendered and
|
|
|
803
842
|
|
|
804
843
|
e.g., [unavatar.io/microlink/microlink.io](https://unavatar.io/microlink/microlink.io)
|
|
805
844
|
|
|
845
|
+
### NetEase Music
|
|
846
|
+
|
|
847
|
+
Get any NetEase Cloud Music user's profile picture by their numeric user ID.
|
|
848
|
+
|
|
849
|
+
e.g., [unavatar.io/netease-music/32953014](https://unavatar.io/netease-music/32953014)
|
|
850
|
+
|
|
806
851
|
### npm
|
|
807
852
|
|
|
808
853
|
Get any npm user's profile picture by their username, including scoped handles.
|
|
@@ -867,6 +912,14 @@ Get any PlayStation Network user's profile picture by their PSN username.
|
|
|
867
912
|
|
|
868
913
|
e.g., [unavatar.io/psnprofiles/Duff85](https://unavatar.io/psnprofiles/Duff85)
|
|
869
914
|
|
|
915
|
+
### QQ
|
|
916
|
+
|
|
917
|
+
Get any QQ user's profile picture by their QQ number.
|
|
918
|
+
|
|
919
|
+
e.g., [unavatar.io/qq/10001](https://unavatar.io/qq/10001)
|
|
920
|
+
|
|
921
|
+
Note: unknown numbers resolve to QQ's default avatar, similar to Gravatar.
|
|
922
|
+
|
|
870
923
|
### Raycast
|
|
871
924
|
|
|
872
925
|
Get any Raycast user's profile picture by their username.
|
|
@@ -1002,6 +1055,12 @@ Get any Vimeo user's profile picture by their username.
|
|
|
1002
1055
|
|
|
1003
1056
|
e.g., [unavatar.io/vimeo/ladieswithlenses](https://unavatar.io/vimeo/ladieswithlenses)
|
|
1004
1057
|
|
|
1058
|
+
### Weibo
|
|
1059
|
+
|
|
1060
|
+
Get any Weibo user's profile picture by their numeric uid or nickname.
|
|
1061
|
+
|
|
1062
|
+
e.g., [unavatar.io/weibo/2803301701](https://unavatar.io/weibo/2803301701)
|
|
1063
|
+
|
|
1005
1064
|
### WhatsApp
|
|
1006
1065
|
|
|
1007
1066
|
Get the profile picture for a WhatsApp channel or chat by ID.
|
|
@@ -1041,6 +1100,12 @@ Available inputs:
|
|
|
1041
1100
|
- `username`: [unavatar.io/youtube/casey](https://unavatar.io/youtube/casey)
|
|
1042
1101
|
- `channel`: [unavatar.io/youtube/UC_x5XG1OV2P6uZZ5FSM9Ttw](https://unavatar.io/youtube/UC_x5XG1OV2P6uZZ5FSM9Ttw)
|
|
1043
1102
|
|
|
1103
|
+
### Zhihu
|
|
1104
|
+
|
|
1105
|
+
Get any Zhihu user's profile picture by their URL token (the handle in the profile URL).
|
|
1106
|
+
|
|
1107
|
+
e.g., [unavatar.io/zhihu/kaifulee](https://unavatar.io/zhihu/kaifulee)
|
|
1108
|
+
|
|
1044
1109
|
## Response Format
|
|
1045
1110
|
|
|
1046
1111
|
A response is returning the user avatar by default.
|
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.
|
|
5
|
+
"version": "3.45.0",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"exports": {
|
|
8
8
|
".": "./src/index.js",
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const API_URL = 'https://api.bilibili.com/x/web-interface/card'
|
|
4
|
+
|
|
5
|
+
const getAvatarUrl = input => `${API_URL}?mid=${encodeURIComponent(input)}`
|
|
6
|
+
|
|
7
|
+
// Users without a custom avatar get the generic `noface.jpg` placeholder.
|
|
8
|
+
const getAvatar = body => {
|
|
9
|
+
if (body?.code !== 0) return
|
|
10
|
+
const face = body.data?.card?.face
|
|
11
|
+
return face && !face.endsWith('/noface.jpg') ? face : undefined
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
module.exports = ({ got }) =>
|
|
15
|
+
async function bilibili (input) {
|
|
16
|
+
const { body, statusCode } = await got(getAvatarUrl(input), {
|
|
17
|
+
responseType: 'json',
|
|
18
|
+
throwHttpErrors: false
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
if (statusCode >= 400) return
|
|
22
|
+
|
|
23
|
+
return getAvatar(body)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
27
|
+
module.exports.getAvatar = getAvatar
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const getAvatarUrl = input => `https://www.douban.com/people/${input}/`
|
|
4
|
+
|
|
5
|
+
const getAvatar = $ => $('img.userface').attr('src')
|
|
6
|
+
|
|
7
|
+
module.exports = ({ createHtmlProvider }) =>
|
|
8
|
+
createHtmlProvider({
|
|
9
|
+
name: 'douban',
|
|
10
|
+
url: getAvatarUrl,
|
|
11
|
+
getter: getAvatar
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
15
|
+
module.exports.getAvatar = getAvatar
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const API_URL = 'https://gitee.com/api/v5/users'
|
|
4
|
+
|
|
5
|
+
const getAvatarUrl = input => `${API_URL}/${encodeURIComponent(input)}`
|
|
6
|
+
|
|
7
|
+
const getAvatar = body => body?.avatar_url || undefined
|
|
8
|
+
|
|
9
|
+
module.exports = ({ got }) =>
|
|
10
|
+
async function gitee (input) {
|
|
11
|
+
const { body, statusCode } = await got(getAvatarUrl(input), {
|
|
12
|
+
responseType: 'json',
|
|
13
|
+
throwHttpErrors: false
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
if (statusCode >= 400) return
|
|
17
|
+
|
|
18
|
+
return getAvatar(body)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
22
|
+
module.exports.getAvatar = getAvatar
|
package/src/providers/index.js
CHANGED
|
@@ -6,6 +6,7 @@ const providersBy = {
|
|
|
6
6
|
'apple-music',
|
|
7
7
|
'apple-store',
|
|
8
8
|
'behance',
|
|
9
|
+
'bilibili',
|
|
9
10
|
'bluesky',
|
|
10
11
|
'buymeacoffee',
|
|
11
12
|
'cashapp',
|
|
@@ -16,19 +17,23 @@ const providersBy = {
|
|
|
16
17
|
'deviantart',
|
|
17
18
|
'discord',
|
|
18
19
|
'dockerhub',
|
|
20
|
+
'douban',
|
|
19
21
|
'dribbble',
|
|
20
22
|
'facebook',
|
|
21
23
|
'flickr',
|
|
24
|
+
'gitee',
|
|
22
25
|
'github',
|
|
23
26
|
'gitlab',
|
|
24
27
|
'google-play',
|
|
25
28
|
'huggingface',
|
|
26
29
|
'hevy',
|
|
27
30
|
'instagram',
|
|
31
|
+
'juejin',
|
|
28
32
|
'ko-fi',
|
|
29
33
|
'linkedin',
|
|
30
34
|
'mastodon',
|
|
31
35
|
'medium',
|
|
36
|
+
'netease-music',
|
|
32
37
|
'npm',
|
|
33
38
|
'onlyfans',
|
|
34
39
|
'openstreetmap',
|
|
@@ -53,15 +58,18 @@ const providersBy = {
|
|
|
53
58
|
'thingiverse',
|
|
54
59
|
'threads',
|
|
55
60
|
'tidal',
|
|
61
|
+
'tieba',
|
|
56
62
|
'tiktok',
|
|
57
63
|
'tumblr',
|
|
58
64
|
'twitch',
|
|
59
65
|
'venmo',
|
|
60
66
|
'vimeo',
|
|
67
|
+
'weibo',
|
|
61
68
|
'wise',
|
|
62
69
|
'x',
|
|
63
70
|
'xboxgamertag',
|
|
64
|
-
'youtube'
|
|
71
|
+
'youtube',
|
|
72
|
+
'zhihu'
|
|
65
73
|
],
|
|
66
74
|
domain: ['microlink']
|
|
67
75
|
}
|
|
@@ -71,6 +79,7 @@ module.exports = ctx => {
|
|
|
71
79
|
'apple-music': require('./apple-music')(ctx),
|
|
72
80
|
'apple-store': require('./apple-store')(ctx),
|
|
73
81
|
behance: require('./behance')(ctx),
|
|
82
|
+
bilibili: require('./bilibili')(ctx),
|
|
74
83
|
bluesky: require('./bluesky')(ctx),
|
|
75
84
|
buymeacoffee: require('./buymeacoffee')(ctx),
|
|
76
85
|
cashapp: require('./cashapp')(ctx),
|
|
@@ -81,10 +90,12 @@ module.exports = ctx => {
|
|
|
81
90
|
deviantart: require('./deviantart')(ctx),
|
|
82
91
|
discord: require('./discord')(ctx),
|
|
83
92
|
dockerhub: require('./dockerhub')(ctx),
|
|
93
|
+
douban: require('./douban')(ctx),
|
|
84
94
|
dribbble: require('./dribbble')(ctx),
|
|
85
95
|
duckduckgo: require('./duckduckgo')(ctx),
|
|
86
96
|
facebook: require('./facebook')(ctx),
|
|
87
97
|
flickr: require('./flickr')(ctx),
|
|
98
|
+
gitee: require('./gitee')(ctx),
|
|
88
99
|
github: require('./github')(ctx),
|
|
89
100
|
gitlab: require('./gitlab')(ctx),
|
|
90
101
|
google: require('./google')(ctx),
|
|
@@ -93,11 +104,13 @@ module.exports = ctx => {
|
|
|
93
104
|
huggingface: require('./huggingface')(ctx),
|
|
94
105
|
hevy: require('./hevy')(ctx),
|
|
95
106
|
instagram: require('./instagram')(ctx),
|
|
107
|
+
juejin: require('./juejin')(ctx),
|
|
96
108
|
'ko-fi': require('./ko-fi')(ctx),
|
|
97
109
|
linkedin: require('./linkedin')(ctx),
|
|
98
110
|
mastodon: require('./mastodon')(ctx),
|
|
99
111
|
medium: require('./medium')(ctx),
|
|
100
112
|
microlink: require('./microlink')(ctx),
|
|
113
|
+
'netease-music': require('./netease-music')(ctx),
|
|
101
114
|
npm: require('./npm')(ctx),
|
|
102
115
|
onlyfans: require('./onlyfans')(ctx),
|
|
103
116
|
openstreetmap: require('./openstreetmap')(ctx),
|
|
@@ -108,6 +121,7 @@ module.exports = ctx => {
|
|
|
108
121
|
primal: require('./primal')(ctx),
|
|
109
122
|
producthunt: require('./producthunt')(ctx),
|
|
110
123
|
psnprofiles: require('./psnprofiles')(ctx),
|
|
124
|
+
qq: require('./qq')(ctx),
|
|
111
125
|
raycast: require('./raycast')(ctx),
|
|
112
126
|
reddit: require('./reddit')(ctx),
|
|
113
127
|
revolut: require('./revolut')(ctx),
|
|
@@ -122,16 +136,19 @@ module.exports = ctx => {
|
|
|
122
136
|
thingiverse: require('./thingiverse')(ctx),
|
|
123
137
|
threads: require('./threads')(ctx),
|
|
124
138
|
tidal: require('./tidal')(ctx),
|
|
139
|
+
tieba: require('./tieba')(ctx),
|
|
125
140
|
tiktok: require('./tiktok')(ctx),
|
|
126
141
|
tumblr: require('./tumblr')(ctx),
|
|
127
142
|
twitch: require('./twitch')(ctx),
|
|
128
143
|
venmo: require('./venmo')(ctx),
|
|
129
144
|
vimeo: require('./vimeo')(ctx),
|
|
145
|
+
weibo: require('./weibo')(ctx),
|
|
130
146
|
whatsapp: require('./whatsapp')(ctx),
|
|
131
147
|
wise: require('./wise')(ctx),
|
|
132
148
|
x: require('./x')(ctx),
|
|
133
149
|
xboxgamertag: require('./xboxgamertag')(ctx),
|
|
134
|
-
youtube: require('./youtube')(ctx)
|
|
150
|
+
youtube: require('./youtube')(ctx),
|
|
151
|
+
zhihu: require('./zhihu')(ctx)
|
|
135
152
|
}
|
|
136
153
|
|
|
137
154
|
return { providers, providersBy }
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const API_URL = 'https://api.juejin.cn/user_api/v1/user/get'
|
|
4
|
+
|
|
5
|
+
const getAvatarUrl = input => `${API_URL}?user_id=${encodeURIComponent(input)}`
|
|
6
|
+
|
|
7
|
+
// Unknown ids still respond `err_no: 0`, but with an empty profile.
|
|
8
|
+
const getAvatar = body => {
|
|
9
|
+
if (body?.err_no !== 0) return
|
|
10
|
+
return body.data?.avatar_large || undefined
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = ({ got }) =>
|
|
14
|
+
async function juejin (input) {
|
|
15
|
+
const { body, statusCode } = await got(getAvatarUrl(input), {
|
|
16
|
+
responseType: 'json',
|
|
17
|
+
throwHttpErrors: false
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
if (statusCode >= 400) return
|
|
21
|
+
|
|
22
|
+
return getAvatar(body)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
26
|
+
module.exports.getAvatar = getAvatar
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const API_URL = 'https://music.163.com/api/v1/user/detail'
|
|
4
|
+
|
|
5
|
+
const getAvatarUrl = input => `${API_URL}/${encodeURIComponent(input)}`
|
|
6
|
+
|
|
7
|
+
// The API returns avatar URLs over plain HTTP; the CDN supports HTTPS.
|
|
8
|
+
const getAvatar = body => {
|
|
9
|
+
const avatarUrl = body?.profile?.avatarUrl
|
|
10
|
+
return avatarUrl ? avatarUrl.replace(/^http:/, 'https:') : undefined
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = ({ got }) =>
|
|
14
|
+
async function neteaseMusic (input) {
|
|
15
|
+
const { body, statusCode } = await got(getAvatarUrl(input), {
|
|
16
|
+
responseType: 'json',
|
|
17
|
+
throwHttpErrors: false
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
if (statusCode >= 400) return
|
|
21
|
+
|
|
22
|
+
return getAvatar(body)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
26
|
+
module.exports.getAvatar = getAvatar
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// qlogo.cn serves a default placeholder for unknown numbers (gravatar-like),
|
|
4
|
+
// so the URL always resolves and misses can't be detected upfront.
|
|
5
|
+
const getAvatarUrl = input =>
|
|
6
|
+
`https://q1.qlogo.cn/g?b=qq&nk=${encodeURIComponent(input)}&s=640`
|
|
7
|
+
|
|
8
|
+
module.exports = () =>
|
|
9
|
+
function qq (input) {
|
|
10
|
+
return getAvatarUrl(input)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const PORTRAIT_URL = 'https://himg.bdimg.com/sys/portrait/item'
|
|
4
|
+
|
|
5
|
+
const getAvatarUrl = input =>
|
|
6
|
+
`https://tieba.baidu.com/i/sys/user_json?un=${encodeURIComponent(
|
|
7
|
+
input
|
|
8
|
+
)}&ie=utf-8`
|
|
9
|
+
|
|
10
|
+
const getAvatar = body => {
|
|
11
|
+
const portrait = body?.creator?.portrait
|
|
12
|
+
return portrait ? `${PORTRAIT_URL}/${portrait}` : undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = ({ got }) =>
|
|
16
|
+
async function tieba (input) {
|
|
17
|
+
// Unknown users respond with an empty body, which is not valid JSON.
|
|
18
|
+
const { body, statusCode } = await got(getAvatarUrl(input), {
|
|
19
|
+
responseType: 'text',
|
|
20
|
+
throwHttpErrors: false
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
if (statusCode >= 400 || !body) return
|
|
24
|
+
|
|
25
|
+
let json
|
|
26
|
+
try {
|
|
27
|
+
json = JSON.parse(body)
|
|
28
|
+
} catch {
|
|
29
|
+
return undefined
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return getAvatar(json)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
36
|
+
module.exports.getAvatar = getAvatar
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const getAvatarUrl = input =>
|
|
4
|
+
/^\d+$/.test(input)
|
|
5
|
+
? `https://m.weibo.cn/u/${input}`
|
|
6
|
+
: `https://m.weibo.cn/n/${encodeURIComponent(input)}`
|
|
7
|
+
|
|
8
|
+
// The profile avatar is served as a signed, expiring crop
|
|
9
|
+
// (`/crop.0.0.W.H.180/file?ssig=…`); rewriting it to `/large/file` yields a
|
|
10
|
+
// stable, unsigned, full-resolution URL. Default avatars live under
|
|
11
|
+
// `/default/`, which is treated as a miss.
|
|
12
|
+
const getAvatar = ({ $, NOT_FOUND }) => {
|
|
13
|
+
const src = $('.m-avatar-box img').attr('src')
|
|
14
|
+
if (!src) return
|
|
15
|
+
|
|
16
|
+
const url = new URL(src, 'https://m.weibo.cn')
|
|
17
|
+
if (url.pathname.includes('/default/')) return NOT_FOUND
|
|
18
|
+
|
|
19
|
+
const filename = url.pathname.slice(url.pathname.lastIndexOf('/'))
|
|
20
|
+
return `${url.origin}/large${filename}`
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// The page sits behind Weibo's visitor gate (a JS cookie-setting redirect)
|
|
24
|
+
// and the avatar is injected after hydration, so it must be prerendered.
|
|
25
|
+
module.exports = ({ createHtmlProvider, NOT_FOUND }) =>
|
|
26
|
+
createHtmlProvider({
|
|
27
|
+
name: 'weibo',
|
|
28
|
+
url: getAvatarUrl,
|
|
29
|
+
getter: $ => getAvatar({ $, NOT_FOUND }),
|
|
30
|
+
htmlOpts: () => ({
|
|
31
|
+
prerender: true
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
36
|
+
module.exports.getAvatar = getAvatar
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const API_URL = 'https://www.zhihu.com/api/v4/members'
|
|
4
|
+
|
|
5
|
+
const getAvatarUrl = input =>
|
|
6
|
+
`${API_URL}/${encodeURIComponent(input)}?include=avatar_url`
|
|
7
|
+
|
|
8
|
+
// Misses (and bot challenges) come back as an `error` payload rather than
|
|
9
|
+
// a plain 404, and default avatars are flagged via `use_default_avatar`.
|
|
10
|
+
const getAvatar = body => {
|
|
11
|
+
if (!body || body.error || body.use_default_avatar) return
|
|
12
|
+
return body.avatar_url || undefined
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
module.exports = ({ got }) =>
|
|
16
|
+
async function zhihu (input) {
|
|
17
|
+
const { body, statusCode } = await got(getAvatarUrl(input), {
|
|
18
|
+
responseType: 'json',
|
|
19
|
+
throwHttpErrors: false
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
if (statusCode >= 400) return
|
|
23
|
+
|
|
24
|
+
return getAvatar(body)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports.getAvatarUrl = getAvatarUrl
|
|
28
|
+
module.exports.getAvatar = getAvatar
|