Status: a service is asked only for the statuses it has - Matrix has no idle and sends offline for invisible, Kick and Sneedchat none; do-not-disturb is online everywhere but Discord, which has the real one
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
@@ -172,8 +172,8 @@ pub(super) async fn joined_member_ids(base: &str, access_token: &str, room_id: &
|
||||
|
||||
/// Pushes a status to the homeserver.
|
||||
///
|
||||
/// Matrix has three presence values - online, unavailable and offline. Idle
|
||||
/// maps to unavailable, which is the closest honest answer to "am I here".
|
||||
/// Matrix has three presence values - online, unavailable and offline. Unavailable
|
||||
/// is idle, which is not offered: only online and offline (invisible) are sent.
|
||||
pub async fn apply_status(state: &AppState, config: &MatrixAccountConfig, status: &str) -> Result<()> {
|
||||
match push_presence(state, config, status).await {
|
||||
// A homeserver limits how often presence may change, and some ask for
|
||||
@@ -217,12 +217,11 @@ async fn push_presence(state: &AppState, config: &MatrixAccountConfig, status: &
|
||||
.get_matrix(&config.account_id())
|
||||
.context("account is no longer configured")?;
|
||||
let access_token = account.access_token;
|
||||
// Matrix has three, and they do not line up one for one. Do-not-disturb
|
||||
// is somebody present who does not want interrupting, which is closest to
|
||||
// unavailable; invisible has no equivalent at all, and offline is the
|
||||
// honest answer - it is what invisible means to everybody looking.
|
||||
// Matrix has online, unavailable and offline. Unavailable is idle, which is
|
||||
// not offered here, so what is sent is online - do-not-disturb is moho's own
|
||||
// switch for its notifications and has nothing to say to the server - or
|
||||
// offline for invisible, which is what invisible means to everybody looking.
|
||||
let presence = match status {
|
||||
"idle" | "dnd" => "unavailable",
|
||||
"invisible" => "offline",
|
||||
_ => "online",
|
||||
};
|
||||
|
||||
@@ -976,3 +976,57 @@ mod tests {
|
||||
assert!(!v.as_object().unwrap().contains_key("remoteId"));
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a service has anything to make of a status.
|
||||
///
|
||||
/// Offered by what each can actually express, and not by what could be faked
|
||||
/// out of something nearby: Discord has all four; IRC has away and back, which
|
||||
/// are idle and online; Matrix has
|
||||
/// online and offline presence, so invisible is real and idle is not; Kick and
|
||||
/// Sneedchat have no presence at all.
|
||||
///
|
||||
/// Do-not-disturb is online with moho's own desktop notifications silenced,
|
||||
/// which every service can have, so it is offered everywhere. Only Discord has
|
||||
/// a real state for it - which also keeps its phone notifications from being
|
||||
/// pushed - and is sent it; for everything else the account stays online.
|
||||
pub fn status_supported(account_id: &str, status: &str) -> bool {
|
||||
match status {
|
||||
"online" | "dnd" => true,
|
||||
"idle" => account_id.starts_with("discord:") || !(account_id.starts_with("matrix:") || account_id.starts_with("kick:") || account_id.starts_with("sneedchat:")),
|
||||
"invisible" => account_id.starts_with("discord:") || account_id.starts_with("matrix:"),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod status_tests {
|
||||
use super::status_supported;
|
||||
|
||||
#[test]
|
||||
fn discord_has_all_of_them() {
|
||||
for s in ["online", "idle", "dnd", "invisible"] {
|
||||
assert!(status_supported("discord:1", s), "{s}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn irc_has_away_and_back_and_no_invisible() {
|
||||
assert!(status_supported("[email protected]", "idle"));
|
||||
assert!(status_supported("[email protected]", "dnd"));
|
||||
assert!(!status_supported("[email protected]", "invisible"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn matrix_has_offline_presence_and_no_idle() {
|
||||
assert!(status_supported("matrix:@me:poa.st", "invisible"));
|
||||
assert!(!status_supported("matrix:@me:poa.st", "idle"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn kick_and_sneedchat_have_nothing_but_do_not_disturb_is_still_a_switch() {
|
||||
for account in ["kick:me", "sneedchat:me"] {
|
||||
assert!(status_supported(account, "online") && status_supported(account, "dnd"), "{account}");
|
||||
assert!(!status_supported(account, "idle") && !status_supported(account, "invisible"), "{account}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -1482,6 +1482,13 @@ pub async fn dispatch(
|
||||
if !matches!(status, "online" | "idle" | "dnd" | "invisible") {
|
||||
return (None, Some("status must be online, idle, dnd or invisible".to_string()));
|
||||
}
|
||||
// What the service has no such state for is left alone: nothing is
|
||||
// recorded and nothing is sent, so an account stays what it was.
|
||||
// Matrix has no idle and answers an idle request by saying nothing
|
||||
// of the kind; Kick and Sneedchat have no status at all.
|
||||
if !crate::model::status_supported(account_id, status) {
|
||||
return (Some(serde_json::json!({ "ok": true, "applied": false, "ignored": true })), None);
|
||||
}
|
||||
// Recorded before it is applied: a status set while disconnected
|
||||
// still has to survive to the next connection.
|
||||
state.runtime.set_account_status(account_id, status);
|
||||
@@ -1502,7 +1509,9 @@ pub async fn dispatch(
|
||||
} else if let Some(sender) = state.runtime.irc_sender(account_id) {
|
||||
// IRC has only away and back.
|
||||
let result = match status {
|
||||
"online" => sender.send(irc::proto::Command::AWAY(None)),
|
||||
// Do not disturb is moho's own switch for its notifications:
|
||||
// to the network it is still online, and not away.
|
||||
"online" | "dnd" => sender.send(irc::proto::Command::AWAY(None)),
|
||||
_ => sender.send(irc::proto::Command::AWAY(Some("Idle".to_string()))),
|
||||
};
|
||||
result.is_ok()
|
||||
|
||||
Reference in New Issue
Block a user