@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.js
CHANGED
|
@@ -1,3 +1,221 @@
|
|
|
1
|
+
// src/generated/rest.ts
|
|
2
|
+
function parseAppPolicy(j) {
|
|
3
|
+
return {
|
|
4
|
+
appId: j["app_id"],
|
|
5
|
+
allowMemberBulkDelete: j["allow_member_bulk_delete"],
|
|
6
|
+
readReceiptsEnabled: j["read_receipts_enabled"],
|
|
7
|
+
typingIndicatorsEnabled: j["typing_indicators_enabled"],
|
|
8
|
+
allowMemberDeleteAnyMessage: j["allow_member_delete_any_message"]
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
function parseUnreadSummary(j) {
|
|
12
|
+
return {
|
|
13
|
+
total: j["total"],
|
|
14
|
+
rooms: j["rooms"].map((e) => parseUnreadRoomItem(e))
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function parseUnreadRoomItem(j) {
|
|
18
|
+
return {
|
|
19
|
+
roomId: j["room_id"],
|
|
20
|
+
name: j["name"],
|
|
21
|
+
roomType: j["room_type"],
|
|
22
|
+
unreadCount: j["unread_count"],
|
|
23
|
+
lastMessageId: j["last_message_id"],
|
|
24
|
+
lastReadMessageId: j["last_read_message_id"],
|
|
25
|
+
lastMessagePreview: j["last_message_preview"],
|
|
26
|
+
memberCount: j["member_count"],
|
|
27
|
+
membersPreview: j["members_preview"] == null ? void 0 : j["members_preview"].map((e) => parseMemberPreview(e))
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function parseMemberPreview(j) {
|
|
31
|
+
return {
|
|
32
|
+
userId: j["user_id"],
|
|
33
|
+
nickname: j["nickname"],
|
|
34
|
+
profileImageUrl: j["profile_image_url"],
|
|
35
|
+
isOnline: j["is_online"]
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function parseRoomUnread(j) {
|
|
39
|
+
return {
|
|
40
|
+
roomId: j["room_id"],
|
|
41
|
+
unreadCount: j["unread_count"],
|
|
42
|
+
lastReadMessageId: j["last_read_message_id"],
|
|
43
|
+
lastMessageId: j["last_message_id"]
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function parseMessageOut(j) {
|
|
47
|
+
return {
|
|
48
|
+
appId: j["app_id"],
|
|
49
|
+
roomId: j["room_id"],
|
|
50
|
+
messageId: j["message_id"],
|
|
51
|
+
sender: j["sender"] == null ? null : parseSenderMini(j["sender"]),
|
|
52
|
+
senderId: j["sender_id"],
|
|
53
|
+
messageType: j["message_type"],
|
|
54
|
+
customType: j["custom_type"],
|
|
55
|
+
content: j["content"],
|
|
56
|
+
data: j["data"],
|
|
57
|
+
meta: j["meta"],
|
|
58
|
+
fileId: j["file_id"],
|
|
59
|
+
file: j["file"],
|
|
60
|
+
files: j["files"],
|
|
61
|
+
replyToId: j["reply_to_id"],
|
|
62
|
+
reactions: j["reactions"] == null ? void 0 : j["reactions"].map((e) => parseReactionEntry(e)),
|
|
63
|
+
createdAt: j["created_at"],
|
|
64
|
+
updatedAt: j["updated_at"],
|
|
65
|
+
deletedAt: j["deleted_at"],
|
|
66
|
+
deletedByUserId: j["deleted_by_user_id"],
|
|
67
|
+
deleteScope: j["delete_scope"],
|
|
68
|
+
editedCount: j["edited_count"],
|
|
69
|
+
createdAtMs: j["created_at_ms"],
|
|
70
|
+
updatedAtMs: j["updated_at_ms"]
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function parseAnnouncementOut(j) {
|
|
74
|
+
return {
|
|
75
|
+
id: j["id"],
|
|
76
|
+
roomId: j["room_id"],
|
|
77
|
+
content: j["content"],
|
|
78
|
+
createdBy: j["created_by"],
|
|
79
|
+
createdAt: j["created_at"],
|
|
80
|
+
updatedAt: j["updated_at"]
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function parseInviteTokenOut(j) {
|
|
84
|
+
return {
|
|
85
|
+
token: j["token"],
|
|
86
|
+
roomId: j["room_id"],
|
|
87
|
+
createdBy: j["created_by"],
|
|
88
|
+
createdAt: j["created_at"],
|
|
89
|
+
expiresAt: j["expires_at"],
|
|
90
|
+
maxUses: j["max_uses"],
|
|
91
|
+
usedCount: j["used_count"],
|
|
92
|
+
revokedAt: j["revoked_at"]
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function parseInviteAcceptResponse(j) {
|
|
96
|
+
return {
|
|
97
|
+
roomId: j["room_id"],
|
|
98
|
+
roomName: j["room_name"],
|
|
99
|
+
wasAlreadyMember: j["was_already_member"]
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function parseDeviceOut(j) {
|
|
103
|
+
return {
|
|
104
|
+
deviceId: j["device_id"],
|
|
105
|
+
tokenType: j["token_type"],
|
|
106
|
+
tokenPreview: j["token_preview"],
|
|
107
|
+
os: j["os"],
|
|
108
|
+
appVersion: j["app_version"],
|
|
109
|
+
pushEnabled: j["push_enabled"],
|
|
110
|
+
lastActiveAt: j["last_active_at"],
|
|
111
|
+
createdAt: j["created_at"]
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function parseDeviceListResponse(j) {
|
|
115
|
+
return {
|
|
116
|
+
items: j["items"].map((e) => parseDeviceOut(e)),
|
|
117
|
+
total: j["total"]
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
function parsePushPreferencesOut(j) {
|
|
121
|
+
return {
|
|
122
|
+
pushEnabled: j["push_enabled"]
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function parseFileOut(j) {
|
|
126
|
+
return {
|
|
127
|
+
id: j["id"],
|
|
128
|
+
kind: j["kind"],
|
|
129
|
+
mime: j["mime"],
|
|
130
|
+
sizeBytes: j["size_bytes"],
|
|
131
|
+
originalName: j["original_name"],
|
|
132
|
+
width: j["width"],
|
|
133
|
+
height: j["height"],
|
|
134
|
+
durationSec: j["duration_sec"],
|
|
135
|
+
thumbnailStatus: j["thumbnail_status"],
|
|
136
|
+
uploadStatus: j["upload_status"],
|
|
137
|
+
createdAt: j["created_at"],
|
|
138
|
+
committedAt: j["committed_at"],
|
|
139
|
+
forwardBlocked: j["forward_blocked"]
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function parseFileCreateResponse(j) {
|
|
143
|
+
return {
|
|
144
|
+
fileId: j["file_id"],
|
|
145
|
+
uploadUrl: j["upload_url"],
|
|
146
|
+
headers: j["headers"],
|
|
147
|
+
expiresAtMs: j["expires_at_ms"]
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
function parseBlockOut(j) {
|
|
151
|
+
return {
|
|
152
|
+
blockedUserId: j["blocked_user_id"],
|
|
153
|
+
reason: j["reason"],
|
|
154
|
+
createdAt: j["created_at"]
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function parseRoomMemberOut(j) {
|
|
158
|
+
return {
|
|
159
|
+
userId: j["user_id"],
|
|
160
|
+
nickname: j["nickname"],
|
|
161
|
+
role: j["role"],
|
|
162
|
+
pushTrigger: j["push_trigger"],
|
|
163
|
+
lastReadMessageId: j["last_read_message_id"],
|
|
164
|
+
joinedAt: j["joined_at"],
|
|
165
|
+
isOnline: j["is_online"],
|
|
166
|
+
mutedUntil: j["muted_until"]
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function parseBulkDeleteResult(j) {
|
|
170
|
+
return {
|
|
171
|
+
cleared: j["cleared"],
|
|
172
|
+
upToMessageId: j["up_to_message_id"]
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function parseBulkDeleteBySenderResult(j) {
|
|
176
|
+
return {
|
|
177
|
+
cleared: j["cleared"]
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function parseJoinRequestOut(j) {
|
|
181
|
+
return {
|
|
182
|
+
userId: j["user_id"],
|
|
183
|
+
status: j["status"],
|
|
184
|
+
reason: j["reason"],
|
|
185
|
+
requestedAt: j["requested_at"],
|
|
186
|
+
processedAt: j["processed_at"],
|
|
187
|
+
processedByUserId: j["processed_by_user_id"]
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function parseJoinRequestListResponse(j) {
|
|
191
|
+
return {
|
|
192
|
+
items: j["items"].map((e) => parseJoinRequestOut(e)),
|
|
193
|
+
total: j["total"],
|
|
194
|
+
limit: j["limit"],
|
|
195
|
+
offset: j["offset"]
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
function parseJoinRequestCreateResponse(j) {
|
|
199
|
+
return {
|
|
200
|
+
status: j["status"],
|
|
201
|
+
roomId: j["room_id"]
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function parseSenderMini(j) {
|
|
205
|
+
return {
|
|
206
|
+
userId: j["user_id"],
|
|
207
|
+
nickname: j["nickname"],
|
|
208
|
+
profileImageUrl: j["profile_image_url"]
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function parseReactionEntry(j) {
|
|
212
|
+
return {
|
|
213
|
+
key: j["key"],
|
|
214
|
+
userIds: j["user_ids"],
|
|
215
|
+
updatedAt: j["updated_at"]
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
1
219
|
// src/errors.ts
|
|
2
220
|
var ErrorCode = {
|
|
3
221
|
INTERNAL: "NC-GEN-001",
|
|
@@ -293,64 +511,31 @@ function tempId() {
|
|
|
293
511
|
}
|
|
294
512
|
|
|
295
513
|
// src/features/room.ts
|
|
296
|
-
function
|
|
297
|
-
|
|
514
|
+
function toFileInline(f) {
|
|
515
|
+
return {
|
|
516
|
+
id: f.id,
|
|
517
|
+
kind: f.kind,
|
|
518
|
+
mime: f.mime,
|
|
519
|
+
size: f.size,
|
|
520
|
+
name: f.name ?? null,
|
|
521
|
+
width: f.width ?? null,
|
|
522
|
+
height: f.height ?? null,
|
|
523
|
+
durationSec: f.duration_sec ?? null,
|
|
524
|
+
thumbnailStatus: f.thumbnail_status,
|
|
525
|
+
forwardBlocked: Boolean(f.forward_blocked)
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
function toMessageOut(j) {
|
|
529
|
+
const base = parseMessageOut(j);
|
|
530
|
+
const raw = j;
|
|
531
|
+
const dataObj = base.data ?? null;
|
|
298
532
|
const customMeta = dataObj && typeof dataObj === "object" && "_customMeta" in dataObj ? dataObj._customMeta : null;
|
|
299
533
|
return {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
nickname: r.sender.nickname ?? null,
|
|
306
|
-
profileImageUrl: r.sender.profile_image_url ?? null
|
|
307
|
-
} : null,
|
|
308
|
-
senderId: r.sender_id ?? null,
|
|
309
|
-
messageType: r.message_type,
|
|
310
|
-
customType: r.custom_type ?? null,
|
|
311
|
-
content: r.content ?? null,
|
|
312
|
-
data: dataObj,
|
|
313
|
-
meta: r.meta ?? null,
|
|
314
|
-
customMeta,
|
|
315
|
-
fileId: r.file_id ?? null,
|
|
316
|
-
file: r.file ? {
|
|
317
|
-
id: r.file.id,
|
|
318
|
-
kind: r.file.kind,
|
|
319
|
-
mime: r.file.mime,
|
|
320
|
-
size: r.file.size,
|
|
321
|
-
name: r.file.name ?? null,
|
|
322
|
-
width: r.file.width ?? null,
|
|
323
|
-
height: r.file.height ?? null,
|
|
324
|
-
durationSec: r.file.duration_sec ?? null,
|
|
325
|
-
thumbnailStatus: r.file.thumbnail_status,
|
|
326
|
-
forwardBlocked: Boolean(r.file.forward_blocked)
|
|
327
|
-
} : null,
|
|
328
|
-
files: r.files && r.files.length > 0 ? r.files.map((f) => ({
|
|
329
|
-
id: f.id,
|
|
330
|
-
kind: f.kind,
|
|
331
|
-
mime: f.mime,
|
|
332
|
-
size: f.size,
|
|
333
|
-
name: f.name ?? null,
|
|
334
|
-
width: f.width ?? null,
|
|
335
|
-
height: f.height ?? null,
|
|
336
|
-
durationSec: f.duration_sec ?? null,
|
|
337
|
-
thumbnailStatus: f.thumbnail_status,
|
|
338
|
-
forwardBlocked: Boolean(f.forward_blocked)
|
|
339
|
-
})) : null,
|
|
340
|
-
replyToId: r.reply_to_id ?? null,
|
|
341
|
-
reactions: (r.reactions ?? []).map((x) => ({
|
|
342
|
-
key: x.key,
|
|
343
|
-
userIds: x.user_ids,
|
|
344
|
-
updatedAt: x.updated_at
|
|
345
|
-
})),
|
|
346
|
-
createdAt: r.created_at,
|
|
347
|
-
createdAtMs: r.created_at_ms,
|
|
348
|
-
updatedAt: r.updated_at ?? null,
|
|
349
|
-
updatedAtMs: r.updated_at_ms ?? null,
|
|
350
|
-
deletedAt: r.deleted_at ?? null,
|
|
351
|
-
deletedByUserId: r.deleted_by_user_id ?? null,
|
|
352
|
-
deleteScope: r.delete_scope ?? null,
|
|
353
|
-
editedCount: r.edited_count ?? 0
|
|
534
|
+
...base,
|
|
535
|
+
file: raw.file ? toFileInline(raw.file) : null,
|
|
536
|
+
files: raw.files && raw.files.length > 0 ? raw.files.map(toFileInline) : null,
|
|
537
|
+
reactions: base.reactions ?? [],
|
|
538
|
+
customMeta
|
|
354
539
|
};
|
|
355
540
|
}
|
|
356
541
|
var Room = class _Room {
|
|
@@ -536,20 +721,24 @@ var Room = class _Room {
|
|
|
536
721
|
async sendFile(file, opts = {}) {
|
|
537
722
|
const kind = opts.kind ?? (file.type.startsWith("image/") ? "image" : file.type.startsWith("video/") ? "video" : "file");
|
|
538
723
|
const name = opts.name ?? file.name;
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
724
|
+
const created = parseFileCreateResponse(
|
|
725
|
+
await this.http.request("POST", "/api/v1/files", {
|
|
726
|
+
kind,
|
|
727
|
+
mime: file.type || "application/octet-stream",
|
|
728
|
+
size: file.size,
|
|
729
|
+
original_name: name,
|
|
730
|
+
// 파일 전달 제한 — uploader-set at presign time. When true,
|
|
731
|
+
// other users can't forward this file to a different room.
|
|
732
|
+
forward_blocked: Boolean(opts.forwardBlocked)
|
|
733
|
+
})
|
|
734
|
+
);
|
|
548
735
|
const presigned = {
|
|
549
|
-
fileId:
|
|
550
|
-
uploadUrl:
|
|
551
|
-
headers
|
|
552
|
-
|
|
736
|
+
fileId: created.fileId,
|
|
737
|
+
uploadUrl: created.uploadUrl,
|
|
738
|
+
// Presigned S3 headers are always string-valued; the generated
|
|
739
|
+
// model types the JSON object loosely as Record<string, unknown>.
|
|
740
|
+
headers: created.headers,
|
|
741
|
+
expiresAtMs: created.expiresAtMs
|
|
553
742
|
};
|
|
554
743
|
await new Promise((resolve, reject) => {
|
|
555
744
|
const xhr = new XMLHttpRequest();
|
|
@@ -604,17 +793,19 @@ var Room = class _Room {
|
|
|
604
793
|
async uploadFileOnly(file, opts = {}) {
|
|
605
794
|
const kind = opts.kind ?? (file.type.startsWith("image/") ? "image" : file.type.startsWith("video/") ? "video" : "file");
|
|
606
795
|
const name = opts.name ?? file.name;
|
|
607
|
-
const
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
796
|
+
const created = parseFileCreateResponse(
|
|
797
|
+
await this.http.request("POST", "/api/v1/files", {
|
|
798
|
+
kind,
|
|
799
|
+
mime: file.type || "application/octet-stream",
|
|
800
|
+
size: file.size,
|
|
801
|
+
original_name: name,
|
|
802
|
+
forward_blocked: Boolean(opts.forwardBlocked)
|
|
803
|
+
})
|
|
804
|
+
);
|
|
614
805
|
await new Promise((resolve, reject) => {
|
|
615
806
|
const xhr = new XMLHttpRequest();
|
|
616
|
-
xhr.open("PUT",
|
|
617
|
-
for (const [k, v] of Object.entries(
|
|
807
|
+
xhr.open("PUT", created.uploadUrl);
|
|
808
|
+
for (const [k, v] of Object.entries(created.headers)) {
|
|
618
809
|
xhr.setRequestHeader(k, v);
|
|
619
810
|
}
|
|
620
811
|
if (opts.onProgress) {
|
|
@@ -641,10 +832,10 @@ var Room = class _Room {
|
|
|
641
832
|
});
|
|
642
833
|
await this.http.request(
|
|
643
834
|
"POST",
|
|
644
|
-
`/api/v1/files/${
|
|
835
|
+
`/api/v1/files/${created.fileId}/commit`,
|
|
645
836
|
{}
|
|
646
837
|
);
|
|
647
|
-
return
|
|
838
|
+
return created.fileId;
|
|
648
839
|
}
|
|
649
840
|
/**
|
|
650
841
|
* List files shared in this room — newest-first, paginated.
|
|
@@ -669,23 +860,26 @@ var Room = class _Room {
|
|
|
669
860
|
}
|
|
670
861
|
);
|
|
671
862
|
return {
|
|
672
|
-
items: raw.items.map((r) =>
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
863
|
+
items: raw.items.map((r) => {
|
|
864
|
+
const f = parseFileOut(r.file);
|
|
865
|
+
return {
|
|
866
|
+
messageId: r.message_id,
|
|
867
|
+
senderId: r.sender_id,
|
|
868
|
+
createdAtMs: r.created_at_ms,
|
|
869
|
+
file: {
|
|
870
|
+
id: f.id,
|
|
871
|
+
kind: f.kind,
|
|
872
|
+
mime: f.mime,
|
|
873
|
+
size: f.sizeBytes,
|
|
874
|
+
name: f.originalName ?? null,
|
|
875
|
+
width: f.width ?? null,
|
|
876
|
+
height: f.height ?? null,
|
|
877
|
+
durationSec: f.durationSec ?? null,
|
|
878
|
+
thumbnailStatus: f.thumbnailStatus,
|
|
879
|
+
forwardBlocked: Boolean(f.forwardBlocked)
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
}),
|
|
689
883
|
nextCursor: raw.next_cursor,
|
|
690
884
|
hasMore: raw.has_more
|
|
691
885
|
};
|
|
@@ -723,11 +917,12 @@ var Room = class _Room {
|
|
|
723
917
|
this.ws.send({ type: "typing", room_id: this.roomId, is_typing: isTyping });
|
|
724
918
|
}
|
|
725
919
|
async edit(messageId, patch) {
|
|
726
|
-
|
|
920
|
+
const raw = await this.http.request(
|
|
727
921
|
"PUT",
|
|
728
922
|
`/api/v1/rooms/${this.roomId}/messages/${messageId}`,
|
|
729
923
|
patch
|
|
730
924
|
);
|
|
925
|
+
return toMessageOut(raw);
|
|
731
926
|
}
|
|
732
927
|
async delete(messageId, scope = "ALL") {
|
|
733
928
|
await this.http.request(
|
|
@@ -752,12 +947,39 @@ var Room = class _Room {
|
|
|
752
947
|
* Other members get a `MEMBER_LEFT` [`roomEvent`](../reference/noverachat.md#events).
|
|
753
948
|
* Idempotent if already a non-member; throws 409 if the caller was KICKED
|
|
754
949
|
* (ask an operator to restore first). For GROUP/SUPER_GROUP, re-entry needs
|
|
755
|
-
* an operator add; for ONE, re-creating the 1:1 rejoins with history kept.
|
|
756
|
-
|
|
950
|
+
* an operator add; for ONE, re-creating the 1:1 rejoins with history kept.
|
|
951
|
+
*
|
|
952
|
+
* Optional `deleteMessages` — atomically clean up chat history as part
|
|
953
|
+
* of leaving. Default is `"none"` (messages survive so the room's
|
|
954
|
+
* history keeps making sense to remaining members).
|
|
955
|
+
* - `"none"` — leave only.
|
|
956
|
+
* - `"mine"` — soft-delete every message YOU sent, then leave. Other
|
|
957
|
+
* members' messages are untouched. Broadcasts
|
|
958
|
+
* `messages_cleared_by_sender` so connected clients hide
|
|
959
|
+
* them locally.
|
|
960
|
+
* - `"all"` — wipe every message in the room, then leave. Subject to
|
|
961
|
+
* the app's `allow_member_bulk_delete` gate and per-room
|
|
962
|
+
* OPERATOR check — a plain member gets 403 back. */
|
|
963
|
+
async leave(options) {
|
|
964
|
+
const mode = options?.deleteMessages ?? "none";
|
|
965
|
+
const query = mode === "none" ? "" : `?delete_messages=${encodeURIComponent(mode)}`;
|
|
757
966
|
await this.http.request(
|
|
758
967
|
"POST",
|
|
759
|
-
`/api/v1/rooms/${this.roomId}/leave`
|
|
968
|
+
`/api/v1/rooms/${this.roomId}/leave${query}`
|
|
969
|
+
);
|
|
970
|
+
}
|
|
971
|
+
/** Delete every message YOU sent in this room. Standalone version of
|
|
972
|
+
* `leave({ deleteMessages: "mine" })` — useful when a user wants to
|
|
973
|
+
* scrub their history without actually leaving. Other members'
|
|
974
|
+
* messages are untouched; broadcasts `messages_cleared_by_sender`
|
|
975
|
+
* so connected clients hide the vanished ones locally.
|
|
976
|
+
* No OPERATOR gate — you own your messages. */
|
|
977
|
+
async deleteMyMessages() {
|
|
978
|
+
const raw = await this.http.request(
|
|
979
|
+
"DELETE",
|
|
980
|
+
`/api/v1/rooms/${this.roomId}/messages/mine`
|
|
760
981
|
);
|
|
982
|
+
return parseBulkDeleteBySenderResult(raw);
|
|
761
983
|
}
|
|
762
984
|
/**
|
|
763
985
|
* Set the caller's per-room push preference — like long-pressing a
|
|
@@ -783,11 +1005,11 @@ var Room = class _Room {
|
|
|
783
1005
|
* hide rendered messages up to (and including) that id.
|
|
784
1006
|
*/
|
|
785
1007
|
async deleteAllMessages() {
|
|
786
|
-
const raw = await this.http.request(
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
1008
|
+
const raw = await this.http.request(
|
|
1009
|
+
"DELETE",
|
|
1010
|
+
`/api/v1/rooms/${this.roomId}/messages`
|
|
1011
|
+
);
|
|
1012
|
+
return parseBulkDeleteResult(raw);
|
|
791
1013
|
}
|
|
792
1014
|
async react(messageId, key) {
|
|
793
1015
|
await this.http.request(
|
|
@@ -874,17 +1096,11 @@ var Room = class _Room {
|
|
|
874
1096
|
* rooms, so the caller must already be a member to read this.
|
|
875
1097
|
*/
|
|
876
1098
|
async listMembers() {
|
|
877
|
-
const raw = await this.http.request(
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
pushTrigger: m.push_trigger,
|
|
883
|
-
lastReadMessageId: m.last_read_message_id,
|
|
884
|
-
joinedAt: m.joined_at,
|
|
885
|
-
isOnline: m.is_online ?? null,
|
|
886
|
-
mutedUntil: m.muted_until ?? null
|
|
887
|
-
}));
|
|
1099
|
+
const raw = await this.http.request(
|
|
1100
|
+
"GET",
|
|
1101
|
+
`/api/v1/rooms/${this.roomId}/members`
|
|
1102
|
+
);
|
|
1103
|
+
return raw.map((m) => parseRoomMemberOut(m));
|
|
888
1104
|
}
|
|
889
1105
|
// -------- mute --------
|
|
890
1106
|
/** OPERATOR-only: mute a member so they can't send messages in this
|
|
@@ -912,13 +1128,11 @@ var Room = class _Room {
|
|
|
912
1128
|
* newer than the caller's `read_watermark`, plus the room's
|
|
913
1129
|
* `last_message_id` so the UI can show a "•" dot regardless of count. */
|
|
914
1130
|
async unread() {
|
|
915
|
-
const raw = await this.http.request(
|
|
916
|
-
|
|
917
|
-
roomId
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
lastMessageId: raw.last_message_id
|
|
921
|
-
};
|
|
1131
|
+
const raw = await this.http.request(
|
|
1132
|
+
"GET",
|
|
1133
|
+
`/api/v1/rooms/${this.roomId}/unread`
|
|
1134
|
+
);
|
|
1135
|
+
return parseRoomUnread(raw);
|
|
922
1136
|
}
|
|
923
1137
|
// -------- freeze --------
|
|
924
1138
|
/** OPERATOR-only moderation: freeze this room. Non-admin messages are
|
|
@@ -953,49 +1167,35 @@ var Room = class _Room {
|
|
|
953
1167
|
}
|
|
954
1168
|
// -------- announcements --------
|
|
955
1169
|
async listAnnouncements() {
|
|
956
|
-
const raw = await this.http.request(
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
createdBy: a.created_by,
|
|
962
|
-
createdAt: a.created_at,
|
|
963
|
-
updatedAt: a.updated_at
|
|
964
|
-
}));
|
|
1170
|
+
const raw = await this.http.request(
|
|
1171
|
+
"GET",
|
|
1172
|
+
`/api/v1/rooms/${this.roomId}/announcements`
|
|
1173
|
+
);
|
|
1174
|
+
return raw.map((a) => parseAnnouncementOut(a));
|
|
965
1175
|
}
|
|
966
1176
|
async getCurrentAnnouncement() {
|
|
967
|
-
const raw = await this.http.request(
|
|
1177
|
+
const raw = await this.http.request(
|
|
1178
|
+
"GET",
|
|
1179
|
+
`/api/v1/rooms/${this.roomId}/announcements/current`
|
|
1180
|
+
);
|
|
968
1181
|
if (!raw) return null;
|
|
969
|
-
return
|
|
970
|
-
id: raw.id,
|
|
971
|
-
roomId: raw.room_id,
|
|
972
|
-
content: raw.content,
|
|
973
|
-
createdBy: raw.created_by,
|
|
974
|
-
createdAt: raw.created_at,
|
|
975
|
-
updatedAt: raw.updated_at
|
|
976
|
-
};
|
|
1182
|
+
return parseAnnouncementOut(raw);
|
|
977
1183
|
}
|
|
978
1184
|
async postAnnouncement(content) {
|
|
979
|
-
const raw = await this.http.request(
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
createdAt: raw.created_at,
|
|
986
|
-
updatedAt: raw.updated_at
|
|
987
|
-
};
|
|
1185
|
+
const raw = await this.http.request(
|
|
1186
|
+
"POST",
|
|
1187
|
+
`/api/v1/rooms/${this.roomId}/announcements`,
|
|
1188
|
+
{ content }
|
|
1189
|
+
);
|
|
1190
|
+
return parseAnnouncementOut(raw);
|
|
988
1191
|
}
|
|
989
1192
|
async updateAnnouncement(id, content) {
|
|
990
|
-
const raw = await this.http.request(
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
createdAt: raw.created_at,
|
|
997
|
-
updatedAt: raw.updated_at
|
|
998
|
-
};
|
|
1193
|
+
const raw = await this.http.request(
|
|
1194
|
+
"PATCH",
|
|
1195
|
+
`/api/v1/rooms/${this.roomId}/announcements/${id}`,
|
|
1196
|
+
{ content }
|
|
1197
|
+
);
|
|
1198
|
+
return parseAnnouncementOut(raw);
|
|
999
1199
|
}
|
|
1000
1200
|
async deleteAnnouncement(id) {
|
|
1001
1201
|
await this.http.request(
|
|
@@ -1017,32 +1217,21 @@ var Room = class _Room {
|
|
|
1017
1217
|
const body = {};
|
|
1018
1218
|
if (opts && "expiresInHours" in opts) body.expires_in_hours = opts.expiresInHours;
|
|
1019
1219
|
if (opts && "maxUses" in opts) body.max_uses = opts.maxUses;
|
|
1020
|
-
const raw = await this.http.request(
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
expiresAt: raw.expires_at,
|
|
1027
|
-
maxUses: raw.max_uses,
|
|
1028
|
-
usedCount: raw.used_count,
|
|
1029
|
-
revokedAt: raw.revoked_at
|
|
1030
|
-
};
|
|
1220
|
+
const raw = await this.http.request(
|
|
1221
|
+
"POST",
|
|
1222
|
+
`/api/v1/rooms/${this.roomId}/invites`,
|
|
1223
|
+
body
|
|
1224
|
+
);
|
|
1225
|
+
return parseInviteTokenOut(raw);
|
|
1031
1226
|
}
|
|
1032
1227
|
/** OPERATOR-only: list all invite tokens for this room (including
|
|
1033
1228
|
* revoked / expired / exhausted), newest first. */
|
|
1034
1229
|
async listInvites() {
|
|
1035
|
-
const raw = await this.http.request(
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
createdAt: r.created_at,
|
|
1041
|
-
expiresAt: r.expires_at,
|
|
1042
|
-
maxUses: r.max_uses,
|
|
1043
|
-
usedCount: r.used_count,
|
|
1044
|
-
revokedAt: r.revoked_at
|
|
1045
|
-
}));
|
|
1230
|
+
const raw = await this.http.request(
|
|
1231
|
+
"GET",
|
|
1232
|
+
`/api/v1/rooms/${this.roomId}/invites`
|
|
1233
|
+
);
|
|
1234
|
+
return raw.map((r) => parseInviteTokenOut(r));
|
|
1046
1235
|
}
|
|
1047
1236
|
/** OPERATOR-only: revoke a token immediately. Idempotent — revoking
|
|
1048
1237
|
* an already-revoked token returns 204 as well. */
|
|
@@ -1052,6 +1241,81 @@ var Room = class _Room {
|
|
|
1052
1241
|
`/api/v1/rooms/${this.roomId}/invites/${encodeURIComponent(token)}`
|
|
1053
1242
|
);
|
|
1054
1243
|
}
|
|
1244
|
+
// -------- join requests --------
|
|
1245
|
+
/**
|
|
1246
|
+
* Ask to join this room (self). Behavior depends on the room's
|
|
1247
|
+
* `joinPolicy`:
|
|
1248
|
+
* - `OPEN` → the caller is added immediately (`status: "joined"`);
|
|
1249
|
+
* - `APPROVAL_REQUIRED` → the request lands in the operators' inbox
|
|
1250
|
+
* (`status: "pending"`) until [`approveJoinRequest`] / reject.
|
|
1251
|
+
* Idempotent for members (`status: "already_member"`).
|
|
1252
|
+
*
|
|
1253
|
+
* @param reason Optional note shown to operators ("하은이 반 친구 엄마예요").
|
|
1254
|
+
*/
|
|
1255
|
+
async requestJoin(reason) {
|
|
1256
|
+
const raw = await this.http.request(
|
|
1257
|
+
"POST",
|
|
1258
|
+
`/api/v1/rooms/${this.roomId}/join-request`,
|
|
1259
|
+
reason ? { reason } : {}
|
|
1260
|
+
);
|
|
1261
|
+
return parseJoinRequestCreateResponse(raw);
|
|
1262
|
+
}
|
|
1263
|
+
/** Cancel the caller's own pending join request. */
|
|
1264
|
+
async cancelJoinRequest() {
|
|
1265
|
+
await this.http.request(
|
|
1266
|
+
"DELETE",
|
|
1267
|
+
`/api/v1/rooms/${this.roomId}/join-request`
|
|
1268
|
+
);
|
|
1269
|
+
}
|
|
1270
|
+
/** OPERATOR-only: list join requests for this room (the approval inbox).
|
|
1271
|
+
* Defaults to pending only — pass a status to see processed history. */
|
|
1272
|
+
async listJoinRequests(opts) {
|
|
1273
|
+
const raw = await this.http.request(
|
|
1274
|
+
"GET",
|
|
1275
|
+
`/api/v1/rooms/${this.roomId}/join-requests`,
|
|
1276
|
+
void 0,
|
|
1277
|
+
{
|
|
1278
|
+
...opts?.status ? { status: opts.status } : {},
|
|
1279
|
+
...opts?.limit !== void 0 ? { limit: opts.limit } : {},
|
|
1280
|
+
...opts?.offset !== void 0 ? { offset: opts.offset } : {}
|
|
1281
|
+
}
|
|
1282
|
+
);
|
|
1283
|
+
return parseJoinRequestListResponse(raw);
|
|
1284
|
+
}
|
|
1285
|
+
/** OPERATOR-only: approve a pending join request — the requester becomes
|
|
1286
|
+
* a MEMBER. [reason] is echoed back to them in later list calls. */
|
|
1287
|
+
async approveJoinRequest(userId, reason) {
|
|
1288
|
+
await this.http.request(
|
|
1289
|
+
"POST",
|
|
1290
|
+
`/api/v1/rooms/${this.roomId}/join-requests/${encodeURIComponent(userId)}/approve`,
|
|
1291
|
+
reason ? { reason } : {}
|
|
1292
|
+
);
|
|
1293
|
+
}
|
|
1294
|
+
/** OPERATOR-only: reject a pending join request. */
|
|
1295
|
+
async rejectJoinRequest(userId, reason) {
|
|
1296
|
+
await this.http.request(
|
|
1297
|
+
"POST",
|
|
1298
|
+
`/api/v1/rooms/${this.roomId}/join-requests/${encodeURIComponent(userId)}/reject`,
|
|
1299
|
+
reason ? { reason } : {}
|
|
1300
|
+
);
|
|
1301
|
+
}
|
|
1302
|
+
// -------- hide / unhide --------
|
|
1303
|
+
/** Hide this room from the caller's own room list (self, reversible —
|
|
1304
|
+
* the KakaoTalk "숨기기"). The room typically reappears via [unhide]
|
|
1305
|
+
* or on new activity, per server policy. Messages are untouched. */
|
|
1306
|
+
async hide() {
|
|
1307
|
+
await this.http.request(
|
|
1308
|
+
"POST",
|
|
1309
|
+
`/api/v1/rooms/${this.roomId}/hide`
|
|
1310
|
+
);
|
|
1311
|
+
}
|
|
1312
|
+
/** Undo [hide] — the room shows up in the caller's list again. */
|
|
1313
|
+
async unhide() {
|
|
1314
|
+
await this.http.request(
|
|
1315
|
+
"POST",
|
|
1316
|
+
`/api/v1/rooms/${this.roomId}/unhide`
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1055
1319
|
// -------- internal dispatch --------
|
|
1056
1320
|
on(event, fn) {
|
|
1057
1321
|
return this.bus.on(event, fn);
|
|
@@ -1300,13 +1564,7 @@ var NoveraChat = class {
|
|
|
1300
1564
|
* destructive buttons to expose. */
|
|
1301
1565
|
async appPolicy() {
|
|
1302
1566
|
const raw = await this.http.request("GET", "/api/v1/app/policy");
|
|
1303
|
-
return
|
|
1304
|
-
appId: raw.app_id,
|
|
1305
|
-
allowMemberBulkDelete: raw.allow_member_bulk_delete,
|
|
1306
|
-
readReceiptsEnabled: raw.read_receipts_enabled ?? true,
|
|
1307
|
-
typingIndicatorsEnabled: raw.typing_indicators_enabled ?? true,
|
|
1308
|
-
deleteAnyMessageEnabled: raw.allow_member_delete_any_message ?? false
|
|
1309
|
-
};
|
|
1567
|
+
return parseAppPolicy(raw);
|
|
1310
1568
|
}
|
|
1311
1569
|
// ---- device tokens (push) ----
|
|
1312
1570
|
/**
|
|
@@ -1316,19 +1574,23 @@ var NoveraChat = class {
|
|
|
1316
1574
|
* safe. Returns the stored device record (token shown only as a prefix).
|
|
1317
1575
|
*/
|
|
1318
1576
|
async registerDevice(params) {
|
|
1319
|
-
const raw = await this.http.request(
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1577
|
+
const raw = await this.http.request(
|
|
1578
|
+
"POST",
|
|
1579
|
+
"/api/v1/users/me/devices",
|
|
1580
|
+
{
|
|
1581
|
+
device_id: params.deviceId,
|
|
1582
|
+
token: params.token,
|
|
1583
|
+
token_type: params.tokenType,
|
|
1584
|
+
os: params.os ?? null,
|
|
1585
|
+
app_version: params.appVersion ?? null
|
|
1586
|
+
}
|
|
1587
|
+
);
|
|
1588
|
+
return parseDeviceOut(raw);
|
|
1327
1589
|
}
|
|
1328
1590
|
/** List the devices registered for the authenticated user. */
|
|
1329
1591
|
async listDevices() {
|
|
1330
1592
|
const raw = await this.http.request("GET", "/api/v1/users/me/devices");
|
|
1331
|
-
return raw.items
|
|
1593
|
+
return parseDeviceListResponse(raw).items;
|
|
1332
1594
|
}
|
|
1333
1595
|
/**
|
|
1334
1596
|
* Turn push delivery on/off for one device without unregistering it —
|
|
@@ -1366,7 +1628,7 @@ var NoveraChat = class {
|
|
|
1366
1628
|
"GET",
|
|
1367
1629
|
"/api/v1/users/me/push-preferences"
|
|
1368
1630
|
);
|
|
1369
|
-
return
|
|
1631
|
+
return parsePushPreferencesOut(raw);
|
|
1370
1632
|
}
|
|
1371
1633
|
/**
|
|
1372
1634
|
* Flip the caller's global push kill switch. Pass `false` to mute
|
|
@@ -1379,7 +1641,7 @@ var NoveraChat = class {
|
|
|
1379
1641
|
"/api/v1/users/me/push-preferences",
|
|
1380
1642
|
{ push_enabled: enabled }
|
|
1381
1643
|
);
|
|
1382
|
-
return
|
|
1644
|
+
return parsePushPreferencesOut(raw);
|
|
1383
1645
|
}
|
|
1384
1646
|
// -------------------------------------------------------------------------
|
|
1385
1647
|
// User blocks — KakaoTalk-style one-way block.
|
|
@@ -1403,11 +1665,7 @@ var NoveraChat = class {
|
|
|
1403
1665
|
`/api/v1/users/me/blocks/${encodeURIComponent(userId)}`,
|
|
1404
1666
|
{ reason: reason ?? null }
|
|
1405
1667
|
);
|
|
1406
|
-
return
|
|
1407
|
-
blockedUserId: raw.blocked_user_id,
|
|
1408
|
-
reason: raw.reason,
|
|
1409
|
-
createdAt: raw.created_at
|
|
1410
|
-
};
|
|
1668
|
+
return parseBlockOut(raw);
|
|
1411
1669
|
}
|
|
1412
1670
|
/** Unblock a user. Idempotent — resolves either way. */
|
|
1413
1671
|
async unblockUser(userId) {
|
|
@@ -1419,23 +1677,7 @@ var NoveraChat = class {
|
|
|
1419
1677
|
/** List every user the caller has blocked, newest first. */
|
|
1420
1678
|
async listBlocks() {
|
|
1421
1679
|
const raw = await this.http.request("GET", "/api/v1/users/me/blocks");
|
|
1422
|
-
return raw.map((r) => (
|
|
1423
|
-
blockedUserId: r.blocked_user_id,
|
|
1424
|
-
reason: r.reason,
|
|
1425
|
-
createdAt: r.created_at
|
|
1426
|
-
}));
|
|
1427
|
-
}
|
|
1428
|
-
_toDeviceInfo(raw) {
|
|
1429
|
-
return {
|
|
1430
|
-
deviceId: raw.device_id,
|
|
1431
|
-
tokenType: raw.token_type,
|
|
1432
|
-
tokenPreview: raw.token_preview,
|
|
1433
|
-
os: raw.os,
|
|
1434
|
-
appVersion: raw.app_version,
|
|
1435
|
-
pushEnabled: raw.push_enabled,
|
|
1436
|
-
lastActiveAt: raw.last_active_at,
|
|
1437
|
-
createdAt: raw.created_at
|
|
1438
|
-
};
|
|
1680
|
+
return raw.map((r) => parseBlockOut(r));
|
|
1439
1681
|
}
|
|
1440
1682
|
/**
|
|
1441
1683
|
* Authorization-gated URL for downloading a file attachment.
|
|
@@ -1493,41 +1735,27 @@ var NoveraChat = class {
|
|
|
1493
1735
|
* shipping date.
|
|
1494
1736
|
*/
|
|
1495
1737
|
async getFileMeta(fileId) {
|
|
1496
|
-
const raw = await this.http.request(
|
|
1738
|
+
const raw = await this.http.request(
|
|
1739
|
+
"GET",
|
|
1740
|
+
`/api/v1/files/${encodeURIComponent(fileId)}/meta`
|
|
1741
|
+
);
|
|
1742
|
+
const f = parseFileOut(raw);
|
|
1497
1743
|
return {
|
|
1498
|
-
id:
|
|
1499
|
-
kind:
|
|
1500
|
-
mime:
|
|
1501
|
-
size:
|
|
1502
|
-
name:
|
|
1503
|
-
width:
|
|
1504
|
-
height:
|
|
1505
|
-
durationSec:
|
|
1506
|
-
thumbnailStatus:
|
|
1507
|
-
forwardBlocked: Boolean(
|
|
1744
|
+
id: f.id,
|
|
1745
|
+
kind: f.kind,
|
|
1746
|
+
mime: f.mime,
|
|
1747
|
+
size: f.sizeBytes,
|
|
1748
|
+
name: f.originalName ?? null,
|
|
1749
|
+
width: f.width ?? null,
|
|
1750
|
+
height: f.height ?? null,
|
|
1751
|
+
durationSec: f.durationSec ?? null,
|
|
1752
|
+
thumbnailStatus: f.thumbnailStatus,
|
|
1753
|
+
forwardBlocked: Boolean(f.forwardBlocked)
|
|
1508
1754
|
};
|
|
1509
1755
|
}
|
|
1510
1756
|
async unreadSummary() {
|
|
1511
1757
|
const raw = await this.http.request("GET", "/api/v1/users/me/unread");
|
|
1512
|
-
return
|
|
1513
|
-
total: raw.total,
|
|
1514
|
-
rooms: raw.rooms.map((r) => ({
|
|
1515
|
-
roomId: r.room_id,
|
|
1516
|
-
name: r.name,
|
|
1517
|
-
roomType: r.room_type ?? null,
|
|
1518
|
-
unreadCount: r.unread_count,
|
|
1519
|
-
lastMessageId: r.last_message_id,
|
|
1520
|
-
lastMessagePreview: r.last_message_preview ?? null,
|
|
1521
|
-
lastReadMessageId: r.last_read_message_id,
|
|
1522
|
-
memberCount: r.member_count ?? 0,
|
|
1523
|
-
members: (r.members_preview ?? []).map((m) => ({
|
|
1524
|
-
userId: m.user_id,
|
|
1525
|
-
nickname: m.nickname ?? null,
|
|
1526
|
-
profileImageUrl: m.profile_image_url ?? null,
|
|
1527
|
-
isOnline: m.is_online ?? null
|
|
1528
|
-
}))
|
|
1529
|
-
}))
|
|
1530
|
-
};
|
|
1758
|
+
return parseUnreadSummary(raw);
|
|
1531
1759
|
}
|
|
1532
1760
|
/**
|
|
1533
1761
|
* Accept an invite link — call this in flows where you receive a
|
|
@@ -1545,12 +1773,11 @@ var NoveraChat = class {
|
|
|
1545
1773
|
* kicked from the target room.
|
|
1546
1774
|
*/
|
|
1547
1775
|
async acceptInvite(token) {
|
|
1548
|
-
const raw = await this.http.request(
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
};
|
|
1776
|
+
const raw = await this.http.request(
|
|
1777
|
+
"POST",
|
|
1778
|
+
`/api/v1/invites/${encodeURIComponent(token)}/accept`
|
|
1779
|
+
);
|
|
1780
|
+
return parseInviteAcceptResponse(raw);
|
|
1554
1781
|
}
|
|
1555
1782
|
// ---- internal ----
|
|
1556
1783
|
dispatch(msg) {
|