Make the daemon build quietly

Forty warnings on every build, which is forty too many to read, so nobody
read any of them. Closes #236.

They were four different things wearing one label, and sorting them was the
work:

Deleted, because nothing anywhere wants them: three event-type constants that
the call sites write as string literals instead, ONION_PORT, EVENT_MEMBER_NEXT,
a CAP helper superseded by the one beside it, a postimg wrapper, and six
accessors on Runtime that duplicate the ones actually in use.

Marked #[cfg(test)], because the tests want them and the binary does not.
That distinction is invisible to a release build's dead_code, and I proved it
the hard way: `open`, `without` and `ServerResponse::is_empty` were all deleted
as orphans and all three broke the test build, which is exactly the trap the
issue warns about. A helper only the tests call is a different thing from one
nobody calls.

Kept with #[allow(dead_code)] and a note, because a wire struct that drops the
fields it does not read stops describing what arrives. Four fields across the
Kick and Sneedchat payloads.

Kept with #[allow(unreachable_patterns)], because both arms are guards against
a dependency growing a variant. Unreachable against today's version of the
crate is not a reason to drop the guard - `cargo fix` would have removed both.

The one worth reading was in the device-grant sign-in: a refresh token assigned
a value nothing reads. Harmless as written, and sitting in the path where a
refresh token going missing already killed an account once.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-28 19:45:50 -04:00
co-authored by Claude Opus 5
parent bda039d693
commit d0efc2ad17
30 changed files with 108 additions and 209 deletions
-13
View File
@@ -458,9 +458,6 @@ impl Capture {
self.active.store(!muted, Ordering::Relaxed);
}
pub fn is_muted(&self) -> bool {
!self.active.load(Ordering::Relaxed)
}
}
@@ -519,11 +516,6 @@ impl Playback {
}
}
/// How much audio is waiting, in samples. For tests and diagnostics.
pub fn queued(&self) -> usize {
self.buffer.lock().unwrap().len()
}
/// The loudest thing heard since this was last called, and the running
/// total of samples received. Reading resets the peak, so successive
/// readings describe successive intervals rather than the whole call.
@@ -631,11 +623,6 @@ fn route_when_ready(stream: Stream, device_id: &str) -> Result<String> {
}
}
/// Points this process's playback stream at `device_id`.
pub fn route_output(device_id: &str) -> Result<bool> {
route_stream(Stream::Output, device_id)
}
#[cfg(test)]
mod tests {
use super::*;
-1
View File
@@ -176,7 +176,6 @@ pub(super) async fn run_gateway(state: &AppState, config: &DiscordAccountConfig,
// Disjoint borrows, so the resume state can be updated while the maps
// are being passed around mutably.
let GatewaySession { resume, channel_map, guild_context } = session;
let account_id = config.account_id();
// Discord asks that a resume go to the url it handed out with the
// session rather than to the front door.
let connect_url = resume.as_ref().map(|r| r.url.clone()).unwrap_or_else(|| GATEWAY_URL.to_string());
-5
View File
@@ -672,11 +672,6 @@ pub async fn disconnect(state: &AppState, account_id: &str) {
}
}
/// Whether a voice connection is currently up for this account.
pub fn is_connected(state: &AppState, account_id: &str) -> bool {
state.voice.drivers.lock().unwrap().contains_key(account_id)
}
#[cfg(test)]
mod tests {
+4 -3
View File
@@ -142,9 +142,10 @@ impl Sealer {
/// Opens a packet sealed the same way.
///
/// Here for the other half of a stream - watching one - and used by the tests
/// either way: a sealer with nothing that can open what it makes is a sealer
/// nothing can check.
/// Only the tests call it: the live path knows its own header length and
/// goes straight to `open_at`. Kept because a sealer with nothing that can
/// open what it makes is a sealer nothing can check.
#[cfg(test)]
pub fn open(mode: Mode, key: &[u8], packet: &[u8], header_len: usize) -> anyhow::Result<Vec<u8>> {
open_at(mode, key, packet, header_len, header_len)
}
+1 -22
View File
@@ -758,30 +758,9 @@ pub(super) async fn end_cap_and_register(
Ok(())
}
/// What the server says it can do, or nothing if it will not say.
///
/// `CAP LS 302` may answer over several lines, each but the last marked with
/// a `*` in the parameter before the list. They are gathered until the one
/// without it, because a capability named on a continuation line is as real
/// as one named on the first.
///
/// A server too old to answer at all is not an error: it times out, this
/// returns None, and the caller falls back to asking for everything the way
/// it always did.
pub(super) async fn offered_caps(sender: &Sender, stream: &mut ClientStream) -> Option<Vec<String>> {
offered_caps_raw(sender, stream).await.map(|raw| {
raw.iter()
// `cap=value` advertises a capability with parameters - SASL names
// its mechanisms this way, STS its duration and port. The name is
// what is requested.
.map(|cap| cap.split('=').next().unwrap_or(cap).to_string())
.collect()
})
}
/// The offer as the server wrote it, values and all.
///
/// Separate from `offered_caps` because two callers want two different things
/// Two callers want two different things from a CAP LS
/// out of one answer: negotiation wants names to compare against
/// `WANTED_CAPS`, and STS wants the value it would otherwise have thrown away.
pub(super) async fn offered_caps_raw(sender: &Sender, stream: &mut ClientStream) -> Option<Vec<String>> {
+2 -3
View File
@@ -472,7 +472,6 @@ const PROGRESS_EVERY: std::time::Duration = std::time::Duration::from_millis(400
/// What a transfer tells the world as it runs.
pub struct Progress {
pub received: u64,
pub total: u64,
/// Bytes a second, over the last interval rather than the whole transfer:
/// an average since the start keeps showing a healthy rate for a transfer
/// that stalled a minute ago.
@@ -647,7 +646,7 @@ async fn stream_to(
let moved = received - mark.1;
let rate = if since.as_secs_f64() > 0.0 { (moved as f64 / since.as_secs_f64()) as u64 } else { 0 };
mark = (std::time::Instant::now(), received);
on_progress(Progress { received, total: offer.size, rate });
on_progress(Progress { received, rate });
}
// After the accounting, so a limited transfer still reports what it
@@ -863,7 +862,7 @@ async fn serve_file(
let moved = sent - mark.1;
let rate = if since.as_secs_f64() > 0.0 { (moved as f64 / since.as_secs_f64()) as u64 } else { 0 };
mark = (std::time::Instant::now(), sent);
on_progress(Progress { received: sent, total: size, rate });
on_progress(Progress { received: sent, rate });
}
limiter.take(n as u64).await;
}
+3
View File
@@ -57,6 +57,9 @@ pub fn parse_keyvalue(args: &[String]) -> Option<(String, String, Option<String>
}
/// Whether a capability name is one of the two spellings of metadata.
/// Only the tests reach this - the capability names are matched inline
/// where they are negotiated.
#[cfg(test)]
pub fn is_metadata_cap(cap: &str) -> bool {
matches!(cap, "draft/metadata" | "metadata-2")
}
-5
View File
@@ -40,15 +40,10 @@ pub mod sasl;
pub use connect::*;
pub use history::*;
// Not `pub use`: these are asked and answered inside this folder.
use drafts::*;
// Not `pub use`: the dispatch is nobody else's business, but its helpers
// are wanted by the files beside it.
use incoming::*;
pub use presence::*;
// Not `pub use`: `who` is asked and answered inside this folder, and its
// names (`ask`, `read_who`) are too plain to sit in the backend's namespace.
use who::*;
pub use send::*;
pub use typing::*;
pub use sasl::*;
+1
View File
@@ -360,6 +360,7 @@ pub(super) struct Who {
impl Who {
/// Somebody who has just arrived and is so far only a rank.
#[cfg(test)]
pub(super) fn ranked(rank: MemberRank) -> Self {
Self { rank, ..Self::default() }
}
+3 -1
View File
@@ -308,7 +308,9 @@ pub(super) fn mode_letter(mode: &ChannelMode) -> char {
ChannelMode::Halfop => 'h',
ChannelMode::Unknown(c) => *c,
// The crate may grow variants; a letter nobody here knows is still
// better shown than swallowed.
// better shown than swallowed. Unreachable against today's version
// of the crate, which is not a reason to drop the guard.
#[allow(unreachable_patterns)]
_ => '?',
}
}
+12 -25
View File
@@ -52,6 +52,10 @@ pub struct Channel {
/// in general, even though it often is for older channels.
pub chatroom_id: u64,
pub slug: String,
/// On the payload and unused here: the channel's owner, which moho
/// shows by slug instead. Kept because a wire struct that drops fields
/// stops being a description of what the site sends.
#[allow(dead_code)]
pub username: String,
pub avatar_url: Option<String>,
/// Chat is restricted to subscribers right now.
@@ -418,7 +422,11 @@ pub fn bare_token(raw: &str) -> String {
pub struct Identity {
#[serde(default)]
pub username: Option<String>,
/// On the payload and unread here: moho identifies a Kick account by
/// its name. Kept so the struct stays a description of what the API
/// sends rather than of what this file happens to want.
#[serde(default)]
#[allow(dead_code)]
pub id: Option<u64>,
}
@@ -703,7 +711,11 @@ pub struct HistoryMessage {
pub id: String,
#[serde(default)]
pub content: String,
/// On the payload and unread here - every message this endpoint
/// returns is an ordinary one. Kept for the same reason as the rest of
/// this struct's unused fields: it says what the wire carries.
#[serde(rename = "type", default)]
#[allow(dead_code)]
pub kind: String,
#[serde(default)]
pub created_at: Option<String>,
@@ -1584,31 +1596,6 @@ pub async fn prediction_latest(
Ok(body.data.and_then(|d| d.prediction.map(|p| (p, d.user_vote))))
}
/// The predictions this channel has run before, newest first.
pub async fn predictions_recent(http: &reqwest::Client, token: Option<&str>, slug: &str) -> Result<Vec<Prediction>> {
#[derive(Deserialize)]
struct RecentEnvelope {
#[serde(default)]
data: Option<RecentData>,
}
#[derive(Deserialize)]
struct RecentData {
#[serde(default)]
predictions: Vec<Prediction>,
}
let url = format!("{API_ROOT}/api/v2/channels/{slug}/predictions/recent");
let mut req = http.get(&url).header("Accept", "application/json");
if let Some(token) = token.filter(|t| !t.is_empty()) {
req = req.bearer_auth(token);
}
let res = req.send().await.context("asking Kick about past predictions")?;
if !res.status().is_success() {
bail!("Kick answered {} about past predictions", res.status());
}
let body: RecentEnvelope = res.json().await.context("reading Kick's answer about past predictions")?;
Ok(body.data.map(|d| d.predictions).unwrap_or_default())
}
/// Puts points on an outcome.
///
/// Ten is Kick's own floor and the client says so before sending, because
+5 -1
View File
@@ -449,7 +449,11 @@ pub(super) async fn try_device_login(state: &AppState, login_id: &str, homeserve
let deadline = tokio::time::Instant::now() + grant.expires_in;
let mut interval = grant.interval;
let mut refresh_token = String::new();
// Declared without a value on purpose: the loop below either assigns it
// or bails, so an initialiser here would be a value nothing ever reads -
// and this is the one variable in this function whose going missing
// silently is a bug with a history (see the refresh path in sync.rs).
let refresh_token;
let access_token = loop {
if tokio::time::Instant::now() >= deadline {
anyhow::bail!("the sign-in code expired before it was approved");
+4 -3
View File
@@ -131,9 +131,6 @@ pub const MEMBERSHIP_TTL_MS: i64 = 90_000;
/// already moved on is still understood.
pub const EVENT_MEMBER: &str = "org.matrix.msc3401.call.member";
/// Where the membership event is going: MSC4143's own name for it.
pub const EVENT_MEMBER_NEXT: &str = "org.matrix.msc4143.rtc.member";
/// What moho puts in `foci_preferred` to mean "I am on the mesh".
///
/// Element's clients read the transports in a membership and connect to the
@@ -577,6 +574,10 @@ async fn openid_again(state: &AppState, account_id: &str) -> anyhow::Result<Valu
/// same on both sides: the smaller id calls the larger. It has to be a
/// property of the pair rather than of who arrived first, because "first" is
/// not something two clients can agree on.
///
/// Kept for the tests that pin the rule down; nothing in the call path
/// asks it yet, which is its own gap rather than a reason to lose it.
#[cfg(test)]
pub fn should_offer(own_key: &str, their_key: &str) -> bool {
own_key < their_key
}
+8
View File
@@ -367,6 +367,11 @@ impl CryptoSession {
);
self.machine.mark_request_as_sent(request.request_id(), &response).await.context("mark_request_as_sent(RoomMessage)")?;
}
// Unreachable today and kept anyway: AnyOutgoingRequest is the
// crate's enum, not ours, and a version that adds a variant
// should leave this sending the ones it knows rather than
// failing to compile.
#[allow(unreachable_patterns)]
_ => {}
}
Ok(())
@@ -768,6 +773,9 @@ impl CryptoSession {
/// `m.room.encrypted` content JSON. See `CollectStrategy::AllDevices`
/// in `encryption_settings()` above for why every device (not just
/// cross-signed ones) gets the key.
/// Used by the live probes and the backup tests; the send path calls
/// `share_and_encrypt_content` directly.
#[cfg(test)]
pub async fn share_and_encrypt(&self, homeserver_url: &str, access_token: &str, room_id: &RoomId, member_ids: Vec<OwnedUserId>, body: &str) -> Result<Value> {
self.share_and_encrypt_content(homeserver_url, access_token, room_id, member_ids, "m.room.message", serde_json::json!({ "msgtype": "m.text", "body": body })).await
}
+2 -3
View File
@@ -197,7 +197,7 @@ pub async fn rehydrate_with_code(state: &crate::state::AppState, account_id: &st
.context("caching the pickle key")?;
let _ = state.accounts.set_matrix_dehydration(account_id, true);
rehydrate(state, account_id, &session, &config).await
rehydrate(account_id, &session, &config).await
}
/// Takes down whatever was left last time, collects what it was sent, and puts
@@ -210,7 +210,7 @@ pub async fn rehydrate_on_connect(state: &crate::state::AppState, account_id: &s
if !config.dehydration_enabled {
return;
}
if let Err(e) = rehydrate(state, account_id, session, &config).await {
if let Err(e) = rehydrate(account_id, session, &config).await {
// Never fatal. A dehydrated device that cannot be collected costs the
// keys it was holding; failing the connection over it would cost the
// whole account.
@@ -219,7 +219,6 @@ pub async fn rehydrate_on_connect(state: &crate::state::AppState, account_id: &s
}
async fn rehydrate(
state: &crate::state::AppState,
account_id: &str,
session: &CryptoSession,
config: &crate::accounts::MatrixAccountConfig,
+29 -23
View File
@@ -57,29 +57,6 @@ pub fn with(content: &Value, user_id: &str, room_id: &str) -> Value {
Value::Object(by_user)
}
/// The list without a room, wherever it appears.
///
/// A person is dropped entirely when their last room goes: an empty array
/// left behind is a person the account claims to have a DM list for and no
/// DMs with, which other clients render as an empty conversation.
pub fn without(content: &Value, room_id: &str) -> Value {
let Some(by_user) = content.as_object() else { return json!({}) };
let mut out = Map::new();
for (user_id, rooms) in by_user {
let kept: Vec<Value> = rooms
.as_array()
.into_iter()
.flatten()
.filter(|r| r.as_str() != Some(room_id))
.cloned()
.collect();
if !kept.is_empty() {
out.insert(user_id.clone(), Value::Array(kept));
}
}
Value::Object(out)
}
/// Reads the account's list once, at connect.
///
/// Asked for directly rather than waited for: a resumed connection gets only
@@ -141,6 +118,35 @@ pub(super) async fn record(state: &AppState, account_id: &str, peer_user_id: &st
}
}
/// The list without a room, wherever it appears.
///
/// A person is dropped entirely when their last room goes: an empty array
/// left behind is a person the account claims to have a DM list for and no
/// DMs with, which other clients render as an empty conversation.
///
/// Only the tests call it. The live path rewrites the whole map rather than
/// subtracting from it, which is a difference worth keeping an eye on: this
/// is the rule the tests pin down, and nothing enforces that the live path
/// still agrees with it.
#[cfg(test)]
pub fn without(content: &Value, room_id: &str) -> Value {
let Some(by_user) = content.as_object() else { return json!({}) };
let mut out = Map::new();
for (user_id, rooms) in by_user {
let kept: Vec<Value> = rooms
.as_array()
.into_iter()
.flatten()
.filter(|r| r.as_str() != Some(room_id))
.cloned()
.collect();
if !kept.is_empty() {
out.insert(user_id.clone(), Value::Array(kept));
}
}
Value::Object(out)
}
#[cfg(test)]
mod tests {
use super::*;
-4
View File
@@ -67,11 +67,7 @@ pub use media::*;
pub use notifications::*;
pub use profile::*;
pub use receipts::*;
// Not `pub use`: this is asked and answered inside the sync loop, and its
// names would say nothing useful in the backend's namespace.
use relock::*;
pub use send::*;
pub use server::*;
pub use sync::*;
// Not `pub use`: an event's handling is this folder's business alone, but
// the files beside it want the helpers.
-1
View File
@@ -210,7 +210,6 @@ mod live {
rtc_focus_url: None,
};
let saved = state.accounts.add_matrix(config).expect("add_matrix failed");
let account_id = saved.account_id();
spawn(state.clone(), saved);
let buffer_name = "nobilis-matrix-phase3-probe";
-3
View File
@@ -10,9 +10,6 @@
use serde_json::Value;
pub const EVENT_ROOM_MESSAGE: &str = "m.room.message";
pub const EVENT_ROOM_NAME: &str = "m.room.name";
pub const EVENT_ROOM_MEMBER: &str = "m.room.member";
pub const EVENT_ROOM_ENCRYPTION: &str = "m.room.encryption";
pub const EVENT_ROOM_ENCRYPTED: &str = "m.room.encrypted";
pub const EVENT_REACTION: &str = "m.reaction";
pub const EVENT_REDACTION: &str = "m.room.redaction";
-1
View File
@@ -1229,7 +1229,6 @@ pub(super) async fn redact_event(homeserver_url: &str, access_token: &str, room_
#[cfg(test)]
mod edit_tests {
use super::*;
/// An edit carries the sender's formatting the same way a new message
/// does. Building this in two places is how a formatted body reached
+2
View File
@@ -54,6 +54,7 @@ impl ServerFacts {
/// String comparison would say `v1.10` is older than `v1.9`, which is the
/// kind of wrong that only shows up once a year and then looks like
/// something else entirely - so the numbers are compared as numbers.
#[cfg(test)]
pub fn speaks(&self, wanted: &str) -> bool {
let parse = |v: &str| -> Option<(u32, u32)> {
let (major, minor) = v.trim_start_matches('v').split_once('.')?;
@@ -73,6 +74,7 @@ impl ServerFacts {
}
/// Whether it has this unstable feature switched on.
#[cfg(test)]
pub fn has_unstable(&self, feature: &str) -> bool {
self.unstable.iter().any(|f| f == feature)
}
-1
View File
@@ -1041,7 +1041,6 @@ fn renamed_to(room: &Value, current: &str) -> Option<String> {
#[cfg(test)]
mod rename_tests {
use super::renamed_to;
use anyhow::Context;
use serde_json::json;
fn name_event(name: &str) -> serde_json::Value {
-3
View File
@@ -92,9 +92,6 @@ impl Session {
Self { http: HttpClient::new(transport, CookieJar::new(), user_agent), base }
}
pub fn base(&self) -> &str {
&self.base
}
/// Whether the current session is authenticated, costing one request.
pub async fn is_authenticated(&self) -> Result<bool> {
-2
View File
@@ -11,8 +11,6 @@ use super::*;
/// host is targeted.
pub const DEFAULT_ONION: &str = "kiwifarmsaaf4t2h7gc3dfc5ojhmqruw2nit3uejrpiagrxeuxiyxcyd.onion";
pub const ONION_PORT: u16 = 443;
pub const DEFAULT_USER_AGENT: &str = "Mozilla/5.0 (Windows NT 10.0; rv:128.0) Gecko/20100101 Firefox/128.0";
/// Spawns the background task that keeps a Sneedchat account's connection
-12
View File
@@ -120,17 +120,6 @@ pub struct PostimgLinks {
pub direct: String,
}
/// Puts an image on postimg.cc and returns where it landed.
///
/// Shared rather than private to Sneedchat, because the two callers want
/// different halves of the same answer: Sneedchat wraps both in the BBCode
/// the site renders, and IRC sends the direct link on its own, having no
/// markup to wrap anything in. The mechanics below are the same either way
/// and were hard enough won that a second copy of them would be a liability.
pub async fn upload_to_postimg(file_path: &str) -> Result<PostimgLinks> {
upload_to_postimg_reporting(file_path, None).await
}
/// The same, saying where it has got to. See `crate::upload::Progress`.
pub async fn upload_to_postimg_reporting(
file_path: &str,
@@ -201,7 +190,6 @@ pub async fn upload_to_postimg_reporting(
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn extracts_the_postimg_thumb_name_from_a_real_result_page() {
+28 -10
View File
@@ -27,6 +27,10 @@ pub struct WireMessage {
#[serde(default)]
pub message_uuid: String,
#[serde(default)]
/// On the wire and unread: the site's own timestamp. Kept because a
/// wire struct that drops the fields it does not use stops describing
/// what arrives.
#[allow(dead_code)]
pub message_date: i64,
/// Nonzero once this message has been edited at least once - the wire
/// has no separate "edit" event, the server just re-sends the same
@@ -52,6 +56,8 @@ pub struct WireMessage {
/// a room. Acting on that guess would have painted ordinary messages as
/// private ones.
#[serde(default)]
/// Likewise: the caller already knows the room, having asked it.
#[allow(dead_code)]
pub room_id: Option<u32>,
}
@@ -269,16 +275,6 @@ impl ServerResponse {
motd: raw.motd.map(|m| m.trim().to_string()).filter(|m| !m.is_empty()),
}
}
pub fn is_empty(&self) -> bool {
self.messages.is_empty()
&& self.whisper.is_none()
&& self.perms.is_none()
&& self.deleted_uuids.is_empty()
&& self.users_joined.is_empty()
&& self.users_left.is_empty()
&& self.plaintext.is_none()
}
}
/// Decode the HTML entities the server escapes in `message_raw`.
@@ -400,6 +396,10 @@ pub fn prepare_whisper(target: &str, body: &str) -> String {
/// is the one somebody will type after reading about this feature anywhere
/// else, and taking it means a message meant for one person cannot go to the
/// room because of the word chosen for it.
///
/// The no-roster form, which only the tests want: everything live goes
/// through `parse_whisper_command_among` with the names it knows.
#[cfg(test)]
pub fn parse_whisper_command(text: &str) -> Option<(String, String)> {
parse_whisper_command_among(text, &[])
}
@@ -470,6 +470,24 @@ pub fn prepare_delete(uuid: &str) -> String {
format!("/delete {uuid}")
}
impl ServerResponse {
/// Whether the frame carried nothing at all.
///
/// Only the tests ask: the live path looks at the piece it wants and
/// ignores the rest, so a frame being wholly empty is not a case it has
/// to name.
#[cfg(test)]
pub fn is_empty(&self) -> bool {
self.messages.is_empty()
&& self.whisper.is_none()
&& self.perms.is_none()
&& self.deleted_uuids.is_empty()
&& self.users_joined.is_empty()
&& self.users_left.is_empty()
&& self.plaintext.is_none()
}
}
#[cfg(test)]
mod tests {
use super::*;
-2
View File
@@ -78,7 +78,6 @@ pub fn safe_name(raw: &str) -> String {
/// Where an export's pieces live while it is being written.
pub struct Paths {
pub root: PathBuf,
pub media: PathBuf,
pub body: PathBuf,
pub index: PathBuf,
@@ -86,7 +85,6 @@ pub struct Paths {
pub fn paths(root: &Path) -> Paths {
Paths {
root: root.to_path_buf(),
media: root.join("media"),
// Accumulated separately from index.html so that an unfinished export
// has no index.html at all, rather than half a page that looks whole.
+3 -3
View File
@@ -1093,9 +1093,9 @@ pub async fn dispatch(
}
}
/// How long it has been running and whether the microphone is hearing
/// anything - polled by the window rather than pushed, because it is
/// only wanted while somebody is watching a timer.
// How long it has been running and whether the microphone is hearing
// anything - polled by the window rather than pushed, because it is
// only wanted while somebody is watching a timer.
"voiceMessageProgress" => match crate::voicenote::progress() {
None => (Some(serde_json::json!({ "recording": false })), None),
Some(p) => (
-57
View File
@@ -1805,12 +1805,6 @@ impl Runtime {
self.discord_voice_channels.lock().unwrap().get(&(account_id.to_string(), guild_id.to_string())).cloned().unwrap_or_default()
}
/// Records where someone is, or that they left. Returns the channel they
/// were in before, so a caller can tell a move from an arrival.
pub fn set_discord_voice_state(&self, account_id: &str, user_id: &str, channel_id: Option<&str>, name: Option<&str>) -> Option<String> {
self.set_discord_voice_presence(account_id, user_id, channel_id, name, None, VoiceFlags::default())
}
/// The same, carrying what they are doing in there.
///
/// Discord puts these on every voice state it sends, and they are the only
@@ -1881,14 +1875,6 @@ impl Runtime {
.insert((account_id.to_string(), user_id.to_string()), url.to_string());
}
pub fn discord_known_avatar(&self, account_id: &str, user_id: &str) -> Option<String> {
self.discord_voice_avatars
.lock()
.unwrap()
.get(&(account_id.to_string(), user_id.to_string()))
.cloned()
}
/// What this account has previously learned to call somebody.
///
/// A typing notice from a DM carries no member object to read a name
@@ -1917,17 +1903,6 @@ impl Runtime {
.cloned()
}
/// Everyone in a voice channel, as (user id, what to call them).
///
/// Nobody is excluded here: a channel list has to show you your own
/// presence, which is how you can tell you are in a call at all.
pub fn discord_voice_members(&self, account_id: &str, channel_id: &str) -> Vec<(String, String)> {
self.discord_voice_roster(account_id, channel_id)
.into_iter()
.map(|m| (m.user_id, m.name))
.collect()
}
/// The same, with what each of them is doing.
/// Records a new offer, unless this person already has too many waiting.
///
@@ -2161,18 +2136,6 @@ impl Runtime {
entry.muted || entry.channels.get(channel_id).copied().unwrap_or(false)
}
/// Every guild this account has settings for, so they can all be applied
/// at once when the channels they name finally exist.
pub fn discord_mute_keys(&self, account_id: &str) -> Vec<String> {
self.discord_mutes
.lock()
.unwrap()
.keys()
.filter(|(account, _)| account == account_id)
.map(|(_, guild)| guild.clone())
.collect()
}
pub fn discord_buffers_in_guild(&self, account_id: &str, guild_id: &str) -> Vec<String> {
let prefix = format!("{account_id}|");
self.discord_guild_id
@@ -2762,15 +2725,6 @@ impl Runtime {
self.matrix_room_avatars.lock().unwrap().get(&(account_id.to_string(), room_id.to_string())).cloned()
}
/// This room's power levels, as the state last said.
pub fn matrix_power_levels(&self, account_id: &str, room_id: &str) -> Option<Value> {
self.matrix_power_levels
.lock()
.unwrap()
.get(&(account_id.to_string(), room_id.to_string()))
.cloned()
}
pub fn set_matrix_power_levels(&self, account_id: &str, room_id: &str, content: Value) {
self.matrix_power_levels.lock().unwrap().insert((account_id.to_string(), room_id.to_string()), content);
}
@@ -2836,17 +2790,6 @@ impl Runtime {
self.matrix_directs.lock().unwrap().get(&(account_id.to_string(), room_id.to_string())).cloned()
}
/// Every room this account calls a direct message.
pub fn matrix_direct_rooms(&self, account_id: &str) -> Vec<String> {
self.matrix_directs
.lock()
.unwrap()
.keys()
.filter(|(account, _)| account == account_id)
.map(|(_, room)| room.clone())
.collect()
}
pub fn matrix_widgets(&self, account_id: &str, room_id: &str) -> Vec<serde_json::Value> {
self.matrix_widgets
.lock()
+1 -2
View File
@@ -103,7 +103,6 @@ pub struct Finished {
/// The same picture before that encoding, for services that want their
/// own - Matrix counts to 1024 and sends integers.
pub waveform_bytes: Vec<u8>,
pub samples: usize,
}
/// Stops the microphone and turns what was said into a file.
@@ -122,7 +121,7 @@ pub fn finish() -> Result<Finished> {
let waveform = base64_of(&waveform_bytes);
let bytes = oggopus::encode(&samples)?;
let path = oggopus::write_temp(&bytes)?;
Ok(Finished { buffer_id: recording.buffer_id, path, duration_secs, waveform, waveform_bytes, samples: samples.len() })
Ok(Finished { buffer_id: recording.buffer_id, path, duration_secs, waveform, waveform_bytes })
}
/// Throws the recording away and closes the microphone.