@quenty/userserviceutils 4.0.0-canary.367.e9fdcbc.0 → 4.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.
package/CHANGELOG.md
CHANGED
|
@@ -3,7 +3,64 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
# [4.0.0
|
|
6
|
+
# [4.0.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/userserviceutils@3.11.0...@quenty/userserviceutils@4.0.0) (2023-10-11)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* User information de-duplication occurs ([bcb5c2a](https://github.com/Quenty/NevermoreEngine/commit/bcb5c2a062fa775e6c0949f2f56f7026456f849a))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# [3.11.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/userserviceutils@3.10.0...@quenty/userserviceutils@3.11.0) (2023-09-21)
|
|
18
|
+
|
|
19
|
+
**Note:** Version bump only for package @quenty/userserviceutils
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# [3.10.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/userserviceutils@3.9.0...@quenty/userserviceutils@3.10.0) (2023-08-23)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### Features
|
|
29
|
+
|
|
30
|
+
* Add UserInfoService:ObserveDisplayName(userId) ([950c121](https://github.com/Quenty/NevermoreEngine/commit/950c121c35d734d7a66415ada647b146e06c4bf7))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# [3.9.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/userserviceutils@3.8.0...@quenty/userserviceutils@3.9.0) (2023-07-28)
|
|
37
|
+
|
|
38
|
+
**Note:** Version bump only for package @quenty/userserviceutils
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# [3.8.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/userserviceutils@3.7.0...@quenty/userserviceutils@3.8.0) (2023-07-25)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
### Features
|
|
48
|
+
|
|
49
|
+
* Add UserInfoService to aggregate user info in a de-duplicated request format ([deb1a79](https://github.com/Quenty/NevermoreEngine/commit/deb1a7914f753f8835ce407a2f94b3f8eac7d812))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# [3.7.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/userserviceutils@3.6.0...@quenty/userserviceutils@3.7.0) (2023-06-17)
|
|
56
|
+
|
|
57
|
+
**Note:** Version bump only for package @quenty/userserviceutils
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# [3.6.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/userserviceutils@3.5.0...@quenty/userserviceutils@3.6.0) (2023-06-05)
|
|
7
64
|
|
|
8
65
|
**Note:** Version bump only for package @quenty/userserviceutils
|
|
9
66
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quenty/userserviceutils",
|
|
3
|
-
"version": "4.0.0
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "Utilities involving UserService in Roblox",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"Roblox",
|
|
@@ -25,12 +25,16 @@
|
|
|
25
25
|
"Quenty"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@quenty/
|
|
29
|
-
"@quenty/
|
|
30
|
-
"@quenty/
|
|
28
|
+
"@quenty/baseobject": "^7.0.0",
|
|
29
|
+
"@quenty/loader": "^7.0.0",
|
|
30
|
+
"@quenty/maid": "^2.6.0",
|
|
31
|
+
"@quenty/math": "^2.5.0",
|
|
32
|
+
"@quenty/promise": "^7.0.0",
|
|
33
|
+
"@quenty/rx": "^8.0.0",
|
|
34
|
+
"@quenty/servicebag": "^7.0.0"
|
|
31
35
|
},
|
|
32
36
|
"publishConfig": {
|
|
33
37
|
"access": "public"
|
|
34
38
|
},
|
|
35
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "fdeae46099587019ec5fc15317dc673aed379400"
|
|
36
40
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
Centralized provider for user info so we can coordinate web requests.
|
|
3
|
+
|
|
4
|
+
@class UserInfoServiceClient
|
|
5
|
+
]=]
|
|
6
|
+
|
|
7
|
+
local require = require(script.Parent.loader).load(script)
|
|
8
|
+
|
|
9
|
+
local UserInfoAggregator = require("UserInfoAggregator")
|
|
10
|
+
local Maid = require("Maid")
|
|
11
|
+
|
|
12
|
+
local UserInfoServiceClient = {}
|
|
13
|
+
UserInfoServiceClient.ServiceName = "UserInfoServiceClient"
|
|
14
|
+
|
|
15
|
+
function UserInfoServiceClient:Init(serviceBag)
|
|
16
|
+
assert(not self._serviceBag, "Already initialized")
|
|
17
|
+
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
18
|
+
self._maid = Maid.new()
|
|
19
|
+
|
|
20
|
+
self._aggregator = UserInfoAggregator.new()
|
|
21
|
+
self._maid:GiveTask(self._aggregator)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
--[=[
|
|
25
|
+
Promises the user info for the given user, aggregating all requests to reduce
|
|
26
|
+
calls into Roblox.
|
|
27
|
+
|
|
28
|
+
@param userId number
|
|
29
|
+
@return Promise<UserInfo>
|
|
30
|
+
]=]
|
|
31
|
+
function UserInfoServiceClient:PromiseUserInfo(userId)
|
|
32
|
+
assert(type(userId) == "number", "Bad userId")
|
|
33
|
+
|
|
34
|
+
return self._aggregator:PromiseUserInfo(userId)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
--[=[
|
|
38
|
+
Promises the user display name for the userId
|
|
39
|
+
|
|
40
|
+
@param userId number
|
|
41
|
+
@return Promise<string>
|
|
42
|
+
]=]
|
|
43
|
+
function UserInfoServiceClient:PromiseDisplayName(userId)
|
|
44
|
+
assert(type(userId) == "number", "Bad userId")
|
|
45
|
+
|
|
46
|
+
return self._aggregator:PromiseDisplayName(userId)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
--[=[
|
|
50
|
+
Observes the user info for the user
|
|
51
|
+
|
|
52
|
+
@param userId number
|
|
53
|
+
@return Observable<UserInfo>
|
|
54
|
+
]=]
|
|
55
|
+
function UserInfoServiceClient:ObserveUserInfo(userId)
|
|
56
|
+
assert(type(userId) == "number", "Bad userId")
|
|
57
|
+
|
|
58
|
+
return self._aggregator:ObserveDisplayName(userId)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
--[=[
|
|
62
|
+
Observes the user display name for the userId
|
|
63
|
+
|
|
64
|
+
@param userId number
|
|
65
|
+
@return Observable<string>
|
|
66
|
+
]=]
|
|
67
|
+
function UserInfoServiceClient:ObserveDisplayName(userId)
|
|
68
|
+
assert(type(userId) == "number", "Bad userId")
|
|
69
|
+
|
|
70
|
+
return self._aggregator:ObserveDisplayName(userId)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
function UserInfoServiceClient:Destroy()
|
|
74
|
+
self._maid:DoCleaning()
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
return UserInfoServiceClient
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
Centralized provider for user info so we can coordinate web requests.
|
|
3
|
+
|
|
4
|
+
@class UserInfoService
|
|
5
|
+
]=]
|
|
6
|
+
|
|
7
|
+
local require = require(script.Parent.loader).load(script)
|
|
8
|
+
|
|
9
|
+
local UserInfoAggregator = require("UserInfoAggregator")
|
|
10
|
+
local Maid = require("Maid")
|
|
11
|
+
|
|
12
|
+
local UserInfoService = {}
|
|
13
|
+
UserInfoService.ServiceName = "UserInfoService"
|
|
14
|
+
|
|
15
|
+
function UserInfoService:Init(serviceBag)
|
|
16
|
+
assert(not self._serviceBag, "Already initialized")
|
|
17
|
+
self._serviceBag = assert(serviceBag, "No serviceBag")
|
|
18
|
+
self._maid = Maid.new()
|
|
19
|
+
|
|
20
|
+
self._aggregator = UserInfoAggregator.new()
|
|
21
|
+
self._maid:GiveTask(self._aggregator)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
--[=[
|
|
25
|
+
Promises the user info for the given user, aggregating all requests to reduce
|
|
26
|
+
calls into Roblox.
|
|
27
|
+
|
|
28
|
+
@param userId number
|
|
29
|
+
@return Promise<UserInfo>
|
|
30
|
+
]=]
|
|
31
|
+
function UserInfoService:PromiseUserInfo(userId)
|
|
32
|
+
assert(type(userId) == "number", "Bad userId")
|
|
33
|
+
|
|
34
|
+
return self._aggregator:PromiseUserInfo(userId)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
--[=[
|
|
38
|
+
Observes the user info for the user
|
|
39
|
+
|
|
40
|
+
@param userId number
|
|
41
|
+
@return Observable<UserInfo>
|
|
42
|
+
]=]
|
|
43
|
+
function UserInfoService:ObserveUserInfo(userId)
|
|
44
|
+
assert(type(userId) == "number", "Bad userId")
|
|
45
|
+
|
|
46
|
+
return self._aggregator:ObserveDisplayName(userId)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
--[=[
|
|
50
|
+
Promises the user display name for the userId
|
|
51
|
+
|
|
52
|
+
@param userId number
|
|
53
|
+
@return Promise<string>
|
|
54
|
+
]=]
|
|
55
|
+
function UserInfoService:PromiseDisplayName(userId)
|
|
56
|
+
assert(type(userId) == "number", "Bad userId")
|
|
57
|
+
|
|
58
|
+
return self._aggregator:PromiseDisplayName(userId)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
--[=[
|
|
62
|
+
Observes the user display name for the userId
|
|
63
|
+
|
|
64
|
+
@param userId number
|
|
65
|
+
@return Observable<string>
|
|
66
|
+
]=]
|
|
67
|
+
function UserInfoService:ObserveDisplayName(userId)
|
|
68
|
+
assert(type(userId) == "number", "Bad userId")
|
|
69
|
+
|
|
70
|
+
return self._aggregator:ObserveDisplayName(userId)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
function UserInfoService:Destroy()
|
|
75
|
+
self._maid:DoCleaning()
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
return UserInfoService
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
--[=[
|
|
2
|
+
Aggregates all requests into one big send request to deduplicate the request
|
|
3
|
+
|
|
4
|
+
@class UserInfoAggregator
|
|
5
|
+
]=]
|
|
6
|
+
|
|
7
|
+
local require = require(script.Parent.loader).load(script)
|
|
8
|
+
|
|
9
|
+
local BaseObject = require("BaseObject")
|
|
10
|
+
local Promise = require("Promise")
|
|
11
|
+
local UserServiceUtils = require("UserServiceUtils")
|
|
12
|
+
local Rx = require("Rx")
|
|
13
|
+
|
|
14
|
+
local MAX_USER_IDS_PER_REQUEST = 200
|
|
15
|
+
|
|
16
|
+
local UserInfoAggregator = setmetatable({}, BaseObject)
|
|
17
|
+
UserInfoAggregator.ClassName = "UserInfoAggregator"
|
|
18
|
+
UserInfoAggregator.__index = UserInfoAggregator
|
|
19
|
+
|
|
20
|
+
function UserInfoAggregator.new()
|
|
21
|
+
local self = setmetatable(BaseObject.new(), UserInfoAggregator)
|
|
22
|
+
|
|
23
|
+
-- TODO: LRU cache this? Limit to 1k or something?
|
|
24
|
+
self._promises = {}
|
|
25
|
+
|
|
26
|
+
self._unsentCount = 0
|
|
27
|
+
self._unsentPromises = {}
|
|
28
|
+
|
|
29
|
+
return self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
--[=[
|
|
33
|
+
Promises the user info for the given user, aggregating all requests to reduce
|
|
34
|
+
calls into Roblox.
|
|
35
|
+
|
|
36
|
+
@param userId number
|
|
37
|
+
@return Promise<UserInfo>
|
|
38
|
+
]=]
|
|
39
|
+
function UserInfoAggregator:PromiseUserInfo(userId)
|
|
40
|
+
assert(type(userId) == "number", "Bad userId")
|
|
41
|
+
|
|
42
|
+
if self._promises[userId] then
|
|
43
|
+
return self._promises[userId]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
local promise = Promise.new()
|
|
47
|
+
|
|
48
|
+
self._unsentPromises[userId] = promise
|
|
49
|
+
self._unsentCount = self._unsentCount + 1
|
|
50
|
+
self._promises[userId] = promise
|
|
51
|
+
|
|
52
|
+
self:_queueAggregatedPromises()
|
|
53
|
+
|
|
54
|
+
return promise
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
--[=[
|
|
58
|
+
Promises the user display name for the userId
|
|
59
|
+
|
|
60
|
+
@param userId number
|
|
61
|
+
@return Promise<string>
|
|
62
|
+
]=]
|
|
63
|
+
function UserInfoAggregator:PromiseDisplayName(userId)
|
|
64
|
+
assert(type(userId) == "number", "Bad userId")
|
|
65
|
+
|
|
66
|
+
return self:PromiseUserInfo(userId)
|
|
67
|
+
:Then(function(userInfo)
|
|
68
|
+
return userInfo.DisplayName
|
|
69
|
+
end)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
--[=[
|
|
73
|
+
Promises the user display name for the userId
|
|
74
|
+
|
|
75
|
+
@param userId number
|
|
76
|
+
@return Promise<string>
|
|
77
|
+
]=]
|
|
78
|
+
function UserInfoAggregator:PromiseDisplayName(userId)
|
|
79
|
+
assert(type(userId) == "number", "Bad userId")
|
|
80
|
+
|
|
81
|
+
return self:PromiseUserInfo(userId)
|
|
82
|
+
:Then(function(userInfo)
|
|
83
|
+
return userInfo.DisplayName
|
|
84
|
+
end)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
--[=[
|
|
88
|
+
Promises the user display name for the userId
|
|
89
|
+
|
|
90
|
+
@param userId number
|
|
91
|
+
@return Promise<boolean>
|
|
92
|
+
]=]
|
|
93
|
+
function UserInfoAggregator:PromiseHasVerifiedBadge(userId)
|
|
94
|
+
assert(type(userId) == "number", "Bad userId")
|
|
95
|
+
|
|
96
|
+
return self:PromiseUserInfo(userId)
|
|
97
|
+
:Then(function(userInfo)
|
|
98
|
+
return userInfo.HasVerifiedBadge
|
|
99
|
+
end)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
--[=[
|
|
103
|
+
Observes the user display name for the userId
|
|
104
|
+
|
|
105
|
+
@param userId number
|
|
106
|
+
@return Observable<UserInfo>
|
|
107
|
+
]=]
|
|
108
|
+
function UserInfoAggregator:ObserveUserInfo(userId)
|
|
109
|
+
assert(type(userId) == "number", "Bad userId")
|
|
110
|
+
|
|
111
|
+
return Rx.fromPromise(self:PromiseUserInfo(userId))
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
--[=[
|
|
115
|
+
Observes the user display name for the userId
|
|
116
|
+
|
|
117
|
+
@param userId number
|
|
118
|
+
@return Observable<string>
|
|
119
|
+
]=]
|
|
120
|
+
function UserInfoAggregator:ObserveDisplayName(userId)
|
|
121
|
+
assert(type(userId) == "number", "Bad userId")
|
|
122
|
+
|
|
123
|
+
return self:ObserveUserInfo():Pipe({
|
|
124
|
+
Rx.map(function(userInfo)
|
|
125
|
+
return userInfo.DisplayName
|
|
126
|
+
end)
|
|
127
|
+
})
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
function UserInfoAggregator:_sendAggregatedPromises(promiseMap)
|
|
131
|
+
assert(promiseMap, "No promiseMap")
|
|
132
|
+
|
|
133
|
+
local userIds = {}
|
|
134
|
+
local unresolvedMap = {}
|
|
135
|
+
for userId, promise in pairs(promiseMap) do
|
|
136
|
+
table.insert(userIds, userId)
|
|
137
|
+
unresolvedMap[userId] = promise
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
if #userIds == 0 then
|
|
141
|
+
return
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
assert(#userIds <= MAX_USER_IDS_PER_REQUEST, "Too many userIds sent")
|
|
145
|
+
|
|
146
|
+
self._maid:GivePromise(UserServiceUtils.promiseUserInfosByUserIds(userIds))
|
|
147
|
+
:Then(function(result)
|
|
148
|
+
assert(type(result) == "table", "Bad result")
|
|
149
|
+
|
|
150
|
+
for _, data in pairs(result) do
|
|
151
|
+
assert(type(data.Id) == "number", "Bad result[?].Id")
|
|
152
|
+
|
|
153
|
+
if unresolvedMap[data.Id] then
|
|
154
|
+
unresolvedMap[data.Id]:Resolve(data)
|
|
155
|
+
unresolvedMap[data.Id] = nil
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
-- Reject other ones
|
|
160
|
+
for userId, promise in pairs(unresolvedMap) do
|
|
161
|
+
promise:Reject(string.format("Failed to get result for userId %d", userId))
|
|
162
|
+
end
|
|
163
|
+
end, function(...)
|
|
164
|
+
for _, item in pairs(unresolvedMap) do
|
|
165
|
+
item:Reject(...)
|
|
166
|
+
end
|
|
167
|
+
end)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
function UserInfoAggregator:_resetQueue()
|
|
171
|
+
local promiseMap = self._unsentPromises
|
|
172
|
+
|
|
173
|
+
self._maid._queue = nil
|
|
174
|
+
self._unsentCount = 0
|
|
175
|
+
self._unsentPromises = {}
|
|
176
|
+
|
|
177
|
+
return promiseMap
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
function UserInfoAggregator:_queueAggregatedPromises()
|
|
181
|
+
if self._unsentCount >= MAX_USER_IDS_PER_REQUEST then
|
|
182
|
+
self:_sendAggregatedPromises(self:_resetQueue())
|
|
183
|
+
return
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
if self._maid._queue then
|
|
187
|
+
return
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
self._maid._queue = task.delay(0.1, function()
|
|
191
|
+
task.spawn(function()
|
|
192
|
+
self:_sendAggregatedPromises(self:_resetQueue())
|
|
193
|
+
end)
|
|
194
|
+
end)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
return UserInfoAggregator
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
--[=[
|
|
2
|
+
Wraps [UserService] API calls with [Promise].
|
|
3
|
+
|
|
2
4
|
@class UserServiceUtils
|
|
3
5
|
]=]
|
|
4
6
|
|
|
@@ -15,11 +17,17 @@ local UserServiceUtils = {}
|
|
|
15
17
|
.Id number -- The Id associated with the UserInfoResponse object
|
|
16
18
|
.Username string -- The username associated with the UserInfoResponse object
|
|
17
19
|
.DisplayName string -- The display name associated with the UserInfoResponse object
|
|
20
|
+
.HasVerifiedBadge boolean -- The HasVerifiedBadge value associated with the user.
|
|
18
21
|
@within UserServiceUtils
|
|
19
22
|
]=]
|
|
20
23
|
|
|
21
24
|
--[=[
|
|
22
25
|
Wraps UserService:GetUserInfosByUserIdsAsync(userIds)
|
|
26
|
+
|
|
27
|
+
::: tip
|
|
28
|
+
User [UserInfoAggregator] via [UserInfoService] to get this deduplicated.
|
|
29
|
+
:::
|
|
30
|
+
|
|
23
31
|
@param userIds { number }
|
|
24
32
|
@return Promise<{ UserInfo }>
|
|
25
33
|
]=]
|
|
@@ -46,6 +54,10 @@ end
|
|
|
46
54
|
--[=[
|
|
47
55
|
Wraps UserService:GetUserInfosByUserIdsAsync({ userId })[1]
|
|
48
56
|
|
|
57
|
+
::: tip
|
|
58
|
+
User [UserInfoAggregator] via [UserInfoService] to get this deduplicated.
|
|
59
|
+
:::
|
|
60
|
+
|
|
49
61
|
@param userId number
|
|
50
62
|
@return Promise<UserInfo>
|
|
51
63
|
]=]
|
|
@@ -67,6 +79,10 @@ end
|
|
|
67
79
|
--[=[
|
|
68
80
|
Wraps UserService:GetUserInfosByUserIdsAsync({ userId })[1].DisplayName
|
|
69
81
|
|
|
82
|
+
::: tip
|
|
83
|
+
User [UserInfoAggregator] via [UserInfoService] to get this deduplicated.
|
|
84
|
+
:::
|
|
85
|
+
|
|
70
86
|
@param userId number
|
|
71
87
|
@return Promise<string>
|
|
72
88
|
]=]
|
|
@@ -82,6 +98,10 @@ end
|
|
|
82
98
|
--[=[
|
|
83
99
|
Wraps UserService:GetUserInfosByUserIdsAsync({ userId })[1].Username
|
|
84
100
|
|
|
101
|
+
::: tip
|
|
102
|
+
User [UserInfoAggregator] via [UserInfoService] to get this deduplicated.
|
|
103
|
+
:::
|
|
104
|
+
|
|
85
105
|
@param userId number
|
|
86
106
|
@return Promise<string>
|
|
87
107
|
]=]
|