slack-term 1.17.0 → 1.19.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 +5 -3
- package/dist/cli.js +59 -59
- package/package.json +1 -1
- package/ts/cli.ts +59 -4
- package/ts/slack.ts +25 -0
package/package.json
CHANGED
package/ts/cli.ts
CHANGED
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
authTest,
|
|
18
18
|
authTestSession,
|
|
19
19
|
conversationInfoSession,
|
|
20
|
+
createChannel,
|
|
21
|
+
inviteToChannel,
|
|
20
22
|
createDraft,
|
|
21
23
|
deleteDraft,
|
|
22
24
|
updateDraft,
|
|
@@ -1128,6 +1130,56 @@ async function main(): Promise<void> {
|
|
|
1128
1130
|
console.log(`private: ${ch.is_private === true}`);
|
|
1129
1131
|
},
|
|
1130
1132
|
)
|
|
1133
|
+
.command(
|
|
1134
|
+
["create <name>", "new <name>"],
|
|
1135
|
+
"Create a channel (confirm-hash safety gate)",
|
|
1136
|
+
(y2) => y2
|
|
1137
|
+
.positional("name", { type: "string", demandOption: true, describe: "Channel name (Slack lowercases it; no spaces)" })
|
|
1138
|
+
.option("private", { type: "boolean", default: false, describe: "Create a private channel" })
|
|
1139
|
+
.option("invite", { type: "string", array: true, default: [], describe: "Users to invite after creating (@handle or Uxxxx, repeatable)" })
|
|
1140
|
+
.option("code", { type: "string", describe: "Safety hash to confirm create" }),
|
|
1141
|
+
async (argv) => {
|
|
1142
|
+
const token = tok(argv as W);
|
|
1143
|
+
// Slack lowercases and strips the leading #; normalize for preview + code.
|
|
1144
|
+
const name = argv.name!.replace(/^#/, "").toLowerCase();
|
|
1145
|
+
const isPrivate = argv.private === true;
|
|
1146
|
+
const invites = (argv.invite as string[]).filter(Boolean);
|
|
1147
|
+
const code = safetyCode(name, String(isPrivate), invites.join(","));
|
|
1148
|
+
if (argv.code !== code) requireCode(argv.code, code, [
|
|
1149
|
+
`--- Creating channel -------------------------`,
|
|
1150
|
+
` name: ${isPrivate ? "🔒 " : "#"}${name}`,
|
|
1151
|
+
` private: ${isPrivate}`,
|
|
1152
|
+
...(invites.length ? [` invite: ${invites.join(", ")}`] : []),
|
|
1153
|
+
`--------------------------------────────────`,
|
|
1154
|
+
]);
|
|
1155
|
+
let created: { id: string; name: string };
|
|
1156
|
+
try {
|
|
1157
|
+
created = await createChannel(token, name, isPrivate);
|
|
1158
|
+
} catch (e: unknown) {
|
|
1159
|
+
console.error(e instanceof Error ? e.message : String(e));
|
|
1160
|
+
process.exit(1);
|
|
1161
|
+
}
|
|
1162
|
+
console.log(`✓ Created ${isPrivate ? "🔒 " : "#"}${created.name} (${created.id})`);
|
|
1163
|
+
if (invites.length) {
|
|
1164
|
+
const ids: string[] = [];
|
|
1165
|
+
for (const ref of invites) {
|
|
1166
|
+
try {
|
|
1167
|
+
ids.push(ref.startsWith("U") || ref.startsWith("W") ? ref : await resolveUserId(token, ref));
|
|
1168
|
+
} catch (e: unknown) {
|
|
1169
|
+
console.error(` ! could not resolve ${ref}: ${e instanceof Error ? e.message : String(e)}`);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
if (ids.length) {
|
|
1173
|
+
try {
|
|
1174
|
+
await inviteToChannel(token, created.id, ids);
|
|
1175
|
+
console.log(`✓ Invited ${ids.length} member${ids.length === 1 ? "" : "s"}`);
|
|
1176
|
+
} catch (e: unknown) {
|
|
1177
|
+
console.error(` ! invite failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
},
|
|
1182
|
+
)
|
|
1131
1183
|
.demandCommand(1, "")
|
|
1132
1184
|
.showHelpOnFail(true),
|
|
1133
1185
|
)
|
|
@@ -1264,14 +1316,17 @@ async function main(): Promise<void> {
|
|
|
1264
1316
|
.option("user-id", { type: "string", describe: "Raw user ID (opens DM)" })
|
|
1265
1317
|
.option("as-bot", { type: "boolean", default: false, describe: "Send via the bot token (xoxb / SLACK_BOT_TOKEN) so a DM notifies the recipient and can be two-way" })
|
|
1266
1318
|
.option("broadcast", { type: "boolean", default: false, describe: "Also send to channel: broadcast a threaded reply back to the channel (Slack's \"Also send to #channel\" checkbox). Only effective with a thread target." })
|
|
1267
|
-
.option("mentions", { type: "boolean", default:
|
|
1319
|
+
.option("mentions", { type: "boolean", default: true, describe: "Convert @handle tokens to real <@USERID> mentions (on by default; resolves via users.list, then channel members for Slack Connect guests). Unresolved handles stay as plain text. The confirm preview shows the converted message before sending. Disable with --no-mentions for literal @text." }),
|
|
1268
1320
|
async (argv) => {
|
|
1269
1321
|
const args: SendArgs = { target: argv.target!, message: argv.message! };
|
|
1270
1322
|
if (argv.code) args.code = argv.code;
|
|
1271
1323
|
if (argv["channel-id"]) args.channelId = argv["channel-id"];
|
|
1272
1324
|
if (argv["user-id"]) args.userId = argv["user-id"];
|
|
1273
1325
|
if (argv.broadcast) args.broadcast = true;
|
|
1274
|
-
|
|
1326
|
+
// Mentions on by default; `--no-mentions` (yargs negation) sets it false.
|
|
1327
|
+
// A message with no `@token` short-circuits inside encodeMentions, so the
|
|
1328
|
+
// common case pays no extra API call.
|
|
1329
|
+
if (argv.mentions !== false) {
|
|
1275
1330
|
args.mentions = true;
|
|
1276
1331
|
// Resolve mentions with the user token (has users:read) even when the
|
|
1277
1332
|
// message itself is sent via the bot token.
|
|
@@ -1394,12 +1449,12 @@ async function main(): Promise<void> {
|
|
|
1394
1449
|
.positional("newText", { type: "string", demandOption: true })
|
|
1395
1450
|
.option("code", { type: "string", describe: "Safety hash to confirm edit" })
|
|
1396
1451
|
.option("channel-id", { type: "string", describe: "Raw channel ID" })
|
|
1397
|
-
.option("mentions", { type: "boolean", default:
|
|
1452
|
+
.option("mentions", { type: "boolean", default: true, describe: "Convert @handle tokens in the new text to real <@USERID> mentions (on by default). Unresolved handles stay as plain text. Disable with --no-mentions for literal @text." }),
|
|
1398
1453
|
async (argv) => {
|
|
1399
1454
|
const args: EditArgs = { target: argv.target!, newText: argv.newText! };
|
|
1400
1455
|
if (argv.code) args.code = argv.code;
|
|
1401
1456
|
if (argv["channel-id"]) args.channelId = argv["channel-id"];
|
|
1402
|
-
if (argv.mentions) args.mentions = true;
|
|
1457
|
+
if (argv.mentions !== false) args.mentions = true;
|
|
1403
1458
|
await cmdEdit(tok(argv as W), args);
|
|
1404
1459
|
},
|
|
1405
1460
|
)
|
package/ts/slack.ts
CHANGED
|
@@ -314,6 +314,31 @@ export async function deleteMessage(
|
|
|
314
314
|
await post(token, "chat.delete", { channel, ts });
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
// Create a public (or private) channel via conversations.create. Slack
|
|
318
|
+
// lowercases the name and rejects spaces/most punctuation; the raw API error
|
|
319
|
+
// (e.g. name_taken, invalid_name_specials) surfaces to the caller. Returns the
|
|
320
|
+
// new channel's { id, name }.
|
|
321
|
+
export async function createChannel(
|
|
322
|
+
token: string,
|
|
323
|
+
name: string,
|
|
324
|
+
isPrivate = false,
|
|
325
|
+
): Promise<{ id: string; name: string }> {
|
|
326
|
+
const resp = (await post(token, "conversations.create", {
|
|
327
|
+
name,
|
|
328
|
+
is_private: isPrivate,
|
|
329
|
+
})) as { channel?: { id?: string; name?: string } };
|
|
330
|
+
return { id: resp.channel?.id ?? "", name: resp.channel?.name ?? name };
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Invite users to a channel via conversations.invite (up to ~1000 ids).
|
|
334
|
+
export async function inviteToChannel(
|
|
335
|
+
token: string,
|
|
336
|
+
channel: string,
|
|
337
|
+
userIds: string[],
|
|
338
|
+
): Promise<void> {
|
|
339
|
+
await post(token, "conversations.invite", { channel, users: userIds.join(",") });
|
|
340
|
+
}
|
|
341
|
+
|
|
317
342
|
export async function listConversations(token: string): Promise<Json> {
|
|
318
343
|
const allChannels: Json[] = [];
|
|
319
344
|
let cursor = "";
|