1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
/**
* Fetch the Mojang username API through api.ashcon.app
*/
import { isUuid, sleep, undashUuid } from './util.js'
import * as nodeFetch from 'node-fetch'
import fetch from 'node-fetch'
import { Agent } from 'https'
// We need to create an agent to prevent memory leaks
const httpsAgent = new Agent({
keepAlive: true
})
interface MojangApiResponse {
/** These uuids are already undashed */
uuid: string | null
username: string | null
}
/**
* Get mojang api data from the session server
*/
export let profileFromUuid = async function profileFromUuid(uuid: string): Promise<MojangApiResponse> {
let fetchResponse: nodeFetch.Response
try {
fetchResponse = await fetch(
// using mojang directly is faster than ashcon lol, also mojang removed the ratelimits from here
`https://sessionserver.mojang.com/session/minecraft/profile/${undashUuid(uuid)}`,
{ agent: () => httpsAgent }
)
} catch {
// if there's an error, wait a second and try again
await sleep(1000)
return await profileFromUuid(uuid)
}
let dataString: string
try {
dataString = await fetchResponse.text()
} catch (err) {
return { uuid: null, username: null }
}
let data
try {
data = JSON.parse(dataString)
} catch {
// if it errors, just return null
return { uuid: null, username: null }
}
return {
uuid: data.id,
username: data.name
}
}
export let profileFromUsername = async function profileFromUsername(username: string): Promise<MojangApiResponse> {
// since we don't care about anything other than the uuid, we can use /uuid/ instead of /user/
let fetchResponse: nodeFetch.Response
try {
fetchResponse = await fetch(
`https://api.mojang.com/users/profiles/minecraft/${username}`,
{ agent: () => httpsAgent }
)
} catch {
// if there's an error, wait a second and try again
await sleep(1000)
return await profileFromUsername(username)
}
let data: any = null
const rawData = await fetchResponse.text()
try {
data = JSON.parse(rawData)
} catch {}
if (!data?.id) {
// return { uuid: null, username: null }
return await profileFromUsernameAlternative(username)
}
return {
uuid: data.id,
username: data.name
}
}
export async function profileFromUsernameAlternative(username: string): Promise<MojangApiResponse> {
let fetchResponse: nodeFetch.Response
try {
fetchResponse = await fetch(
`https://api.ashcon.app/mojang/v2/user/${username}`,
{ agent: () => httpsAgent }
)
} catch {
// if there's an error, wait a second and try again
await sleep(1000)
return await profileFromUsernameAlternative(username)
}
let data
try {
data = await fetchResponse.json()
} catch {
return { uuid: null, username: null }
}
if (!data.uuid)
return { uuid: null, username: null }
return {
uuid: undashUuid(data.uuid),
username: data.username
}
}
export let profileFromUser = async function profileFromUser(user: string): Promise<MojangApiResponse> {
if (isUuid(user)) {
return await profileFromUuid(user)
} else
return await profileFromUsername(user)
}
// this is necessary for mocking in the tests because es6
export function mockProfileFromUuid($value) { profileFromUuid = $value }
export function mockProfileFromUsername($value) { profileFromUsername = $value }
export function mockProfileFromUser($value) { profileFromUser = $value }
|