feat(daemon): expose watching over the RPC surface and the CLI
The watch subscription was reachable only from the library. It is most useful against a daemon -- that is the session that stays connected long enough to receive the server's status pushes -- so it needs the wire. - proto: user.watch / user.unwatch / user.watched, with a Watched response - SessionApi gains watch_user / unwatch_user / watched_users, so a local and a remote session are still interchangeable above the seam - CLI: `watch add`, `watch remove`, `watch list`, documented in SKILL.md including the caveat that a one-shot run's watch ends with the run - docs/openrpc.json regenerated from the wire types
This commit is contained in:
@@ -309,6 +309,7 @@ fn dispatch(ctx: &Ctx, command: Commands) -> CliResult {
|
||||
social::message_read(ctx, listen_span(duration, follow))
|
||||
}
|
||||
Commands::User(args) => peer::user(ctx, &args),
|
||||
Commands::Watch(command) => peer::watch(ctx, &command),
|
||||
Commands::Whoami => peer::whoami(ctx),
|
||||
// main.rs runs these itself: some need no account, the rest need the
|
||||
// config store in scope. The arms only keep the match exhaustive.
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
//! Being a peer on the network: `serve`, `whoami`, and `user`.
|
||||
|
||||
use super::{Ctx, Session};
|
||||
use crate::cli::{ServeArgs, UserArgs};
|
||||
use crate::cli::{ServeArgs, UserArgs, WatchCommand};
|
||||
use crate::output::{
|
||||
CliError, CliResult, UploadRecord, UserRecord, WhoamiRecord,
|
||||
CliError, CliResult, UploadRecord, UserRecord, WatchedUserRecord,
|
||||
WhoamiRecord,
|
||||
};
|
||||
use soulseek_rs::types::{UploadInfo, UploadStatus, UserInfo};
|
||||
use std::collections::HashMap;
|
||||
@@ -94,6 +95,40 @@ pub fn user(ctx: &Ctx, args: &UserArgs) -> CliResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Start or stop watching a user, or list who is being watched.
|
||||
///
|
||||
/// A watch lives in the session that made it, so `add` against a one-shot CLI
|
||||
/// run ends with that run. Pointed at a daemon it persists for that daemon's
|
||||
/// lifetime, which is the point of the command.
|
||||
pub fn watch(ctx: &Ctx, command: &WatchCommand) -> CliResult {
|
||||
let session = Session::open(ctx)?;
|
||||
match command {
|
||||
WatchCommand::Add { user } => {
|
||||
session.client.watch_user(user).map_err(|e| {
|
||||
CliError::connection(format!("cannot watch {user}: {e}"))
|
||||
})?;
|
||||
ctx.out.status(&format!("watching {user}"));
|
||||
}
|
||||
WatchCommand::Remove { user } => {
|
||||
session.client.unwatch_user(user).map_err(|e| {
|
||||
CliError::connection(format!("cannot unwatch {user}: {e}"))
|
||||
})?;
|
||||
ctx.out.status(&format!("no longer watching {user}"));
|
||||
}
|
||||
WatchCommand::List => {
|
||||
for user in session.client.watched_users() {
|
||||
let status = session
|
||||
.client
|
||||
.user_info(&user)
|
||||
.and_then(|info| info.presence)
|
||||
.map(|p| p.status.to_string());
|
||||
ctx.out.emit(&WatchedUserRecord { user, status });
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Stay online sharing files, and — with `--wishlist` — re-run the standing
|
||||
/// searches on the interval the server sets while doing so.
|
||||
pub fn serve(
|
||||
|
||||
Reference in New Issue
Block a user