Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,9 @@ $ ably-interactive
* [`ably channels occupancy subscribe CHANNEL`](#ably-channels-occupancy-subscribe-channel)
* [`ably channels presence`](#ably-channels-presence)
* [`ably channels presence enter CHANNEL`](#ably-channels-presence-enter-channel)
* [`ably channels presence get-all CHANNEL`](#ably-channels-presence-get-all-channel)
* [`ably channels presence subscribe CHANNEL`](#ably-channels-presence-subscribe-channel)
* [`ably channels presence update CHANNEL`](#ably-channels-presence-update-channel)
* [`ably channels publish CHANNEL MESSAGE`](#ably-channels-publish-channel-message)
* [`ably channels subscribe CHANNELS`](#ably-channels-subscribe-channels)
* [`ably channels update CHANNEL SERIAL MESSAGE`](#ably-channels-update-channel-serial-message)
Expand Down Expand Up @@ -1932,6 +1934,38 @@ EXAMPLES

_See code: [src/commands/channels/presence/enter.ts](https://github.com/ably/ably-cli/blob/v0.17.0/src/commands/channels/presence/enter.ts)_

## `ably channels presence get-all CHANNEL`

Get all current presence members on a channel

```
USAGE
$ ably channels presence get-all CHANNEL [-v] [--json | --pretty-json] [--limit <value>]

ARGUMENTS
CHANNEL Channel name to get presence members for

FLAGS
-v, --verbose Output verbose logs
--json Output in JSON format
--limit=<value> [default: 100] Maximum number of results to return
--pretty-json Output in colorized JSON format

DESCRIPTION
Get all current presence members on a channel

EXAMPLES
$ ably channels presence get-all my-channel

$ ably channels presence get-all my-channel --limit 50

$ ably channels presence get-all my-channel --json

$ ably channels presence get-all my-channel --pretty-json
```

_See code: [src/commands/channels/presence/get-all.ts](https://github.com/ably/ably-cli/blob/v0.17.0/src/commands/channels/presence/get-all.ts)_

## `ably channels presence subscribe CHANNEL`

Subscribe to presence events on a channel
Expand Down Expand Up @@ -1970,6 +2004,40 @@ EXAMPLES

_See code: [src/commands/channels/presence/subscribe.ts](https://github.com/ably/ably-cli/blob/v0.17.0/src/commands/channels/presence/subscribe.ts)_

## `ably channels presence update CHANNEL`

Update presence data on a channel

```
USAGE
$ ably channels presence update CHANNEL --data <value> [-v] [--json | --pretty-json] [--client-id <value>] [-D
<value>]

ARGUMENTS
CHANNEL Channel to update presence on

FLAGS
-D, --duration=<value> Automatically exit after N seconds
-v, --verbose Output verbose logs
--client-id=<value> Overrides any default client ID when using API authentication. Use "none" to explicitly set
no client ID. Not applicable when using token authentication.
--data=<value> (required) JSON data to associate with the presence update
--json Output in JSON format
--pretty-json Output in colorized JSON format

DESCRIPTION
Update presence data on a channel

EXAMPLES
$ ably channels presence update my-channel --data '{"status":"away"}'

$ ably channels presence update my-channel --data '{"status":"busy"}' --json

$ ably channels presence update my-channel --data '{"status":"online"}' --duration 60
```

_See code: [src/commands/channels/presence/update.ts](https://github.com/ably/ably-cli/blob/v0.17.0/src/commands/channels/presence/update.ts)_

## `ably channels publish CHANNEL MESSAGE`

Publish a message to an Ably channel
Expand Down
20 changes: 19 additions & 1 deletion src/base-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1481,9 +1481,27 @@ export abstract class AblyBaseCommand extends InteractiveBaseCommand {
flagName: string,
flags: BaseFlags = {},
): Record<string, unknown> {
const trimmed = value.trim();
try {
return JSON.parse(value.trim());
return JSON.parse(trimmed);
} catch (error) {
// If parsing fails and the value is wrapped in a single pair of quotes
// (common when shells pass through values like '{"a":1}'), strip them and retry.
if (
trimmed.length >= 2 &&
((trimmed.startsWith("'") && trimmed.endsWith("'")) ||
(trimmed.startsWith('"') && trimmed.endsWith('"')))
) {
try {
return JSON.parse(trimmed.slice(1, -1));
} catch (innerError) {
this.fail(
`Invalid ${flagName} JSON: ${innerError instanceof Error ? innerError.message : String(innerError)}`,
flags,
"parse",
);
}
}
this.fail(
`Invalid ${flagName} JSON: ${error instanceof Error ? error.message : String(error)}`,
flags,
Expand Down
90 changes: 47 additions & 43 deletions src/commands/channels/presence/enter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ import { Args, Flags } from "@oclif/core";
import * as Ably from "ably";
import { AblyBaseCommand } from "../../../base-command.js";
import { clientIdFlag, durationFlag, productApiFlags } from "../../../flags.js";
import { errorMessage } from "../../../utils/errors.js";
import { isJsonData } from "../../../utils/json-formatter.js";
import {
formatClientId,
formatEventType,
formatIndex,
formatLabel,
formatListening,
formatMessageTimestamp,
formatProgress,
formatResource,
formatSuccess,
formatTimestamp,
formatMessageTimestamp,
formatIndex,
formatLabel,
formatClientId,
formatEventType,
} from "../../../utils/output.js";

export default class ChannelsPresenceEnter extends AblyBaseCommand {
Expand Down Expand Up @@ -71,23 +71,7 @@ export default class ChannelsPresenceEnter extends AblyBaseCommand {
// Parse data if provided
let data: unknown = undefined;
if (flags.data) {
try {
let trimmed = (flags.data as string).trim();
if (
(trimmed.startsWith("'") && trimmed.endsWith("'")) ||
(trimmed.startsWith('"') && trimmed.endsWith('"'))
) {
trimmed = trimmed.slice(1, -1);
}
data = JSON.parse(trimmed);
} catch (error) {
this.fail(
`Invalid data JSON: ${errorMessage(error)}`,
flags,
"presenceEnter",
{ data: flags.data },
);
}
data = this.parseJsonFlag(flags.data, "data", flags);
}

this.channel = client.channels.get(channelName);
Expand Down Expand Up @@ -133,7 +117,7 @@ export default class ChannelsPresenceEnter extends AblyBaseCommand {
);

if (this.shouldOutputJson(flags)) {
this.logJsonEvent(event, flags);
this.logJsonEvent({ presenceMessage: event }, flags);
} else {
const sequencePrefix = flags["sequence-numbers"]
? `${formatIndex(this.sequenceCounter)}`
Expand All @@ -159,53 +143,73 @@ export default class ChannelsPresenceEnter extends AblyBaseCommand {
});
}

if (!this.shouldOutputJson(flags)) {
this.log(
formatProgress(
`Entering presence on channel: ${formatResource(channelName)}`,
),
);
}

// Enter presence
this.logCliEvent(
flags,
"presence",
"entering",
`Entering presence on channel ${channelName}`,
{ channel: channelName, clientId: client.auth.clientId, data: data },
{ channel: channelName, clientId: client.auth.clientId, data },
);

await this.channel.presence.enter(data);

const enterEvent = {
action: "enter",
channel: channelName,
clientId: client.auth.clientId,
data: data,
timestamp: new Date().toISOString(),
};
this.logCliEvent(
flags,
"presence",
"entered",
`Entered presence on channel ${channelName}`,
enterEvent,
{ channel: channelName, clientId: client.auth.clientId },
);

if (this.shouldOutputJson(flags)) {
this.logJsonResult(enterEvent, flags);
this.logJsonResult(
{
presenceMessage: {
action: "enter",
channel: channelName,
clientId: client.auth.clientId,
connectionId: client.connection.id,
data: data ?? null,
timestamp: new Date().toISOString(),
},
},
flags,
);
} else {
this.log(
formatSuccess(
`Entered presence on channel: ${formatResource(channelName)}.`,
),
);

if (flags["show-others"]) {
this.log(formatListening("Listening for presence events."));
} else {
this.log(formatListening("Staying present."));
this.log(
`${formatLabel("Client ID")} ${formatClientId(client.auth.clientId)}`,
);
this.log(`${formatLabel("Connection ID")} ${client.connection.id}`);
if (data !== undefined) {
this.log(`${formatLabel("Data")} ${JSON.stringify(data)}`);
}
this.log(
formatListening(
flags["show-others"]
? "Listening for presence events."
: "Holding presence.",
),
);
}

this.logCliEvent(
this.logJsonStatus(
"holding",
"Holding presence. Press Ctrl+C to exit.",
flags,
"presence",
"listening",
"Listening for presence events. Press Ctrl+C to exit.",
);

// Wait until the user interrupts or the optional duration elapses
Expand Down
Loading
Loading