@aligndottech/connector-core 0.5.0 → 0.6.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/dist/fetchers/confluence.d.ts +2 -1
- package/dist/fetchers/confluence.d.ts.map +1 -1
- package/dist/fetchers/confluence.js +14 -9
- package/dist/fetchers/confluence.js.map +1 -1
- package/dist/fetchers/errors.d.ts +43 -5
- package/dist/fetchers/errors.d.ts.map +1 -1
- package/dist/fetchers/errors.js +115 -6
- package/dist/fetchers/errors.js.map +1 -1
- package/dist/fetchers/git.d.ts +2 -1
- package/dist/fetchers/git.d.ts.map +1 -1
- package/dist/fetchers/git.js +8 -1
- package/dist/fetchers/git.js.map +1 -1
- package/dist/fetchers/github.d.ts +2 -1
- package/dist/fetchers/github.d.ts.map +1 -1
- package/dist/fetchers/github.js +12 -2
- package/dist/fetchers/github.js.map +1 -1
- package/dist/fetchers/gitlab.d.ts +4 -2
- package/dist/fetchers/gitlab.d.ts.map +1 -1
- package/dist/fetchers/gitlab.js +35 -5
- package/dist/fetchers/gitlab.js.map +1 -1
- package/dist/fetchers/index.d.ts +1 -1
- package/dist/fetchers/index.d.ts.map +1 -1
- package/dist/fetchers/index.js +1 -1
- package/dist/fetchers/index.js.map +1 -1
- package/dist/fetchers/jira.d.ts +2 -1
- package/dist/fetchers/jira.d.ts.map +1 -1
- package/dist/fetchers/jira.js +14 -11
- package/dist/fetchers/jira.js.map +1 -1
- package/dist/fetchers/linear.d.ts +2 -1
- package/dist/fetchers/linear.d.ts.map +1 -1
- package/dist/fetchers/linear.js +22 -4
- package/dist/fetchers/linear.js.map +1 -1
- package/dist/fetchers/notion.d.ts +4 -1
- package/dist/fetchers/notion.d.ts.map +1 -1
- package/dist/fetchers/notion.js +64 -30
- package/dist/fetchers/notion.js.map +1 -1
- package/dist/fetchers/slack.d.ts +12 -3
- package/dist/fetchers/slack.d.ts.map +1 -1
- package/dist/fetchers/slack.js +232 -30
- package/dist/fetchers/slack.js.map +1 -1
- package/dist/fetchers/teams.d.ts +2 -1
- package/dist/fetchers/teams.d.ts.map +1 -1
- package/dist/fetchers/teams.js +21 -4
- package/dist/fetchers/teams.js.map +1 -1
- package/dist/fetchers/util/time.d.ts +15 -0
- package/dist/fetchers/util/time.d.ts.map +1 -0
- package/dist/fetchers/util/time.js +24 -0
- package/dist/fetchers/util/time.js.map +1 -0
- package/dist/fetchers/zoom.d.ts +6 -1
- package/dist/fetchers/zoom.d.ts.map +1 -1
- package/dist/fetchers/zoom.js +105 -35
- package/dist/fetchers/zoom.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types/fetcher.d.ts +54 -0
- package/dist/types/fetcher.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/fetchers/slack.js
CHANGED
|
@@ -1,14 +1,129 @@
|
|
|
1
1
|
import { fetch } from 'undici';
|
|
2
|
+
import { toIsoOrUndefined } from './util/time.js';
|
|
3
|
+
import { FetcherAuthError } from './errors.js';
|
|
2
4
|
async function slackGet(endpoint, token, params = {}) {
|
|
3
5
|
const qs = new URLSearchParams(params);
|
|
4
6
|
const res = await fetch(`https://slack.com/api/${endpoint}?${qs}`, {
|
|
5
7
|
headers: { Authorization: `Bearer ${token}` },
|
|
6
8
|
});
|
|
7
9
|
const data = (await res.json());
|
|
8
|
-
if (!data.ok)
|
|
9
|
-
|
|
10
|
+
if (!data.ok) {
|
|
11
|
+
// Slack answers HTTP 200 with ok:false and an error code; these codes are its 401.
|
|
12
|
+
const code = String(data.error);
|
|
13
|
+
if (SLACK_AUTH_ERRORS.has(code))
|
|
14
|
+
throw new FetcherAuthError('Slack', code);
|
|
15
|
+
throw new Error(`Slack API error on ${endpoint}: ${code}`);
|
|
16
|
+
}
|
|
10
17
|
return data;
|
|
11
18
|
}
|
|
19
|
+
/** Slack's error codes that mean the token itself was refused (its documented auth family). */
|
|
20
|
+
const SLACK_AUTH_ERRORS = new Set(['invalid_auth', 'not_authed', 'token_revoked', 'token_expired', 'account_inactive']);
|
|
21
|
+
/**
|
|
22
|
+
* Bounds, every one overridable through opts so tests never sleep and a large
|
|
23
|
+
* workspace can be widened deliberately. Each one, when it fires, becomes a line
|
|
24
|
+
* the user reads in the fetch report - a cap nobody is told about is
|
|
25
|
+
* indistinguishable from a thin tool (ALI-828).
|
|
26
|
+
*
|
|
27
|
+
* The numbers are derived, not measured: 200 channels at the 3-second Tier-2
|
|
28
|
+
* delay is 10 minutes of sleeping on its own, and 8 minutes is what the 15-minute
|
|
29
|
+
* `align setup --local` budget leaves for Slack once the other sources are paid
|
|
30
|
+
* for. The reference workspace (61 channels) reaches neither.
|
|
31
|
+
*/
|
|
32
|
+
const SLACK_MAX_CHANNELS = 200;
|
|
33
|
+
const SLACK_MAX_HISTORY_PAGES = 5; // ~5,000 messages inside the daysBack window
|
|
34
|
+
const SLACK_MAX_REPLY_PAGES = 3; // ~3,000 messages in one thread
|
|
35
|
+
/**
|
|
36
|
+
* Page sizes at each endpoint's documented maximum: "under 1000" for list and
|
|
37
|
+
* history, 1000 (the default and the max) for replies. 0.5.0 sent
|
|
38
|
+
* conversations.replies with no limit, so a thread of up to 1000 messages came
|
|
39
|
+
* back in one call; a smaller page here would cut a KEPT thread at
|
|
40
|
+
* maxReplyPages and move its bytes, which the golden contract forbids. Slack's
|
|
41
|
+
* 2025 15-object cap applies only to commercially distributed non-Marketplace
|
|
42
|
+
* apps; a user's own app, which is what the CLI token belongs to, is exempt.
|
|
43
|
+
*/
|
|
44
|
+
const SLACK_LIST_PAGE_SIZE = 999;
|
|
45
|
+
const SLACK_HISTORY_PAGE_SIZE = 999;
|
|
46
|
+
const SLACK_REPLIES_PAGE_SIZE = 1000;
|
|
47
|
+
const SLACK_TIME_BUDGET_MS = 8 * 60_000;
|
|
48
|
+
/**
|
|
49
|
+
* Message subtypes that are the WORKSPACE talking, not a person: joins, leaves,
|
|
50
|
+
* topic changes, pins, and app output. A thread made only of these is machinery,
|
|
51
|
+
* and on the Align demo workspace 35 of 39 captured "threads" were exactly that -
|
|
52
|
+
* a tombstone root under the Align app's own replies (ALI-828).
|
|
53
|
+
*
|
|
54
|
+
* A closed denylist rather than "any subtype at all": `thread_broadcast` is a
|
|
55
|
+
* human reply that was also posted to the channel, and `file_share` is a human
|
|
56
|
+
* sharing a file with a comment. Both are decision content and both carry a
|
|
57
|
+
* subtype. Every member here has its own case in fetchers.slack.test.ts.
|
|
58
|
+
*/
|
|
59
|
+
const SYSTEM_SUBTYPES = new Set([
|
|
60
|
+
'bot_message',
|
|
61
|
+
'tombstone',
|
|
62
|
+
'channel_join',
|
|
63
|
+
'channel_leave',
|
|
64
|
+
'channel_topic',
|
|
65
|
+
'channel_purpose',
|
|
66
|
+
'channel_name',
|
|
67
|
+
'channel_archive',
|
|
68
|
+
'channel_unarchive',
|
|
69
|
+
'group_join',
|
|
70
|
+
'group_leave',
|
|
71
|
+
'pinned_item',
|
|
72
|
+
'unpinned_item',
|
|
73
|
+
'bot_add',
|
|
74
|
+
'bot_remove',
|
|
75
|
+
'reminder_add',
|
|
76
|
+
'group_topic',
|
|
77
|
+
'group_purpose',
|
|
78
|
+
'group_name',
|
|
79
|
+
'group_archive',
|
|
80
|
+
'group_unarchive',
|
|
81
|
+
'channel_convert_to_private',
|
|
82
|
+
'channel_convert_to_public',
|
|
83
|
+
'channel_posting_permissions',
|
|
84
|
+
'ekm_access_denied',
|
|
85
|
+
]);
|
|
86
|
+
/**
|
|
87
|
+
* A message a person wrote. `bot_id` catches an app posting AS a user (which
|
|
88
|
+
* carries no subtype at all), and a message with no `user` has nobody to
|
|
89
|
+
* attribute it to.
|
|
90
|
+
*
|
|
91
|
+
* Deliberately not "is this the Align bot": the CLI's Slack path uses a user
|
|
92
|
+
* token, whose `auth.test` names the HUMAN, so an identity filter would delete
|
|
93
|
+
* the user's own messages. The shape test catches Align's bot as one member of
|
|
94
|
+
* the class of all bots, and stays vendor-neutral.
|
|
95
|
+
*/
|
|
96
|
+
function isHumanMessage(m) {
|
|
97
|
+
if (m.bot_id)
|
|
98
|
+
return false;
|
|
99
|
+
// A fired /remind or a Slackbot reply carries no bot_id and no subtype, only this user.
|
|
100
|
+
if (m.user === 'USLACKBOT')
|
|
101
|
+
return false;
|
|
102
|
+
if (m.subtype && SYSTEM_SUBTYPES.has(m.subtype))
|
|
103
|
+
return false;
|
|
104
|
+
return Boolean(m.user);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Follow `response_metadata.next_cursor` for up to `maxPages` pages, or until
|
|
108
|
+
* `enough(rows)` says to stop. `truncated` is true when a cursor was still there
|
|
109
|
+
* when paging stopped, which is the fact the report needs: rows exist that this
|
|
110
|
+
* read did not see.
|
|
111
|
+
*/
|
|
112
|
+
async function slackPaged(endpoint, token, params, rowsKey, maxPages, enough = () => false) {
|
|
113
|
+
const rows = [];
|
|
114
|
+
let cursor;
|
|
115
|
+
let pages = 0;
|
|
116
|
+
for (;;) {
|
|
117
|
+
const data = await slackGet(endpoint, token, { ...params, ...(cursor ? { cursor } : {}) });
|
|
118
|
+
rows.push(...(data[rowsKey] ?? []));
|
|
119
|
+
pages += 1;
|
|
120
|
+
cursor = data.response_metadata?.next_cursor || undefined;
|
|
121
|
+
if (!cursor)
|
|
122
|
+
return { rows, truncated: false };
|
|
123
|
+
if (enough(rows) || pages >= maxPages)
|
|
124
|
+
return { rows, truncated: true };
|
|
125
|
+
}
|
|
126
|
+
}
|
|
12
127
|
/** Resolve a Slack user id to a display name (cached - one users.info call per unique user). */
|
|
13
128
|
function makeUserResolver(token) {
|
|
14
129
|
const cache = new Map();
|
|
@@ -37,67 +152,154 @@ function makeUserResolver(token) {
|
|
|
37
152
|
}
|
|
38
153
|
/**
|
|
39
154
|
* Read-only personal Slack fetcher: threaded conversations (>=2 replies) the
|
|
40
|
-
* token can see, within `daysBack
|
|
41
|
-
*
|
|
155
|
+
* token can see, within `daysBack`, that hold at least one HUMAN message. Title
|
|
156
|
+
* and author come from the first human message, so a thread whose root was
|
|
157
|
+
* deleted or posted by a bot is still captured when a person spoke in it, and a
|
|
158
|
+
* thread made only of bot and system output is not captured at all.
|
|
159
|
+
*
|
|
160
|
+
* Channels, history and replies are all paged. Every bound (`maxChannels`,
|
|
161
|
+
* `maxHistoryPages`, `maxReplyPages`, `timeBudgetMs`) and every filter that
|
|
162
|
+
* fires is counted into the fetch report, so a thin result comes with its
|
|
163
|
+
* reason. A delay between channels keeps under Slack's Tier-2 rate limit
|
|
164
|
+
* (override via `interChannelDelayMs`).
|
|
42
165
|
*/
|
|
43
166
|
export class SlackFetcher {
|
|
44
167
|
async fetch(opts) {
|
|
168
|
+
return (await this.fetchWithReport(opts)).items;
|
|
169
|
+
}
|
|
170
|
+
async fetchWithReport(opts) {
|
|
45
171
|
const limit = opts.limit ?? 50;
|
|
46
172
|
const daysBack = opts.daysBack ?? 90;
|
|
47
173
|
const delayMs = opts.interChannelDelayMs ?? 3000;
|
|
48
|
-
const
|
|
174
|
+
const maxChannels = opts.maxChannels ?? SLACK_MAX_CHANNELS;
|
|
175
|
+
const maxHistoryPages = opts.maxHistoryPages ?? SLACK_MAX_HISTORY_PAGES;
|
|
176
|
+
const maxReplyPages = opts.maxReplyPages ?? SLACK_MAX_REPLY_PAGES;
|
|
177
|
+
const timeBudgetMs = opts.timeBudgetMs ?? SLACK_TIME_BUDGET_MS;
|
|
178
|
+
const startedAt = Date.now();
|
|
179
|
+
const oldest = String(Math.floor(startedAt / 1000) - daysBack * 86400);
|
|
49
180
|
await slackGet('auth.test', opts.token);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
181
|
+
// Read list pages until one shows MORE channels than the cap, so the report
|
|
182
|
+
// can name the surplus it saw rather than "some". At most one page past the
|
|
183
|
+
// cap is read, and a cursor left after that means the surplus is a floor.
|
|
184
|
+
const list = await slackPaged('conversations.list', opts.token, { types: 'public_channel,private_channel', exclude_archived: 'true', limit: String(SLACK_LIST_PAGE_SIZE) }, 'channels', Number.POSITIVE_INFINITY, (rows) => rows.length > maxChannels);
|
|
185
|
+
const channels = list.rows.slice(0, maxChannels);
|
|
186
|
+
const channelSurplus = list.rows.length - channels.length;
|
|
56
187
|
const resolveUser = makeUserResolver(opts.token);
|
|
57
188
|
const items = [];
|
|
189
|
+
let threadsScanned = 0;
|
|
190
|
+
let shortMessages = 0;
|
|
191
|
+
let noHumanThreads = 0;
|
|
192
|
+
let historyCut = 0;
|
|
193
|
+
let repliesCut = 0;
|
|
194
|
+
let channelsUnreadable = 0;
|
|
195
|
+
let threadsUnreadable = 0;
|
|
196
|
+
let channelsOutOfTime = 0;
|
|
58
197
|
for (let channelIndex = 0; channelIndex < channels.length; channelIndex++) {
|
|
59
198
|
const channel = channels[channelIndex];
|
|
60
199
|
if (items.length >= limit)
|
|
61
200
|
break;
|
|
62
|
-
|
|
63
|
-
|
|
201
|
+
if (channelIndex > 0) {
|
|
202
|
+
// Checked BEFORE paying the delay: the budget bounds the loop, not the sleep.
|
|
203
|
+
if (Date.now() - startedAt > timeBudgetMs) {
|
|
204
|
+
channelsOutOfTime = channels.length - channelIndex;
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
if (delayMs > 0)
|
|
64
208
|
await new Promise((r) => setTimeout(r, delayMs));
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const
|
|
71
|
-
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
const hist = await slackPaged('conversations.history', opts.token, { channel: channel.id, oldest, limit: String(SLACK_HISTORY_PAGE_SIZE) }, 'messages', maxHistoryPages);
|
|
212
|
+
if (hist.truncated)
|
|
213
|
+
historyCut += 1;
|
|
214
|
+
const threads = hist.rows.filter((m) => (m.reply_count ?? 0) >= 2);
|
|
215
|
+
shortMessages += hist.rows.length - threads.length;
|
|
72
216
|
for (const thread of threads) {
|
|
73
217
|
if (items.length >= limit)
|
|
74
218
|
break;
|
|
219
|
+
threadsScanned += 1;
|
|
75
220
|
try {
|
|
76
|
-
const replies = await
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
221
|
+
const replies = await slackPaged('conversations.replies', opts.token, { channel: channel.id, ts: thread.ts, limit: String(SLACK_REPLIES_PAGE_SIZE) }, 'messages', maxReplyPages);
|
|
222
|
+
// A truncated thread is still an item; dropping it would lose the
|
|
223
|
+
// decision to protect a byte count. The truncation is reported instead.
|
|
224
|
+
if (replies.truncated)
|
|
225
|
+
repliesCut += 1;
|
|
226
|
+
// Dedupe by ts: Slack does not say whether a cursor page of replies carries
|
|
227
|
+
// the parent again, and a repeated root would double its text.
|
|
228
|
+
const seenTs = new Set();
|
|
229
|
+
const allMsgs = [];
|
|
230
|
+
for (const m of replies.rows) {
|
|
231
|
+
if (seenTs.has(m.ts))
|
|
232
|
+
continue;
|
|
233
|
+
seenTs.add(m.ts);
|
|
234
|
+
allMsgs.push(m);
|
|
235
|
+
}
|
|
236
|
+
// The thread's identity comes from the first HUMAN message: a deleted or
|
|
237
|
+
// bot root has no title and nobody to attribute, but the conversation
|
|
238
|
+
// under it may be entirely real.
|
|
239
|
+
const firstHuman = allMsgs.find(isHumanMessage);
|
|
240
|
+
if (!firstHuman) {
|
|
241
|
+
noHumanThreads += 1; // machinery, not a conversation
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
// Every fetched message, bot replies included: a bot reply inside a human
|
|
245
|
+
// thread is often the CI output being discussed.
|
|
81
246
|
const text = allMsgs.map((m) => m.text ?? '').join('\n');
|
|
82
|
-
const author = await resolveUser(
|
|
247
|
+
const author = await resolveUser(firstHuman.user);
|
|
248
|
+
// The root's ts: epoch seconds with microseconds, so the thread is dated by when it started.
|
|
249
|
+
const createdAt = toIsoOrUndefined(Number(thread.ts) * 1000);
|
|
83
250
|
items.push({
|
|
84
251
|
source_url: `https://slack.com/archives/${channel.id}/p${thread.ts.replace('.', '')}`,
|
|
85
252
|
platform: 'slack',
|
|
86
253
|
raw_text: `[#${channel.name}] Thread:\n${text}`,
|
|
87
|
-
title: (
|
|
254
|
+
title: (firstHuman.text ?? `Thread in #${channel.name}`).slice(0, 80),
|
|
255
|
+
...(createdAt ? { created_at: createdAt } : {}),
|
|
88
256
|
...(author ? { author } : {}),
|
|
89
257
|
});
|
|
90
258
|
}
|
|
91
259
|
catch {
|
|
92
|
-
|
|
260
|
+
threadsUnreadable += 1;
|
|
93
261
|
}
|
|
94
262
|
}
|
|
95
263
|
}
|
|
96
264
|
catch {
|
|
97
|
-
|
|
265
|
+
channelsUnreadable += 1;
|
|
98
266
|
}
|
|
99
267
|
}
|
|
100
|
-
|
|
268
|
+
// A fixed order, so two runs over the same workspace print the same report.
|
|
269
|
+
const skips = [];
|
|
270
|
+
if (shortMessages > 0) {
|
|
271
|
+
skips.push({ kind: 'shape', count: shortMessages, detail: 'messages with fewer than 2 replies (this fetcher reads threads only)' });
|
|
272
|
+
}
|
|
273
|
+
if (noHumanThreads > 0) {
|
|
274
|
+
skips.push({ kind: 'shape', count: noHumanThreads, detail: 'threads with no human message (bot or system output only)' });
|
|
275
|
+
}
|
|
276
|
+
if (channelSurplus > 0) {
|
|
277
|
+
skips.push({
|
|
278
|
+
kind: 'page_cap',
|
|
279
|
+
count: channelSurplus,
|
|
280
|
+
detail: `${list.truncated ? 'or more ' : ''}channels not scanned (the first ${maxChannels} were; raise maxChannels)`,
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
if (historyCut > 0) {
|
|
284
|
+
skips.push({ kind: 'page_cap', count: historyCut, detail: `channels whose history was cut at ${maxHistoryPages} page(s) (raise maxHistoryPages)` });
|
|
285
|
+
}
|
|
286
|
+
if (repliesCut > 0) {
|
|
287
|
+
skips.push({ kind: 'page_cap', count: repliesCut, detail: `threads whose replies were cut at ${maxReplyPages} page(s) (raise maxReplyPages)` });
|
|
288
|
+
}
|
|
289
|
+
if (channelsOutOfTime > 0) {
|
|
290
|
+
skips.push({
|
|
291
|
+
kind: 'time_budget',
|
|
292
|
+
count: channelsOutOfTime,
|
|
293
|
+
detail: `channels not scanned (the ${Math.round(timeBudgetMs / 60_000)} minute Slack time budget ran out)`,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
if (channelsUnreadable > 0) {
|
|
297
|
+
skips.push({ kind: 'error', count: channelsUnreadable, detail: 'channels the token could not read' });
|
|
298
|
+
}
|
|
299
|
+
if (threadsUnreadable > 0) {
|
|
300
|
+
skips.push({ kind: 'error', count: threadsUnreadable, detail: 'threads whose replies could not be read' });
|
|
301
|
+
}
|
|
302
|
+
return { items, report: { platform: 'slack', scanned: threadsScanned, requested: limit, skips } };
|
|
101
303
|
}
|
|
102
304
|
}
|
|
103
305
|
//# sourceMappingURL=slack.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"slack.js","sourceRoot":"","sources":["../../src/fetchers/slack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"slack.js","sourceRoot":"","sources":["../../src/fetchers/slack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAE/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,KAAK,UAAU,QAAQ,CACrB,QAAgB,EAChB,KAAa,EACb,SAAiC,EAAE;IAEnC,MAAM,EAAE,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,yBAAyB,QAAQ,IAAI,EAAE,EAAE,EAAE;QACjE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;KAC9C,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA4B,CAAC;IAC3D,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACb,mFAAmF;QACnF,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,iBAAiB,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,MAAM,IAAI,gBAAgB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC3E,MAAM,IAAI,KAAK,CAAC,sBAAsB,QAAQ,KAAK,IAAI,EAAE,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,+FAA+F;AAC/F,MAAM,iBAAiB,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,CAAC,CAAC,CAAC;AAgBxH;;;;;;;;;;GAUG;AACH,MAAM,kBAAkB,GAAG,GAAG,CAAC;AAC/B,MAAM,uBAAuB,GAAG,CAAC,CAAC,CAAC,6CAA6C;AAChF,MAAM,qBAAqB,GAAG,CAAC,CAAC,CAAC,gCAAgC;AAEjE;;;;;;;;GAQG;AACH,MAAM,oBAAoB,GAAG,GAAG,CAAC;AACjC,MAAM,uBAAuB,GAAG,GAAG,CAAC;AACpC,MAAM,uBAAuB,GAAG,IAAI,CAAC;AACrC,MAAM,oBAAoB,GAAG,CAAC,GAAG,MAAM,CAAC;AAExC;;;;;;;;;;GAUG;AACH,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,aAAa;IACb,WAAW;IACX,cAAc;IACd,eAAe;IACf,eAAe;IACf,iBAAiB;IACjB,cAAc;IACd,iBAAiB;IACjB,mBAAmB;IACnB,YAAY;IACZ,aAAa;IACb,aAAa;IACb,eAAe;IACf,SAAS;IACT,YAAY;IACZ,cAAc;IACd,aAAa;IACb,eAAe;IACf,YAAY;IACZ,eAAe;IACf,iBAAiB;IACjB,4BAA4B;IAC5B,2BAA2B;IAC3B,6BAA6B;IAC7B,mBAAmB;CACpB,CAAC,CAAC;AAEH;;;;;;;;;GASG;AACH,SAAS,cAAc,CAAC,CAAe;IACrC,IAAI,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IAC3B,wFAAwF;IACxF,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,CAAC,CAAC,OAAO,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAC9D,OAAO,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC;AAED;;;;;GAKG;AACH,KAAK,UAAU,UAAU,CACvB,QAAgB,EAChB,KAAa,EACb,MAA8B,EAC9B,OAAgC,EAChC,QAAgB,EAChB,SAAiC,GAAG,EAAE,CAAC,KAAK;IAE5C,MAAM,IAAI,GAAQ,EAAE,CAAC;IACrB,IAAI,MAA0B,CAAC;IAC/B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,SAAS,CAAC;QACR,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,GAAG,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QAC3F,IAAI,CAAC,IAAI,CAAC,GAAG,CAAE,IAAI,CAAC,OAAO,CAAqB,IAAI,EAAE,CAAC,CAAC,CAAC;QACzD,KAAK,IAAI,CAAC,CAAC;QACX,MAAM,GAAI,IAAI,CAAC,iBAA0D,EAAE,WAAW,IAAI,SAAS,CAAC;QACpG,IAAI,CAAC,MAAM;YAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QAC/C,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,QAAQ;YAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IAC1E,CAAC;AACH,CAAC;AAED,gGAAgG;AAChG,SAAS,gBAAgB,CAAC,KAAa;IACrC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAoE,CAAC;IAC1F,OAAO,KAAK,EAAE,MAA0B,EAA0E,EAAE;QAClH,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QAC9B,IAAI,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC;YAAE,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,SAAS,CAAC;QAC7D,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,YAAY,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YACnE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAIzB,CAAC;YACF,MAAM,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,SAAS,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,OAAO,EAAE,YAAY,IAAI,CAAC,CAAC,IAAI,IAAI,MAAM,CAAC;YAChG,MAAM,QAAQ,GAAG;gBACf,IAAI;gBACJ,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxD,CAAC;YACF,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YAC5B,OAAO,QAAQ,CAAC;QAClB,CAAC;QAAC,MAAM,CAAC;YACP,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC,8BAA8B;YACvD,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,YAAY;IACvB,KAAK,CAAC,KAAK,CAAC,IAA6B;QACvC,OAAO,CAAC,MAAM,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,IAA6B;QACjD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAI,IAAI,CAAC,QAA+B,IAAI,EAAE,CAAC;QAC7D,MAAM,OAAO,GAAI,IAAI,CAAC,mBAA0C,IAAI,IAAI,CAAC;QACzE,MAAM,WAAW,GAAI,IAAI,CAAC,WAAkC,IAAI,kBAAkB,CAAC;QACnF,MAAM,eAAe,GAAI,IAAI,CAAC,eAAsC,IAAI,uBAAuB,CAAC;QAChG,MAAM,aAAa,GAAI,IAAI,CAAC,aAAoC,IAAI,qBAAqB,CAAC;QAC1F,MAAM,YAAY,GAAI,IAAI,CAAC,YAAmC,IAAI,oBAAoB,CAAC;QACvF,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,QAAQ,GAAG,KAAK,CAAC,CAAC;QAEvE,MAAM,QAAQ,CAAC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAExC,4EAA4E;QAC5E,4EAA4E;QAC5E,0EAA0E;QAC1E,MAAM,IAAI,GAAG,MAAM,UAAU,CAC3B,oBAAoB,EACpB,IAAI,CAAC,KAAK,EACV,EAAE,KAAK,EAAE,gCAAgC,EAAE,gBAAgB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,oBAAoB,CAAC,EAAE,EAC1G,UAAU,EACV,MAAM,CAAC,iBAAiB,EACxB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,WAAW,CACpC,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC;QACjD,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC;QAE1D,MAAM,WAAW,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACjD,MAAM,KAAK,GAAkB,EAAE,CAAC;QAChC,IAAI,cAAc,GAAG,CAAC,CAAC;QACvB,IAAI,aAAa,GAAG,CAAC,CAAC;QACtB,IAAI,cAAc,GAAG,CAAC,CAAC;QACvB,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,kBAAkB,GAAG,CAAC,CAAC;QAC3B,IAAI,iBAAiB,GAAG,CAAC,CAAC;QAC1B,IAAI,iBAAiB,GAAG,CAAC,CAAC;QAE1B,KAAK,IAAI,YAAY,GAAG,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAC,MAAM,EAAE,YAAY,EAAE,EAAE,CAAC;YAC1E,MAAM,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC;YACvC,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK;gBAAE,MAAM;YACjC,IAAI,YAAY,GAAG,CAAC,EAAE,CAAC;gBACrB,8EAA8E;gBAC9E,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,GAAG,YAAY,EAAE,CAAC;oBAC1C,iBAAiB,GAAG,QAAQ,CAAC,MAAM,GAAG,YAAY,CAAC;oBACnD,MAAM;gBACR,CAAC;gBACD,IAAI,OAAO,GAAG,CAAC;oBAAE,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;YACpE,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,UAAU,CAC3B,uBAAuB,EACvB,IAAI,CAAC,KAAK,EACV,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,uBAAuB,CAAC,EAAE,EACvE,UAAU,EACV,eAAe,CAChB,CAAC;gBACF,IAAI,IAAI,CAAC,SAAS;oBAAE,UAAU,IAAI,CAAC,CAAC;gBACpC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;gBACnE,aAAa,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;gBAEnD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;oBAC7B,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK;wBAAE,MAAM;oBACjC,cAAc,IAAI,CAAC,CAAC;oBACpB,IAAI,CAAC;wBACH,MAAM,OAAO,GAAG,MAAM,UAAU,CAC9B,uBAAuB,EACvB,IAAI,CAAC,KAAK,EACV,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,uBAAuB,CAAC,EAAE,EAC9E,UAAU,EACV,aAAa,CACd,CAAC;wBACF,kEAAkE;wBAClE,wEAAwE;wBACxE,IAAI,OAAO,CAAC,SAAS;4BAAE,UAAU,IAAI,CAAC,CAAC;wBACvC,4EAA4E;wBAC5E,+DAA+D;wBAC/D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAU,CAAC;wBACjC,MAAM,OAAO,GAAmB,EAAE,CAAC;wBACnC,KAAK,MAAM,CAAC,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;4BAC7B,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;gCAAE,SAAS;4BAC/B,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;4BACjB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;wBAClB,CAAC;wBACD,yEAAyE;wBACzE,sEAAsE;wBACtE,iCAAiC;wBACjC,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;wBAChD,IAAI,CAAC,UAAU,EAAE,CAAC;4BAChB,cAAc,IAAI,CAAC,CAAC,CAAC,gCAAgC;4BACrD,SAAS;wBACX,CAAC;wBAED,0EAA0E;wBAC1E,iDAAiD;wBACjD,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;wBACzD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;wBAClD,6FAA6F;wBAC7F,MAAM,SAAS,GAAG,gBAAgB,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;wBAC7D,KAAK,CAAC,IAAI,CAAC;4BACT,UAAU,EAAE,8BAA8B,OAAO,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE;4BACrF,QAAQ,EAAE,OAAO;4BACjB,QAAQ,EAAE,KAAK,OAAO,CAAC,IAAI,cAAc,IAAI,EAAE;4BAC/C,KAAK,EAAE,CAAC,UAAU,CAAC,IAAI,IAAI,cAAc,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;4BACrE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;4BAC/C,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;yBAC9B,CAAC,CAAC;oBACL,CAAC;oBAAC,MAAM,CAAC;wBACP,iBAAiB,IAAI,CAAC,CAAC;oBACzB,CAAC;gBACH,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,kBAAkB,IAAI,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;QAED,4EAA4E;QAC5E,MAAM,KAAK,GAAgB,EAAE,CAAC;QAC9B,IAAI,aAAa,GAAG,CAAC,EAAE,CAAC;YACtB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,sEAAsE,EAAE,CAAC,CAAC;QACtI,CAAC;QACD,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,2DAA2D,EAAE,CAAC,CAAC;QAC5H,CAAC;QACD,IAAI,cAAc,GAAG,CAAC,EAAE,CAAC;YACvB,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,UAAU;gBAChB,KAAK,EAAE,cAAc;gBACrB,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,mCAAmC,WAAW,2BAA2B;aACrH,CAAC,CAAC;QACL,CAAC;QACD,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,qCAAqC,eAAe,kCAAkC,EAAE,CAAC,CAAC;QACtJ,CAAC;QACD,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACnB,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,qCAAqC,aAAa,gCAAgC,EAAE,CAAC,CAAC;QAClJ,CAAC;QACD,IAAI,iBAAiB,GAAG,CAAC,EAAE,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,aAAa;gBACnB,KAAK,EAAE,iBAAiB;gBACxB,MAAM,EAAE,6BAA6B,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,MAAM,CAAC,oCAAoC;aAC3G,CAAC,CAAC;QACL,CAAC;QACD,IAAI,kBAAkB,GAAG,CAAC,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,mCAAmC,EAAE,CAAC,CAAC;QACxG,CAAC;QACD,IAAI,iBAAiB,GAAG,CAAC,EAAE,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,yCAAyC,EAAE,CAAC,CAAC;QAC7G,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC;IACpG,CAAC;CACF"}
|
package/dist/fetchers/teams.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ConnectorFetcher, ConnectorFetcherOptions, FetcherItem } from '../types/fetcher.js';
|
|
1
|
+
import type { ConnectorFetcher, ConnectorFetcherOptions, FetcherItem, FetchResult } from '../types/fetcher.js';
|
|
2
2
|
/**
|
|
3
3
|
* Read-only personal Teams fetcher: recent channel messages (+replies) across the
|
|
4
4
|
* caller's joined teams. Author = the message author. Needs admin-consented
|
|
@@ -6,5 +6,6 @@ import type { ConnectorFetcher, ConnectorFetcherOptions, FetcherItem } from '../
|
|
|
6
6
|
*/
|
|
7
7
|
export declare class TeamsFetcher implements ConnectorFetcher {
|
|
8
8
|
fetch(opts: ConnectorFetcherOptions): Promise<FetcherItem[]>;
|
|
9
|
+
fetchWithReport(opts: ConnectorFetcherOptions): Promise<FetchResult>;
|
|
9
10
|
}
|
|
10
11
|
//# sourceMappingURL=teams.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teams.d.ts","sourceRoot":"","sources":["../../src/fetchers/teams.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"teams.d.ts","sourceRoot":"","sources":["../../src/fetchers/teams.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AA6D/G;;;;GAIG;AACH,qBAAa,YAAa,YAAW,gBAAgB;IAC7C,KAAK,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAI5D,eAAe,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,WAAW,CAAC;CAiD3E"}
|
package/dist/fetchers/teams.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { fetch } from 'undici';
|
|
2
|
+
import { toIsoOrUndefined } from './util/time.js';
|
|
3
|
+
import { providerError, refusedBody } from './errors.js';
|
|
2
4
|
function stripHtml(html) {
|
|
3
5
|
return html.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
|
|
4
6
|
}
|
|
@@ -7,14 +9,22 @@ async function graphGet(path, token) {
|
|
|
7
9
|
headers: { Authorization: `Bearer ${token}` },
|
|
8
10
|
});
|
|
9
11
|
if (!res.ok) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
// Read the body once: the consent branch needs Graph's error code, and providerError
|
|
13
|
+
// needs the same bytes for the message.
|
|
14
|
+
const raw = await refusedBody(res);
|
|
15
|
+
let code = '';
|
|
16
|
+
try {
|
|
17
|
+
code = (JSON.parse(raw).error?.code) ?? '';
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
/* not JSON; no code to read */
|
|
21
|
+
}
|
|
12
22
|
if (res.status === 403 || code.includes('Authorization') || code.includes('Consent')) {
|
|
13
23
|
throw new Error('Teams requires admin consent for ChannelMessage.Read.All. ' +
|
|
14
24
|
'Ask your Microsoft 365 admin to grant consent, or see: ' +
|
|
15
25
|
'https://entra.microsoft.com/#view/Microsoft_AAD_IAM/ConsentPoliciesMenuBlade');
|
|
16
26
|
}
|
|
17
|
-
throw
|
|
27
|
+
throw await providerError('Teams', { status: res.status, text: async () => raw });
|
|
18
28
|
}
|
|
19
29
|
return res.json();
|
|
20
30
|
}
|
|
@@ -30,9 +40,13 @@ function extractText(body) {
|
|
|
30
40
|
*/
|
|
31
41
|
export class TeamsFetcher {
|
|
32
42
|
async fetch(opts) {
|
|
43
|
+
return (await this.fetchWithReport(opts)).items;
|
|
44
|
+
}
|
|
45
|
+
async fetchWithReport(opts) {
|
|
33
46
|
const limit = opts.limit ?? 50;
|
|
34
47
|
const teams = await graphGet('/me/joinedTeams', opts.token);
|
|
35
48
|
const items = [];
|
|
49
|
+
let scanned = 0;
|
|
36
50
|
for (const team of teams.value) {
|
|
37
51
|
if (items.length >= limit)
|
|
38
52
|
break;
|
|
@@ -45,6 +59,7 @@ export class TeamsFetcher {
|
|
|
45
59
|
for (const msg of msgs.value) {
|
|
46
60
|
if (items.length >= limit)
|
|
47
61
|
break;
|
|
62
|
+
scanned += 1;
|
|
48
63
|
const mainText = extractText(msg.body);
|
|
49
64
|
const replyTexts = (msg.replies ?? []).map((r) => extractText(r.body)).filter(Boolean);
|
|
50
65
|
const raw_text = [
|
|
@@ -56,11 +71,13 @@ export class TeamsFetcher {
|
|
|
56
71
|
.filter(Boolean)
|
|
57
72
|
.join('\n');
|
|
58
73
|
const fromName = msg.from?.user?.displayName;
|
|
74
|
+
const createdAt = toIsoOrUndefined(msg.createdDateTime);
|
|
59
75
|
items.push({
|
|
60
76
|
source_url: msg.webUrl ?? 'https://teams.microsoft.com',
|
|
61
77
|
platform: 'teams',
|
|
62
78
|
raw_text,
|
|
63
79
|
title: (msg.subject ?? mainText).slice(0, 80) || `Message in ${team.displayName}`,
|
|
80
|
+
...(createdAt ? { created_at: createdAt } : {}),
|
|
64
81
|
...(fromName ? { author: { name: fromName } } : {}),
|
|
65
82
|
});
|
|
66
83
|
}
|
|
@@ -70,7 +87,7 @@ export class TeamsFetcher {
|
|
|
70
87
|
}
|
|
71
88
|
}
|
|
72
89
|
}
|
|
73
|
-
return items;
|
|
90
|
+
return { items, report: { platform: 'teams', scanned, requested: limit, skips: [] } };
|
|
74
91
|
}
|
|
75
92
|
}
|
|
76
93
|
//# sourceMappingURL=teams.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"teams.js","sourceRoot":"","sources":["../../src/fetchers/teams.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"teams.js","sourceRoot":"","sources":["../../src/fetchers/teams.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalise a source timestamp to ISO-8601 Z, or undefined.
|
|
3
|
+
*
|
|
4
|
+
* Undefined rather than a fallback: a null date says "unknown", and a plausible
|
|
5
|
+
* wrong date is indistinguishable from a measurement to everything downstream.
|
|
6
|
+
* NaN is checked explicitly because every comparison against NaN is false in
|
|
7
|
+
* both directions, so an unchecked bad date does not error, it silently vacates
|
|
8
|
+
* whatever filter reads it later.
|
|
9
|
+
*
|
|
10
|
+
* One writer for the normalisation, so ten fetchers cannot disagree about what
|
|
11
|
+
* "ISO" means. Accepts epoch milliseconds as a number for sources that hand out
|
|
12
|
+
* a numeric timestamp (Slack's `ts` is epoch seconds; callers multiply).
|
|
13
|
+
*/
|
|
14
|
+
export declare function toIsoOrUndefined(value: string | number | undefined | null): string | undefined;
|
|
15
|
+
//# sourceMappingURL=time.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.d.ts","sourceRoot":"","sources":["../../../src/fetchers/util/time.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,SAAS,CAO9F"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Normalise a source timestamp to ISO-8601 Z, or undefined.
|
|
3
|
+
*
|
|
4
|
+
* Undefined rather than a fallback: a null date says "unknown", and a plausible
|
|
5
|
+
* wrong date is indistinguishable from a measurement to everything downstream.
|
|
6
|
+
* NaN is checked explicitly because every comparison against NaN is false in
|
|
7
|
+
* both directions, so an unchecked bad date does not error, it silently vacates
|
|
8
|
+
* whatever filter reads it later.
|
|
9
|
+
*
|
|
10
|
+
* One writer for the normalisation, so ten fetchers cannot disagree about what
|
|
11
|
+
* "ISO" means. Accepts epoch milliseconds as a number for sources that hand out
|
|
12
|
+
* a numeric timestamp (Slack's `ts` is epoch seconds; callers multiply).
|
|
13
|
+
*/
|
|
14
|
+
export function toIsoOrUndefined(value) {
|
|
15
|
+
if (value === undefined || value === null || value === '')
|
|
16
|
+
return undefined;
|
|
17
|
+
const ms = typeof value === 'number' ? value : Date.parse(value);
|
|
18
|
+
// A zero or negative instant is not a source date either: Number('') is 0, and
|
|
19
|
+
// an epoch stamp in a decision graph is a fabrication with a plausible face.
|
|
20
|
+
if (Number.isNaN(ms) || ms <= 0)
|
|
21
|
+
return undefined;
|
|
22
|
+
return new Date(ms).toISOString();
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=time.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"time.js","sourceRoot":"","sources":["../../../src/fetchers/util/time.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAyC;IACxE,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,SAAS,CAAC;IAC5E,MAAM,EAAE,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACjE,+EAA+E;IAC/E,6EAA6E;IAC7E,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAClD,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;AACpC,CAAC"}
|
package/dist/fetchers/zoom.d.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import type { ConnectorFetcher, ConnectorFetcherOptions, FetcherItem } from '../types/fetcher.js';
|
|
1
|
+
import type { ConnectorFetcher, ConnectorFetcherOptions, FetcherItem, FetchResult } from '../types/fetcher.js';
|
|
2
2
|
/**
|
|
3
3
|
* Read-only personal Zoom fetcher: cloud-recording transcripts (VTT), parsed to
|
|
4
4
|
* plain text. Author = the meeting host. `uuid` (single meeting) rides on opts.
|
|
5
|
+
* Lists `/users/me/recordings` in 30-day windows back through `daysBack`
|
|
6
|
+
* (default 90), paging each with `next_page_token` up to `limit`; a meeting
|
|
7
|
+
* with no completed transcript, or one whose transcript will not download, is
|
|
8
|
+
* counted into the report rather than dropped in silence.
|
|
5
9
|
*/
|
|
6
10
|
export declare class ZoomFetcher implements ConnectorFetcher {
|
|
7
11
|
fetch(opts: ConnectorFetcherOptions): Promise<FetcherItem[]>;
|
|
12
|
+
fetchWithReport(opts: ConnectorFetcherOptions): Promise<FetchResult>;
|
|
8
13
|
}
|
|
9
14
|
//# sourceMappingURL=zoom.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zoom.d.ts","sourceRoot":"","sources":["../../src/fetchers/zoom.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"zoom.d.ts","sourceRoot":"","sources":["../../src/fetchers/zoom.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,WAAW,EAAE,WAAW,EAAa,MAAM,qBAAqB,CAAC;AA4E1H;;;;;;;GAOG;AACH,qBAAa,WAAY,YAAW,gBAAgB;IAC5C,KAAK,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAI5D,eAAe,CAAC,IAAI,EAAE,uBAAuB,GAAG,OAAO,CAAC,WAAW,CAAC;CA8E3E"}
|