@amykit/plugin-slack 0.5.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.md +61 -0
- package/dist/SlackApi.d.ts +67 -0
- package/dist/SlackApi.d.ts.map +1 -0
- package/dist/SlackApi.js +143 -0
- package/dist/SlackApi.js.map +1 -0
- package/dist/SlackConversation.d.ts +111 -0
- package/dist/SlackConversation.d.ts.map +1 -0
- package/dist/SlackConversation.js +504 -0
- package/dist/SlackConversation.js.map +1 -0
- package/dist/ThreadMap.d.ts +150 -0
- package/dist/ThreadMap.d.ts.map +1 -0
- package/dist/ThreadMap.js +444 -0
- package/dist/ThreadMap.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +44 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +43 -0
- package/dist/plugin.js.map +1 -0
- package/dist/secret.d.ts +13 -0
- package/dist/secret.d.ts.map +1 -0
- package/dist/secret.js +56 -0
- package/dist/secret.js.map +1 -0
- package/package.json +36 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { TooLarge } from "./SlackApi.js";
|
|
5
|
+
import { ThreadMap, privateDirectory } from "./ThreadMap.js";
|
|
6
|
+
/**
|
|
7
|
+
* One Slack thread per piece of work, in one channel.
|
|
8
|
+
*
|
|
9
|
+
* The thread is remembered on disk by work id, so a restart posts into the
|
|
10
|
+
* thread it already opened instead of opening a second one. A root Slack
|
|
11
|
+
* accepted just before a crash is found again in the channel's history
|
|
12
|
+
* rather than posted twice.
|
|
13
|
+
*/
|
|
14
|
+
/** Large enough for any screenshot, small enough that a daemon never holds a video. */
|
|
15
|
+
const DEFAULT_MAX_FILE_BYTES = 25 * 1024 * 1024;
|
|
16
|
+
/** Long enough for an operator's holiday, short enough that screenshots do not pile up. */
|
|
17
|
+
export const DEFAULT_RETENTION_DAYS = 30;
|
|
18
|
+
/** Pruning walks the state directory, so it happens at most this often. */
|
|
19
|
+
const PRUNE_EVERY_MS = 60 * 60 * 1000;
|
|
20
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
21
|
+
/** What marks a root as amy's, and whose work it opened. */
|
|
22
|
+
const ROOT_EVENT = "amy_work_thread";
|
|
23
|
+
export class SlackConversation {
|
|
24
|
+
api;
|
|
25
|
+
options;
|
|
26
|
+
threads;
|
|
27
|
+
filesDirectory;
|
|
28
|
+
opening = new Map();
|
|
29
|
+
botUser;
|
|
30
|
+
prunedAt = Number.NEGATIVE_INFINITY;
|
|
31
|
+
constructor(api, options) {
|
|
32
|
+
this.api = api;
|
|
33
|
+
this.options = options;
|
|
34
|
+
this.threads = new ThreadMap(options.directory, {
|
|
35
|
+
maxWaitMs: options.lockWaitMs,
|
|
36
|
+
leaseMs: options.leaseMs,
|
|
37
|
+
// A root replaced in another channel takes its downloads with it.
|
|
38
|
+
onDisplaced: (ts) => this.dropFiles(ts),
|
|
39
|
+
});
|
|
40
|
+
this.filesDirectory = path.join(options.directory, "files");
|
|
41
|
+
// The operator's screenshots, fetched with the bot's token: private.
|
|
42
|
+
privateDirectory(this.filesDirectory);
|
|
43
|
+
}
|
|
44
|
+
open(workId, title) {
|
|
45
|
+
let known;
|
|
46
|
+
try {
|
|
47
|
+
// Known first, which marks it used, so the prune after it keeps it.
|
|
48
|
+
known = this.threads.known(workId, this.options.channel);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
// A promise-returning method rejects; it does not throw at the caller.
|
|
52
|
+
return Promise.reject(error instanceof Error ? error : new Error(String(error)));
|
|
53
|
+
}
|
|
54
|
+
this.pruneNowAndThen();
|
|
55
|
+
if (known)
|
|
56
|
+
return Promise.resolve({ id: known });
|
|
57
|
+
// Two looks at the same work in one process must not open two threads.
|
|
58
|
+
const pending = this.opening.get(workId);
|
|
59
|
+
if (pending)
|
|
60
|
+
return pending;
|
|
61
|
+
const opened = this.startThread(workId, title).finally(() => this.opening.delete(workId));
|
|
62
|
+
this.opening.set(workId, opened);
|
|
63
|
+
return opened;
|
|
64
|
+
}
|
|
65
|
+
async post(thread, message) {
|
|
66
|
+
if (message.files?.length) {
|
|
67
|
+
// Uploading needs `files:write`, which this plugin does not ask for:
|
|
68
|
+
// amy asks in words, and the operator's pictures come the other way.
|
|
69
|
+
throw new Error("@amykit/plugin-slack does not upload files; post their paths as text");
|
|
70
|
+
}
|
|
71
|
+
// Writing to a thread is using it, as reading its replies is.
|
|
72
|
+
this.threads.used(thread.id);
|
|
73
|
+
this.pruneNowAndThen();
|
|
74
|
+
const { body } = await this.api.call("chat.postMessage", {
|
|
75
|
+
channel: this.options.channel,
|
|
76
|
+
thread_ts: thread.id,
|
|
77
|
+
text: message.text,
|
|
78
|
+
});
|
|
79
|
+
return String(body.ts);
|
|
80
|
+
}
|
|
81
|
+
async replies(thread, since) {
|
|
82
|
+
const after = instantToMicros(since);
|
|
83
|
+
if (Number.isNaN(after))
|
|
84
|
+
throw new Error(`replies since ${since}: not an instant`);
|
|
85
|
+
// Reading a thread is using it, whether or not the caller opened it
|
|
86
|
+
// first — marked before the prune, so the prune never takes it.
|
|
87
|
+
this.threads.used(thread.id);
|
|
88
|
+
this.pruneNowAndThen();
|
|
89
|
+
const bot = await this.botUserId();
|
|
90
|
+
const answers = [];
|
|
91
|
+
for (const message of await this.threadMessages(thread.id, after)) {
|
|
92
|
+
if (!this.isOperatorAnswer(message, thread.id, bot))
|
|
93
|
+
continue;
|
|
94
|
+
const at = tsToMicros(message.ts);
|
|
95
|
+
if (at <= after)
|
|
96
|
+
continue;
|
|
97
|
+
const { paths, skipped } = await this.downloadAll(thread.id, message.files ?? []);
|
|
98
|
+
answers.push({
|
|
99
|
+
at: microsToInstant(at),
|
|
100
|
+
author: message.user,
|
|
101
|
+
text: [message.text ?? "", ...skipped].filter(Boolean).join("\n"),
|
|
102
|
+
files: paths,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
return answers.sort((a, b) => a.at.localeCompare(b.at));
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Whether the token can do what this plugin does, asked of Slack itself.
|
|
109
|
+
*
|
|
110
|
+
* `auth.test` for the token, its `x-oauth-scopes` header for the scopes,
|
|
111
|
+
* and `conversations.info` for whether the channel is one the bot can see
|
|
112
|
+
* — which also says whether the history scope it needs is the public or
|
|
113
|
+
* the private one.
|
|
114
|
+
*/
|
|
115
|
+
async checks() {
|
|
116
|
+
try {
|
|
117
|
+
const { body, headers } = await this.api.call("auth.test");
|
|
118
|
+
const scopes = (headers["x-oauth-scopes"] ?? "").split(",").map((scope) => scope.trim()).filter(Boolean);
|
|
119
|
+
const checks = [{ label: "slack token", ok: true, detail: `${String(body.user)} in ${String(body.team)}` }];
|
|
120
|
+
return checks.concat(await this.channelChecks(scopes));
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
return [{ label: "slack token", ok: false, detail: messageOf(error) }];
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Forgets one piece of work: its thread's memory and every file downloaded
|
|
128
|
+
* from it. The thread stays in Slack; the next `open` starts a new one.
|
|
129
|
+
*/
|
|
130
|
+
forget(workId) {
|
|
131
|
+
const ts = this.threads.forget(workId);
|
|
132
|
+
if (ts)
|
|
133
|
+
this.dropFiles(ts);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Removes what nobody used within the retention: a downloaded file, which
|
|
137
|
+
* the next read of its reply fetches again, and a thread's memory, whose
|
|
138
|
+
* files go with it. Returns how many of each went.
|
|
139
|
+
*/
|
|
140
|
+
prune(now = this.clock()) {
|
|
141
|
+
// Whatever a forget marked and did not get to remove goes first, even
|
|
142
|
+
// when retention keeps everything else: that was a decision, not an age.
|
|
143
|
+
sweepForgotten(this.filesDirectory);
|
|
144
|
+
const days = this.options.retentionDays ?? DEFAULT_RETENTION_DAYS;
|
|
145
|
+
if (days <= 0)
|
|
146
|
+
return { threads: 0, files: 0 };
|
|
147
|
+
const cutoff = now.getTime() - days * DAY_MS;
|
|
148
|
+
const threads = this.threads.prune(cutoff);
|
|
149
|
+
for (const ts of threads)
|
|
150
|
+
this.dropFiles(ts);
|
|
151
|
+
return { threads: threads.length, files: pruneFiles(this.filesDirectory, cutoff) };
|
|
152
|
+
}
|
|
153
|
+
clock() {
|
|
154
|
+
return this.options.now?.() ?? new Date();
|
|
155
|
+
}
|
|
156
|
+
/** Where one thread's downloads live, so forgetting it takes them too. */
|
|
157
|
+
threadFiles(ts) {
|
|
158
|
+
return path.join(this.filesDirectory, safeName(ts));
|
|
159
|
+
}
|
|
160
|
+
/** Beside the thread's files, not in them, so removing those leaves it. */
|
|
161
|
+
forgottenMarker(ts) {
|
|
162
|
+
return path.join(this.filesDirectory, `${safeName(ts)}.forgotten`);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Marks the thread forgotten, then removes its files. The marker is what a
|
|
166
|
+
* download still in flight — in this process or another — reads after it
|
|
167
|
+
* publishes, so it cannot bring a forgotten thread's file back.
|
|
168
|
+
*/
|
|
169
|
+
dropFiles(ts) {
|
|
170
|
+
privateDirectory(this.filesDirectory);
|
|
171
|
+
fs.writeFileSync(this.forgottenMarker(ts), "", { mode: 0o600 });
|
|
172
|
+
fs.rmSync(this.threadFiles(ts), { recursive: true, force: true });
|
|
173
|
+
}
|
|
174
|
+
/** Throttled, and never the reason a call fails: pruning is housekeeping. */
|
|
175
|
+
pruneNowAndThen() {
|
|
176
|
+
const now = this.clock();
|
|
177
|
+
if (now.getTime() - this.prunedAt < PRUNE_EVERY_MS)
|
|
178
|
+
return;
|
|
179
|
+
this.prunedAt = now.getTime();
|
|
180
|
+
try {
|
|
181
|
+
this.prune(now);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// A file another process removed first, or one still being written.
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async channelChecks(scopes) {
|
|
188
|
+
const label = `slack channel ${this.options.channel}`;
|
|
189
|
+
try {
|
|
190
|
+
const { body } = await this.api.call("conversations.info", { channel: this.options.channel });
|
|
191
|
+
const channel = (body.channel ?? {});
|
|
192
|
+
const isPrivate = channel.is_private === true;
|
|
193
|
+
const name = `#${channel.name ?? "?"}`;
|
|
194
|
+
// A public channel can be read without joining it, but `chat:write`
|
|
195
|
+
// only posts where the bot is a member.
|
|
196
|
+
const checks = [
|
|
197
|
+
channel.is_member === false
|
|
198
|
+
? { label, ok: false, detail: `the bot is not in ${name}; invite it there with /invite` }
|
|
199
|
+
: { label, ok: true, detail: name },
|
|
200
|
+
];
|
|
201
|
+
const needed = [
|
|
202
|
+
"chat:write",
|
|
203
|
+
isPrivate ? "groups:read" : "channels:read",
|
|
204
|
+
isPrivate ? "groups:history" : "channels:history",
|
|
205
|
+
"files:read",
|
|
206
|
+
];
|
|
207
|
+
const missing = needed.filter((scope) => !scopes.includes(scope));
|
|
208
|
+
checks.push({
|
|
209
|
+
label: "slack scopes",
|
|
210
|
+
ok: missing.length === 0,
|
|
211
|
+
detail: missing.length === 0 ? needed.join(", ") : `missing ${missing.join(", ")}`,
|
|
212
|
+
});
|
|
213
|
+
return checks;
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
return [{ label, ok: false, detail: messageOf(error) }];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Exactly one process posts a root per attempt; an attempt that follows
|
|
221
|
+
* one that may have reached Slack looks for that root before posting.
|
|
222
|
+
*/
|
|
223
|
+
async startThread(workId, title) {
|
|
224
|
+
const channel = this.options.channel;
|
|
225
|
+
const ts = await this.threads.open(workId, channel, async (recoverSince) => {
|
|
226
|
+
const recovered = recoverSince === undefined ? undefined : await this.findRoot(workId, recoverSince);
|
|
227
|
+
if (recovered)
|
|
228
|
+
return { ts: recovered, posted: false };
|
|
229
|
+
const { body } = await this.api.call("chat.postMessage", {
|
|
230
|
+
channel,
|
|
231
|
+
text: title,
|
|
232
|
+
metadata: JSON.stringify({ event_type: ROOT_EVENT, event_payload: { work_id: workId } }),
|
|
233
|
+
});
|
|
234
|
+
return { ts: String(body.ts), posted: true };
|
|
235
|
+
}, async (superseded) => {
|
|
236
|
+
// Best effort: a root that cannot be deleted is a stray message in the
|
|
237
|
+
// channel, never a second thread this machine remembers.
|
|
238
|
+
await this.api.call("chat.delete", { channel, ts: superseded }).catch(() => undefined);
|
|
239
|
+
});
|
|
240
|
+
return { id: ts };
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* The root this bot posted for this work since `since`, if Slack took one.
|
|
244
|
+
*
|
|
245
|
+
* Matched by the work id carried in the root's metadata, never by its
|
|
246
|
+
* text: two items can share a title, and a title can change.
|
|
247
|
+
*/
|
|
248
|
+
async findRoot(workId, since) {
|
|
249
|
+
const bot = await this.botUserId();
|
|
250
|
+
const messages = await this.pages("conversations.history", {
|
|
251
|
+
channel: this.options.channel,
|
|
252
|
+
oldest: msToTs(since - 1000),
|
|
253
|
+
include_all_metadata: "true",
|
|
254
|
+
});
|
|
255
|
+
const root = messages.find((message) => message.user === bot &&
|
|
256
|
+
(!message.thread_ts || message.thread_ts === message.ts) &&
|
|
257
|
+
message.metadata?.event_type === ROOT_EVENT &&
|
|
258
|
+
message.metadata.event_payload?.work_id === workId);
|
|
259
|
+
return root?.ts;
|
|
260
|
+
}
|
|
261
|
+
/** Every message in the thread after `afterMicros`, across pages. */
|
|
262
|
+
threadMessages(ts, afterMicros) {
|
|
263
|
+
return this.pages("conversations.replies", { channel: this.options.channel, ts, oldest: microsToTs(afterMicros) });
|
|
264
|
+
}
|
|
265
|
+
async pages(method, args) {
|
|
266
|
+
const messages = [];
|
|
267
|
+
let cursor;
|
|
268
|
+
do {
|
|
269
|
+
const { body } = await this.api.call(method, { ...args, cursor });
|
|
270
|
+
messages.push(...(body.messages ?? []));
|
|
271
|
+
const next = body.response_metadata?.next_cursor;
|
|
272
|
+
cursor = body.has_more === true && next ? next : undefined;
|
|
273
|
+
} while (cursor);
|
|
274
|
+
return messages;
|
|
275
|
+
}
|
|
276
|
+
/** By author id, never by a marker in the text. */
|
|
277
|
+
isOperatorAnswer(message, threadTs, bot) {
|
|
278
|
+
if (!message.ts || message.ts === threadTs)
|
|
279
|
+
return false;
|
|
280
|
+
if (message.bot_id || message.user === bot)
|
|
281
|
+
return false;
|
|
282
|
+
if (message.subtype && message.subtype !== "thread_broadcast" && message.subtype !== "file_share")
|
|
283
|
+
return false;
|
|
284
|
+
return message.user === this.options.operator;
|
|
285
|
+
}
|
|
286
|
+
/** Cached once known; a failure is not, so the next poll asks again. */
|
|
287
|
+
botUserId() {
|
|
288
|
+
this.botUser ??= this.api.call("auth.test").then(({ body }) => String(body.user_id), (error) => {
|
|
289
|
+
this.botUser = undefined;
|
|
290
|
+
throw error;
|
|
291
|
+
});
|
|
292
|
+
return this.botUser;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* The attachments as local paths, and a line naming each one too large to
|
|
296
|
+
* fetch — declared so by Slack, or found so while it arrived — so the
|
|
297
|
+
* reader knows a file was there rather than never seeing it.
|
|
298
|
+
*/
|
|
299
|
+
async downloadAll(threadTs, files) {
|
|
300
|
+
const limit = this.options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
301
|
+
const paths = [];
|
|
302
|
+
const skipped = [];
|
|
303
|
+
for (const file of files) {
|
|
304
|
+
const url = file.url_private_download ?? file.url_private;
|
|
305
|
+
if (!file.id || !url)
|
|
306
|
+
continue;
|
|
307
|
+
const name = file.name ?? file.id;
|
|
308
|
+
const tooLarge = `[${name} was not downloaded: it is larger than ${limit} bytes]`;
|
|
309
|
+
if ((file.size ?? 0) > limit) {
|
|
310
|
+
skipped.push(tooLarge);
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
try {
|
|
314
|
+
const kept = await this.download(threadTs, path.join(this.threadFiles(threadTs), safeName(file.id), safeName(name)), url, limit);
|
|
315
|
+
if (kept)
|
|
316
|
+
paths.push(kept);
|
|
317
|
+
}
|
|
318
|
+
catch (error) {
|
|
319
|
+
if (!(error instanceof TooLarge))
|
|
320
|
+
throw error;
|
|
321
|
+
skipped.push(tooLarge);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return { paths, skipped };
|
|
325
|
+
}
|
|
326
|
+
/** The file's local path, or nothing when its thread was forgotten. */
|
|
327
|
+
async download(threadTs, target, url, limit) {
|
|
328
|
+
if (fs.existsSync(this.forgottenMarker(threadTs)))
|
|
329
|
+
return undefined;
|
|
330
|
+
if (keptAgain(target))
|
|
331
|
+
return target;
|
|
332
|
+
const bytes = await this.api.download(url, limit);
|
|
333
|
+
fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
|
|
334
|
+
// Staged and renamed, so a path that exists is always a whole file.
|
|
335
|
+
const partial = `${target}.${randomUUID()}.partial`;
|
|
336
|
+
try {
|
|
337
|
+
fs.writeFileSync(partial, bytes, { flag: "wx", mode: 0o600 });
|
|
338
|
+
fs.renameSync(partial, target);
|
|
339
|
+
}
|
|
340
|
+
finally {
|
|
341
|
+
fs.rmSync(partial, { force: true });
|
|
342
|
+
}
|
|
343
|
+
// Published first, checked after: a forget that landed while the bytes
|
|
344
|
+
// were arriving is seen here, whichever of the two finished first.
|
|
345
|
+
if (fs.existsSync(this.forgottenMarker(threadTs))) {
|
|
346
|
+
fs.rmSync(this.threadFiles(threadTs), { recursive: true, force: true });
|
|
347
|
+
return undefined;
|
|
348
|
+
}
|
|
349
|
+
return target;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Whether a file was already downloaded, marking it used if so: retention
|
|
354
|
+
* counts from the last use. One pruned between the look and the touch is a
|
|
355
|
+
* miss, and is fetched again.
|
|
356
|
+
*/
|
|
357
|
+
function keptAgain(target) {
|
|
358
|
+
const now = new Date();
|
|
359
|
+
try {
|
|
360
|
+
fs.utimesSync(target, now, now);
|
|
361
|
+
return true;
|
|
362
|
+
}
|
|
363
|
+
catch (error) {
|
|
364
|
+
if (error.code === "ENOENT")
|
|
365
|
+
return false;
|
|
366
|
+
throw error;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Removes files older than `cutoff`, then the directories they leave empty.
|
|
371
|
+
* The forgotten markers directly under `files/` are kept for good: a stale
|
|
372
|
+
* reference read later must still find one. Anything deeper is an attachment,
|
|
373
|
+
* whatever it is called, and ages like one.
|
|
374
|
+
*/
|
|
375
|
+
function pruneFiles(directory, cutoff, top = true) {
|
|
376
|
+
let removed = 0;
|
|
377
|
+
for (const entry of direntsOf(directory)) {
|
|
378
|
+
const full = path.join(directory, entry.name);
|
|
379
|
+
if (entry.isDirectory()) {
|
|
380
|
+
removed += pruneFiles(full, cutoff, false);
|
|
381
|
+
removeIfEmpty(full);
|
|
382
|
+
}
|
|
383
|
+
else if (!(top && entry.name.endsWith(".forgotten")) && removeIfStale(full, cutoff)) {
|
|
384
|
+
removed += 1;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return removed;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Moves the file aside, then asks its age again, and puts it back if a
|
|
391
|
+
* reader touched it first. A reader's touch lands either before the move —
|
|
392
|
+
* seen here, and the file stays — or after it, where it misses and the file
|
|
393
|
+
* is fetched again; so a path a reader was handed is never removed under it.
|
|
394
|
+
*/
|
|
395
|
+
function removeIfStale(file, cutoff) {
|
|
396
|
+
// Another prune may have taken any of these first; then it is handled.
|
|
397
|
+
const before = mtimeOf(file);
|
|
398
|
+
if (before === undefined || before >= cutoff)
|
|
399
|
+
return false;
|
|
400
|
+
const aside = `${file}.${randomUUID()}.pruning`;
|
|
401
|
+
if (!ignoringGone(() => fs.renameSync(file, aside)))
|
|
402
|
+
return false;
|
|
403
|
+
const after = mtimeOf(aside);
|
|
404
|
+
if (after === undefined)
|
|
405
|
+
return false;
|
|
406
|
+
if (after >= cutoff) {
|
|
407
|
+
ignoringGone(() => fs.renameSync(aside, file));
|
|
408
|
+
return false;
|
|
409
|
+
}
|
|
410
|
+
fs.rmSync(aside, { force: true });
|
|
411
|
+
return true;
|
|
412
|
+
}
|
|
413
|
+
function mtimeOf(file) {
|
|
414
|
+
try {
|
|
415
|
+
return fs.statSync(file).mtimeMs;
|
|
416
|
+
}
|
|
417
|
+
catch (error) {
|
|
418
|
+
if (error.code === "ENOENT")
|
|
419
|
+
return undefined;
|
|
420
|
+
throw error;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
/** Runs it; false when what it touched was already gone. */
|
|
424
|
+
function ignoringGone(act) {
|
|
425
|
+
try {
|
|
426
|
+
act();
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
catch (error) {
|
|
430
|
+
if (error.code === "ENOENT")
|
|
431
|
+
return false;
|
|
432
|
+
throw error;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
/** Removes the files of every thread a marker says was forgotten; idempotent. */
|
|
436
|
+
function sweepForgotten(directory) {
|
|
437
|
+
for (const entry of direntsOf(directory)) {
|
|
438
|
+
if (!entry.isFile() || !entry.name.endsWith(".forgotten"))
|
|
439
|
+
continue;
|
|
440
|
+
fs.rmSync(path.join(directory, entry.name.slice(0, -".forgotten".length)), { recursive: true, force: true });
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
function direntsOf(directory) {
|
|
444
|
+
try {
|
|
445
|
+
return fs.readdirSync(directory, { withFileTypes: true });
|
|
446
|
+
}
|
|
447
|
+
catch (error) {
|
|
448
|
+
if (error.code === "ENOENT")
|
|
449
|
+
return [];
|
|
450
|
+
throw error;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
/** A download may be filling it again right now; then it is not empty. */
|
|
454
|
+
function removeIfEmpty(directory) {
|
|
455
|
+
try {
|
|
456
|
+
fs.rmdirSync(directory);
|
|
457
|
+
}
|
|
458
|
+
catch (error) {
|
|
459
|
+
const code = error.code;
|
|
460
|
+
if (code !== "ENOTEMPTY" && code !== "EEXIST" && code !== "ENOENT")
|
|
461
|
+
throw error;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Slack's `ts` is seconds with a microsecond fraction, read exactly: a reply
|
|
466
|
+
* a fraction of a millisecond after `since` is still after it.
|
|
467
|
+
*/
|
|
468
|
+
function tsToMicros(ts) {
|
|
469
|
+
const [seconds = "0", fraction = ""] = ts.split(".");
|
|
470
|
+
return Number(seconds) * 1_000_000 + Number(fraction.padEnd(6, "0").slice(0, 6));
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* A reply's instant to the microsecond, as ISO 8601 with six fractional
|
|
474
|
+
* digits. Handed back as the next `since` it is exactly the reply's, so the
|
|
475
|
+
* reply is not returned twice and one a microsecond later is not lost.
|
|
476
|
+
*/
|
|
477
|
+
function microsToInstant(micros) {
|
|
478
|
+
const iso = new Date(Math.floor(micros / 1000)).toISOString();
|
|
479
|
+
return iso.replace(/Z$/, `${String(micros % 1000).padStart(3, "0")}Z`);
|
|
480
|
+
}
|
|
481
|
+
/** An instant to the microsecond: the digits past the millisecond are read too. */
|
|
482
|
+
function instantToMicros(instant) {
|
|
483
|
+
const ms = Date.parse(instant);
|
|
484
|
+
if (Number.isNaN(ms))
|
|
485
|
+
return Number.NaN;
|
|
486
|
+
const extra = /\.\d{3}(\d{1,3})Z$/.exec(instant)?.[1] ?? "";
|
|
487
|
+
return ms * 1000 + Number(extra.padEnd(3, "0"));
|
|
488
|
+
}
|
|
489
|
+
/** Exact, where going through milliseconds would drop the last three digits. */
|
|
490
|
+
function microsToTs(micros) {
|
|
491
|
+
return `${Math.floor(micros / 1_000_000)}.${String(micros % 1_000_000).padStart(6, "0")}`;
|
|
492
|
+
}
|
|
493
|
+
function msToTs(ms) {
|
|
494
|
+
return (ms / 1000).toFixed(6);
|
|
495
|
+
}
|
|
496
|
+
/** A name from a message, made into one path segment. */
|
|
497
|
+
function safeName(name) {
|
|
498
|
+
const cleaned = name.replace(/[^A-Za-z0-9._-]/g, "_").replace(/^\.+/, "_");
|
|
499
|
+
return cleaned || "file";
|
|
500
|
+
}
|
|
501
|
+
function messageOf(error) {
|
|
502
|
+
return error instanceof Error ? error.message : String(error);
|
|
503
|
+
}
|
|
504
|
+
//# sourceMappingURL=SlackConversation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SlackConversation.js","sourceRoot":"","sources":["../src/SlackConversation.ts"],"names":[],"mappings":"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"}
|