@ably/cli
Version:
Ably CLI for Pub/Sub, Chat and Spaces
110 lines (109 loc) • 5.18 kB
JavaScript
import { Flags } from "@oclif/core";
import { ChatBaseCommand } from "../../chat-base-command.js";
import chalk from "chalk";
export default class RoomsList extends ChatBaseCommand {
static description = "List active chat rooms";
static examples = [
"$ ably rooms list",
"$ ably rooms list --prefix my-room",
"$ ably rooms list --limit 50",
"$ ably rooms list --json",
"$ ably rooms list --pretty-json",
];
static flags = {
...ChatBaseCommand.globalFlags,
limit: Flags.integer({
default: 100,
description: "Maximum number of rooms to return",
}),
prefix: Flags.string({
char: "p",
description: "Filter rooms by prefix",
}),
};
async run() {
const { flags } = await this.parse(RoomsList);
try {
// REST client for channel enumeration
const rest = await this.createAblyRestClient(flags);
if (!rest)
return;
// Build params for channel listing
// We request more channels than the limit to account for filtering
const params = {
limit: flags.limit * 5, // Request more to allow for filtering
};
if (flags.prefix) {
params.prefix = flags.prefix;
}
// Fetch channels
const channelsResponse = await rest.request("get", "/channels", 2, params, null);
if (channelsResponse.statusCode !== 200) {
this.error(`Failed to list rooms: ${channelsResponse.statusCode}`);
return;
}
// Filter to only include chat channels
const allChannels = channelsResponse.items || [];
// Map to store deduplicated rooms
const chatRooms = new Map();
// Filter for chat channels and deduplicate
for (const channel of allChannels) {
const { channelId } = channel;
// Check if this is a chat channel (has ::$chat suffix)
if (channelId.includes("::$chat")) {
// Extract the base room name (everything before the first ::$chat)
// We need to escape the $ in the regex pattern since it's a special character
const roomNameMatch = channelId.match(/^(.+?)::\$chat.*$/);
if (roomNameMatch && roomNameMatch[1]) {
const roomName = roomNameMatch[1];
// Only add if we haven't seen this room before
if (!chatRooms.has(roomName)) {
// Store the original channel data but with the simple room name
const roomData = { ...channel, channelId: roomName, roomName };
chatRooms.set(roomName, roomData);
}
}
}
}
// Convert map to array
const rooms = [...chatRooms.values()];
// Limit the results to the requested number
const limitedRooms = rooms.slice(0, flags.limit);
// Output rooms based on format
if (this.shouldOutputJson(flags)) {
// Wrap the array in an object for formatJsonOutput
this.log(this.formatJsonOutput({ items: limitedRooms }, flags));
}
else {
if (limitedRooms.length === 0) {
this.log("No active chat rooms found.");
return;
}
this.log(`Found ${chalk.cyan(limitedRooms.length.toString())} active chat rooms:`);
for (const room of limitedRooms) {
this.log(`${chalk.green(room.roomName)}`);
// Show occupancy if available
if (room.status?.occupancy?.metrics) {
const { metrics } = room.status.occupancy;
this.log(` ${chalk.dim("Connections:")} ${metrics.connections || 0}`);
this.log(` ${chalk.dim("Publishers:")} ${metrics.publishers || 0}`);
this.log(` ${chalk.dim("Subscribers:")} ${metrics.subscribers || 0}`);
if (metrics.presenceConnections !== undefined) {
this.log(` ${chalk.dim("Presence Connections:")} ${metrics.presenceConnections}`);
}
if (metrics.presenceMembers !== undefined) {
this.log(` ${chalk.dim("Presence Members:")} ${metrics.presenceMembers}`);
}
}
this.log(""); // Add a line break between rooms
}
if (rooms.length > flags.limit) {
this.log(chalk.yellow(`Showing ${flags.limit} of ${rooms.length} rooms. Use --limit to show more.`));
}
}
}
catch (error) {
this.error(`Error listing rooms: ${error instanceof Error ? error.message : String(error)}`);
}
}
}