@kyoji2/intercom-cli 0.1.0 → 0.1.6
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 +40 -1
- package/dist/index.js +21645 -20721
- package/package.json +7 -3
- package/src/cli/registry.ts +924 -0
- package/src/client.ts +10 -0
- package/src/commands/admins.ts +2 -2
- package/src/commands/articles.ts +8 -8
- package/src/commands/auth.ts +3 -3
- package/src/commands/companies.ts +6 -6
- package/src/commands/contacts.ts +13 -13
- package/src/commands/conversations.ts +72 -15
- package/src/commands/events.ts +4 -4
- package/src/commands/index.ts +24 -10
- package/src/commands/overview.ts +4 -2
- package/src/commands/replyPayload.ts +46 -0
- package/src/commands/tags.ts +6 -6
- package/src/commands/ticketTypes.ts +76 -0
- package/src/commands/tickets.ts +398 -0
- package/src/index.ts +19 -623
- package/src/utils/config.ts +31 -24
- package/src/utils/index.ts +1 -0
- package/src/utils/output.ts +1 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import { createClient, handleIntercomError } from "../client.ts";
|
|
3
|
+
import { CLIError, type GlobalOptions, getTokenAsync, output } from "../utils/index.ts";
|
|
4
|
+
|
|
5
|
+
export interface TicketTypeListOptions extends GlobalOptions {}
|
|
6
|
+
|
|
7
|
+
export interface TicketTypeGetOptions extends GlobalOptions {
|
|
8
|
+
id: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async function requireToken(configDir: string): Promise<string> {
|
|
12
|
+
const token = await getTokenAsync(configDir);
|
|
13
|
+
if (!token) {
|
|
14
|
+
throw new CLIError("Not logged in", 401, "Run 'intercom login' to authenticate.");
|
|
15
|
+
}
|
|
16
|
+
return token;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function cmdTicketTypeList(options: TicketTypeListOptions): Promise<void> {
|
|
20
|
+
const token = await requireToken(options.configDir);
|
|
21
|
+
const spinner = ora("Listing ticket types...").start();
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
25
|
+
const result = await client.ticketTypes.list();
|
|
26
|
+
|
|
27
|
+
spinner.stop();
|
|
28
|
+
|
|
29
|
+
const ticketTypes = (result.data ?? [])
|
|
30
|
+
.filter((tt): tt is NonNullable<typeof tt> => tt !== undefined)
|
|
31
|
+
.map((tt) => ({
|
|
32
|
+
id: tt.id,
|
|
33
|
+
name: tt.name,
|
|
34
|
+
description: tt.description,
|
|
35
|
+
category: tt.category,
|
|
36
|
+
icon: tt.icon,
|
|
37
|
+
archived: tt.archived,
|
|
38
|
+
created_at: tt.created_at,
|
|
39
|
+
updated_at: tt.updated_at,
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
output({ total: ticketTypes.length, ticket_types: ticketTypes }, options.format);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
spinner.fail("Failed to list ticket types");
|
|
45
|
+
handleIntercomError(error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function cmdTicketTypeGet(options: TicketTypeGetOptions): Promise<void> {
|
|
50
|
+
const token = await requireToken(options.configDir);
|
|
51
|
+
const spinner = ora("Fetching ticket type...").start();
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
55
|
+
const ticketType = await client.ticketTypes.get({ ticket_type_id: options.id });
|
|
56
|
+
|
|
57
|
+
spinner.stop();
|
|
58
|
+
|
|
59
|
+
output(
|
|
60
|
+
{
|
|
61
|
+
id: ticketType?.id,
|
|
62
|
+
name: ticketType?.name,
|
|
63
|
+
description: ticketType?.description,
|
|
64
|
+
category: ticketType?.category,
|
|
65
|
+
icon: ticketType?.icon,
|
|
66
|
+
archived: ticketType?.archived,
|
|
67
|
+
created_at: ticketType?.created_at,
|
|
68
|
+
updated_at: ticketType?.updated_at,
|
|
69
|
+
},
|
|
70
|
+
options.format,
|
|
71
|
+
);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
spinner.fail("Failed to fetch ticket type");
|
|
74
|
+
handleIntercomError(error);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import ora from "ora";
|
|
2
|
+
import { createClient, handleIntercomError } from "../client.ts";
|
|
3
|
+
import { CLIError, type GlobalOptions, getTokenAsync, output } from "../utils/index.ts";
|
|
4
|
+
import { buildAdminReplyPayload } from "./replyPayload.ts";
|
|
5
|
+
|
|
6
|
+
export interface TicketGetOptions extends GlobalOptions {
|
|
7
|
+
id: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface TicketCreateOptions extends GlobalOptions {
|
|
11
|
+
ticketTypeId: string;
|
|
12
|
+
contactId: string;
|
|
13
|
+
title?: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
companyId?: string;
|
|
16
|
+
assigneeId?: string;
|
|
17
|
+
json?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface TicketUpdateOptions extends GlobalOptions {
|
|
21
|
+
id: string;
|
|
22
|
+
stateId?: string;
|
|
23
|
+
assigneeId?: string;
|
|
24
|
+
open?: boolean;
|
|
25
|
+
snoozedUntil?: string;
|
|
26
|
+
adminId?: string;
|
|
27
|
+
json?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface TicketDeleteOptions extends GlobalOptions {
|
|
31
|
+
id: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface TicketSearchOptions extends GlobalOptions {
|
|
35
|
+
state?: string;
|
|
36
|
+
assignee?: string;
|
|
37
|
+
json?: string;
|
|
38
|
+
limit?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface TicketReplyOptions extends GlobalOptions {
|
|
42
|
+
id: string;
|
|
43
|
+
adminId: string;
|
|
44
|
+
body: string;
|
|
45
|
+
messageType?: string;
|
|
46
|
+
json?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface TicketCloseOptions extends GlobalOptions {
|
|
50
|
+
id: string;
|
|
51
|
+
adminId: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface TicketAssignOptions extends GlobalOptions {
|
|
55
|
+
id: string;
|
|
56
|
+
adminId: string;
|
|
57
|
+
assigneeId: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function requireToken(configDir: string): Promise<string> {
|
|
61
|
+
const token = await getTokenAsync(configDir);
|
|
62
|
+
if (!token) {
|
|
63
|
+
throw new CLIError("Not logged in", 401, "Run 'intercom login' to authenticate.");
|
|
64
|
+
}
|
|
65
|
+
return token;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export async function cmdTicketGet(options: TicketGetOptions): Promise<void> {
|
|
69
|
+
const token = await requireToken(options.configDir);
|
|
70
|
+
const spinner = ora("Fetching ticket...").start();
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
74
|
+
const ticket = await client.tickets.get({ ticket_id: options.id });
|
|
75
|
+
|
|
76
|
+
spinner.stop();
|
|
77
|
+
|
|
78
|
+
output(
|
|
79
|
+
{
|
|
80
|
+
id: ticket?.id,
|
|
81
|
+
ticket_id: ticket?.ticket_id,
|
|
82
|
+
category: ticket?.category,
|
|
83
|
+
ticket_type: ticket?.ticket_type,
|
|
84
|
+
ticket_state: ticket?.ticket_state,
|
|
85
|
+
ticket_attributes: ticket?.ticket_attributes,
|
|
86
|
+
contacts: ticket?.contacts,
|
|
87
|
+
admin_assignee_id: ticket?.admin_assignee_id,
|
|
88
|
+
team_assignee_id: ticket?.team_assignee_id,
|
|
89
|
+
created_at: ticket?.created_at,
|
|
90
|
+
updated_at: ticket?.updated_at,
|
|
91
|
+
open: ticket?.open,
|
|
92
|
+
snoozed_until: ticket?.snoozed_until,
|
|
93
|
+
is_shared: ticket?.is_shared,
|
|
94
|
+
linked_objects: ticket?.linked_objects,
|
|
95
|
+
ticket_parts: ticket?.ticket_parts,
|
|
96
|
+
},
|
|
97
|
+
options.format,
|
|
98
|
+
);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
spinner.fail("Failed to fetch ticket");
|
|
101
|
+
handleIntercomError(error);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export async function cmdTicketCreate(options: TicketCreateOptions): Promise<void> {
|
|
106
|
+
const token = await requireToken(options.configDir);
|
|
107
|
+
const spinner = ora("Creating ticket...").start();
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
111
|
+
|
|
112
|
+
let payload: Record<string, unknown>;
|
|
113
|
+
if (options.json) {
|
|
114
|
+
payload = JSON.parse(options.json);
|
|
115
|
+
} else {
|
|
116
|
+
if (!options.ticketTypeId || !options.contactId) {
|
|
117
|
+
throw new CLIError(
|
|
118
|
+
"Missing required options",
|
|
119
|
+
400,
|
|
120
|
+
"Provide --type-id and --contact-id, or use --json for full payload.",
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
payload = {
|
|
125
|
+
ticket_type_id: options.ticketTypeId,
|
|
126
|
+
contacts: [{ id: options.contactId }],
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const ticketAttributes: Record<string, string> = {};
|
|
130
|
+
if (options.title) {
|
|
131
|
+
ticketAttributes._default_title_ = options.title;
|
|
132
|
+
}
|
|
133
|
+
if (options.description) {
|
|
134
|
+
ticketAttributes._default_description_ = options.description;
|
|
135
|
+
}
|
|
136
|
+
if (Object.keys(ticketAttributes).length > 0) {
|
|
137
|
+
payload.ticket_attributes = ticketAttributes;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (options.companyId) {
|
|
141
|
+
payload.company_id = options.companyId;
|
|
142
|
+
}
|
|
143
|
+
if (options.assigneeId) {
|
|
144
|
+
payload.assignment = { admin_assignee_id: options.assigneeId };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const ticket = await client.tickets.create(payload as unknown as Parameters<typeof client.tickets.create>[0]);
|
|
149
|
+
|
|
150
|
+
spinner.succeed("Ticket created");
|
|
151
|
+
|
|
152
|
+
output(
|
|
153
|
+
{
|
|
154
|
+
id: ticket?.id,
|
|
155
|
+
ticket_id: ticket?.ticket_id,
|
|
156
|
+
category: ticket?.category,
|
|
157
|
+
ticket_type: ticket?.ticket_type,
|
|
158
|
+
ticket_state: ticket?.ticket_state,
|
|
159
|
+
open: ticket?.open,
|
|
160
|
+
},
|
|
161
|
+
options.format,
|
|
162
|
+
);
|
|
163
|
+
} catch (error) {
|
|
164
|
+
spinner.fail("Failed to create ticket");
|
|
165
|
+
handleIntercomError(error);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export async function cmdTicketUpdate(options: TicketUpdateOptions): Promise<void> {
|
|
170
|
+
const token = await requireToken(options.configDir);
|
|
171
|
+
const spinner = ora("Updating ticket...").start();
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
175
|
+
|
|
176
|
+
let payload: Record<string, unknown> = { ticket_id: options.id };
|
|
177
|
+
|
|
178
|
+
if (options.json) {
|
|
179
|
+
const jsonData = JSON.parse(options.json);
|
|
180
|
+
payload = { ...payload, ...jsonData };
|
|
181
|
+
} else {
|
|
182
|
+
if (options.stateId) {
|
|
183
|
+
payload.ticket_state_id = options.stateId;
|
|
184
|
+
}
|
|
185
|
+
if (options.assigneeId) {
|
|
186
|
+
payload.assignee_id = options.assigneeId;
|
|
187
|
+
}
|
|
188
|
+
if (options.open !== undefined) {
|
|
189
|
+
payload.open = options.open;
|
|
190
|
+
}
|
|
191
|
+
if (options.snoozedUntil) {
|
|
192
|
+
payload.snoozed_until = Number.parseInt(options.snoozedUntil, 10);
|
|
193
|
+
}
|
|
194
|
+
if (options.adminId) {
|
|
195
|
+
payload.admin_id = Number.parseInt(options.adminId, 10);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const ticket = await client.tickets.update(payload as unknown as Parameters<typeof client.tickets.update>[0]);
|
|
200
|
+
|
|
201
|
+
spinner.succeed("Ticket updated");
|
|
202
|
+
|
|
203
|
+
output(
|
|
204
|
+
{
|
|
205
|
+
id: ticket?.id,
|
|
206
|
+
ticket_id: ticket?.ticket_id,
|
|
207
|
+
ticket_state: ticket?.ticket_state,
|
|
208
|
+
admin_assignee_id: ticket?.admin_assignee_id,
|
|
209
|
+
team_assignee_id: ticket?.team_assignee_id,
|
|
210
|
+
open: ticket?.open,
|
|
211
|
+
snoozed_until: ticket?.snoozed_until,
|
|
212
|
+
},
|
|
213
|
+
options.format,
|
|
214
|
+
);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
spinner.fail("Failed to update ticket");
|
|
217
|
+
handleIntercomError(error);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export async function cmdTicketDelete(options: TicketDeleteOptions): Promise<void> {
|
|
222
|
+
const token = await requireToken(options.configDir);
|
|
223
|
+
const spinner = ora("Deleting ticket...").start();
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
227
|
+
await client.tickets.deleteTicket({ ticket_id: options.id });
|
|
228
|
+
|
|
229
|
+
spinner.succeed("Ticket deleted");
|
|
230
|
+
|
|
231
|
+
output({ deleted: true, id: options.id }, options.format);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
spinner.fail("Failed to delete ticket");
|
|
234
|
+
handleIntercomError(error);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export async function cmdTicketSearch(options: TicketSearchOptions): Promise<void> {
|
|
239
|
+
const token = await requireToken(options.configDir);
|
|
240
|
+
const spinner = ora("Searching tickets...").start();
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
244
|
+
|
|
245
|
+
let query: Record<string, unknown>;
|
|
246
|
+
if (options.json) {
|
|
247
|
+
query = JSON.parse(options.json);
|
|
248
|
+
} else if (options.state) {
|
|
249
|
+
query = {
|
|
250
|
+
query: {
|
|
251
|
+
field: "open",
|
|
252
|
+
operator: "=",
|
|
253
|
+
value: options.state === "open",
|
|
254
|
+
},
|
|
255
|
+
};
|
|
256
|
+
} else if (options.assignee) {
|
|
257
|
+
query = {
|
|
258
|
+
query: {
|
|
259
|
+
field: "admin_assignee_id",
|
|
260
|
+
operator: "=",
|
|
261
|
+
value: options.assignee,
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
} else {
|
|
265
|
+
query = {
|
|
266
|
+
query: {
|
|
267
|
+
field: "open",
|
|
268
|
+
operator: "=",
|
|
269
|
+
value: true,
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
if (options.limit) {
|
|
275
|
+
query.pagination = { per_page: Number.parseInt(options.limit, 10) };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const searchPayload = { query: query.query, pagination: query.pagination };
|
|
279
|
+
const result = await client.tickets.search(searchPayload as Parameters<typeof client.tickets.search>[0]);
|
|
280
|
+
|
|
281
|
+
spinner.stop();
|
|
282
|
+
|
|
283
|
+
const tickets: unknown[] = [];
|
|
284
|
+
for await (const ticket of result) {
|
|
285
|
+
tickets.push({
|
|
286
|
+
id: ticket?.id,
|
|
287
|
+
ticket_id: ticket?.ticket_id,
|
|
288
|
+
category: ticket?.category,
|
|
289
|
+
ticket_state: ticket?.ticket_state,
|
|
290
|
+
open: ticket?.open,
|
|
291
|
+
created_at: ticket?.created_at,
|
|
292
|
+
updated_at: ticket?.updated_at,
|
|
293
|
+
});
|
|
294
|
+
if (options.limit && tickets.length >= Number.parseInt(options.limit, 10)) break;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
output({ total: tickets.length, tickets }, options.format);
|
|
298
|
+
} catch (error) {
|
|
299
|
+
spinner.fail("Failed to search tickets");
|
|
300
|
+
handleIntercomError(error);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
export async function cmdTicketReply(options: TicketReplyOptions): Promise<void> {
|
|
305
|
+
const token = await requireToken(options.configDir);
|
|
306
|
+
const spinner = ora("Sending reply...").start();
|
|
307
|
+
|
|
308
|
+
try {
|
|
309
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
310
|
+
|
|
311
|
+
const result = await client.tickets.reply({
|
|
312
|
+
ticket_id: options.id,
|
|
313
|
+
body: buildAdminReplyPayload({
|
|
314
|
+
adminId: options.adminId,
|
|
315
|
+
body: options.body,
|
|
316
|
+
messageType: options.messageType,
|
|
317
|
+
json: options.json,
|
|
318
|
+
}),
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
spinner.succeed("Reply sent");
|
|
322
|
+
|
|
323
|
+
output(
|
|
324
|
+
{
|
|
325
|
+
type: result.type,
|
|
326
|
+
id: result.id,
|
|
327
|
+
part_type: result.part_type,
|
|
328
|
+
body: result.body,
|
|
329
|
+
created_at: result.created_at,
|
|
330
|
+
author: result.author,
|
|
331
|
+
},
|
|
332
|
+
options.format,
|
|
333
|
+
);
|
|
334
|
+
} catch (error) {
|
|
335
|
+
spinner.fail("Failed to send reply");
|
|
336
|
+
handleIntercomError(error);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export async function cmdTicketClose(options: TicketCloseOptions): Promise<void> {
|
|
341
|
+
const token = await requireToken(options.configDir);
|
|
342
|
+
const spinner = ora("Closing ticket...").start();
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
346
|
+
|
|
347
|
+
const ticket = await client.tickets.update({
|
|
348
|
+
ticket_id: options.id,
|
|
349
|
+
open: false,
|
|
350
|
+
admin_id: Number.parseInt(options.adminId, 10),
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
spinner.succeed("Ticket closed");
|
|
354
|
+
|
|
355
|
+
output(
|
|
356
|
+
{
|
|
357
|
+
id: ticket?.id,
|
|
358
|
+
ticket_id: ticket?.ticket_id,
|
|
359
|
+
open: ticket?.open,
|
|
360
|
+
ticket_state: ticket?.ticket_state,
|
|
361
|
+
},
|
|
362
|
+
options.format,
|
|
363
|
+
);
|
|
364
|
+
} catch (error) {
|
|
365
|
+
spinner.fail("Failed to close ticket");
|
|
366
|
+
handleIntercomError(error);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
export async function cmdTicketAssign(options: TicketAssignOptions): Promise<void> {
|
|
371
|
+
const token = await requireToken(options.configDir);
|
|
372
|
+
const spinner = ora("Assigning ticket...").start();
|
|
373
|
+
|
|
374
|
+
try {
|
|
375
|
+
const client = createClient({ token, dryRun: options.dryRun });
|
|
376
|
+
|
|
377
|
+
const ticket = await client.tickets.update({
|
|
378
|
+
ticket_id: options.id,
|
|
379
|
+
assignee_id: options.assigneeId,
|
|
380
|
+
admin_id: Number.parseInt(options.adminId, 10),
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
spinner.succeed("Ticket assigned");
|
|
384
|
+
|
|
385
|
+
output(
|
|
386
|
+
{
|
|
387
|
+
id: ticket?.id,
|
|
388
|
+
ticket_id: ticket?.ticket_id,
|
|
389
|
+
admin_assignee_id: ticket?.admin_assignee_id,
|
|
390
|
+
team_assignee_id: ticket?.team_assignee_id,
|
|
391
|
+
},
|
|
392
|
+
options.format,
|
|
393
|
+
);
|
|
394
|
+
} catch (error) {
|
|
395
|
+
spinner.fail("Failed to assign ticket");
|
|
396
|
+
handleIntercomError(error);
|
|
397
|
+
}
|
|
398
|
+
}
|