@noverachat/sdk-web 0.0.3 → 0.2.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.ko.md +8 -8
- package/README.md +8 -8
- package/dist/index.cjs +485 -258
- package/dist/index.d.cts +212 -169
- package/dist/index.d.ts +212 -169
- package/dist/index.js +485 -258
- package/package.json +1 -1
- package/src/client.ts +50 -157
- package/src/features/room.ts +274 -344
- package/src/generated/rest.ts +529 -0
- package/src/index.ts +4 -0
- package/src/types.ts +62 -177
package/dist/index.cjs
CHANGED
|
@@ -27,6 +27,224 @@ __export(index_exports, {
|
|
|
27
27
|
});
|
|
28
28
|
module.exports = __toCommonJS(index_exports);
|
|
29
29
|
|
|
30
|
+
// src/generated/rest.ts
|
|
31
|
+
function parseAppPolicy(j) {
|
|
32
|
+
return {
|
|
33
|
+
appId: j["app_id"],
|
|
34
|
+
allowMemberBulkDelete: j["allow_member_bulk_delete"],
|
|
35
|
+
readReceiptsEnabled: j["read_receipts_enabled"],
|
|
36
|
+
typingIndicatorsEnabled: j["typing_indicators_enabled"],
|
|
37
|
+
allowMemberDeleteAnyMessage: j["allow_member_delete_any_message"]
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function parseUnreadSummary(j) {
|
|
41
|
+
return {
|
|
42
|
+
total: j["total"],
|
|
43
|
+
rooms: j["rooms"].map((e) => parseUnreadRoomItem(e))
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function parseUnreadRoomItem(j) {
|
|
47
|
+
return {
|
|
48
|
+
roomId: j["room_id"],
|
|
49
|
+
name: j["name"],
|
|
50
|
+
roomType: j["room_type"],
|
|
51
|
+
unreadCount: j["unread_count"],
|
|
52
|
+
lastMessageId: j["last_message_id"],
|
|
53
|
+
lastReadMessageId: j["last_read_message_id"],
|
|
54
|
+
lastMessagePreview: j["last_message_preview"],
|
|
55
|
+
memberCount: j["member_count"],
|
|
56
|
+
membersPreview: j["members_preview"] == null ? void 0 : j["members_preview"].map((e) => parseMemberPreview(e))
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function parseMemberPreview(j) {
|
|
60
|
+
return {
|
|
61
|
+
userId: j["user_id"],
|
|
62
|
+
nickname: j["nickname"],
|
|
63
|
+
profileImageUrl: j["profile_image_url"],
|
|
64
|
+
isOnline: j["is_online"]
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function parseRoomUnread(j) {
|
|
68
|
+
return {
|
|
69
|
+
roomId: j["room_id"],
|
|
70
|
+
unreadCount: j["unread_count"],
|
|
71
|
+
lastReadMessageId: j["last_read_message_id"],
|
|
72
|
+
lastMessageId: j["last_message_id"]
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function parseMessageOut(j) {
|
|
76
|
+
return {
|
|
77
|
+
appId: j["app_id"],
|
|
78
|
+
roomId: j["room_id"],
|
|
79
|
+
messageId: j["message_id"],
|
|
80
|
+
sender: j["sender"] == null ? null : parseSenderMini(j["sender"]),
|
|
81
|
+
senderId: j["sender_id"],
|
|
82
|
+
messageType: j["message_type"],
|
|
83
|
+
customType: j["custom_type"],
|
|
84
|
+
content: j["content"],
|
|
85
|
+
data: j["data"],
|
|
86
|
+
meta: j["meta"],
|
|
87
|
+
fileId: j["file_id"],
|
|
88
|
+
file: j["file"],
|
|
89
|
+
files: j["files"],
|
|
90
|
+
replyToId: j["reply_to_id"],
|
|
91
|
+
reactions: j["reactions"] == null ? void 0 : j["reactions"].map((e) => parseReactionEntry(e)),
|
|
92
|
+
createdAt: j["created_at"],
|
|
93
|
+
updatedAt: j["updated_at"],
|
|
94
|
+
deletedAt: j["deleted_at"],
|
|
95
|
+
deletedByUserId: j["deleted_by_user_id"],
|
|
96
|
+
deleteScope: j["delete_scope"],
|
|
97
|
+
editedCount: j["edited_count"],
|
|
98
|
+
createdAtMs: j["created_at_ms"],
|
|
99
|
+
updatedAtMs: j["updated_at_ms"]
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function parseAnnouncementOut(j) {
|
|
103
|
+
return {
|
|
104
|
+
id: j["id"],
|
|
105
|
+
roomId: j["room_id"],
|
|
106
|
+
content: j["content"],
|
|
107
|
+
createdBy: j["created_by"],
|
|
108
|
+
createdAt: j["created_at"],
|
|
109
|
+
updatedAt: j["updated_at"]
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function parseInviteTokenOut(j) {
|
|
113
|
+
return {
|
|
114
|
+
token: j["token"],
|
|
115
|
+
roomId: j["room_id"],
|
|
116
|
+
createdBy: j["created_by"],
|
|
117
|
+
createdAt: j["created_at"],
|
|
118
|
+
expiresAt: j["expires_at"],
|
|
119
|
+
maxUses: j["max_uses"],
|
|
120
|
+
usedCount: j["used_count"],
|
|
121
|
+
revokedAt: j["revoked_at"]
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function parseInviteAcceptResponse(j) {
|
|
125
|
+
return {
|
|
126
|
+
roomId: j["room_id"],
|
|
127
|
+
roomName: j["room_name"],
|
|
128
|
+
wasAlreadyMember: j["was_already_member"]
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
function parseDeviceOut(j) {
|
|
132
|
+
return {
|
|
133
|
+
deviceId: j["device_id"],
|
|
134
|
+
tokenType: j["token_type"],
|
|
135
|
+
tokenPreview: j["token_preview"],
|
|
136
|
+
os: j["os"],
|
|
137
|
+
appVersion: j["app_version"],
|
|
138
|
+
pushEnabled: j["push_enabled"],
|
|
139
|
+
lastActiveAt: j["last_active_at"],
|
|
140
|
+
createdAt: j["created_at"]
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function parseDeviceListResponse(j) {
|
|
144
|
+
return {
|
|
145
|
+
items: j["items"].map((e) => parseDeviceOut(e)),
|
|
146
|
+
total: j["total"]
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function parsePushPreferencesOut(j) {
|
|
150
|
+
return {
|
|
151
|
+
pushEnabled: j["push_enabled"]
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function parseFileOut(j) {
|
|
155
|
+
return {
|
|
156
|
+
id: j["id"],
|
|
157
|
+
kind: j["kind"],
|
|
158
|
+
mime: j["mime"],
|
|
159
|
+
sizeBytes: j["size_bytes"],
|
|
160
|
+
originalName: j["original_name"],
|
|
161
|
+
width: j["width"],
|
|
162
|
+
height: j["height"],
|
|
163
|
+
durationSec: j["duration_sec"],
|
|
164
|
+
thumbnailStatus: j["thumbnail_status"],
|
|
165
|
+
uploadStatus: j["upload_status"],
|
|
166
|
+
createdAt: j["created_at"],
|
|
167
|
+
committedAt: j["committed_at"],
|
|
168
|
+
forwardBlocked: j["forward_blocked"]
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function parseFileCreateResponse(j) {
|
|
172
|
+
return {
|
|
173
|
+
fileId: j["file_id"],
|
|
174
|
+
uploadUrl: j["upload_url"],
|
|
175
|
+
headers: j["headers"],
|
|
176
|
+
expiresAtMs: j["expires_at_ms"]
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function parseBlockOut(j) {
|
|
180
|
+
return {
|
|
181
|
+
blockedUserId: j["blocked_user_id"],
|
|
182
|
+
reason: j["reason"],
|
|
183
|
+
createdAt: j["created_at"]
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function parseRoomMemberOut(j) {
|
|
187
|
+
return {
|
|
188
|
+
userId: j["user_id"],
|
|
189
|
+
nickname: j["nickname"],
|
|
190
|
+
role: j["role"],
|
|
191
|
+
pushTrigger: j["push_trigger"],
|
|
192
|
+
lastReadMessageId: j["last_read_message_id"],
|
|
193
|
+
joinedAt: j["joined_at"],
|
|
194
|
+
isOnline: j["is_online"],
|
|
195
|
+
mutedUntil: j["muted_until"]
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function parseBulkDeleteResult(j) {
|
|
199
|
+
return {
|
|
200
|
+
cleared: j["cleared"],
|
|
201
|
+
upToMessageId: j["up_to_message_id"]
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function parseBulkDeleteBySenderResult(j) {
|
|
205
|
+
return {
|
|
206
|
+
cleared: j["cleared"]
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function parseJoinRequestOut(j) {
|
|
210
|
+
return {
|
|
211
|
+
userId: j["user_id"],
|
|
212
|
+
status: j["status"],
|
|
213
|
+
reason: j["reason"],
|
|
214
|
+
requestedAt: j["requested_at"],
|
|
215
|
+
processedAt: j["processed_at"],
|
|
216
|
+
processedByUserId: j["processed_by_user_id"]
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function parseJoinRequestListResponse(j) {
|
|
220
|
+
return {
|
|
221
|
+
items: j["items"].map((e) => parseJoinRequestOut(e)),
|
|
222
|
+
total: j["total"],
|
|
223
|
+
limit: j["limit"],
|
|
224
|
+
offset: j["offset"]
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function parseJoinRequestCreateResponse(j) {
|
|
228
|
+
return {
|
|
229
|
+
status: j["status"],
|
|
230
|
+
roomId: j["room_id"]
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function parseSenderMini(j) {
|
|
234
|
+
return {
|
|
235
|
+
userId: j["user_id"],
|
|
236
|
+
nickname: j["nickname"],
|
|
237
|
+
profileImageUrl: j["profile_image_url"]
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function parseReactionEntry(j) {
|
|
241
|
+
return {
|
|
242
|
+
key: j["key"],
|
|
243
|
+
userIds: j["user_ids"],
|
|
244
|
+
updatedAt: j["updated_at"]
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
30
248
|
// src/errors.ts
|
|
31
249
|
var ErrorCode = {
|
|
32
250
|
INTERNAL: "NC-GEN-001",
|
|
@@ -322,64 +540,31 @@ function tempId() {
|
|
|
322
540
|
}
|
|
323
541
|
|
|
324
542
|
// src/features/room.ts
|
|
325
|
-
function
|
|
326
|
-
|
|
543
|
+
function toFileInline(f) {
|
|
544
|
+
return {
|
|
545
|
+
id: f.id,
|
|
546
|
+
kind: f.kind,
|
|
547
|
+
mime: f.mime,
|
|
548
|
+
size: f.size,
|
|
549
|
+
name: f.name ?? null,
|
|
550
|
+
width: f.width ?? null,
|
|
551
|
+
height: f.height ?? null,
|
|
552
|
+
durationSec: f.duration_sec ?? null,
|
|
553
|
+
thumbnailStatus: f.thumbnail_status,
|
|
554
|
+
forwardBlocked: Boolean(f.forward_blocked)
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
function toMessageOut(j) {
|
|
558
|
+
const base = parseMessageOut(j);
|
|
559
|
+
const raw = j;
|
|
560
|
+
const dataObj = base.data ?? null;
|
|
327
561
|
const customMeta = dataObj && typeof dataObj === "object" && "_customMeta" in dataObj ? dataObj._customMeta : null;
|
|
328
562
|
return {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
nickname: r.sender.nickname ?? null,
|
|
335
|
-
profileImageUrl: r.sender.profile_image_url ?? null
|
|
336
|
-
} : null,
|
|
337
|
-
senderId: r.sender_id ?? null,
|
|
338
|
-
messageType: r.message_type,
|
|
339
|
-
customType: r.custom_type ?? null,
|
|
340
|
-
content: r.content ?? null,
|
|
341
|
-
data: dataObj,
|
|
342
|
-
meta: r.meta ?? null,
|
|
343
|
-
customMeta,
|
|
344
|
-
fileId: r.file_id ?? null,
|
|
345
|
-
file: r.file ? {
|
|
346
|
-
id: r.file.id,
|
|
347
|
-
kind: r.file.kind,
|
|
348
|
-
mime: r.file.mime,
|
|
349
|
-
size: r.file.size,
|
|
350
|
-
name: r.file.name ?? null,
|
|
351
|
-
width: r.file.width ?? null,
|
|
352
|
-
height: r.file.height ?? null,
|
|
353
|
-
durationSec: r.file.duration_sec ?? null,
|
|
354
|
-
thumbnailStatus: r.file.thumbnail_status,
|
|
355
|
-
forwardBlocked: Boolean(r.file.forward_blocked)
|
|
356
|
-
} : null,
|
|
357
|
-
files: r.files && r.files.length > 0 ? r.files.map((f) => ({
|
|
358
|
-
id: f.id,
|
|
359
|
-
kind: f.kind,
|
|
360
|
-
mime: f.mime,
|
|
361
|
-
size: f.size,
|
|
362
|
-
name: f.name ?? null,
|
|
363
|
-
width: f.width ?? null,
|
|
364
|
-
height: f.height ?? null,
|
|
365
|
-
durationSec: f.duration_sec ?? null,
|
|
366
|
-
thumbnailStatus: f.thumbnail_status,
|
|
367
|
-
forwardBlocked: Boolean(f.forward_blocked)
|
|
368
|
-
})) : null,
|
|
369
|
-
replyToId: r.reply_to_id ?? null,
|
|
370
|
-
reactions: (r.reactions ?? []).map((x) => ({
|
|
371
|
-
key: x.key,
|
|
372
|
-
userIds: x.user_ids,
|
|
373
|
-
updatedAt: x.updated_at
|
|
374
|
-
})),
|
|
375
|
-
createdAt: r.created_at,
|
|
376
|
-
createdAtMs: r.created_at_ms,
|
|
377
|
-
updatedAt: r.updated_at ?? null,
|
|
378
|
-
updatedAtMs: r.updated_at_ms ?? null,
|
|
379
|
-
deletedAt: r.deleted_at ?? null,
|
|
380
|
-
deletedByUserId: r.deleted_by_user_id ?? null,
|
|
381
|
-
deleteScope: r.delete_scope ?? null,
|
|
382
|
-
editedCount: r.edited_count ?? 0
|
|
563
|
+
...base,
|
|
564
|
+
file: raw.file ? toFileInline(raw.file) : null,
|
|
565
|
+
files: raw.files && raw.files.length > 0 ? raw.files.map(toFileInline) : null,
|
|
566
|
+
reactions: base.reactions ?? [],
|
|
567
|
+
customMeta
|
|
383
568
|
};
|
|
384
569
|
}
|
|
385
570
|
var Room = class _Room {
|
|
@@ -565,20 +750,24 @@ var Room = class _Room {
|
|
|
565
750
|
async sendFile(file, opts = {}) {
|
|
566
751
|
const kind = opts.kind ?? (file.type.startsWith("image/") ? "image" : file.type.startsWith("video/") ? "video" : "file");
|
|
567
752
|
const name = opts.name ?? file.name;
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
753
|
+
const created = parseFileCreateResponse(
|
|
754
|
+
await this.http.request("POST", "/api/v1/files", {
|
|
755
|
+
kind,
|
|
756
|
+
mime: file.type || "application/octet-stream",
|
|
757
|
+
size: file.size,
|
|
758
|
+
original_name: name,
|
|
759
|
+
// 파일 전달 제한 — uploader-set at presign time. When true,
|
|
760
|
+
// other users can't forward this file to a different room.
|
|
761
|
+
forward_blocked: Boolean(opts.forwardBlocked)
|
|
762
|
+
})
|
|
763
|
+
);
|
|
577
764
|
const presigned = {
|
|
578
|
-
fileId:
|
|
579
|
-
uploadUrl:
|
|
580
|
-
headers
|
|
581
|
-
|
|
765
|
+
fileId: created.fileId,
|
|
766
|
+
uploadUrl: created.uploadUrl,
|
|
767
|
+
// Presigned S3 headers are always string-valued; the generated
|
|
768
|
+
// model types the JSON object loosely as Record<string, unknown>.
|
|
769
|
+
headers: created.headers,
|
|
770
|
+
expiresAtMs: created.expiresAtMs
|
|
582
771
|
};
|
|
583
772
|
await new Promise((resolve, reject) => {
|
|
584
773
|
const xhr = new XMLHttpRequest();
|
|
@@ -633,17 +822,19 @@ var Room = class _Room {
|
|
|
633
822
|
async uploadFileOnly(file, opts = {}) {
|
|
634
823
|
const kind = opts.kind ?? (file.type.startsWith("image/") ? "image" : file.type.startsWith("video/") ? "video" : "file");
|
|
635
824
|
const name = opts.name ?? file.name;
|
|
636
|
-
const
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
825
|
+
const created = parseFileCreateResponse(
|
|
826
|
+
await this.http.request("POST", "/api/v1/files", {
|
|
827
|
+
kind,
|
|
828
|
+
mime: file.type || "application/octet-stream",
|
|
829
|
+
size: file.size,
|
|
830
|
+
original_name: name,
|
|
831
|
+
forward_blocked: Boolean(opts.forwardBlocked)
|
|
832
|
+
})
|
|
833
|
+
);
|
|
643
834
|
await new Promise((resolve, reject) => {
|
|
644
835
|
const xhr = new XMLHttpRequest();
|
|
645
|
-
xhr.open("PUT",
|
|
646
|
-
for (const [k, v] of Object.entries(
|
|
836
|
+
xhr.open("PUT", created.uploadUrl);
|
|
837
|
+
for (const [k, v] of Object.entries(created.headers)) {
|
|
647
838
|
xhr.setRequestHeader(k, v);
|
|
648
839
|
}
|
|
649
840
|
if (opts.onProgress) {
|
|
@@ -670,10 +861,10 @@ var Room = class _Room {
|
|
|
670
861
|
});
|
|
671
862
|
await this.http.request(
|
|
672
863
|
"POST",
|
|
673
|
-
`/api/v1/files/${
|
|
864
|
+
`/api/v1/files/${created.fileId}/commit`,
|
|
674
865
|
{}
|
|
675
866
|
);
|
|
676
|
-
return
|
|
867
|
+
return created.fileId;
|
|
677
868
|
}
|
|
678
869
|
/**
|
|
679
870
|
* List files shared in this room — newest-first, paginated.
|
|
@@ -698,23 +889,26 @@ var Room = class _Room {
|
|
|
698
889
|
}
|
|
699
890
|
);
|
|
700
891
|
return {
|
|
701
|
-
items: raw.items.map((r) =>
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
892
|
+
items: raw.items.map((r) => {
|
|
893
|
+
const f = parseFileOut(r.file);
|
|
894
|
+
return {
|
|
895
|
+
messageId: r.message_id,
|
|
896
|
+
senderId: r.sender_id,
|
|
897
|
+
createdAtMs: r.created_at_ms,
|
|
898
|
+
file: {
|
|
899
|
+
id: f.id,
|
|
900
|
+
kind: f.kind,
|
|
901
|
+
mime: f.mime,
|
|
902
|
+
size: f.sizeBytes,
|
|
903
|
+
name: f.originalName ?? null,
|
|
904
|
+
width: f.width ?? null,
|
|
905
|
+
height: f.height ?? null,
|
|
906
|
+
durationSec: f.durationSec ?? null,
|
|
907
|
+
thumbnailStatus: f.thumbnailStatus,
|
|
908
|
+
forwardBlocked: Boolean(f.forwardBlocked)
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
}),
|
|
718
912
|
nextCursor: raw.next_cursor,
|
|
719
913
|
hasMore: raw.has_more
|
|
720
914
|
};
|
|
@@ -752,11 +946,12 @@ var Room = class _Room {
|
|
|
752
946
|
this.ws.send({ type: "typing", room_id: this.roomId, is_typing: isTyping });
|
|
753
947
|
}
|
|
754
948
|
async edit(messageId, patch) {
|
|
755
|
-
|
|
949
|
+
const raw = await this.http.request(
|
|
756
950
|
"PUT",
|
|
757
951
|
`/api/v1/rooms/${this.roomId}/messages/${messageId}`,
|
|
758
952
|
patch
|
|
759
953
|
);
|
|
954
|
+
return toMessageOut(raw);
|
|
760
955
|
}
|
|
761
956
|
async delete(messageId, scope = "ALL") {
|
|
762
957
|
await this.http.request(
|
|
@@ -781,12 +976,39 @@ var Room = class _Room {
|
|
|
781
976
|
* Other members get a `MEMBER_LEFT` [`roomEvent`](../reference/noverachat.md#events).
|
|
782
977
|
* Idempotent if already a non-member; throws 409 if the caller was KICKED
|
|
783
978
|
* (ask an operator to restore first). For GROUP/SUPER_GROUP, re-entry needs
|
|
784
|
-
* an operator add; for ONE, re-creating the 1:1 rejoins with history kept.
|
|
785
|
-
|
|
979
|
+
* an operator add; for ONE, re-creating the 1:1 rejoins with history kept.
|
|
980
|
+
*
|
|
981
|
+
* Optional `deleteMessages` — atomically clean up chat history as part
|
|
982
|
+
* of leaving. Default is `"none"` (messages survive so the room's
|
|
983
|
+
* history keeps making sense to remaining members).
|
|
984
|
+
* - `"none"` — leave only.
|
|
985
|
+
* - `"mine"` — soft-delete every message YOU sent, then leave. Other
|
|
986
|
+
* members' messages are untouched. Broadcasts
|
|
987
|
+
* `messages_cleared_by_sender` so connected clients hide
|
|
988
|
+
* them locally.
|
|
989
|
+
* - `"all"` — wipe every message in the room, then leave. Subject to
|
|
990
|
+
* the app's `allow_member_bulk_delete` gate and per-room
|
|
991
|
+
* OPERATOR check — a plain member gets 403 back. */
|
|
992
|
+
async leave(options) {
|
|
993
|
+
const mode = options?.deleteMessages ?? "none";
|
|
994
|
+
const query = mode === "none" ? "" : `?delete_messages=${encodeURIComponent(mode)}`;
|
|
786
995
|
await this.http.request(
|
|
787
996
|
"POST",
|
|
788
|
-
`/api/v1/rooms/${this.roomId}/leave`
|
|
997
|
+
`/api/v1/rooms/${this.roomId}/leave${query}`
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
/** Delete every message YOU sent in this room. Standalone version of
|
|
1001
|
+
* `leave({ deleteMessages: "mine" })` — useful when a user wants to
|
|
1002
|
+
* scrub their history without actually leaving. Other members'
|
|
1003
|
+
* messages are untouched; broadcasts `messages_cleared_by_sender`
|
|
1004
|
+
* so connected clients hide the vanished ones locally.
|
|
1005
|
+
* No OPERATOR gate — you own your messages. */
|
|
1006
|
+
async deleteMyMessages() {
|
|
1007
|
+
const raw = await this.http.request(
|
|
1008
|
+
"DELETE",
|
|
1009
|
+
`/api/v1/rooms/${this.roomId}/messages/mine`
|
|
789
1010
|
);
|
|
1011
|
+
return parseBulkDeleteBySenderResult(raw);
|
|
790
1012
|
}
|
|
791
1013
|
/**
|
|
792
1014
|
* Set the caller's per-room push preference — like long-pressing a
|
|
@@ -812,11 +1034,11 @@ var Room = class _Room {
|
|
|
812
1034
|
* hide rendered messages up to (and including) that id.
|
|
813
1035
|
*/
|
|
814
1036
|
async deleteAllMessages() {
|
|
815
|
-
const raw = await this.http.request(
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1037
|
+
const raw = await this.http.request(
|
|
1038
|
+
"DELETE",
|
|
1039
|
+
`/api/v1/rooms/${this.roomId}/messages`
|
|
1040
|
+
);
|
|
1041
|
+
return parseBulkDeleteResult(raw);
|
|
820
1042
|
}
|
|
821
1043
|
async react(messageId, key) {
|
|
822
1044
|
await this.http.request(
|
|
@@ -903,17 +1125,11 @@ var Room = class _Room {
|
|
|
903
1125
|
* rooms, so the caller must already be a member to read this.
|
|
904
1126
|
*/
|
|
905
1127
|
async listMembers() {
|
|
906
|
-
const raw = await this.http.request(
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
pushTrigger: m.push_trigger,
|
|
912
|
-
lastReadMessageId: m.last_read_message_id,
|
|
913
|
-
joinedAt: m.joined_at,
|
|
914
|
-
isOnline: m.is_online ?? null,
|
|
915
|
-
mutedUntil: m.muted_until ?? null
|
|
916
|
-
}));
|
|
1128
|
+
const raw = await this.http.request(
|
|
1129
|
+
"GET",
|
|
1130
|
+
`/api/v1/rooms/${this.roomId}/members`
|
|
1131
|
+
);
|
|
1132
|
+
return raw.map((m) => parseRoomMemberOut(m));
|
|
917
1133
|
}
|
|
918
1134
|
// -------- mute --------
|
|
919
1135
|
/** OPERATOR-only: mute a member so they can't send messages in this
|
|
@@ -941,13 +1157,11 @@ var Room = class _Room {
|
|
|
941
1157
|
* newer than the caller's `read_watermark`, plus the room's
|
|
942
1158
|
* `last_message_id` so the UI can show a "•" dot regardless of count. */
|
|
943
1159
|
async unread() {
|
|
944
|
-
const raw = await this.http.request(
|
|
945
|
-
|
|
946
|
-
roomId
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
lastMessageId: raw.last_message_id
|
|
950
|
-
};
|
|
1160
|
+
const raw = await this.http.request(
|
|
1161
|
+
"GET",
|
|
1162
|
+
`/api/v1/rooms/${this.roomId}/unread`
|
|
1163
|
+
);
|
|
1164
|
+
return parseRoomUnread(raw);
|
|
951
1165
|
}
|
|
952
1166
|
// -------- freeze --------
|
|
953
1167
|
/** OPERATOR-only moderation: freeze this room. Non-admin messages are
|
|
@@ -982,49 +1196,35 @@ var Room = class _Room {
|
|
|
982
1196
|
}
|
|
983
1197
|
// -------- announcements --------
|
|
984
1198
|
async listAnnouncements() {
|
|
985
|
-
const raw = await this.http.request(
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
createdBy: a.created_by,
|
|
991
|
-
createdAt: a.created_at,
|
|
992
|
-
updatedAt: a.updated_at
|
|
993
|
-
}));
|
|
1199
|
+
const raw = await this.http.request(
|
|
1200
|
+
"GET",
|
|
1201
|
+
`/api/v1/rooms/${this.roomId}/announcements`
|
|
1202
|
+
);
|
|
1203
|
+
return raw.map((a) => parseAnnouncementOut(a));
|
|
994
1204
|
}
|
|
995
1205
|
async getCurrentAnnouncement() {
|
|
996
|
-
const raw = await this.http.request(
|
|
1206
|
+
const raw = await this.http.request(
|
|
1207
|
+
"GET",
|
|
1208
|
+
`/api/v1/rooms/${this.roomId}/announcements/current`
|
|
1209
|
+
);
|
|
997
1210
|
if (!raw) return null;
|
|
998
|
-
return
|
|
999
|
-
id: raw.id,
|
|
1000
|
-
roomId: raw.room_id,
|
|
1001
|
-
content: raw.content,
|
|
1002
|
-
createdBy: raw.created_by,
|
|
1003
|
-
createdAt: raw.created_at,
|
|
1004
|
-
updatedAt: raw.updated_at
|
|
1005
|
-
};
|
|
1211
|
+
return parseAnnouncementOut(raw);
|
|
1006
1212
|
}
|
|
1007
1213
|
async postAnnouncement(content) {
|
|
1008
|
-
const raw = await this.http.request(
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
createdAt: raw.created_at,
|
|
1015
|
-
updatedAt: raw.updated_at
|
|
1016
|
-
};
|
|
1214
|
+
const raw = await this.http.request(
|
|
1215
|
+
"POST",
|
|
1216
|
+
`/api/v1/rooms/${this.roomId}/announcements`,
|
|
1217
|
+
{ content }
|
|
1218
|
+
);
|
|
1219
|
+
return parseAnnouncementOut(raw);
|
|
1017
1220
|
}
|
|
1018
1221
|
async updateAnnouncement(id, content) {
|
|
1019
|
-
const raw = await this.http.request(
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
createdAt: raw.created_at,
|
|
1026
|
-
updatedAt: raw.updated_at
|
|
1027
|
-
};
|
|
1222
|
+
const raw = await this.http.request(
|
|
1223
|
+
"PATCH",
|
|
1224
|
+
`/api/v1/rooms/${this.roomId}/announcements/${id}`,
|
|
1225
|
+
{ content }
|
|
1226
|
+
);
|
|
1227
|
+
return parseAnnouncementOut(raw);
|
|
1028
1228
|
}
|
|
1029
1229
|
async deleteAnnouncement(id) {
|
|
1030
1230
|
await this.http.request(
|
|
@@ -1046,32 +1246,21 @@ var Room = class _Room {
|
|
|
1046
1246
|
const body = {};
|
|
1047
1247
|
if (opts && "expiresInHours" in opts) body.expires_in_hours = opts.expiresInHours;
|
|
1048
1248
|
if (opts && "maxUses" in opts) body.max_uses = opts.maxUses;
|
|
1049
|
-
const raw = await this.http.request(
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
expiresAt: raw.expires_at,
|
|
1056
|
-
maxUses: raw.max_uses,
|
|
1057
|
-
usedCount: raw.used_count,
|
|
1058
|
-
revokedAt: raw.revoked_at
|
|
1059
|
-
};
|
|
1249
|
+
const raw = await this.http.request(
|
|
1250
|
+
"POST",
|
|
1251
|
+
`/api/v1/rooms/${this.roomId}/invites`,
|
|
1252
|
+
body
|
|
1253
|
+
);
|
|
1254
|
+
return parseInviteTokenOut(raw);
|
|
1060
1255
|
}
|
|
1061
1256
|
/** OPERATOR-only: list all invite tokens for this room (including
|
|
1062
1257
|
* revoked / expired / exhausted), newest first. */
|
|
1063
1258
|
async listInvites() {
|
|
1064
|
-
const raw = await this.http.request(
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
createdAt: r.created_at,
|
|
1070
|
-
expiresAt: r.expires_at,
|
|
1071
|
-
maxUses: r.max_uses,
|
|
1072
|
-
usedCount: r.used_count,
|
|
1073
|
-
revokedAt: r.revoked_at
|
|
1074
|
-
}));
|
|
1259
|
+
const raw = await this.http.request(
|
|
1260
|
+
"GET",
|
|
1261
|
+
`/api/v1/rooms/${this.roomId}/invites`
|
|
1262
|
+
);
|
|
1263
|
+
return raw.map((r) => parseInviteTokenOut(r));
|
|
1075
1264
|
}
|
|
1076
1265
|
/** OPERATOR-only: revoke a token immediately. Idempotent — revoking
|
|
1077
1266
|
* an already-revoked token returns 204 as well. */
|
|
@@ -1081,6 +1270,81 @@ var Room = class _Room {
|
|
|
1081
1270
|
`/api/v1/rooms/${this.roomId}/invites/${encodeURIComponent(token)}`
|
|
1082
1271
|
);
|
|
1083
1272
|
}
|
|
1273
|
+
// -------- join requests --------
|
|
1274
|
+
/**
|
|
1275
|
+
* Ask to join this room (self). Behavior depends on the room's
|
|
1276
|
+
* `joinPolicy`:
|
|
1277
|
+
* - `OPEN` → the caller is added immediately (`status: "joined"`);
|
|
1278
|
+
* - `APPROVAL_REQUIRED` → the request lands in the operators' inbox
|
|
1279
|
+
* (`status: "pending"`) until [`approveJoinRequest`] / reject.
|
|
1280
|
+
* Idempotent for members (`status: "already_member"`).
|
|
1281
|
+
*
|
|
1282
|
+
* @param reason Optional note shown to operators ("하은이 반 친구 엄마예요").
|
|
1283
|
+
*/
|
|
1284
|
+
async requestJoin(reason) {
|
|
1285
|
+
const raw = await this.http.request(
|
|
1286
|
+
"POST",
|
|
1287
|
+
`/api/v1/rooms/${this.roomId}/join-request`,
|
|
1288
|
+
reason ? { reason } : {}
|
|
1289
|
+
);
|
|
1290
|
+
return parseJoinRequestCreateResponse(raw);
|
|
1291
|
+
}
|
|
1292
|
+
/** Cancel the caller's own pending join request. */
|
|
1293
|
+
async cancelJoinRequest() {
|
|
1294
|
+
await this.http.request(
|
|
1295
|
+
"DELETE",
|
|
1296
|
+
`/api/v1/rooms/${this.roomId}/join-request`
|
|
1297
|
+
);
|
|
1298
|
+
}
|
|
1299
|
+
/** OPERATOR-only: list join requests for this room (the approval inbox).
|
|
1300
|
+
* Defaults to pending only — pass a status to see processed history. */
|
|
1301
|
+
async listJoinRequests(opts) {
|
|
1302
|
+
const raw = await this.http.request(
|
|
1303
|
+
"GET",
|
|
1304
|
+
`/api/v1/rooms/${this.roomId}/join-requests`,
|
|
1305
|
+
void 0,
|
|
1306
|
+
{
|
|
1307
|
+
...opts?.status ? { status: opts.status } : {},
|
|
1308
|
+
...opts?.limit !== void 0 ? { limit: opts.limit } : {},
|
|
1309
|
+
...opts?.offset !== void 0 ? { offset: opts.offset } : {}
|
|
1310
|
+
}
|
|
1311
|
+
);
|
|
1312
|
+
return parseJoinRequestListResponse(raw);
|
|
1313
|
+
}
|
|
1314
|
+
/** OPERATOR-only: approve a pending join request — the requester becomes
|
|
1315
|
+
* a MEMBER. [reason] is echoed back to them in later list calls. */
|
|
1316
|
+
async approveJoinRequest(userId, reason) {
|
|
1317
|
+
await this.http.request(
|
|
1318
|
+
"POST",
|
|
1319
|
+
`/api/v1/rooms/${this.roomId}/join-requests/${encodeURIComponent(userId)}/approve`,
|
|
1320
|
+
reason ? { reason } : {}
|
|
1321
|
+
);
|
|
1322
|
+
}
|
|
1323
|
+
/** OPERATOR-only: reject a pending join request. */
|
|
1324
|
+
async rejectJoinRequest(userId, reason) {
|
|
1325
|
+
await this.http.request(
|
|
1326
|
+
"POST",
|
|
1327
|
+
`/api/v1/rooms/${this.roomId}/join-requests/${encodeURIComponent(userId)}/reject`,
|
|
1328
|
+
reason ? { reason } : {}
|
|
1329
|
+
);
|
|
1330
|
+
}
|
|
1331
|
+
// -------- hide / unhide --------
|
|
1332
|
+
/** Hide this room from the caller's own room list (self, reversible —
|
|
1333
|
+
* the KakaoTalk "숨기기"). The room typically reappears via [unhide]
|
|
1334
|
+
* or on new activity, per server policy. Messages are untouched. */
|
|
1335
|
+
async hide() {
|
|
1336
|
+
await this.http.request(
|
|
1337
|
+
"POST",
|
|
1338
|
+
`/api/v1/rooms/${this.roomId}/hide`
|
|
1339
|
+
);
|
|
1340
|
+
}
|
|
1341
|
+
/** Undo [hide] — the room shows up in the caller's list again. */
|
|
1342
|
+
async unhide() {
|
|
1343
|
+
await this.http.request(
|
|
1344
|
+
"POST",
|
|
1345
|
+
`/api/v1/rooms/${this.roomId}/unhide`
|
|
1346
|
+
);
|
|
1347
|
+
}
|
|
1084
1348
|
// -------- internal dispatch --------
|
|
1085
1349
|
on(event, fn) {
|
|
1086
1350
|
return this.bus.on(event, fn);
|
|
@@ -1329,13 +1593,7 @@ var NoveraChat = class {
|
|
|
1329
1593
|
* destructive buttons to expose. */
|
|
1330
1594
|
async appPolicy() {
|
|
1331
1595
|
const raw = await this.http.request("GET", "/api/v1/app/policy");
|
|
1332
|
-
return
|
|
1333
|
-
appId: raw.app_id,
|
|
1334
|
-
allowMemberBulkDelete: raw.allow_member_bulk_delete,
|
|
1335
|
-
readReceiptsEnabled: raw.read_receipts_enabled ?? true,
|
|
1336
|
-
typingIndicatorsEnabled: raw.typing_indicators_enabled ?? true,
|
|
1337
|
-
deleteAnyMessageEnabled: raw.allow_member_delete_any_message ?? false
|
|
1338
|
-
};
|
|
1596
|
+
return parseAppPolicy(raw);
|
|
1339
1597
|
}
|
|
1340
1598
|
// ---- device tokens (push) ----
|
|
1341
1599
|
/**
|
|
@@ -1345,19 +1603,23 @@ var NoveraChat = class {
|
|
|
1345
1603
|
* safe. Returns the stored device record (token shown only as a prefix).
|
|
1346
1604
|
*/
|
|
1347
1605
|
async registerDevice(params) {
|
|
1348
|
-
const raw = await this.http.request(
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1606
|
+
const raw = await this.http.request(
|
|
1607
|
+
"POST",
|
|
1608
|
+
"/api/v1/users/me/devices",
|
|
1609
|
+
{
|
|
1610
|
+
device_id: params.deviceId,
|
|
1611
|
+
token: params.token,
|
|
1612
|
+
token_type: params.tokenType,
|
|
1613
|
+
os: params.os ?? null,
|
|
1614
|
+
app_version: params.appVersion ?? null
|
|
1615
|
+
}
|
|
1616
|
+
);
|
|
1617
|
+
return parseDeviceOut(raw);
|
|
1356
1618
|
}
|
|
1357
1619
|
/** List the devices registered for the authenticated user. */
|
|
1358
1620
|
async listDevices() {
|
|
1359
1621
|
const raw = await this.http.request("GET", "/api/v1/users/me/devices");
|
|
1360
|
-
return raw.items
|
|
1622
|
+
return parseDeviceListResponse(raw).items;
|
|
1361
1623
|
}
|
|
1362
1624
|
/**
|
|
1363
1625
|
* Turn push delivery on/off for one device without unregistering it —
|
|
@@ -1395,7 +1657,7 @@ var NoveraChat = class {
|
|
|
1395
1657
|
"GET",
|
|
1396
1658
|
"/api/v1/users/me/push-preferences"
|
|
1397
1659
|
);
|
|
1398
|
-
return
|
|
1660
|
+
return parsePushPreferencesOut(raw);
|
|
1399
1661
|
}
|
|
1400
1662
|
/**
|
|
1401
1663
|
* Flip the caller's global push kill switch. Pass `false` to mute
|
|
@@ -1408,7 +1670,7 @@ var NoveraChat = class {
|
|
|
1408
1670
|
"/api/v1/users/me/push-preferences",
|
|
1409
1671
|
{ push_enabled: enabled }
|
|
1410
1672
|
);
|
|
1411
|
-
return
|
|
1673
|
+
return parsePushPreferencesOut(raw);
|
|
1412
1674
|
}
|
|
1413
1675
|
// -------------------------------------------------------------------------
|
|
1414
1676
|
// User blocks — KakaoTalk-style one-way block.
|
|
@@ -1432,11 +1694,7 @@ var NoveraChat = class {
|
|
|
1432
1694
|
`/api/v1/users/me/blocks/${encodeURIComponent(userId)}`,
|
|
1433
1695
|
{ reason: reason ?? null }
|
|
1434
1696
|
);
|
|
1435
|
-
return
|
|
1436
|
-
blockedUserId: raw.blocked_user_id,
|
|
1437
|
-
reason: raw.reason,
|
|
1438
|
-
createdAt: raw.created_at
|
|
1439
|
-
};
|
|
1697
|
+
return parseBlockOut(raw);
|
|
1440
1698
|
}
|
|
1441
1699
|
/** Unblock a user. Idempotent — resolves either way. */
|
|
1442
1700
|
async unblockUser(userId) {
|
|
@@ -1448,23 +1706,7 @@ var NoveraChat = class {
|
|
|
1448
1706
|
/** List every user the caller has blocked, newest first. */
|
|
1449
1707
|
async listBlocks() {
|
|
1450
1708
|
const raw = await this.http.request("GET", "/api/v1/users/me/blocks");
|
|
1451
|
-
return raw.map((r) => (
|
|
1452
|
-
blockedUserId: r.blocked_user_id,
|
|
1453
|
-
reason: r.reason,
|
|
1454
|
-
createdAt: r.created_at
|
|
1455
|
-
}));
|
|
1456
|
-
}
|
|
1457
|
-
_toDeviceInfo(raw) {
|
|
1458
|
-
return {
|
|
1459
|
-
deviceId: raw.device_id,
|
|
1460
|
-
tokenType: raw.token_type,
|
|
1461
|
-
tokenPreview: raw.token_preview,
|
|
1462
|
-
os: raw.os,
|
|
1463
|
-
appVersion: raw.app_version,
|
|
1464
|
-
pushEnabled: raw.push_enabled,
|
|
1465
|
-
lastActiveAt: raw.last_active_at,
|
|
1466
|
-
createdAt: raw.created_at
|
|
1467
|
-
};
|
|
1709
|
+
return raw.map((r) => parseBlockOut(r));
|
|
1468
1710
|
}
|
|
1469
1711
|
/**
|
|
1470
1712
|
* Authorization-gated URL for downloading a file attachment.
|
|
@@ -1522,41 +1764,27 @@ var NoveraChat = class {
|
|
|
1522
1764
|
* shipping date.
|
|
1523
1765
|
*/
|
|
1524
1766
|
async getFileMeta(fileId) {
|
|
1525
|
-
const raw = await this.http.request(
|
|
1767
|
+
const raw = await this.http.request(
|
|
1768
|
+
"GET",
|
|
1769
|
+
`/api/v1/files/${encodeURIComponent(fileId)}/meta`
|
|
1770
|
+
);
|
|
1771
|
+
const f = parseFileOut(raw);
|
|
1526
1772
|
return {
|
|
1527
|
-
id:
|
|
1528
|
-
kind:
|
|
1529
|
-
mime:
|
|
1530
|
-
size:
|
|
1531
|
-
name:
|
|
1532
|
-
width:
|
|
1533
|
-
height:
|
|
1534
|
-
durationSec:
|
|
1535
|
-
thumbnailStatus:
|
|
1536
|
-
forwardBlocked: Boolean(
|
|
1773
|
+
id: f.id,
|
|
1774
|
+
kind: f.kind,
|
|
1775
|
+
mime: f.mime,
|
|
1776
|
+
size: f.sizeBytes,
|
|
1777
|
+
name: f.originalName ?? null,
|
|
1778
|
+
width: f.width ?? null,
|
|
1779
|
+
height: f.height ?? null,
|
|
1780
|
+
durationSec: f.durationSec ?? null,
|
|
1781
|
+
thumbnailStatus: f.thumbnailStatus,
|
|
1782
|
+
forwardBlocked: Boolean(f.forwardBlocked)
|
|
1537
1783
|
};
|
|
1538
1784
|
}
|
|
1539
1785
|
async unreadSummary() {
|
|
1540
1786
|
const raw = await this.http.request("GET", "/api/v1/users/me/unread");
|
|
1541
|
-
return
|
|
1542
|
-
total: raw.total,
|
|
1543
|
-
rooms: raw.rooms.map((r) => ({
|
|
1544
|
-
roomId: r.room_id,
|
|
1545
|
-
name: r.name,
|
|
1546
|
-
roomType: r.room_type ?? null,
|
|
1547
|
-
unreadCount: r.unread_count,
|
|
1548
|
-
lastMessageId: r.last_message_id,
|
|
1549
|
-
lastMessagePreview: r.last_message_preview ?? null,
|
|
1550
|
-
lastReadMessageId: r.last_read_message_id,
|
|
1551
|
-
memberCount: r.member_count ?? 0,
|
|
1552
|
-
members: (r.members_preview ?? []).map((m) => ({
|
|
1553
|
-
userId: m.user_id,
|
|
1554
|
-
nickname: m.nickname ?? null,
|
|
1555
|
-
profileImageUrl: m.profile_image_url ?? null,
|
|
1556
|
-
isOnline: m.is_online ?? null
|
|
1557
|
-
}))
|
|
1558
|
-
}))
|
|
1559
|
-
};
|
|
1787
|
+
return parseUnreadSummary(raw);
|
|
1560
1788
|
}
|
|
1561
1789
|
/**
|
|
1562
1790
|
* Accept an invite link — call this in flows where you receive a
|
|
@@ -1574,12 +1802,11 @@ var NoveraChat = class {
|
|
|
1574
1802
|
* kicked from the target room.
|
|
1575
1803
|
*/
|
|
1576
1804
|
async acceptInvite(token) {
|
|
1577
|
-
const raw = await this.http.request(
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
};
|
|
1805
|
+
const raw = await this.http.request(
|
|
1806
|
+
"POST",
|
|
1807
|
+
`/api/v1/invites/${encodeURIComponent(token)}/accept`
|
|
1808
|
+
);
|
|
1809
|
+
return parseInviteAcceptResponse(raw);
|
|
1583
1810
|
}
|
|
1584
1811
|
// ---- internal ----
|
|
1585
1812
|
dispatch(msg) {
|