@hasna/connectors 1.3.22 → 1.3.23
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/bin/index.js +109 -59
- package/bin/mcp.js +236 -182
- package/bin/serve.js +77 -33
- package/connectors/connect-dropbox/src/api/bulk.ts +285 -0
- package/connectors/connect-dropbox/src/api/index.ts +4 -0
- package/connectors/connect-dropbox/src/cli/index.ts +151 -0
- package/connectors/connect-github/src/api/bulk.ts +328 -0
- package/connectors/connect-github/src/api/index.ts +4 -0
- package/connectors/connect-github/src/cli/index.ts +145 -0
- package/connectors/connect-gmail/src/api/history.ts +82 -0
- package/connectors/connect-gmail/src/api/index.ts +12 -0
- package/connectors/connect-gmail/src/api/settings.ts +280 -0
- package/connectors/connect-gmail/src/api/watch.ts +40 -0
- package/connectors/connect-gmail/src/cli/index.ts +237 -0
- package/connectors/connect-googleads/src/api/bulk.ts +354 -0
- package/connectors/connect-googleads/src/api/index.ts +4 -0
- package/connectors/connect-googleads/src/cli/index.ts +192 -0
- package/connectors/connect-googlecalendar/src/api/acl.ts +66 -0
- package/connectors/connect-googlecalendar/src/api/bulk.ts +452 -0
- package/connectors/connect-googlecalendar/src/api/freebusy.ts +35 -0
- package/connectors/connect-googlecalendar/src/api/index.ts +9 -0
- package/connectors/connect-googlecalendar/src/cli/index.ts +522 -0
- package/connectors/connect-googlecalendar/src/types/index.ts +90 -0
- package/connectors/connect-googlecontacts/src/api/bulk.ts +365 -0
- package/connectors/connect-googlecontacts/src/api/groups.ts +164 -0
- package/connectors/connect-googlecontacts/src/api/index.ts +8 -0
- package/connectors/connect-googlecontacts/src/cli/index.ts +340 -0
- package/connectors/connect-googledocs/src/api/bulk.ts +301 -0
- package/connectors/connect-googledocs/src/api/index.ts +4 -0
- package/connectors/connect-googledocs/src/cli/index.ts +187 -0
- package/connectors/connect-googledrive/src/api/bulk.ts +505 -0
- package/connectors/connect-googledrive/src/api/changes.ts +139 -0
- package/connectors/connect-googledrive/src/api/client.ts +34 -0
- package/connectors/connect-googledrive/src/api/index.ts +12 -0
- package/connectors/connect-googledrive/src/api/revisions.ts +132 -0
- package/connectors/connect-googledrive/src/cli/index.ts +624 -0
- package/connectors/connect-googledrive/src/index.ts +2 -0
- package/connectors/connect-googlephotos/src/api/bulk.ts +455 -0
- package/connectors/connect-googlephotos/src/api/index.ts +8 -0
- package/connectors/connect-googlephotos/src/cli/index.ts +185 -0
- package/connectors/connect-googletasks/src/api/bulk.ts +359 -0
- package/connectors/connect-googletasks/src/api/index.ts +1 -0
- package/connectors/connect-googletasks/src/cli/index.ts +178 -0
- package/connectors/connect-linear/src/api/bulk.ts +219 -0
- package/connectors/connect-linear/src/api/index.ts +4 -0
- package/connectors/connect-linear/src/cli/index.ts +116 -0
- package/connectors/connect-shopify/src/api/bulk.ts +288 -0
- package/connectors/connect-shopify/src/api/index.ts +4 -0
- package/connectors/connect-shopify/src/cli/index.ts +108 -0
- package/connectors/connect-slack/src/api/bulk.ts +237 -0
- package/connectors/connect-slack/src/api/index.ts +4 -0
- package/connectors/connect-slack/src/cli/index.ts +157 -0
- package/connectors/connect-stripe/src/api/bulk.ts +488 -0
- package/connectors/connect-stripe/src/api/index.ts +4 -0
- package/connectors/connect-stripe/src/cli/index.ts +206 -0
- package/dist/index.js +75 -33
- package/dist/lib/runner.d.ts +15 -5
- package/package.json +2 -2
- package/connectors/connect-cloudflare/bun.lock +0 -32
- package/connectors/connect-gmail/bin/index.js +0 -7027
- package/connectors/connect-gmail/dist/cli/index.js +0 -7035
- package/connectors/connect-gmail/dist/index.js +0 -2174
- package/dist/server/server.test.d.ts +0 -1
|
@@ -866,5 +866,113 @@ collectionsCmd
|
|
|
866
866
|
}
|
|
867
867
|
});
|
|
868
868
|
|
|
869
|
+
// ============================================
|
|
870
|
+
// Bulk Operations Commands
|
|
871
|
+
// ============================================
|
|
872
|
+
const bulkCmd = program
|
|
873
|
+
.command('bulk')
|
|
874
|
+
.description('Bulk operations');
|
|
875
|
+
|
|
876
|
+
bulkCmd
|
|
877
|
+
.command('products')
|
|
878
|
+
.description('Bulk product operations')
|
|
879
|
+
.requiredOption('--ids <ids>', 'Comma-separated product IDs')
|
|
880
|
+
.requiredOption('--action <action>', 'Action: delete, activate, archive')
|
|
881
|
+
.option('--concurrency <number>', 'Max concurrent operations', '10')
|
|
882
|
+
.option('--dry-run', 'Preview without executing')
|
|
883
|
+
.action(async function(this: Command, opts) {
|
|
884
|
+
try {
|
|
885
|
+
const client = getClient();
|
|
886
|
+
const result = await client.bulk.products({
|
|
887
|
+
productIds: opts.ids.split(',').map((id: string) => parseInt(id.trim())),
|
|
888
|
+
action: opts.action as 'delete' | 'activate' | 'archive',
|
|
889
|
+
concurrency: parseInt(opts.concurrency),
|
|
890
|
+
dryRun: opts.dryRun,
|
|
891
|
+
onProgress: (current, total) => info(`Progress: ${current}/${total}`),
|
|
892
|
+
});
|
|
893
|
+
print(result, getFormat(this));
|
|
894
|
+
info(`Success: ${result.success}, Failed: ${result.failed}, Total: ${result.total}`);
|
|
895
|
+
if (result.errors.length > 0) {
|
|
896
|
+
warn('Errors:');
|
|
897
|
+
result.errors.forEach(e => error(` ${e.productId}: ${e.error}`));
|
|
898
|
+
}
|
|
899
|
+
} catch (err) {
|
|
900
|
+
error(String(err));
|
|
901
|
+
process.exit(1);
|
|
902
|
+
}
|
|
903
|
+
});
|
|
904
|
+
|
|
905
|
+
bulkCmd
|
|
906
|
+
.command('orders')
|
|
907
|
+
.description('Bulk order operations')
|
|
908
|
+
.requiredOption('--ids <ids>', 'Comma-separated order IDs')
|
|
909
|
+
.requiredOption('--action <action>', 'Action: close, open, cancel')
|
|
910
|
+
.option('--cancel-reason <reason>', 'Cancel reason: customer, inventory, fraud, declined, other')
|
|
911
|
+
.option('--email', 'Send cancel email')
|
|
912
|
+
.option('--restock', 'Restock on cancel')
|
|
913
|
+
.option('--concurrency <number>', 'Max concurrent operations', '10')
|
|
914
|
+
.option('--dry-run', 'Preview without executing')
|
|
915
|
+
.action(async function(this: Command, opts) {
|
|
916
|
+
try {
|
|
917
|
+
const client = getClient();
|
|
918
|
+
const result = await client.bulk.orders({
|
|
919
|
+
orderIds: opts.ids.split(',').map((id: string) => parseInt(id.trim())),
|
|
920
|
+
action: opts.action as 'close' | 'open' | 'cancel',
|
|
921
|
+
cancelReason: opts.cancelReason,
|
|
922
|
+
cancelEmail: opts.email,
|
|
923
|
+
cancelRestock: opts.restock,
|
|
924
|
+
concurrency: parseInt(opts.concurrency),
|
|
925
|
+
dryRun: opts.dryRun,
|
|
926
|
+
onProgress: (current, total) => info(`Progress: ${current}/${total}`),
|
|
927
|
+
});
|
|
928
|
+
print(result, getFormat(this));
|
|
929
|
+
info(`Success: ${result.success}, Failed: ${result.failed}, Total: ${result.total}`);
|
|
930
|
+
if (result.errors.length > 0) {
|
|
931
|
+
warn('Errors:');
|
|
932
|
+
result.errors.forEach(e => error(` ${e.orderId}: ${e.error}`));
|
|
933
|
+
}
|
|
934
|
+
} catch (err) {
|
|
935
|
+
error(String(err));
|
|
936
|
+
process.exit(1);
|
|
937
|
+
}
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
bulkCmd
|
|
941
|
+
.command('inventory')
|
|
942
|
+
.description('Bulk inventory operations')
|
|
943
|
+
.requiredOption('--items <items>', 'Items as "itemId:locationId:qty" pairs, comma-separated')
|
|
944
|
+
.requiredOption('--action <action>', 'Action: set, adjust')
|
|
945
|
+
.option('--concurrency <number>', 'Max concurrent operations', '10')
|
|
946
|
+
.option('--dry-run', 'Preview without executing')
|
|
947
|
+
.action(async function(this: Command, opts) {
|
|
948
|
+
try {
|
|
949
|
+
const client = getClient();
|
|
950
|
+
const items = opts.items.split(',').map((pair: string) => {
|
|
951
|
+
const [inventoryItemId, locationId, available] = pair.trim().split(':');
|
|
952
|
+
return {
|
|
953
|
+
inventoryItemId: parseInt(inventoryItemId),
|
|
954
|
+
locationId: parseInt(locationId),
|
|
955
|
+
available: parseInt(available),
|
|
956
|
+
};
|
|
957
|
+
});
|
|
958
|
+
const result = await client.bulk.inventory({
|
|
959
|
+
items,
|
|
960
|
+
action: opts.action as 'set' | 'adjust',
|
|
961
|
+
concurrency: parseInt(opts.concurrency),
|
|
962
|
+
dryRun: opts.dryRun,
|
|
963
|
+
onProgress: (current, total) => info(`Progress: ${current}/${total}`),
|
|
964
|
+
});
|
|
965
|
+
print(result, getFormat(this));
|
|
966
|
+
info(`Success: ${result.success}, Failed: ${result.failed}, Total: ${result.total}`);
|
|
967
|
+
if (result.errors.length > 0) {
|
|
968
|
+
warn('Errors:');
|
|
969
|
+
result.errors.forEach(e => error(` item ${e.inventoryItemId} @ location ${e.locationId}: ${e.error}`));
|
|
970
|
+
}
|
|
971
|
+
} catch (err) {
|
|
972
|
+
error(String(err));
|
|
973
|
+
process.exit(1);
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
|
|
869
977
|
// Parse and execute
|
|
870
978
|
program.parse();
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import type { SlackClient } from './client';
|
|
2
|
+
|
|
3
|
+
// ============================================
|
|
4
|
+
// Bulk Operation Types
|
|
5
|
+
// ============================================
|
|
6
|
+
|
|
7
|
+
export interface BulkOperationOptions {
|
|
8
|
+
/** Maximum concurrent API calls (default: 10) */
|
|
9
|
+
concurrency?: number;
|
|
10
|
+
/** Dry run - don't actually modify */
|
|
11
|
+
dryRun?: boolean;
|
|
12
|
+
/** Progress callback */
|
|
13
|
+
onProgress?: (current: number, total: number, item: unknown) => void;
|
|
14
|
+
/** Error callback */
|
|
15
|
+
onError?: (error: Error, item: unknown) => void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// --- Channel Bulk Operations ---
|
|
19
|
+
|
|
20
|
+
export interface BulkChannelOptions extends BulkOperationOptions {
|
|
21
|
+
channelIds: string[];
|
|
22
|
+
/** Action to perform */
|
|
23
|
+
action: 'archive' | 'unarchive';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface BulkChannelResult {
|
|
27
|
+
total: number;
|
|
28
|
+
success: number;
|
|
29
|
+
failed: number;
|
|
30
|
+
errors: Array<{ channelId: string; error: string }>;
|
|
31
|
+
results: Array<{ channelId: string; response: unknown }>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// --- Message Bulk Operations ---
|
|
35
|
+
|
|
36
|
+
export interface BulkMessageOptions extends BulkOperationOptions {
|
|
37
|
+
/** Messages to delete, each with channel and timestamp */
|
|
38
|
+
messages: Array<{ channel: string; ts: string }>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface BulkMessageResult {
|
|
42
|
+
total: number;
|
|
43
|
+
success: number;
|
|
44
|
+
failed: number;
|
|
45
|
+
errors: Array<{ channel: string; ts: string; error: string }>;
|
|
46
|
+
results: Array<{ channel: string; ts: string; response: unknown }>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// --- User Bulk Operations ---
|
|
50
|
+
|
|
51
|
+
export interface BulkUserOptions extends BulkOperationOptions {
|
|
52
|
+
userIds: string[];
|
|
53
|
+
/** Action: set presence for all users */
|
|
54
|
+
action: 'set_presence';
|
|
55
|
+
/** Presence value: 'auto' or 'away' */
|
|
56
|
+
presence?: 'auto' | 'away';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface BulkUserResult {
|
|
60
|
+
total: number;
|
|
61
|
+
success: number;
|
|
62
|
+
failed: number;
|
|
63
|
+
errors: Array<{ userId: string; error: string }>;
|
|
64
|
+
results: Array<{ userId: string; response: unknown }>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ============================================
|
|
68
|
+
// Bulk Operations API
|
|
69
|
+
// ============================================
|
|
70
|
+
|
|
71
|
+
export class BulkApi {
|
|
72
|
+
private readonly client: SlackClient;
|
|
73
|
+
|
|
74
|
+
constructor(client: SlackClient) {
|
|
75
|
+
this.client = client;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ============================================
|
|
79
|
+
// Bulk Channel Operations
|
|
80
|
+
// ============================================
|
|
81
|
+
|
|
82
|
+
async channels(options: BulkChannelOptions): Promise<BulkChannelResult> {
|
|
83
|
+
const { channelIds, action, concurrency = 10, dryRun = false, onProgress, onError } = options;
|
|
84
|
+
|
|
85
|
+
const result: BulkChannelResult = {
|
|
86
|
+
total: channelIds.length,
|
|
87
|
+
success: 0,
|
|
88
|
+
failed: 0,
|
|
89
|
+
errors: [],
|
|
90
|
+
results: [],
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
if (channelIds.length === 0) return result;
|
|
94
|
+
|
|
95
|
+
const chunks = this.chunkArray(channelIds, concurrency);
|
|
96
|
+
|
|
97
|
+
for (const chunk of chunks) {
|
|
98
|
+
await Promise.all(
|
|
99
|
+
chunk.map(async (channelId) => {
|
|
100
|
+
try {
|
|
101
|
+
if (dryRun) {
|
|
102
|
+
result.success++;
|
|
103
|
+
} else {
|
|
104
|
+
const endpoint = action === 'archive' ? 'conversations.archive' : 'conversations.unarchive';
|
|
105
|
+
const response = await this.client.post<{ ok: boolean; channel: Record<string, unknown> }>(
|
|
106
|
+
endpoint,
|
|
107
|
+
{ channel: channelId }
|
|
108
|
+
);
|
|
109
|
+
result.success++;
|
|
110
|
+
result.results.push({ channelId, response });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (onProgress) onProgress(result.success + result.failed, result.total, channelId);
|
|
114
|
+
} catch (err) {
|
|
115
|
+
result.failed++;
|
|
116
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
117
|
+
result.errors.push({ channelId, error: errorMessage });
|
|
118
|
+
if (onError) onError(err instanceof Error ? err : new Error(errorMessage), channelId);
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ============================================
|
|
128
|
+
// Bulk Message Operations
|
|
129
|
+
// ============================================
|
|
130
|
+
|
|
131
|
+
async messages(options: BulkMessageOptions): Promise<BulkMessageResult> {
|
|
132
|
+
const { messages, concurrency = 10, dryRun = false, onProgress, onError } = options;
|
|
133
|
+
|
|
134
|
+
const result: BulkMessageResult = {
|
|
135
|
+
total: messages.length,
|
|
136
|
+
success: 0,
|
|
137
|
+
failed: 0,
|
|
138
|
+
errors: [],
|
|
139
|
+
results: [],
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
if (messages.length === 0) return result;
|
|
143
|
+
|
|
144
|
+
const chunks = this.chunkArray(messages, concurrency);
|
|
145
|
+
|
|
146
|
+
for (const chunk of chunks) {
|
|
147
|
+
await Promise.all(
|
|
148
|
+
chunk.map(async (msg) => {
|
|
149
|
+
const { channel, ts } = msg;
|
|
150
|
+
try {
|
|
151
|
+
if (dryRun) {
|
|
152
|
+
result.success++;
|
|
153
|
+
} else {
|
|
154
|
+
const response = await this.client.post<{ ok: boolean }>(
|
|
155
|
+
'chat.delete',
|
|
156
|
+
{ channel, ts }
|
|
157
|
+
);
|
|
158
|
+
result.success++;
|
|
159
|
+
result.results.push({ channel, ts, response });
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (onProgress) onProgress(result.success + result.failed, result.total, msg);
|
|
163
|
+
} catch (err) {
|
|
164
|
+
result.failed++;
|
|
165
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
166
|
+
result.errors.push({ channel, ts, error: errorMessage });
|
|
167
|
+
if (onError) onError(err instanceof Error ? err : new Error(errorMessage), msg);
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return result;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ============================================
|
|
177
|
+
// Bulk User Operations
|
|
178
|
+
// ============================================
|
|
179
|
+
|
|
180
|
+
async users(options: BulkUserOptions): Promise<BulkUserResult> {
|
|
181
|
+
const { userIds, action, presence = 'auto', concurrency = 10, dryRun = false, onProgress, onError } = options;
|
|
182
|
+
|
|
183
|
+
const result: BulkUserResult = {
|
|
184
|
+
total: userIds.length,
|
|
185
|
+
success: 0,
|
|
186
|
+
failed: 0,
|
|
187
|
+
errors: [],
|
|
188
|
+
results: [],
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
if (userIds.length === 0) return result;
|
|
192
|
+
|
|
193
|
+
const chunks = this.chunkArray(userIds, concurrency);
|
|
194
|
+
|
|
195
|
+
for (const chunk of chunks) {
|
|
196
|
+
await Promise.all(
|
|
197
|
+
chunk.map(async (userId) => {
|
|
198
|
+
try {
|
|
199
|
+
if (dryRun) {
|
|
200
|
+
result.success++;
|
|
201
|
+
} else {
|
|
202
|
+
// Slack's users.setPresence only sets the current user's presence,
|
|
203
|
+
// but we still iterate through userIds for tracking purposes
|
|
204
|
+
const response = await this.client.post<{ ok: boolean }>(
|
|
205
|
+
'users.setPresence',
|
|
206
|
+
{ presence }
|
|
207
|
+
);
|
|
208
|
+
result.success++;
|
|
209
|
+
result.results.push({ userId, response });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (onProgress) onProgress(result.success + result.failed, result.total, userId);
|
|
213
|
+
} catch (err) {
|
|
214
|
+
result.failed++;
|
|
215
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
216
|
+
result.errors.push({ userId, error: errorMessage });
|
|
217
|
+
if (onError) onError(err instanceof Error ? err : new Error(errorMessage), userId);
|
|
218
|
+
}
|
|
219
|
+
})
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// ============================================
|
|
227
|
+
// Helper Methods
|
|
228
|
+
// ============================================
|
|
229
|
+
|
|
230
|
+
private chunkArray<T>(array: T[], size: number): T[][] {
|
|
231
|
+
const chunks: T[][] = [];
|
|
232
|
+
for (let i = 0; i < array.length; i += size) {
|
|
233
|
+
chunks.push(array.slice(i, i + size));
|
|
234
|
+
}
|
|
235
|
+
return chunks;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -3,11 +3,13 @@ import { SlackClient } from './client';
|
|
|
3
3
|
import { ChannelsApi } from './channels';
|
|
4
4
|
import { MessagesApi } from './messages';
|
|
5
5
|
import { UsersApi } from './users';
|
|
6
|
+
import { BulkApi } from './bulk';
|
|
6
7
|
|
|
7
8
|
export { SlackClient } from './client';
|
|
8
9
|
export { ChannelsApi } from './channels';
|
|
9
10
|
export { MessagesApi } from './messages';
|
|
10
11
|
export { UsersApi } from './users';
|
|
12
|
+
export { BulkApi } from './bulk';
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Main Slack API class
|
|
@@ -18,12 +20,14 @@ export class Slack {
|
|
|
18
20
|
public readonly channels: ChannelsApi;
|
|
19
21
|
public readonly messages: MessagesApi;
|
|
20
22
|
public readonly users: UsersApi;
|
|
23
|
+
public readonly bulk: BulkApi;
|
|
21
24
|
|
|
22
25
|
constructor(config: SlackConfig) {
|
|
23
26
|
this.client = new SlackClient(config);
|
|
24
27
|
this.channels = new ChannelsApi(this.client);
|
|
25
28
|
this.messages = new MessagesApi(this.client);
|
|
26
29
|
this.users = new UsersApi(this.client);
|
|
30
|
+
this.bulk = new BulkApi(this.client);
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
/**
|
|
@@ -491,4 +491,161 @@ program
|
|
|
491
491
|
}
|
|
492
492
|
});
|
|
493
493
|
|
|
494
|
+
// ============================================
|
|
495
|
+
// Bulk Operations
|
|
496
|
+
// ============================================
|
|
497
|
+
|
|
498
|
+
const bulkCmd = program
|
|
499
|
+
.command('bulk')
|
|
500
|
+
.description('Bulk operations');
|
|
501
|
+
|
|
502
|
+
bulkCmd
|
|
503
|
+
.command('channels')
|
|
504
|
+
.description('Bulk archive/unarchive channels')
|
|
505
|
+
.option('--ids <ids>', 'Comma-separated channel IDs')
|
|
506
|
+
.option('--action <action>', 'Action: archive or unarchive', 'archive')
|
|
507
|
+
.option('--concurrency <n>', 'Max concurrent requests', '10')
|
|
508
|
+
.option('--dry-run', 'Preview without executing')
|
|
509
|
+
.action(async (opts) => {
|
|
510
|
+
try {
|
|
511
|
+
const client = getClient();
|
|
512
|
+
const format = program.opts().format as OutputFormat;
|
|
513
|
+
|
|
514
|
+
if (!opts.ids) {
|
|
515
|
+
error('--ids is required (comma-separated channel IDs)');
|
|
516
|
+
process.exit(1);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const channelIds = opts.ids.split(',').map((id: string) => id.trim()).filter(Boolean);
|
|
520
|
+
|
|
521
|
+
info(`Bulk ${opts.action} ${channelIds.length} channels (concurrency: ${opts.concurrency}, dry-run: ${!!opts.dryRun})`);
|
|
522
|
+
|
|
523
|
+
const result = await client.bulk.channels({
|
|
524
|
+
channelIds,
|
|
525
|
+
action: opts.action as 'archive' | 'unarchive',
|
|
526
|
+
concurrency: parseInt(opts.concurrency, 10),
|
|
527
|
+
dryRun: opts.dryRun,
|
|
528
|
+
onProgress: (current, total) => {
|
|
529
|
+
process.stdout.write(`\rProgress: ${current}/${total}`);
|
|
530
|
+
},
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
console.log();
|
|
534
|
+
if (format === 'json') {
|
|
535
|
+
print(result, format);
|
|
536
|
+
} else {
|
|
537
|
+
info(`Done: ${result.success} succeeded, ${result.failed} failed out of ${result.total}`);
|
|
538
|
+
if (result.errors.length > 0) {
|
|
539
|
+
error('Failures:');
|
|
540
|
+
result.errors.forEach((e: { channelId: string; error: string }) => {
|
|
541
|
+
console.error(` - ${e.channelId}: ${e.error}`);
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
} catch (e) {
|
|
546
|
+
error((e as Error).message);
|
|
547
|
+
process.exit(1);
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
bulkCmd
|
|
552
|
+
.command('messages')
|
|
553
|
+
.description('Bulk delete messages')
|
|
554
|
+
.option('--messages <msgs>', 'Messages as "channel:ts" pairs, comma-separated')
|
|
555
|
+
.option('--concurrency <n>', 'Max concurrent requests', '10')
|
|
556
|
+
.option('--dry-run', 'Preview without executing')
|
|
557
|
+
.action(async (opts) => {
|
|
558
|
+
try {
|
|
559
|
+
const client = getClient();
|
|
560
|
+
const format = program.opts().format as OutputFormat;
|
|
561
|
+
|
|
562
|
+
if (!opts.messages) {
|
|
563
|
+
error('--messages is required (comma-separated "channel:ts" pairs)');
|
|
564
|
+
process.exit(1);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const messages = opts.messages.split(',').map((pair: string) => {
|
|
568
|
+
const [channel, ts] = pair.trim().split(':');
|
|
569
|
+
return { channel, ts };
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
info(`Bulk delete ${messages.length} messages (concurrency: ${opts.concurrency}, dry-run: ${!!opts.dryRun})`);
|
|
573
|
+
|
|
574
|
+
const result = await client.bulk.messages({
|
|
575
|
+
messages,
|
|
576
|
+
concurrency: parseInt(opts.concurrency, 10),
|
|
577
|
+
dryRun: opts.dryRun,
|
|
578
|
+
onProgress: (current, total) => {
|
|
579
|
+
process.stdout.write(`\rProgress: ${current}/${total}`);
|
|
580
|
+
},
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
console.log();
|
|
584
|
+
if (format === 'json') {
|
|
585
|
+
print(result, format);
|
|
586
|
+
} else {
|
|
587
|
+
info(`Done: ${result.success} succeeded, ${result.failed} failed out of ${result.total}`);
|
|
588
|
+
if (result.errors.length > 0) {
|
|
589
|
+
error('Failures:');
|
|
590
|
+
result.errors.forEach((e: { channel: string; ts: string; error: string }) => {
|
|
591
|
+
console.error(` - ${e.channel}@${e.ts}: ${e.error}`);
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
} catch (e) {
|
|
596
|
+
error((e as Error).message);
|
|
597
|
+
process.exit(1);
|
|
598
|
+
}
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
bulkCmd
|
|
602
|
+
.command('users')
|
|
603
|
+
.description('Bulk set user presence')
|
|
604
|
+
.option('--ids <ids>', 'Comma-separated user IDs')
|
|
605
|
+
.option('--presence <value>', 'Presence: auto or away', 'auto')
|
|
606
|
+
.option('--concurrency <n>', 'Max concurrent requests', '10')
|
|
607
|
+
.option('--dry-run', 'Preview without executing')
|
|
608
|
+
.action(async (opts) => {
|
|
609
|
+
try {
|
|
610
|
+
const client = getClient();
|
|
611
|
+
const format = program.opts().format as OutputFormat;
|
|
612
|
+
|
|
613
|
+
if (!opts.ids) {
|
|
614
|
+
error('--ids is required (comma-separated user IDs)');
|
|
615
|
+
process.exit(1);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const userIds = opts.ids.split(',').map((id: string) => id.trim()).filter(Boolean);
|
|
619
|
+
|
|
620
|
+
info(`Bulk set presence for ${userIds.length} users (concurrency: ${opts.concurrency}, dry-run: ${!!opts.dryRun})`);
|
|
621
|
+
|
|
622
|
+
const result = await client.bulk.users({
|
|
623
|
+
userIds,
|
|
624
|
+
action: 'set_presence',
|
|
625
|
+
presence: opts.presence as 'auto' | 'away',
|
|
626
|
+
concurrency: parseInt(opts.concurrency, 10),
|
|
627
|
+
dryRun: opts.dryRun,
|
|
628
|
+
onProgress: (current, total) => {
|
|
629
|
+
process.stdout.write(`\rProgress: ${current}/${total}`);
|
|
630
|
+
},
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
console.log();
|
|
634
|
+
if (format === 'json') {
|
|
635
|
+
print(result, format);
|
|
636
|
+
} else {
|
|
637
|
+
info(`Done: ${result.success} succeeded, ${result.failed} failed out of ${result.total}`);
|
|
638
|
+
if (result.errors.length > 0) {
|
|
639
|
+
error('Failures:');
|
|
640
|
+
result.errors.forEach((e: { userId: string; error: string }) => {
|
|
641
|
+
console.error(` - ${e.userId}: ${e.error}`);
|
|
642
|
+
});
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
} catch (e) {
|
|
646
|
+
error((e as Error).message);
|
|
647
|
+
process.exit(1);
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
|
|
494
651
|
program.parse();
|