wldx-cli 1.0.0 → 1.1.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 +15 -20
- package/package.json +9 -6
- package/src/chat/format.mjs +40 -8
- package/src/chat/lib.mjs +71 -23
- package/src/chat/watch.mjs +46 -15
- package/src/cli.mjs +13 -252
- package/src/commands/calls.mjs +135 -32
- package/src/commands/channel.mjs +99 -21
- package/src/commands/chat.mjs +77 -22
- package/src/commands/conversations.mjs +146 -29
- package/src/commands/file.mjs +13 -5
- package/src/commands/inbox.mjs +20 -13
- package/src/commands/index.mjs +5 -29
- package/src/commands/live.mjs +82 -24
- package/src/commands/meetings.mjs +114 -27
- package/src/commands/phone.mjs +145 -33
- package/src/commands/reports.mjs +219 -44
- package/src/commands/section.mjs +49 -8
- package/src/commands/user.mjs +42 -13
- package/src/history/text.mjs +30 -4
- package/src/history/wda.mjs +20 -10
- package/src/live/socket.mjs +25 -7
- package/src/live/stats.mjs +41 -9
- package/src/live/text.mjs +122 -23
- package/src/live/views.mjs +93 -23
- package/src/phone/control.mjs +93 -33
- package/src/phone/events.mjs +38 -7
- package/src/phone/pbx.mjs +29 -11
- package/src/phone/sip.mjs +87 -17
- package/src/phone/xml.mjs +2 -1
- package/src/stream.mjs +10 -5
- package/src/version.mjs +0 -1
- package/src/auth.mjs +0 -274
- package/src/commands/login.mjs +0 -126
- package/src/commands/profile.mjs +0 -51
- package/src/commands/status.mjs +0 -95
- package/src/completion.mjs +0 -132
- package/src/errors.mjs +0 -30
- package/src/files.mjs +0 -98
- package/src/format.mjs +0 -82
- package/src/http.mjs +0 -64
- package/src/services.mjs +0 -58
package/src/commands/channel.mjs
CHANGED
|
@@ -1,16 +1,42 @@
|
|
|
1
1
|
// wx channel: channels of the Wildix stream service and their members, as the logged-in user (the agent skill's
|
|
2
2
|
// channels.sh, members.sh and search.sh channels).
|
|
3
|
-
import {UsageError} from '
|
|
4
|
-
import {localTime, paint, table} from '
|
|
5
|
-
import {apiPath, query} from '
|
|
3
|
+
import {UsageError} from 'wldx-core/errors';
|
|
4
|
+
import {localTime, paint, table} from 'wldx-core/format';
|
|
5
|
+
import {apiPath, query} from 'wldx-core/http';
|
|
6
6
|
import {channelId} from '../stream.mjs';
|
|
7
7
|
import {channelDetails, channelLines, channelUserIds, done, nextPage} from '../chat/format.mjs';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
CHANNEL_ID,
|
|
10
|
+
NOT_IMPLEMENTED,
|
|
11
|
+
api,
|
|
12
|
+
checkId,
|
|
13
|
+
checkInt,
|
|
14
|
+
nameIn,
|
|
15
|
+
newChannelId,
|
|
16
|
+
notImplemented,
|
|
17
|
+
withNames,
|
|
18
|
+
} from '../chat/lib.mjs';
|
|
9
19
|
|
|
10
20
|
const ACCESS = ['private', 'public'];
|
|
11
21
|
const UNSET = [
|
|
12
|
-
'subject',
|
|
13
|
-
'
|
|
22
|
+
'subject',
|
|
23
|
+
'description',
|
|
24
|
+
'picture',
|
|
25
|
+
'pictureColor',
|
|
26
|
+
'access',
|
|
27
|
+
'broadcast',
|
|
28
|
+
'context',
|
|
29
|
+
'contextPtt',
|
|
30
|
+
'contextEvents',
|
|
31
|
+
'whatsappStatus',
|
|
32
|
+
'messagingStatus',
|
|
33
|
+
'kiteAssign',
|
|
34
|
+
'service',
|
|
35
|
+
'serviceTitle',
|
|
36
|
+
'assignee',
|
|
37
|
+
'sms',
|
|
38
|
+
'mms',
|
|
39
|
+
'whatsapp',
|
|
14
40
|
];
|
|
15
41
|
const SORT = ['relevance', 'recency', 'recency_asc'];
|
|
16
42
|
const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
@@ -20,7 +46,8 @@ function email(value, what = '--invite') {
|
|
|
20
46
|
return value;
|
|
21
47
|
}
|
|
22
48
|
|
|
23
|
-
const channelResult = async (ctx, client, r) =>
|
|
49
|
+
const channelResult = async (ctx, client, r) =>
|
|
50
|
+
withNames(ctx, r, [...channelUserIds(r?.channel), r?.channel?.createdBy], client);
|
|
24
51
|
const channelText = (r, ctx) => (r?.channel ? channelDetails(r.channel, r, ctx) : 'No channel.');
|
|
25
52
|
|
|
26
53
|
const get = {
|
|
@@ -52,7 +79,8 @@ const queryCmd = {
|
|
|
52
79
|
|
|
53
80
|
const search = {
|
|
54
81
|
summary: 'search your channels by subject or member',
|
|
55
|
-
usage:
|
|
82
|
+
usage:
|
|
83
|
+
'wx channel search <query> [--type direct|group] [--member USERID] [--size 1-50] [--cursor C] [--sort relevance|recency|recency_asc]',
|
|
56
84
|
options: {
|
|
57
85
|
type: {type: 'string', choices: ['direct', 'group'], description: 'only 1:1 or group channels'},
|
|
58
86
|
member: {type: 'string', placeholder: 'USERID', description: 'only channels with this member'},
|
|
@@ -73,7 +101,12 @@ const search = {
|
|
|
73
101
|
});
|
|
74
102
|
const client = api(ctx);
|
|
75
103
|
const r = await client.call('GET', `/search/channels${q}`);
|
|
76
|
-
return withNames(
|
|
104
|
+
return withNames(
|
|
105
|
+
ctx,
|
|
106
|
+
r,
|
|
107
|
+
(r?.results ?? []).flatMap((x) => channelUserIds(x.channel)),
|
|
108
|
+
client,
|
|
109
|
+
);
|
|
77
110
|
},
|
|
78
111
|
text(r, ctx) {
|
|
79
112
|
const list = (r?.results ?? []).map((x) => x.channel).filter(Boolean);
|
|
@@ -84,7 +117,8 @@ const search = {
|
|
|
84
117
|
|
|
85
118
|
const create = {
|
|
86
119
|
summary: 'create a group channel',
|
|
87
|
-
usage:
|
|
120
|
+
usage:
|
|
121
|
+
'wx channel create --subject S [--description D] [--access private|public] [--member USERID]... [--invite EMAIL]... [--id 8-4-4-4-8] [--auto-transcribe | --no-auto-transcribe]',
|
|
88
122
|
details: [
|
|
89
123
|
'An id of 8-4-4-4-8 characters is always sent (one is made when --id is not given): without it the service makes',
|
|
90
124
|
'a 36-character id that none of its own calls accepts. --auto-transcribe makes a conference in the channel start',
|
|
@@ -104,7 +138,8 @@ const create = {
|
|
|
104
138
|
async run(ctx) {
|
|
105
139
|
const {values} = ctx;
|
|
106
140
|
if (!values.subject) throw new UsageError('--subject is required', ctx.usage());
|
|
107
|
-
if (values['auto-transcribe'] && values['no-auto-transcribe'])
|
|
141
|
+
if (values['auto-transcribe'] && values['no-auto-transcribe'])
|
|
142
|
+
throw new UsageError('Give --auto-transcribe or --no-auto-transcribe, not both', ctx.usage());
|
|
108
143
|
const id = values.id ?? newChannelId();
|
|
109
144
|
if (!CHANNEL_ID.test(id)) throw new UsageError(`--id must look like 8-4-4-4-8 characters of [0-9a-z_]: ${id}`);
|
|
110
145
|
const body = {channelId: id, subject: values.subject};
|
|
@@ -138,7 +173,11 @@ const direct = {
|
|
|
138
173
|
args: [0, 0],
|
|
139
174
|
async run(ctx) {
|
|
140
175
|
const client = api(ctx);
|
|
141
|
-
return channelResult(
|
|
176
|
+
return channelResult(
|
|
177
|
+
ctx,
|
|
178
|
+
client,
|
|
179
|
+
await client.call('POST', '/channels/direct/get-or-create', directBody(ctx.values, ctx.usage())),
|
|
180
|
+
);
|
|
142
181
|
},
|
|
143
182
|
text: channelText,
|
|
144
183
|
};
|
|
@@ -171,7 +210,8 @@ const update = {
|
|
|
171
210
|
const set = {};
|
|
172
211
|
for (const k of ['subject', 'description', 'access']) if (values[k] !== undefined) set[k] = values[k];
|
|
173
212
|
const unset = values.unset ?? [];
|
|
174
|
-
if (!Object.keys(set).length && !unset.length)
|
|
213
|
+
if (!Object.keys(set).length && !unset.length)
|
|
214
|
+
throw new UsageError('Nothing to change: give --subject, --description, --access or --unset', ctx.usage());
|
|
175
215
|
const body = {set};
|
|
176
216
|
if (unset.length) body.unset = unset;
|
|
177
217
|
return api(ctx).call('PATCH', apiPath`/channels/${ch}`, body);
|
|
@@ -200,7 +240,11 @@ const del = {
|
|
|
200
240
|
args: [1, 1],
|
|
201
241
|
async run(ctx) {
|
|
202
242
|
const ch = channelId(ctx.positionals[0]);
|
|
203
|
-
if (!ctx.values.yes)
|
|
243
|
+
if (!ctx.values.yes)
|
|
244
|
+
throw new UsageError(
|
|
245
|
+
'delete removes the channel for everyone: confirm with the person, then add --yes',
|
|
246
|
+
ctx.usage(),
|
|
247
|
+
);
|
|
204
248
|
return api(ctx).call('DELETE', apiPath`/channels/${ch}`);
|
|
205
249
|
},
|
|
206
250
|
text: done('Deleted'),
|
|
@@ -216,16 +260,26 @@ const members = {
|
|
|
216
260
|
args: [1, 1],
|
|
217
261
|
async run(ctx) {
|
|
218
262
|
const ch = channelId(ctx.positionals[0]);
|
|
219
|
-
const q = query({
|
|
263
|
+
const q = query({
|
|
264
|
+
limit: checkInt('limit', ctx.values.limit, 1, 100),
|
|
265
|
+
offset: checkInt('offset', ctx.values.offset, 0, 1000),
|
|
266
|
+
});
|
|
220
267
|
const client = api(ctx);
|
|
221
268
|
const r = await client.call('GET', apiPath`/channels/${ch}/members` + q);
|
|
222
|
-
return withNames(
|
|
269
|
+
return withNames(
|
|
270
|
+
ctx,
|
|
271
|
+
r,
|
|
272
|
+
(r?.members ?? []).map((m) => m.userId),
|
|
273
|
+
client,
|
|
274
|
+
);
|
|
223
275
|
},
|
|
224
276
|
text(r, ctx) {
|
|
225
277
|
const c = paint(ctx);
|
|
226
278
|
const list = r?.members ?? [];
|
|
227
279
|
if (!list.length) return 'No members.';
|
|
228
|
-
return table(
|
|
280
|
+
return table(
|
|
281
|
+
list.map((m) => [c.bold(nameIn(r, m.userId)), m.userId, m.role ?? '', c.dim(`joined ${localTime(m.joinedAt)}`)]),
|
|
282
|
+
);
|
|
229
283
|
},
|
|
230
284
|
};
|
|
231
285
|
|
|
@@ -242,7 +296,17 @@ const member = {
|
|
|
242
296
|
const r = await client.call('GET', apiPath`/channels/${ch}/members/${user}`);
|
|
243
297
|
return withNames(ctx, r, [r?.member?.userId], client);
|
|
244
298
|
},
|
|
245
|
-
text: (r, ctx) =>
|
|
299
|
+
text: (r, ctx) =>
|
|
300
|
+
r?.member
|
|
301
|
+
? table([
|
|
302
|
+
[
|
|
303
|
+
paint(ctx).bold(nameIn(r, r.member.userId)),
|
|
304
|
+
r.member.userId,
|
|
305
|
+
r.member.role ?? '',
|
|
306
|
+
`joined ${localTime(r.member.joinedAt)}`,
|
|
307
|
+
],
|
|
308
|
+
])
|
|
309
|
+
: 'No member.',
|
|
246
310
|
};
|
|
247
311
|
|
|
248
312
|
const add = {
|
|
@@ -291,13 +355,24 @@ const readStatus = {
|
|
|
291
355
|
const ch = channelId(ctx.positionals[0]);
|
|
292
356
|
const client = api(ctx);
|
|
293
357
|
const r = await client.call('GET', apiPath`/channels/${ch}/read-status`);
|
|
294
|
-
return withNames(
|
|
358
|
+
return withNames(
|
|
359
|
+
ctx,
|
|
360
|
+
r,
|
|
361
|
+
(r?.members ?? []).map((m) => m.userId),
|
|
362
|
+
client,
|
|
363
|
+
);
|
|
295
364
|
},
|
|
296
365
|
text(r, ctx) {
|
|
297
366
|
const c = paint(ctx);
|
|
298
367
|
const list = r?.members ?? [];
|
|
299
368
|
if (!list.length) return 'No members.';
|
|
300
|
-
return table(
|
|
369
|
+
return table(
|
|
370
|
+
list.map((m) => [
|
|
371
|
+
c.bold(nameIn(r, m.userId)),
|
|
372
|
+
m.lastReadVisibleSeq === undefined ? c.dim('nothing read') : `up to #${m.lastReadVisibleSeq}`,
|
|
373
|
+
c.dim(localTime(m.lastReadAt)),
|
|
374
|
+
]),
|
|
375
|
+
);
|
|
301
376
|
},
|
|
302
377
|
};
|
|
303
378
|
|
|
@@ -312,7 +387,10 @@ const channel = {
|
|
|
312
387
|
direct,
|
|
313
388
|
'direct-id': directId,
|
|
314
389
|
update,
|
|
315
|
-
join: simple('join', `join a public channel${NOT_IMPLEMENTED}`, 'POST', '/join', {
|
|
390
|
+
join: simple('join', `join a public channel${NOT_IMPLEMENTED}`, 'POST', '/join', {
|
|
391
|
+
details: notImplemented('JoinChannel'),
|
|
392
|
+
text: 'Joined',
|
|
393
|
+
}),
|
|
316
394
|
leave: simple('leave', 'leave a channel', 'POST', '/leave', {text: 'Left'}),
|
|
317
395
|
delete: del,
|
|
318
396
|
members,
|
package/src/commands/chat.mjs
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
// wx chat: channels and messages of the Wildix stream service, as the logged-in user (the agent skill's
|
|
2
2
|
// messages.sh, mentions.sh, search.sh messages and watch.sh). The inbox state is `wx inbox`, channels and their
|
|
3
3
|
// members `wx channel`, files `wx file`, people `wx user`.
|
|
4
|
-
import {AuthError, UsageError} from '
|
|
5
|
-
import {localTime, oneLine, paint, table} from '
|
|
6
|
-
import {apiPath, query} from '
|
|
4
|
+
import {AuthError, UsageError} from 'wldx-core/errors';
|
|
5
|
+
import {localTime, oneLine, paint, table} from 'wldx-core/format';
|
|
6
|
+
import {apiPath, query} from 'wldx-core/http';
|
|
7
7
|
import {channelId, messageId} from '../stream.mjs';
|
|
8
8
|
import {channelLines, channelUserIds, done, messageLines, messageText, nextPage} from '../chat/format.mjs';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
api,
|
|
11
|
+
attachmentOf,
|
|
12
|
+
checkId,
|
|
13
|
+
checkInt,
|
|
14
|
+
checkReaction,
|
|
15
|
+
meId,
|
|
16
|
+
messageUserIds,
|
|
17
|
+
nameIn,
|
|
18
|
+
readText,
|
|
19
|
+
uploadFile,
|
|
20
|
+
withNames,
|
|
21
|
+
} from '../chat/lib.mjs';
|
|
10
22
|
import {EVENT_CHOICES, EVENT_GROUPS, eventText, watch} from '../chat/watch.mjs';
|
|
11
23
|
|
|
12
24
|
const SEQ_MAX = 999_999_999_999;
|
|
@@ -19,7 +31,8 @@ async function ownMessage(ctx, client, ch, msg) {
|
|
|
19
31
|
const r = await client.call('GET', apiPath`/channels/${ch}/messages/${msg}`);
|
|
20
32
|
const author = r?.message?.userId;
|
|
21
33
|
const me = await meId(ctx, client);
|
|
22
|
-
if (!author || author !== me)
|
|
34
|
+
if (!author || author !== me)
|
|
35
|
+
throw new AuthError(`The message is not yours (author: ${author ?? 'unknown'}, you: ${me}). Nothing was changed.`);
|
|
23
36
|
return r.message;
|
|
24
37
|
}
|
|
25
38
|
|
|
@@ -36,7 +49,9 @@ export function editBody(message, text, markdown) {
|
|
|
36
49
|
forward: ref(message.forward),
|
|
37
50
|
markdown: markdown ? true : message.markdown,
|
|
38
51
|
};
|
|
39
|
-
return Object.fromEntries(
|
|
52
|
+
return Object.fromEntries(
|
|
53
|
+
Object.entries(body).filter(([, v]) => v !== undefined && v !== null && !(Array.isArray(v) && !v.length)),
|
|
54
|
+
);
|
|
40
55
|
}
|
|
41
56
|
|
|
42
57
|
const sentText = (verb) => (r, ctx) => {
|
|
@@ -56,7 +71,10 @@ const list = {
|
|
|
56
71
|
async run(ctx) {
|
|
57
72
|
const {values} = ctx;
|
|
58
73
|
const client = api(ctx);
|
|
59
|
-
const r = await client.call(
|
|
74
|
+
const r = await client.call(
|
|
75
|
+
'GET',
|
|
76
|
+
`/channels${query({filter: values.filter, section: values.section, cursor: values.cursor})}`,
|
|
77
|
+
);
|
|
60
78
|
return withNames(ctx, r, (r?.channels ?? []).flatMap(channelUserIds), client);
|
|
61
79
|
},
|
|
62
80
|
text(r, ctx) {
|
|
@@ -91,7 +109,8 @@ const read = {
|
|
|
91
109
|
if (!messages.length) return 'No messages.';
|
|
92
110
|
const lines = messageLines(messages, r, ctx);
|
|
93
111
|
const oldest = Math.min(...messages.map((m) => m.timelineSeq ?? Infinity));
|
|
94
|
-
if (messages.length >= 30 && Number.isFinite(oldest) && oldest > 0)
|
|
112
|
+
if (messages.length >= 30 && Number.isFinite(oldest) && oldest > 0)
|
|
113
|
+
lines.push(paint(ctx).dim(`Older: --to ${oldest - 1}`));
|
|
95
114
|
return lines;
|
|
96
115
|
},
|
|
97
116
|
};
|
|
@@ -99,7 +118,8 @@ const read = {
|
|
|
99
118
|
const send = {
|
|
100
119
|
summary: 'send a message to a channel ("-" reads the text from stdin)',
|
|
101
120
|
usage: 'wx chat send <channel> <text|-> [--markdown] [--reply-to MESSAGE] [--mention USERID]... [--file PATH]...',
|
|
102
|
-
details:
|
|
121
|
+
details:
|
|
122
|
+
'Sends as the logged-in user. --file uploads the file and attaches it (files in credential folders are refused).',
|
|
103
123
|
options: {
|
|
104
124
|
markdown: {type: 'boolean', description: 'the text is markdown'},
|
|
105
125
|
'reply-to': {type: 'string', description: 'quote this message (id or inbox link)'},
|
|
@@ -151,8 +171,11 @@ const get = {
|
|
|
151
171
|
];
|
|
152
172
|
if (m.quote) rows.push(['Reply to', `${nameIn(r, m.quote.userId)}: ${oneLine(m.quote.text ?? '', 100)}`]);
|
|
153
173
|
if (m.mentions?.length) rows.push(['Mentions', m.mentions.map((id) => nameIn(r, id)).join(', ')]);
|
|
154
|
-
for (const f of m.attachments ?? [])
|
|
155
|
-
|
|
174
|
+
for (const f of m.attachments ?? [])
|
|
175
|
+
rows.push(['File', `${f.name} (${f.mime ?? '?'}, ${f.size ?? '?'} bytes) fsId ${f.fsId}`]);
|
|
176
|
+
const counts = Object.entries(m.reactionCounts ?? {})
|
|
177
|
+
.map(([t, n]) => `${t} ${n}`)
|
|
178
|
+
.join(', ');
|
|
156
179
|
if (counts) rows.push(['Reactions', counts]);
|
|
157
180
|
if (m.pinned) rows.push(['Pinned', `${localTime(m.pinnedAt)}${m.pinnedBy ? ` by ${nameIn(r, m.pinnedBy)}` : ''}`]);
|
|
158
181
|
const body = m.messageType === 'deleted' || m.deletedAt ? ['(deleted)'] : String(m.text ?? '').split('\n');
|
|
@@ -184,12 +207,17 @@ const edit = {
|
|
|
184
207
|
const del = {
|
|
185
208
|
summary: 'delete your own message, for everyone (needs --yes)',
|
|
186
209
|
usage: 'wx chat delete <channel> <message> --yes',
|
|
187
|
-
details:
|
|
210
|
+
details:
|
|
211
|
+
'Only your own messages (else exit 2, nothing changed). Confirm with the person first: --yes is not the confirmation.',
|
|
188
212
|
options: {yes: {type: 'boolean', description: 'yes, delete it for everyone'}},
|
|
189
213
|
args: [2, 2],
|
|
190
214
|
async run(ctx) {
|
|
191
215
|
const {ch, msg} = target(ctx.positionals);
|
|
192
|
-
if (!ctx.values.yes)
|
|
216
|
+
if (!ctx.values.yes)
|
|
217
|
+
throw new UsageError(
|
|
218
|
+
'delete removes the message for everyone: confirm with the person, then add --yes',
|
|
219
|
+
ctx.usage(),
|
|
220
|
+
);
|
|
193
221
|
const client = api(ctx);
|
|
194
222
|
await ownMessage(ctx, client, ch, msg);
|
|
195
223
|
return client.call('DELETE', apiPath`/channels/${ch}/messages/${msg}`);
|
|
@@ -226,7 +254,8 @@ const reactCmd = (add) => ({
|
|
|
226
254
|
const reactions = {
|
|
227
255
|
summary: 'who reacted to a message: one entry per person and type',
|
|
228
256
|
usage: 'wx chat reactions <channel> <message> [--type TYPE] [--users]',
|
|
229
|
-
details:
|
|
257
|
+
details:
|
|
258
|
+
'The JSON gives user ids; --users adds the name, e-mail and bot of each (the text output always shows names).',
|
|
230
259
|
options: {
|
|
231
260
|
type: {type: 'string', description: 'only this reaction type'},
|
|
232
261
|
users: {type: 'boolean', description: 'add the name, e-mail and bot of each person (JSON)'},
|
|
@@ -269,8 +298,16 @@ const mentions = {
|
|
|
269
298
|
args: [0, 0],
|
|
270
299
|
async run(ctx) {
|
|
271
300
|
const client = api(ctx);
|
|
272
|
-
const r = await client.call(
|
|
273
|
-
|
|
301
|
+
const r = await client.call(
|
|
302
|
+
'GET',
|
|
303
|
+
`/mentions${query({filter: ctx.values.unread && 'unread', cursor: ctx.values.cursor})}`,
|
|
304
|
+
);
|
|
305
|
+
return withNames(
|
|
306
|
+
ctx,
|
|
307
|
+
r,
|
|
308
|
+
(r?.mentions ?? []).flatMap((x) => [...messageUserIds(x.message), x.channel?.peerId]),
|
|
309
|
+
client,
|
|
310
|
+
);
|
|
274
311
|
},
|
|
275
312
|
text(r, ctx) {
|
|
276
313
|
const c = paint(ctx);
|
|
@@ -291,7 +328,8 @@ const SORT = ['relevance', 'recency', 'recency_asc'];
|
|
|
291
328
|
|
|
292
329
|
const search = {
|
|
293
330
|
summary: 'search messages in your channels',
|
|
294
|
-
usage:
|
|
331
|
+
usage:
|
|
332
|
+
'wx chat search <query> [--channel C] [--sender USERID]... [--attachments] [--links] [--size 1-50] [--cursor C] [--sort relevance|recency|recency_asc]',
|
|
295
333
|
options: {
|
|
296
334
|
channel: {type: 'string', description: 'only in this channel (id or inbox link)'},
|
|
297
335
|
sender: {type: 'string', multiple: true, placeholder: 'USERID', description: 'only messages of this person'},
|
|
@@ -316,7 +354,12 @@ const search = {
|
|
|
316
354
|
});
|
|
317
355
|
const client = api(ctx);
|
|
318
356
|
const r = await client.call('GET', `/search/messages${q}`);
|
|
319
|
-
return withNames(
|
|
357
|
+
return withNames(
|
|
358
|
+
ctx,
|
|
359
|
+
r,
|
|
360
|
+
(r?.results ?? []).flatMap((x) => messageUserIds(x.message)),
|
|
361
|
+
client,
|
|
362
|
+
);
|
|
320
363
|
},
|
|
321
364
|
text(r, ctx) {
|
|
322
365
|
const c = paint(ctx);
|
|
@@ -335,7 +378,8 @@ const search = {
|
|
|
335
378
|
|
|
336
379
|
const watchCmd = {
|
|
337
380
|
summary: 'follow your chats live: messages, reactions, reads, members, channel changes',
|
|
338
|
-
usage:
|
|
381
|
+
usage:
|
|
382
|
+
'wx chat watch [--channel C]... [--from USERID]... [--type TYPE]... [--mine] [--count N] [--for SECONDS] [--raw]',
|
|
339
383
|
details: [
|
|
340
384
|
'Read only: nothing is sent and nothing is marked read. One line per event (one JSON object per line with',
|
|
341
385
|
'--output json): READY when listening, then message.new, reaction.new, read.receipt.updated, member.added,',
|
|
@@ -348,8 +392,18 @@ const watchCmd = {
|
|
|
348
392
|
].join('\n'),
|
|
349
393
|
options: {
|
|
350
394
|
channel: {type: 'string', multiple: true, placeholder: 'C', description: 'only this channel (id or inbox link)'},
|
|
351
|
-
from: {
|
|
352
|
-
|
|
395
|
+
from: {
|
|
396
|
+
type: 'string',
|
|
397
|
+
multiple: true,
|
|
398
|
+
placeholder: 'USERID',
|
|
399
|
+
description: 'only what this person did (messages, reading, typing)',
|
|
400
|
+
},
|
|
401
|
+
type: {
|
|
402
|
+
type: 'string',
|
|
403
|
+
multiple: true,
|
|
404
|
+
choices: EVENT_CHOICES,
|
|
405
|
+
description: 'only these events: a group or one event',
|
|
406
|
+
},
|
|
353
407
|
mine: {type: 'boolean', description: 'also what you did yourself'},
|
|
354
408
|
count: {type: 'string', placeholder: 'N', description: 'stop after N events (1-100000)'},
|
|
355
409
|
for: {type: 'string', placeholder: 'SECONDS', description: 'stop after this long (1-604800)'},
|
|
@@ -370,7 +424,8 @@ const watchCmd = {
|
|
|
370
424
|
});
|
|
371
425
|
},
|
|
372
426
|
textEvent(e, ctx) {
|
|
373
|
-
if (ctx.values.raw && !['READY', 'RECONNECTING', 'RECONNECTED', 'END'].includes(e.event))
|
|
427
|
+
if (ctx.values.raw && !['READY', 'RECONNECTING', 'RECONNECTED', 'END'].includes(e.event))
|
|
428
|
+
return `${e.event} ${JSON.stringify(e)}`;
|
|
374
429
|
return eventText(e, ctx);
|
|
375
430
|
},
|
|
376
431
|
};
|