@omelhorsite/sdk 0.3.0 → 0.4.1
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 +113 -131
- package/dist/index.js +1049 -1246
- package/dist/types/auth/device.d.ts +1 -1
- package/dist/types/auth/index.d.ts +2 -2
- package/dist/types/auth/tokens.d.ts +15 -15
- package/dist/types/client.d.ts +10 -10
- package/dist/types/errors.d.ts +12 -15
- package/dist/types/http.d.ts +74 -118
- package/dist/types/index.d.ts +2 -2
- package/dist/types/internal/attachments.d.ts +17 -0
- package/dist/types/internal/helpers.d.ts +9 -0
- package/dist/types/listing.d.ts +90 -0
- package/dist/types/local/qr.d.ts +1 -1
- package/dist/types/local/wordlist.d.ts +2 -3
- package/dist/types/resources/account.d.ts +19 -19
- package/dist/types/resources/admin/authorizedApplications.d.ts +133 -0
- package/dist/types/resources/admin/chests.d.ts +51 -0
- package/dist/types/resources/admin/eventAlerts.d.ts +60 -0
- package/dist/types/resources/admin/identities.d.ts +99 -0
- package/dist/types/resources/admin/index.d.ts +77 -0
- package/dist/types/resources/admin/jobs.d.ts +97 -0
- package/dist/types/resources/admin/myOauthApplications.d.ts +282 -0
- package/dist/types/resources/admin/notepads.d.ts +49 -0
- package/dist/types/resources/admin/oauthApplications.d.ts +309 -0
- package/dist/types/resources/admin/quotas.d.ts +114 -0
- package/dist/types/resources/admin/shortLinks.d.ts +201 -0
- package/dist/types/resources/admin/types.d.ts +222 -0
- package/dist/types/resources/admin/vocalSeparations.d.ts +98 -0
- package/dist/types/resources/admin.d.ts +17 -13
- package/dist/types/resources/auth/index.d.ts +1 -1
- package/dist/types/resources/auth/passkeys.d.ts +127 -163
- package/dist/types/resources/auth/sessions.d.ts +116 -161
- package/dist/types/resources/chests.d.ts +27 -31
- package/dist/types/resources/content/analysis.d.ts +62 -0
- package/dist/types/resources/content/blogs.d.ts +471 -0
- package/dist/types/resources/content/feedbacks.d.ts +263 -0
- package/dist/types/resources/content/index.d.ts +52 -0
- package/dist/types/resources/content/intel/articles.d.ts +230 -0
- package/dist/types/resources/content/intel/config.d.ts +135 -0
- package/dist/types/resources/content/intel/index.d.ts +53 -0
- package/dist/types/resources/content/intel/items.d.ts +91 -0
- package/dist/types/resources/content/intel/reports.d.ts +108 -0
- package/dist/types/resources/content/intel/scripts.d.ts +152 -0
- package/dist/types/resources/content/intel/sources.d.ts +236 -0
- package/dist/types/resources/content/intel/stats.d.ts +105 -0
- package/dist/types/resources/content/intel/types.d.ts +86 -0
- package/dist/types/resources/content/jokes.d.ts +93 -0
- package/dist/types/resources/content/notifications.d.ts +147 -0
- package/dist/types/resources/content/serviceUsages.d.ts +73 -0
- package/dist/types/resources/content/servicesStatus.d.ts +194 -0
- package/dist/types/resources/content/siteConfig.d.ts +43 -0
- package/dist/types/resources/content/spaceInvaders.d.ts +154 -0
- package/dist/types/resources/content.d.ts +41 -236
- package/dist/types/resources/dynamicQrs.d.ts +29 -45
- package/dist/types/resources/forms.d.ts +37 -58
- package/dist/types/resources/jobs.d.ts +35 -44
- package/dist/types/resources/library/annotations.d.ts +162 -0
- package/dist/types/resources/library/books.d.ts +534 -0
- package/dist/types/resources/library/chat.d.ts +259 -0
- package/dist/types/resources/library/index.d.ts +39 -0
- package/dist/types/resources/library/shelves.d.ts +237 -0
- package/dist/types/resources/library/types.d.ts +120 -0
- package/dist/types/resources/library.d.ts +20 -22
- package/dist/types/resources/media.d.ts +48 -61
- package/dist/types/resources/movies/addons.d.ts +388 -0
- package/dist/types/resources/movies/collections.d.ts +352 -0
- package/dist/types/resources/movies/index.d.ts +29 -0
- package/dist/types/resources/movies/types.d.ts +50 -0
- package/dist/types/resources/movies/watchProgress.d.ts +277 -0
- package/dist/types/resources/movies.d.ts +27 -53
- package/dist/types/resources/music/artists.d.ts +182 -246
- package/dist/types/resources/music/imports.d.ts +190 -218
- package/dist/types/resources/music/index.d.ts +8 -7
- package/dist/types/resources/music/playlists.d.ts +85 -117
- package/dist/types/resources/music/social.d.ts +153 -228
- package/dist/types/resources/music/songs.d.ts +165 -227
- package/dist/types/resources/realtime.d.ts +75 -88
- package/dist/types/resources/shortLinks.d.ts +38 -47
- package/dist/types/resources/social/groupChats.d.ts +458 -0
- package/dist/types/resources/social/index.d.ts +19 -0
- package/dist/types/resources/social/messages.d.ts +424 -0
- package/dist/types/resources/social/relationships.d.ts +322 -0
- package/dist/types/resources/social/types.d.ts +37 -0
- package/dist/types/resources/social.d.ts +21 -3
- package/dist/types/resources/storage/upload.d.ts +42 -56
- package/dist/types/resources/storage.d.ts +94 -109
- package/dist/types/resources/tickets.d.ts +19 -10
- package/dist/types/resources/tools/backgroundRemoval.d.ts +11 -13
- package/dist/types/resources/tools/captions.d.ts +107 -135
- package/dist/types/resources/tools/upscale.d.ts +12 -16
- package/dist/types/types.d.ts +29 -38
- package/package.json +1 -1
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
/** Direct messages: the record, the listing and `oms.social.messages`. */
|
|
2
|
+
import { Resource } from "../../http";
|
|
3
|
+
import type { ListParams } from "../../listing";
|
|
4
|
+
import type { User } from "../account";
|
|
5
|
+
import type { FileInput, Id, NativeFile, Paginated, RequestOptions, Timestamp } from "../../types";
|
|
6
|
+
import type { AttachmentKind } from "./types";
|
|
7
|
+
/**
|
|
8
|
+
* Primary key of a direct message. An INTEGER: `messages` is one of the tables
|
|
9
|
+
* that kept an auto-increment key, so `id` and `quoted_message_id` are JSON
|
|
10
|
+
* numbers while `sender_id` and `receiver_id` beside them are strings.
|
|
11
|
+
*/
|
|
12
|
+
export type MessageId = number;
|
|
13
|
+
/** `content` of a direct message is capped at 1024 characters. Longer is a `400`. */
|
|
14
|
+
export declare const MESSAGE_CONTENT_MAX_LENGTH = 1024;
|
|
15
|
+
/**
|
|
16
|
+
* The ceiling that actually applies to an attachment the server decides is an
|
|
17
|
+
* image: 20 MiB.
|
|
18
|
+
*
|
|
19
|
+
* The two caps disagree and the gap is not harmless. The server checks 25
|
|
20
|
+
* MiB, then hands anything whose `content_type` starts with `image/` to the
|
|
21
|
+
* webp recompressor, which checks 20 MiB and fails with an error that is not
|
|
22
|
+
* turned into a `400`. So a 22 MiB JPEG is a **500** with a Discord error
|
|
23
|
+
* alert behind it, where a 22 MiB zip is accepted.
|
|
24
|
+
*
|
|
25
|
+
* {@link DirectMessagesNamespace.send} refuses that band client-side when the
|
|
26
|
+
* size is knowable, which is the only place the mistake is still cheap.
|
|
27
|
+
*/
|
|
28
|
+
export declare const MESSAGE_IMAGE_MAX_BYTES: number;
|
|
29
|
+
/**
|
|
30
|
+
* Pixel ceilings the server applies to an image attachment before any
|
|
31
|
+
* full-frame decode: 12000px on either side, and 50 megapixels of area. Past
|
|
32
|
+
* either one the answer is `400` with the dimensions in the body, and
|
|
33
|
+
* deliberately no error alert.
|
|
34
|
+
*
|
|
35
|
+
* Not checkable here - the SDK does not decode images - so this is
|
|
36
|
+
* documentation, not a guard.
|
|
37
|
+
*/
|
|
38
|
+
export declare const MESSAGE_IMAGE_MAX_DIMENSION_PX = 12000;
|
|
39
|
+
/** @see MESSAGE_IMAGE_MAX_DIMENSION_PX */
|
|
40
|
+
export declare const MESSAGE_IMAGE_MAX_PIXELS = 50000000;
|
|
41
|
+
/** Preview of the message a message quotes. Truncated to 120 characters. */
|
|
42
|
+
export interface QuotedMessagePreview {
|
|
43
|
+
readonly id: MessageId;
|
|
44
|
+
readonly sender_id: Id;
|
|
45
|
+
readonly content: string;
|
|
46
|
+
readonly attachment_kind: AttachmentKind | null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* A direct message between two users.
|
|
50
|
+
*
|
|
51
|
+
* Every row carries BOTH ends of the conversation as fully rendered
|
|
52
|
+
* {@link User} objects. That is most of the bytes of a page of messages, it is
|
|
53
|
+
* the same two records repeated a hundred times, and there is no way to ask
|
|
54
|
+
* for rows without them. Budget for it on a mobile connection.
|
|
55
|
+
*/
|
|
56
|
+
export interface DirectMessage {
|
|
57
|
+
/** Integer. */
|
|
58
|
+
readonly id: MessageId;
|
|
59
|
+
readonly created_at: Timestamp;
|
|
60
|
+
readonly updated_at: Timestamp;
|
|
61
|
+
/** Never null and never empty. */
|
|
62
|
+
readonly content: string;
|
|
63
|
+
readonly sender_id: Id;
|
|
64
|
+
readonly receiver_id: Id;
|
|
65
|
+
/**
|
|
66
|
+
* DO NOT READ THIS AS "THE OTHER PERSON SAW IT".
|
|
67
|
+
*
|
|
68
|
+
* It is set by {@link DirectMessagesNamespace.list} on every row the listing
|
|
69
|
+
* matched, in both directions, so it means "somebody who can see this
|
|
70
|
+
* message listed it" and nothing more precise. On a message the CALLER
|
|
71
|
+
* sent, it was almost certainly stamped by the caller's own listing rather
|
|
72
|
+
* than by the recipient's - opening a thread marks your own outgoing
|
|
73
|
+
* messages read. A "seen" tick built on this field lies.
|
|
74
|
+
*
|
|
75
|
+
* On a message the caller RECEIVED it is meaningful, in the weak sense that
|
|
76
|
+
* their own client fetched it at that time. It is what
|
|
77
|
+
* {@link DirectMessagesNamespace.conversedUsers} counts, and the only
|
|
78
|
+
* unread signal the API has.
|
|
79
|
+
*/
|
|
80
|
+
readonly read_at: Timestamp | null;
|
|
81
|
+
readonly attachment_kind: AttachmentKind | null;
|
|
82
|
+
readonly quoted_message_id: MessageId | null;
|
|
83
|
+
/** Null until the sender edits it. */
|
|
84
|
+
readonly edited_at: Timestamp | null;
|
|
85
|
+
readonly has_image: boolean;
|
|
86
|
+
readonly has_attachment: boolean;
|
|
87
|
+
readonly attachment_filename: string | null;
|
|
88
|
+
readonly attachment_byte_size: number | null;
|
|
89
|
+
readonly quoted_preview: QuotedMessagePreview | null;
|
|
90
|
+
/** The full sender record, nested. */
|
|
91
|
+
readonly sender: User;
|
|
92
|
+
/** The full receiver record, nested. */
|
|
93
|
+
readonly receiver: User;
|
|
94
|
+
}
|
|
95
|
+
/** One row of {@link DirectMessagesNamespace.conversedUsers}. */
|
|
96
|
+
export interface ConversedUser extends User {
|
|
97
|
+
readonly last_message: {
|
|
98
|
+
readonly id: MessageId;
|
|
99
|
+
readonly preview: string;
|
|
100
|
+
readonly from_me: boolean;
|
|
101
|
+
readonly created_at: Timestamp;
|
|
102
|
+
} | null;
|
|
103
|
+
readonly unread_count: number;
|
|
104
|
+
}
|
|
105
|
+
/** Filter columns of `GET /messages`. */
|
|
106
|
+
export declare const MESSAGE_FILTER_COLUMNS: readonly ["id", "created_at", "updated_at", "content", "sender_id", "receiver_id", "read_at"];
|
|
107
|
+
/** `extra_options` keys of `GET /messages`. */
|
|
108
|
+
export declare const MESSAGE_EXTRA_OPTION_KEYS: readonly ["user_id", "other_user_id"];
|
|
109
|
+
/** `extra_options` of `GET /messages`. Each matches rows where the user is either end. */
|
|
110
|
+
export interface MessageExtraOptions {
|
|
111
|
+
readonly user_id?: Id;
|
|
112
|
+
readonly other_user_id?: Id;
|
|
113
|
+
}
|
|
114
|
+
/** Filters accepted by {@link DirectMessagesNamespace.list}. */
|
|
115
|
+
export interface ListMessagesParams extends ListParams<(typeof MESSAGE_FILTER_COLUMNS)[number], MessageExtraOptions> {
|
|
116
|
+
/**
|
|
117
|
+
* The other end of the conversation. Sent as
|
|
118
|
+
* `extra_options[other_user_id]`, which the server expands to
|
|
119
|
+
* `sender_id = ? OR receiver_id = ?` INSIDE the caller's own scope, so it
|
|
120
|
+
* yields exactly the thread with that person.
|
|
121
|
+
*
|
|
122
|
+
* Pass this on every call you can. It is the only thing that keeps
|
|
123
|
+
* {@link DirectMessagesNamespace.list}'s read-marking side effect from
|
|
124
|
+
* covering your whole mailbox - see the method's docs.
|
|
125
|
+
*/
|
|
126
|
+
readonly withUser?: Id;
|
|
127
|
+
/**
|
|
128
|
+
* `extra_options[user_id]`: rows where this user is EITHER end.
|
|
129
|
+
*
|
|
130
|
+
* Redundant for a normal caller - the listing scope is already the caller's
|
|
131
|
+
* own messages, so passing your own id narrows nothing - and it cannot widen
|
|
132
|
+
* the scope to somebody else's mailbox either. It is exposed only so a
|
|
133
|
+
* caller porting existing code can keep the wire identical.
|
|
134
|
+
*/
|
|
135
|
+
readonly userId?: Id;
|
|
136
|
+
/** `search[content]`: partial, case- and accent-insensitive. */
|
|
137
|
+
readonly contentContains?: string;
|
|
138
|
+
/** `exact_search[sender_id]`. */
|
|
139
|
+
readonly senderId?: Id;
|
|
140
|
+
/** `exact_search[receiver_id]`. */
|
|
141
|
+
readonly receiverId?: Id;
|
|
142
|
+
/**
|
|
143
|
+
* `exact_search[read_at]` set to the null sentinel, i.e. `read_at IS NULL`.
|
|
144
|
+
*
|
|
145
|
+
* Reading unread messages is what MARKS them read, so this filter is
|
|
146
|
+
* self-consuming: the same call that returns them empties the set it
|
|
147
|
+
* matched. Use {@link DirectMessagesNamespace.conversedUsers} for a badge
|
|
148
|
+
* count that survives being looked at.
|
|
149
|
+
*/
|
|
150
|
+
readonly unreadOnly?: boolean;
|
|
151
|
+
}
|
|
152
|
+
/** Body of {@link DirectMessagesNamespace.send}. */
|
|
153
|
+
export interface SendMessageInput {
|
|
154
|
+
/** Recipient. A user id string, never a handle. */
|
|
155
|
+
readonly receiverId: Id;
|
|
156
|
+
/**
|
|
157
|
+
* Up to {@link MESSAGE_CONTENT_MAX_LENGTH} characters.
|
|
158
|
+
*
|
|
159
|
+
* Required UNLESS `attachment` is present: an attachment-only message has
|
|
160
|
+
* the literal string `"No Content"` substituted for its missing caption. So
|
|
161
|
+
* an attachment sent with no caption comes back with
|
|
162
|
+
* `content: "No Content"`, in English, unlocalised, and every client has to
|
|
163
|
+
* special-case that string when rendering. Send a caption if you have one.
|
|
164
|
+
*/
|
|
165
|
+
readonly content?: string;
|
|
166
|
+
/**
|
|
167
|
+
* One file, up to {@link MESSAGE_ATTACHMENT_MAX_BYTES} - or
|
|
168
|
+
* {@link MESSAGE_IMAGE_MAX_BYTES} if the server decides it is an image.
|
|
169
|
+
*
|
|
170
|
+
* A React Native pick (`{ uri, name, type }`) may be passed straight through;
|
|
171
|
+
* the transport appends it verbatim to RN's `FormData` and refuses loudly on
|
|
172
|
+
* any other runtime, where it would silently upload as `"[object Object]"`.
|
|
173
|
+
*
|
|
174
|
+
* An `image/*` attachment is NOT stored as sent: the server re-encodes it to
|
|
175
|
+
* webp (quality 75, or 50 past 5 MiB) and renames it
|
|
176
|
+
* `message_image_<timestamp>.webp`, so `attachment_filename` on the record
|
|
177
|
+
* will not be the name you gave. Everything else is stored byte for byte.
|
|
178
|
+
*/
|
|
179
|
+
readonly attachment?: FileInput | NativeFile;
|
|
180
|
+
/**
|
|
181
|
+
* Id of a message to quote. Any message id the server can find - there is
|
|
182
|
+
* no check that the quoted row is in this conversation, or even visible to
|
|
183
|
+
* either party. `quoted_preview` on the answer is rendered from it
|
|
184
|
+
* unconditionally, so quoting a stranger's message id leaks 120 characters
|
|
185
|
+
* of it. Only ever pass an id you took out of this same thread.
|
|
186
|
+
*/
|
|
187
|
+
readonly quotedMessageId?: MessageId;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* One-to-one messages, reachable as `oms.social.messages`.
|
|
191
|
+
*
|
|
192
|
+
* ## `list()` IS NOT A READ
|
|
193
|
+
*
|
|
194
|
+
* {@link DirectMessagesNamespace.list} marks every matched row read before it
|
|
195
|
+
* serialises anything. Read it in full before you call it from a poller, a
|
|
196
|
+
* prefetch, or a retry loop.
|
|
197
|
+
*/
|
|
198
|
+
export declare class DirectMessagesNamespace extends Resource {
|
|
199
|
+
/**
|
|
200
|
+
* `GET /messages` - the caller's messages, sent and received, as one listing.
|
|
201
|
+
*
|
|
202
|
+
* ## This GET writes. Three ways it will surprise you.
|
|
203
|
+
*
|
|
204
|
+
* The server marks the matched rows read before rendering, and it does so
|
|
205
|
+
* on the UNBOUNDED scope: every filter applies, and the page modifier
|
|
206
|
+
* deliberately does not, because marking only the first page would leave
|
|
207
|
+
* older messages showing unread. The consequences:
|
|
208
|
+
*
|
|
209
|
+
* 1. **An unfiltered `list()` marks your ENTIRE mailbox read**, every
|
|
210
|
+
* conversation, in one `UPDATE`. There is no read-only variant. Always
|
|
211
|
+
* pass {@link ListMessagesParams.withUser}.
|
|
212
|
+
* 2. **It also stamps `read_at` on messages YOU sent.** The listing covers
|
|
213
|
+
* sent and received alike, and the read-marking does not know the
|
|
214
|
+
* difference. So opening a thread sets `read_at` on your own outgoing
|
|
215
|
+
* messages, and any UI that renders "seen" from `read_at` on the sender's
|
|
216
|
+
* side is reading your own visit back to you as the recipient's. Treat
|
|
217
|
+
* `read_at` on a message whose `sender_id` is yours as meaningless.
|
|
218
|
+
* 3. **`updated_at` does NOT move.** The index's `ETag` is computed from the
|
|
219
|
+
* relation's count and its maximum `updated_at`, so a conditional GET can
|
|
220
|
+
* answer `304` while `read_at` underneath has changed. The SDK never
|
|
221
|
+
* sends `If-None-Match`, so this only bites a host that added its own
|
|
222
|
+
* caching layer.
|
|
223
|
+
*
|
|
224
|
+
* Retrying is safe in the sense that the second `UPDATE` writes the same
|
|
225
|
+
* rows again; it is not safe in the sense that the damage is already done on
|
|
226
|
+
* attempt one.
|
|
227
|
+
*
|
|
228
|
+
* ## Ordering and paging
|
|
229
|
+
*
|
|
230
|
+
* The server declares no default order. Offset pagination over an unordered
|
|
231
|
+
* query in Postgres may repeat a row on one page and drop it from the next,
|
|
232
|
+
* so the SDK always sends one: `created_at:desc` unless you pass `order`.
|
|
233
|
+
* Newest first is what a chat view wants, since it opens at the bottom.
|
|
234
|
+
*
|
|
235
|
+
* With no page modifier at all the server would force `1:500`; the SDK sends
|
|
236
|
+
* an explicit page so {@link Paginated.pageSize} matches what the rows were
|
|
237
|
+
* counted against.
|
|
238
|
+
*
|
|
239
|
+
* ## Filters that do not exist
|
|
240
|
+
*
|
|
241
|
+
* The filter columns are `id, created_at, updated_at, content, sender_id,
|
|
242
|
+
* receiver_id, read_at`, and unknown keys FAIL CLOSED with `400 "Unknown
|
|
243
|
+
* search filter: ..."` rather than being dropped. So there is no filter for
|
|
244
|
+
* `attachment_kind`, none for `edited_at`, and no way to ask for "messages
|
|
245
|
+
* since X" other than `search[created_at]`, which is a partial string match
|
|
246
|
+
* on the rendered timestamp and is not a range query. Fetch and filter here.
|
|
247
|
+
*
|
|
248
|
+
* Costs one request against the general authenticated ceiling (600/min).
|
|
249
|
+
*/
|
|
250
|
+
list(params?: ListMessagesParams, options?: RequestOptions): Promise<Paginated<DirectMessage>>;
|
|
251
|
+
/**
|
|
252
|
+
* One conversation, newest first. `list()` with
|
|
253
|
+
* {@link ListMessagesParams.withUser} already filled in.
|
|
254
|
+
*
|
|
255
|
+
* This is the call a chat screen should make, and the reason it exists as its
|
|
256
|
+
* own method is the read-marking described on {@link list}: scoping the
|
|
257
|
+
* listing is what scopes the `UPDATE`. Opening a thread marks that thread
|
|
258
|
+
* read, which is what a user expects. Calling `list()` bare does not.
|
|
259
|
+
*/
|
|
260
|
+
conversation(userId: Id, params?: Omit<ListMessagesParams, "withUser">, options?: RequestOptions): Promise<Paginated<DirectMessage>>;
|
|
261
|
+
/**
|
|
262
|
+
* `GET /messages/conversed_users` - one row per person the caller has ever
|
|
263
|
+
* exchanged a message with, newest conversation first, each carrying the
|
|
264
|
+
* latest message either way and the caller's unread count from that sender.
|
|
265
|
+
*
|
|
266
|
+
* This is the inbox list, and it is the ONLY endpoint here that reports
|
|
267
|
+
* unread counts without destroying them: it does not mark anything read.
|
|
268
|
+
* Poll this one, not {@link list}.
|
|
269
|
+
*
|
|
270
|
+
* Not paginated and not filterable - it is a bespoke action, not a listing,
|
|
271
|
+
* so it carries no `ETag` and ignores every modifier. The whole set comes
|
|
272
|
+
* back on every call, and the cost is bounded by how many people you have
|
|
273
|
+
* talked to rather than by how many messages there are: one `DISTINCT ON`
|
|
274
|
+
* for the latest message per counterpart, one grouped `COUNT` for the unread
|
|
275
|
+
* numbers, and only that bounded set is hydrated.
|
|
276
|
+
*
|
|
277
|
+
* `last_message.preview` is 120 characters of content, or a bracketed
|
|
278
|
+
* placeholder (`"[image]"`, `"[audio]"`, `"[video]"`, `"[file]"`) when the
|
|
279
|
+
* newest message is an attachment. It is always a string, never `null`.
|
|
280
|
+
*
|
|
281
|
+
* A user you have messaged appears here even if one of you has since blocked
|
|
282
|
+
* the other; the rows are built from message history, not from
|
|
283
|
+
* {@link Relationship}. Cross-reference
|
|
284
|
+
* {@link RelationshipsNamespace.list} if the UI needs to hide those.
|
|
285
|
+
*/
|
|
286
|
+
conversedUsers(options?: RequestOptions): Promise<ConversedUser[]>;
|
|
287
|
+
/**
|
|
288
|
+
* `GET /messages/:id` - one message, by id.
|
|
289
|
+
*
|
|
290
|
+
* Scoped to the caller's own messages, so a message between two other people
|
|
291
|
+
* is `404 "Resource not found"` rather than `403`. Does NOT mark anything
|
|
292
|
+
* read, which makes it the safe way to re-read a single row.
|
|
293
|
+
*
|
|
294
|
+
* Returns exactly the same fields as a row from {@link list}.
|
|
295
|
+
*/
|
|
296
|
+
get(id: MessageId, options?: RequestOptions): Promise<DirectMessage>;
|
|
297
|
+
/**
|
|
298
|
+
* `POST /messages` - sends one message. `201` with the created record.
|
|
299
|
+
*
|
|
300
|
+
* The sender is always the authenticated user; there is no way to say
|
|
301
|
+
* otherwise and `sender_id` in the body is ignored.
|
|
302
|
+
*
|
|
303
|
+
* ## What "the recipient blocked me" looks like
|
|
304
|
+
*
|
|
305
|
+
* A `401 "You are not authorized to create this resource"` is how a block is
|
|
306
|
+
* announced - after the request, never before it, because the block that
|
|
307
|
+
* stops you is invisible to you: rows where you are the `accepter` of a
|
|
308
|
+
* `block` are hidden from you. You cannot look up whether you are blocked;
|
|
309
|
+
* you can only be refused.
|
|
310
|
+
*
|
|
311
|
+
* The test is one-directional. If YOU blocked THEM, nothing here stops you
|
|
312
|
+
* from messaging them, and the message is delivered.
|
|
313
|
+
*
|
|
314
|
+
* ## Transport
|
|
315
|
+
*
|
|
316
|
+
* Sent as JSON when there is no attachment and as `multipart/form-data` when
|
|
317
|
+
* there is - the same route accepts both, and multipart is what makes a file
|
|
318
|
+
* possible at all. In multipart the numeric `quoted_message_id` goes out as
|
|
319
|
+
* text and the server casts it back; that is normal and not a precision risk
|
|
320
|
+
* at these magnitudes.
|
|
321
|
+
*
|
|
322
|
+
* The field is named `attachment`. A legacy `image` field is still accepted
|
|
323
|
+
* and funnelled to the same blob, but nothing new should send it.
|
|
324
|
+
*
|
|
325
|
+
* ## What is refused here rather than by the server
|
|
326
|
+
*
|
|
327
|
+
* An image between {@link MESSAGE_IMAGE_MAX_BYTES} and
|
|
328
|
+
* {@link MESSAGE_ATTACHMENT_MAX_BYTES} passes the general cap and then dies
|
|
329
|
+
* inside the webp recompressor, which is a **500** with an error alert behind
|
|
330
|
+
* it rather than a `400`. This method throws before sending when the size is
|
|
331
|
+
* knowable. It stays silent when it is not - a `ReadableStream` with no
|
|
332
|
+
* declared `size`, or a picker that reported none - because buffering a file
|
|
333
|
+
* just to measure it would be worse than the server's answer.
|
|
334
|
+
*
|
|
335
|
+
* NOT retried: this is a `POST`, and a replay after a lost response sends the
|
|
336
|
+
* message twice.
|
|
337
|
+
*
|
|
338
|
+
* @throws {OmsError} `invalid_request` when neither content nor an
|
|
339
|
+
* attachment is present, when content is over the length cap, or when a
|
|
340
|
+
* knowably-oversized image would 500.
|
|
341
|
+
* @throws {OmsApiError} `401` when the recipient has blocked the caller,
|
|
342
|
+
* `400` when the recipient does not exist or the content is empty.
|
|
343
|
+
*/
|
|
344
|
+
send(input: SendMessageInput, options?: RequestOptions): Promise<DirectMessage>;
|
|
345
|
+
/**
|
|
346
|
+
* `PATCH /messages/:id` - rewrites the content of a message you sent.
|
|
347
|
+
*
|
|
348
|
+
* `content` is the only writable field. An attachment cannot be added,
|
|
349
|
+
* replaced or removed after the fact, and `receiver_id` is fixed.
|
|
350
|
+
*
|
|
351
|
+
* Allowed only while you are the sender AND the message is younger than
|
|
352
|
+
* {@link MESSAGE_EDIT_WINDOW_MS}. Past the window the answer is `401`, not
|
|
353
|
+
* `403` and not `422`. Use {@link canEditMessage} to grey the button out,
|
|
354
|
+
* and still handle the `401`: the window is measured on the server's clock.
|
|
355
|
+
*
|
|
356
|
+
* A successful edit stamps `edited_at`, which every client renders as an
|
|
357
|
+
* "edited" marker. It is stamped only when the content actually changes: a
|
|
358
|
+
* no-op PATCH with an identical string does NOT mark it edited, but any
|
|
359
|
+
* different string does, including whitespace.
|
|
360
|
+
*
|
|
361
|
+
* There is no cable broadcast for an edit. The other side sees the new text
|
|
362
|
+
* whenever it next fetches, and never learns that it changed.
|
|
363
|
+
*/
|
|
364
|
+
edit(id: MessageId, content: string, options?: RequestOptions): Promise<DirectMessage>;
|
|
365
|
+
/**
|
|
366
|
+
* `DELETE /messages/:id` - `204`, empty body.
|
|
367
|
+
*
|
|
368
|
+
* The sender may delete at any time; there is no edit window on destroy and
|
|
369
|
+
* the receiver cannot delete a message they were sent. The row and its blob
|
|
370
|
+
* go for both parties at once, so this is "unsend", not "hide from my copy".
|
|
371
|
+
*
|
|
372
|
+
* A message that another message quotes can still be deleted. The quoting
|
|
373
|
+
* row keeps its `quoted_preview` - the snapshot was serialised at render
|
|
374
|
+
* time from the association, so once the quoted row is gone the preview
|
|
375
|
+
* becomes `null` on the next fetch and the quote silently empties.
|
|
376
|
+
*
|
|
377
|
+
* Not retried, like every write. A `DELETE` replayed after a torn connection
|
|
378
|
+
* would answer `404` for a row it deleted perfectly well.
|
|
379
|
+
*/
|
|
380
|
+
delete(id: MessageId, options?: RequestOptions): Promise<void>;
|
|
381
|
+
/**
|
|
382
|
+
* `GET /messages/:id/attachment` - the bytes of the attached file.
|
|
383
|
+
*
|
|
384
|
+
* The route answers `302` to a presigned object-store URL, and `fetch`
|
|
385
|
+
* follows that redirect on its own. Which means this method works everywhere
|
|
386
|
+
* EXCEPT a browser in cookie mode: the request carries credentials, the
|
|
387
|
+
* redirected request arrives at the store with `Origin: null`, and the store
|
|
388
|
+
* answers a wildcard `Access-Control-Allow-Origin`, which CORS forbids for a
|
|
389
|
+
* credentialed request. There, use {@link attachmentUrl} and let an `<img>`,
|
|
390
|
+
* a `<video>` or an `<a download>` follow the redirect with no CORS check.
|
|
391
|
+
*
|
|
392
|
+
* Filename, byte size and kind are already on the {@link DirectMessage},
|
|
393
|
+
* which is why this hands back bare bytes.
|
|
394
|
+
*
|
|
395
|
+
* @throws {OmsError} `unsupported` when the redirect was blocked by CORS.
|
|
396
|
+
* @throws {OmsApiError} `404 "Message not found"` when the message is not
|
|
397
|
+
* yours, `404 "No attachment"` when it carries none.
|
|
398
|
+
*/
|
|
399
|
+
attachment(id: MessageId, options?: RequestOptions): Promise<Blob>;
|
|
400
|
+
/**
|
|
401
|
+
* Absolute URL for a message attachment, for an `<img>`, an `<a download>` or
|
|
402
|
+
* a new tab. In a browser in cookie mode this is the only way to reach the
|
|
403
|
+
* bytes; see {@link attachment}.
|
|
404
|
+
*
|
|
405
|
+
* Asynchronous because the credential has to be resolved first, and resolving
|
|
406
|
+
* it may involve a token refresh.
|
|
407
|
+
*
|
|
408
|
+
* - **cookie mode**: no credential in the URL; the browser attaches the
|
|
409
|
+
* host-only `oms_session` cookie itself. Do not put `crossorigin` on the
|
|
410
|
+
* element - that turns a no-cors load into a CORS one and re-creates the
|
|
411
|
+
* failure this method exists to avoid.
|
|
412
|
+
* - **token mode**: the token is appended as `?token=`, which is what a
|
|
413
|
+
* native client needs because an `<img>` cannot carry a header.
|
|
414
|
+
*
|
|
415
|
+
* THE TOKEN-MODE URL CONTAINS A LIVE CREDENTIAL. Build it at the moment of
|
|
416
|
+
* use, never store it, never log it, and never hand it to anything outside
|
|
417
|
+
* your own page - fetch the bytes with {@link attachment} and pass a `blob:`
|
|
418
|
+
* URL instead.
|
|
419
|
+
*
|
|
420
|
+
* There is a legacy twin at `/messages/:id/image`. It resolves to the same
|
|
421
|
+
* blob for image messages and is not worth calling from new code.
|
|
422
|
+
*/
|
|
423
|
+
attachmentUrl(id: MessageId): Promise<string>;
|
|
424
|
+
}
|