Migrate interpolated service messages to the t! macro (8 modules)
This commit is contained in:
parent
b462d37bd5
commit
d207c3d60d
133 changed files with 782 additions and 702 deletions
|
|
@ -1,5 +1,6 @@
|
|||
use echo_api::Store;
|
||||
use echo_api::{Sender, ServiceCtx};
|
||||
use echo_api::t;
|
||||
|
||||
// A sane cap so a runaway list can't bloat an account or flood a user on identify.
|
||||
const MAX_AJOIN: usize = 25;
|
||||
|
|
@ -23,12 +24,12 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
}
|
||||
let key = args.get(3).copied().unwrap_or("");
|
||||
if db.ajoin_list(account).len() >= MAX_AJOIN {
|
||||
ctx.notice(me, from.uid, format!("Your auto-join list is full (max {MAX_AJOIN})."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Your auto-join list is full (max {max}).", max = MAX_AJOIN));
|
||||
return;
|
||||
}
|
||||
match db.ajoin_add(account, channel, key) {
|
||||
Ok(true) => ctx.notice(me, from.uid, format!("Added \x02{channel}\x02 to your auto-join list.")),
|
||||
Ok(false) => ctx.notice(me, from.uid, format!("Updated the key for \x02{channel}\x02 on your auto-join list.")),
|
||||
Ok(true) => ctx.notice(me, from.uid, t!(ctx, "Added \x02{channel}\x02 to your auto-join list.", channel = channel)),
|
||||
Ok(false) => ctx.notice(me, from.uid, t!(ctx, "Updated the key for \x02{channel}\x02 on your auto-join list.", channel = channel)),
|
||||
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
||||
}
|
||||
}
|
||||
|
|
@ -38,8 +39,8 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
return;
|
||||
};
|
||||
match db.ajoin_del(account, channel) {
|
||||
Ok(true) => ctx.notice(me, from.uid, format!("Removed \x02{channel}\x02 from your auto-join list.")),
|
||||
Ok(false) => ctx.notice(me, from.uid, format!("\x02{channel}\x02 isn't on your auto-join list.")),
|
||||
Ok(true) => ctx.notice(me, from.uid, t!(ctx, "Removed \x02{channel}\x02 from your auto-join list.", channel = channel)),
|
||||
Ok(false) => ctx.notice(me, from.uid, t!(ctx, "\x02{channel}\x02 isn't on your auto-join list.", channel = channel)),
|
||||
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
||||
}
|
||||
}
|
||||
|
|
@ -49,15 +50,15 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
ctx.notice(me, from.uid, "Your auto-join list is empty. Add channels with \x02AJOIN ADD <#channel>\x02.");
|
||||
return;
|
||||
}
|
||||
ctx.notice(me, from.uid, format!("Your auto-join list ({}):", list.len()));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Your auto-join list ({count}):", count = list.len()));
|
||||
for e in &list {
|
||||
if e.key.is_empty() {
|
||||
ctx.notice(me, from.uid, format!(" \x02{}\x02", e.channel));
|
||||
ctx.notice(me, from.uid, t!(ctx, " \x02{channel}\x02", channel = e.channel));
|
||||
} else {
|
||||
ctx.notice(me, from.uid, format!(" \x02{}\x02 (key: {})", e.channel, e.key));
|
||||
ctx.notice(me, from.uid, t!(ctx, " \x02{channel}\x02 (key: {key})", channel = e.channel, key = e.key));
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(other) => ctx.notice(me, from.uid, format!("Unknown AJOIN command \x02{other}\x02. Use \x02ADD\x02, \x02DEL\x02 or \x02LIST\x02.")),
|
||||
Some(other) => ctx.notice(me, from.uid, t!(ctx, "Unknown AJOIN command \x02{other}\x02. Use \x02ADD\x02, \x02DEL\x02 or \x02LIST\x02.", other = other)),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use echo_api::{access_role, Sender, ServiceCtx, Store};
|
||||
use echo_api::t;
|
||||
|
||||
// ALIST: list the channels the sender's account founds or has access on.
|
||||
pub fn handle(me: &str, from: &Sender, ctx: &mut ServiceCtx, db: &dyn Store) {
|
||||
|
|
@ -21,8 +22,8 @@ pub fn handle(me: &str, from: &Sender, ctx: &mut ServiceCtx, db: &dyn Store) {
|
|||
return;
|
||||
}
|
||||
rows.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
ctx.notice(me, from.uid, format!("Channels you have access on ({}):", rows.len()));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Channels you have access on ({count}):", count = rows.len()));
|
||||
for (chan, role) in rows {
|
||||
ctx.notice(me, from.uid, format!(" \x02{chan}\x02 ({role})"));
|
||||
ctx.notice(me, from.uid, t!(ctx, " \x02{chan}\x02 ({role})", chan = chan, role = role));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use echo_api::{CertError, Store};
|
||||
use echo_api::{Sender, ServiceCtx};
|
||||
use echo_api::t;
|
||||
|
||||
// CERT ADD|DEL|LIST [password] [fingerprint]: manage the TLS certificate
|
||||
// fingerprints that may log in to your account via SASL EXTERNAL. An identified
|
||||
|
|
@ -16,7 +17,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
if fps.is_empty() {
|
||||
ctx.notice(me, from.uid, "No certificate fingerprints are registered to your account.");
|
||||
} else {
|
||||
ctx.notice(me, from.uid, format!("Registered fingerprints: {}", fps.join(", ")));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Registered fingerprints: {list}", list = fps.join(", ")));
|
||||
}
|
||||
}
|
||||
Some("ADD") => {
|
||||
|
|
@ -28,7 +29,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
return;
|
||||
};
|
||||
match db.certfp_add(&account, fp) {
|
||||
Ok(()) => ctx.notice(me, from.uid, format!("Added fingerprint \x02{fp}\x02. You can now log in with SASL EXTERNAL.")),
|
||||
Ok(()) => ctx.notice(me, from.uid, t!(ctx, "Added fingerprint \x02{fp}\x02. You can now log in with SASL EXTERNAL.", fp = fp)),
|
||||
Err(CertError::InUse) => ctx.notice(me, from.uid, "That fingerprint is already registered."),
|
||||
Err(CertError::Invalid) => ctx.notice(me, from.uid, "That does not look like a certificate fingerprint."),
|
||||
Err(CertError::Full) => ctx.notice(me, from.uid, "You have too many fingerprints registered. Remove one first."),
|
||||
|
|
@ -44,7 +45,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
return;
|
||||
};
|
||||
match db.certfp_del(&account, fp) {
|
||||
Ok(true) => ctx.notice(me, from.uid, format!("Removed fingerprint \x02{fp}\x02.")),
|
||||
Ok(true) => ctx.notice(me, from.uid, t!(ctx, "Removed fingerprint \x02{fp}\x02.", fp = fp)),
|
||||
Ok(false) => ctx.notice(me, from.uid, "No such fingerprint is registered to your account."),
|
||||
Err(_) => ctx.notice(me, from.uid, "Could not remove the fingerprint, please try again later."),
|
||||
}
|
||||
|
|
@ -75,7 +76,7 @@ fn resolve<'a>(me: &str, from: &Sender, ctx: &mut ServiceCtx, db: &mut dyn Store
|
|||
// verify can't be spammed to stall the engine.
|
||||
fn verify(me: &str, from: &Sender, ctx: &mut ServiceCtx, db: &mut dyn Store, password: &str) -> Option<String> {
|
||||
if let Some(secs) = db.auth_lockout(from.nick) {
|
||||
ctx.notice(me, from.uid, format!("Too many failed attempts. Please wait {secs}s and try again."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Too many failed attempts. Please wait {secs}s and try again.", secs = secs));
|
||||
return None;
|
||||
}
|
||||
match db.authenticate(from.nick, password).map(str::to_string) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use echo_api::{CodeKind, Store};
|
||||
use echo_api::{Sender, ServiceCtx};
|
||||
use echo_api::t;
|
||||
|
||||
// CONFIRM <code>: confirm your account's email with the code you were emailed.
|
||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
||||
|
|
@ -12,7 +13,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
return;
|
||||
};
|
||||
if db.is_verified(&account) {
|
||||
ctx.notice(me, from.uid, format!("\x02{account}\x02 is already confirmed."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "\x02{account}\x02 is already confirmed.", account = account));
|
||||
return;
|
||||
}
|
||||
if !db.take_code(&account, CodeKind::Confirm, code) {
|
||||
|
|
@ -20,7 +21,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
return;
|
||||
}
|
||||
match db.verify_account(&account) {
|
||||
Ok(()) => ctx.notice(me, from.uid, format!("\x02{account}\x02 is now confirmed. Thanks!")),
|
||||
Ok(()) => ctx.notice(me, from.uid, t!(ctx, "\x02{account}\x02 is now confirmed. Thanks!", account = account)),
|
||||
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use echo_api::Store;
|
||||
use echo_api::{Sender, ServiceCtx};
|
||||
use echo_api::NetView;
|
||||
use echo_api::t;
|
||||
|
||||
// DROP <password>: delete your account. Re-authenticates as confirmation, releases
|
||||
// and drops the channels you found, and logs you out.
|
||||
|
|
@ -16,7 +17,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net:
|
|||
// Throttle the confirmation verify like IDENTIFY so the ~1s check can't be
|
||||
// spammed to stall the engine, and record the attempt.
|
||||
if let Some(secs) = db.auth_lockout(account) {
|
||||
ctx.notice(me, from.uid, format!("Too many failed attempts. Please wait {secs}s and try again."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Too many failed attempts. Please wait {secs}s and try again.", secs = secs));
|
||||
return;
|
||||
}
|
||||
if db.authenticate(account, password).is_none() {
|
||||
|
|
@ -34,12 +35,12 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net:
|
|||
for uid in net.uids_logged_into(account) {
|
||||
ctx.logout(&uid);
|
||||
}
|
||||
ctx.notice(me, from.uid, format!("Your account \x02{account}\x02 has been dropped."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Your account \x02{account}\x02 has been dropped.", account = account));
|
||||
if !dropped.is_empty() {
|
||||
ctx.notice(me, from.uid, format!("Channels released: {}.", dropped.join(", ")));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Channels released: {list}.", list = dropped.join(", ")));
|
||||
}
|
||||
if !inherited.is_empty() {
|
||||
let list: Vec<String> = inherited.iter().map(|(c, s)| format!("{c} -> {s}")).collect();
|
||||
ctx.notice(me, from.uid, format!("Channels passed to their successor: {}.", list.join(", ")));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Channels passed to their successor: {list}.", list = list.join(", ")));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use echo_api::{Priv, Sender, ServiceCtx, Store};
|
||||
use echo_api::t;
|
||||
|
||||
// GETEMAIL <email>: list accounts registered with a matching email. Oper-only.
|
||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &dyn Store) {
|
||||
|
|
@ -12,8 +13,8 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
};
|
||||
let accounts = db.accounts_by_email(pattern);
|
||||
if accounts.is_empty() {
|
||||
ctx.notice(me, from.uid, format!("No accounts use an email matching \x02{pattern}\x02."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "No accounts use an email matching \x02{pattern}\x02.", pattern = pattern));
|
||||
return;
|
||||
}
|
||||
ctx.notice(me, from.uid, format!("Accounts with email matching \x02{pattern}\x02: {}", accounts.join(", ")));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Accounts with email matching \x02{pattern}\x02: {list}", pattern = pattern, list = accounts.join(", ")));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use echo_api::Store;
|
||||
use echo_api::{Sender, ServiceCtx};
|
||||
use echo_api::NetView;
|
||||
use echo_api::t;
|
||||
|
||||
// GHOST/RECOVER <nick> [password]: rename off a session using a nick you own,
|
||||
// either by being identified to its account or giving that account's password.
|
||||
|
|
@ -11,11 +12,11 @@ use echo_api::NetView;
|
|||
pub fn handle(me: &str, guest_nick: &str, guest_seq: &mut u32, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView, db: &mut dyn Store, regain: bool) {
|
||||
let cmd = if regain { "RECOVER" } else { "GHOST" };
|
||||
let Some(&target) = args.get(1) else {
|
||||
ctx.notice(me, from.uid, format!("Syntax: {cmd} <nick> [password]"));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Syntax: {cmd} <nick> [password]", cmd = cmd));
|
||||
return;
|
||||
};
|
||||
let Some(account) = db.resolve_account(target).map(str::to_string) else {
|
||||
ctx.notice(me, from.uid, format!("\x02{target}\x02 isn't registered."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "\x02{target}\x02 isn't registered.", target = target));
|
||||
return;
|
||||
};
|
||||
// Ownership by an identified session, or by password — throttled and recorded
|
||||
|
|
@ -25,7 +26,7 @@ pub fn handle(me: &str, guest_nick: &str, guest_seq: &mut u32, from: &Sender, ar
|
|||
true
|
||||
} else if let Some(&pw) = args.get(2) {
|
||||
if let Some(secs) = db.auth_lockout(&account) {
|
||||
ctx.notice(me, from.uid, format!("Too many failed attempts. Please wait {secs}s and try again."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Too many failed attempts. Please wait {secs}s and try again.", secs = secs));
|
||||
return;
|
||||
}
|
||||
let ok = db.authenticate(&account, pw).is_some();
|
||||
|
|
@ -38,7 +39,7 @@ pub fn handle(me: &str, guest_nick: &str, guest_seq: &mut u32, from: &Sender, ar
|
|||
false
|
||||
};
|
||||
if !owns {
|
||||
ctx.notice(me, from.uid, format!("Access denied. Identify to \x02{account}\x02 or give its password."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Access denied. Identify to \x02{account}\x02 or give its password.", account = account));
|
||||
return;
|
||||
}
|
||||
// Free the nick if someone else is holding it.
|
||||
|
|
@ -50,10 +51,10 @@ pub fn handle(me: &str, guest_nick: &str, guest_seq: &mut u32, from: &Sender, ar
|
|||
Some(ghost) => {
|
||||
let guest = echo_api::next_guest_nick(guest_nick, guest_seq, net, &*db);
|
||||
ctx.force_nick(&ghost, &guest);
|
||||
ctx.notice(me, from.uid, format!("\x02{target}\x02 has been freed."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "\x02{target}\x02 has been freed.", target = target));
|
||||
}
|
||||
None if !regain => {
|
||||
ctx.notice(me, from.uid, format!("Nobody is using \x02{target}\x02."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Nobody is using \x02{target}\x02.", target = target));
|
||||
return;
|
||||
}
|
||||
None => {} // already free — RECOVER will simply regain it
|
||||
|
|
@ -61,6 +62,6 @@ pub fn handle(me: &str, guest_nick: &str, guest_seq: &mut u32, from: &Sender, ar
|
|||
// RECOVER puts the caller back onto the nick (queued after any guest-rename).
|
||||
if regain {
|
||||
ctx.force_nick(from.uid, target);
|
||||
ctx.notice(me, from.uid, format!("You have regained \x02{target}\x02."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "You have regained \x02{target}\x02.", target = target));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use echo_api::Store;
|
||||
use echo_api::{Sender, ServiceCtx};
|
||||
use echo_api::t;
|
||||
|
||||
// GLIST: list the nicks grouped to your account.
|
||||
pub fn handle(me: &str, from: &Sender, ctx: &mut ServiceCtx, db: &dyn Store) {
|
||||
|
|
@ -9,9 +10,9 @@ pub fn handle(me: &str, from: &Sender, ctx: &mut ServiceCtx, db: &dyn Store) {
|
|||
};
|
||||
let mut nicks = db.grouped_nicks(account);
|
||||
nicks.sort();
|
||||
ctx.notice(me, from.uid, format!("Nicks grouped to \x02{account}\x02:"));
|
||||
ctx.notice(me, from.uid, format!(" \x02{account}\x02 (main)"));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Nicks grouped to \x02{account}\x02:", account = account));
|
||||
ctx.notice(me, from.uid, t!(ctx, " \x02{account}\x02 (main)", account = account));
|
||||
for n in nicks {
|
||||
ctx.notice(me, from.uid, format!(" \x02{n}\x02"));
|
||||
ctx.notice(me, from.uid, t!(ctx, " \x02{n}\x02", n = n));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use echo_api::Store;
|
||||
use echo_api::{Sender, ServiceCtx};
|
||||
use echo_api::t;
|
||||
|
||||
// GROUP <account> <password>: link your current nick to an existing account, so
|
||||
// you can identify to it under this nick too.
|
||||
|
|
@ -12,7 +13,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
// password-guessing oracle against any account (and each ~1s verify blocks the
|
||||
// engine). Also feeds the auth audit feed.
|
||||
if let Some(secs) = db.auth_lockout(account) {
|
||||
ctx.notice(me, from.uid, format!("Too many failed attempts. Please wait {secs}s and try again."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Too many failed attempts. Please wait {secs}s and try again.", secs = secs));
|
||||
return;
|
||||
}
|
||||
let Some(canonical) = db.authenticate(account, password).map(str::to_string) else {
|
||||
|
|
@ -23,7 +24,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
};
|
||||
db.note_auth(account, true);
|
||||
if db.account(from.nick).is_some() {
|
||||
ctx.notice(me, from.uid, format!("\x02{}\x02 is itself a registered account.", from.nick));
|
||||
ctx.notice(me, from.uid, t!(ctx, "\x02{nick}\x02 is itself a registered account.", nick = from.nick));
|
||||
return;
|
||||
}
|
||||
// Guard the namespace like REGISTER does: grouping RESERVES from.nick (SET KILL
|
||||
|
|
@ -36,16 +37,16 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
}
|
||||
}
|
||||
if db.is_forbidden(echo_api::ForbidKind::Nick, from.nick).is_some() {
|
||||
ctx.notice(me, from.uid, format!("The nick \x02{}\x02 is reserved and can't be grouped.", from.nick));
|
||||
ctx.notice(me, from.uid, t!(ctx, "The nick \x02{nick}\x02 is reserved and can't be grouped.", nick = from.nick));
|
||||
return;
|
||||
}
|
||||
const MAX_GROUPED: usize = 25;
|
||||
if db.grouped_nicks(&canonical).len() >= MAX_GROUPED {
|
||||
ctx.notice(me, from.uid, format!("\x02{canonical}\x02 already has the maximum of {MAX_GROUPED} grouped nicks."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "\x02{canonical}\x02 already has the maximum of {max} grouped nicks.", canonical = canonical, max = MAX_GROUPED));
|
||||
return;
|
||||
}
|
||||
match db.group_nick(from.nick, &canonical) {
|
||||
Ok(()) => ctx.notice(me, from.uid, format!("Your nick \x02{}\x02 is now grouped to \x02{canonical}\x02.", from.nick)),
|
||||
Ok(()) => ctx.notice(me, from.uid, t!(ctx, "Your nick \x02{nick}\x02 is now grouped to \x02{canonical}\x02.", nick = from.nick, canonical = canonical)),
|
||||
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use echo_api::{human_time, AuthThen, Store};
|
||||
use echo_api::{Sender, ServiceCtx};
|
||||
use echo_api::t;
|
||||
|
||||
// IDENTIFY [account] <password>: log in. The account defaults to the current
|
||||
// nick, so both the bare-password and account+password forms work.
|
||||
|
|
@ -14,7 +15,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
};
|
||||
// Distinguish an unregistered account from a wrong password.
|
||||
if !db.exists(account_name) {
|
||||
ctx.fail(me, from.uid, "IDENTIFY", "ACCOUNT_NOT_REGISTERED", format!("\x02{account_name}\x02 isn't registered."));
|
||||
ctx.fail(me, from.uid, "IDENTIFY", "ACCOUNT_NOT_REGISTERED", t!(ctx, "\x02{account_name}\x02 isn't registered.", account_name = account_name));
|
||||
return;
|
||||
}
|
||||
// A suspended account can't be logged into (checked before the password so it
|
||||
|
|
@ -23,16 +24,16 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
if let Some(acc) = db.resolve_account(account_name).map(str::to_string) {
|
||||
if db.is_suspended(&acc) {
|
||||
if let Some(s) = db.suspension(&acc) {
|
||||
let mut msg = format!("\x02{acc}\x02 is suspended, so you can't log in to it right now. It was suspended by \x02{}\x02 on {}", s.by, human_time(s.ts));
|
||||
let mut msg = t!(ctx, "\x02{acc}\x02 is suspended, so you can't log in to it right now. It was suspended by \x02{by}\x02 on {when}", acc = acc, by = s.by, when = human_time(s.ts));
|
||||
if s.reason.trim().is_empty() {
|
||||
msg.push('.');
|
||||
} else {
|
||||
msg.push_str(&format!(" — reason: {}", s.reason));
|
||||
msg.push_str(&t!(ctx, " — reason: {reason}", reason = s.reason));
|
||||
}
|
||||
if let Some(exp) = s.expires {
|
||||
msg.push_str(&format!(" The suspension is due to lift on {}.", human_time(exp)));
|
||||
msg.push_str(&t!(ctx, " The suspension is due to lift on {when}.", when = human_time(exp)));
|
||||
}
|
||||
msg.push_str(" If you think this is a mistake, please contact the network staff.");
|
||||
msg.push_str(&t!(ctx, " If you think this is a mistake, please contact the network staff."));
|
||||
ctx.fail(me, from.uid, "IDENTIFY", "ACCOUNT_SUSPENDED", msg);
|
||||
}
|
||||
return;
|
||||
|
|
@ -40,7 +41,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
}
|
||||
// Refuse while throttled, so a password can't be brute-forced.
|
||||
if let Some(secs) = db.auth_lockout(account_name) {
|
||||
ctx.fail(me, from.uid, "IDENTIFY", "RATE_LIMITED", format!("Too many failed attempts. Please wait {secs}s and try again."));
|
||||
ctx.fail(me, from.uid, "IDENTIFY", "RATE_LIMITED", t!(ctx, "Too many failed attempts. Please wait {secs}s and try again.", secs = secs));
|
||||
return;
|
||||
}
|
||||
// Fetch the verifier cheaply and hand the (~1s) PBKDF2 verify to the engine to
|
||||
|
|
@ -59,7 +60,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
Some((account, verifier)) => {
|
||||
// Already identified to this account: skip the (wasted) verify.
|
||||
if from.account == Some(account.as_str()) {
|
||||
ctx.notice(me, from.uid, format!("You're already identified as \x02{}\x02.", account));
|
||||
ctx.notice(me, from.uid, t!(ctx, "You're already identified as \x02{account}\x02.", account = account));
|
||||
return;
|
||||
}
|
||||
ctx.defer_authenticate(
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use echo_api::{human_time, NetView, Priv, Store};
|
||||
use echo_api::{Sender, ServiceCtx};
|
||||
use echo_api::t;
|
||||
|
||||
// INFO [account]: show an account's registration details. The email and other
|
||||
// private fields are shown to the account's own owner, or to an oper with the
|
||||
|
|
@ -7,13 +8,13 @@ use echo_api::{Sender, ServiceCtx};
|
|||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net: &dyn NetView, db: &dyn Store) {
|
||||
let name = args.get(1).copied().unwrap_or(from.nick);
|
||||
let Some(acct) = db.account(name) else {
|
||||
ctx.notice(me, from.uid, format!("\x02{name}\x02 isn't registered."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "\x02{name}\x02 isn't registered.", name = name));
|
||||
return;
|
||||
};
|
||||
let is_owner = from.account == Some(acct.name.as_str());
|
||||
let privileged = is_owner || from.privs.has(Priv::Auspex);
|
||||
ctx.notice(me, from.uid, format!("Information for \x02{}\x02:", acct.name));
|
||||
ctx.notice(me, from.uid, format!(" Registered : {}", human_time(acct.ts)));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Information for \x02{name}\x02:", name = acct.name));
|
||||
ctx.notice(me, from.uid, t!(ctx, " Registered : {when}", when = human_time(acct.ts)));
|
||||
// Last-seen is public by default; SET HIDE STATUS keeps it to owner and opers.
|
||||
if privileged || !acct.hide_status {
|
||||
let last_seen = if net.uids_logged_into(&acct.name).is_empty() {
|
||||
|
|
@ -21,30 +22,30 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net:
|
|||
} else {
|
||||
"now (online)".to_string()
|
||||
};
|
||||
ctx.notice(me, from.uid, format!(" Last seen : {last_seen}"));
|
||||
ctx.notice(me, from.uid, t!(ctx, " Last seen : {last_seen}", last_seen = last_seen));
|
||||
}
|
||||
// A greet is public — the bot shows it in-channel to everyone anyway.
|
||||
if !acct.greet.is_empty() {
|
||||
ctx.notice(me, from.uid, format!(" Greet : {}", acct.greet));
|
||||
ctx.notice(me, from.uid, t!(ctx, " Greet : {greet}", greet = acct.greet));
|
||||
}
|
||||
if privileged {
|
||||
if let Some(s) = db.suspension(&acct.name) {
|
||||
ctx.notice(me, from.uid, format!(" Suspended : by \x02{}\x02 — {}", s.by, s.reason));
|
||||
ctx.notice(me, from.uid, t!(ctx, " Suspended : by \x02{by}\x02 — {reason}", by = s.by, reason = s.reason));
|
||||
}
|
||||
match &acct.email {
|
||||
Some(email) if acct.verified => ctx.notice(me, from.uid, format!(" Email : {email}")),
|
||||
Some(email) => ctx.notice(me, from.uid, format!(" Email : {email} (unconfirmed)")),
|
||||
Some(email) if acct.verified => ctx.notice(me, from.uid, t!(ctx, " Email : {email}", email = email)),
|
||||
Some(email) => ctx.notice(me, from.uid, t!(ctx, " Email : {email} (unconfirmed)", email = email)),
|
||||
None => ctx.notice(me, from.uid, " Email : (none set)"),
|
||||
}
|
||||
let ajoin = db.ajoin_list(&acct.name);
|
||||
if !ajoin.is_empty() {
|
||||
ctx.notice(me, from.uid, format!(" Auto-join : {} channel(s) — see \x02AJOIN LIST\x02", ajoin.len()));
|
||||
ctx.notice(me, from.uid, t!(ctx, " Auto-join : {count} channel(s) — see \x02AJOIN LIST\x02", count = ajoin.len()));
|
||||
}
|
||||
}
|
||||
// A staff note is for operators' eyes only, never the account's owner.
|
||||
if from.privs.has(Priv::Auspex) {
|
||||
if let Some(note) = db.account_note(&acct.name) {
|
||||
ctx.notice(me, from.uid, format!(" Staff note : {note}"));
|
||||
ctx.notice(me, from.uid, t!(ctx, " Staff note : {note}", note = note));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ impl Service for NickServ {
|
|||
Some("LIST") => list::handle(me, from, args, ctx, db),
|
||||
Some("GETEMAIL") => getemail::handle(me, from, args, ctx, db),
|
||||
Some("HELP") => echo_api::help(me, from, ctx, BLURB, TOPICS, args.get(1).copied()),
|
||||
Some(other) => ctx.notice(me, from.uid, format!("I don't know the command \x02{other}\x02. Try \x02HELP\x02.")),
|
||||
Some(other) => ctx.notice(me, from.uid, echo_api::t!(ctx, "I don't know the command \x02{other}\x02. Try \x02HELP\x02.", other = other)),
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use echo_api::{human_time, Priv, Sender, ServiceCtx, Store};
|
||||
use echo_api::t;
|
||||
|
||||
// A cap so a broad glob can't flood the requesting oper.
|
||||
const MAX_SHOWN: usize = 100;
|
||||
|
|
@ -13,11 +14,11 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
let mut matches = db.accounts_matching(pattern);
|
||||
matches.sort_by_key(|a| a.name.to_ascii_lowercase());
|
||||
|
||||
ctx.notice(me, from.uid, format!("Accounts matching \x02{pattern}\x02:"));
|
||||
ctx.notice(me, from.uid, t!(ctx, "Accounts matching \x02{pattern}\x02:", pattern = pattern));
|
||||
for a in matches.iter().take(MAX_SHOWN) {
|
||||
let flag = if a.verified { "" } else { " (unconfirmed)" };
|
||||
ctx.notice(me, from.uid, format!(" \x02{}\x02 registered {}{}", a.name, human_time(a.ts), flag));
|
||||
ctx.notice(me, from.uid, t!(ctx, " \x02{name}\x02 registered {when}{flag}", name = a.name, when = human_time(a.ts), flag = flag));
|
||||
}
|
||||
let more = if matches.len() > MAX_SHOWN { format!(", showing the first {MAX_SHOWN}") } else { String::new() };
|
||||
ctx.notice(me, from.uid, format!("End of list — {} match(es){more}.", matches.len()));
|
||||
let more = if matches.len() > MAX_SHOWN { t!(ctx, ", showing the first {max}", max = MAX_SHOWN) } else { String::new() };
|
||||
ctx.notice(me, from.uid, t!(ctx, "End of list — {count} match(es){more}.", count = matches.len(), more = more));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use echo_api::{NetView, Sender, ServiceCtx, Store};
|
||||
use echo_api::t;
|
||||
|
||||
// LOGOUT: log out and rename to a guest nick (prefix + a per-logout sequence).
|
||||
pub fn handle(me: &str, guest_nick: &str, guest_seq: &mut u32, from: &Sender, ctx: &mut ServiceCtx, net: &dyn NetView, db: &dyn Store) {
|
||||
|
|
@ -9,5 +10,5 @@ pub fn handle(me: &str, guest_nick: &str, guest_seq: &mut u32, from: &Sender, ct
|
|||
let guest = echo_api::next_guest_nick(guest_nick, guest_seq, net, db);
|
||||
ctx.logout(from.uid);
|
||||
ctx.force_nick(from.uid, &guest);
|
||||
ctx.notice(me, from.uid, format!("You're now logged out. Your nick is now \x02{}\x02.", guest));
|
||||
ctx.notice(me, from.uid, t!(ctx, "You're now logged out. Your nick is now \x02{guest}\x02.", guest = guest));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use echo_api::{Priv, Sender, ServiceCtx, Store};
|
||||
use echo_api::t;
|
||||
|
||||
// NOEXPIRE <account> {ON|OFF}: pin an account so inactivity-expiry never drops
|
||||
// it (or lift the pin). Oper-only (Priv::Admin) — protecting a record from
|
||||
|
|
@ -13,13 +14,13 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
return;
|
||||
};
|
||||
let Some(account) = db.resolve_account(target).map(str::to_string) else {
|
||||
ctx.notice(me, from.uid, format!("\x02{target}\x02 isn't registered."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "\x02{target}\x02 isn't registered.", target = target));
|
||||
return;
|
||||
};
|
||||
match db.set_account_noexpire(&account, on) {
|
||||
Ok(true) if on => ctx.notice(me, from.uid, format!("\x02{account}\x02 will no longer expire.")),
|
||||
Ok(true) => ctx.notice(me, from.uid, format!("\x02{account}\x02 can expire from inactivity again.")),
|
||||
Ok(false) => ctx.notice(me, from.uid, format!("\x02{account}\x02 was already set that way.")),
|
||||
Ok(true) if on => ctx.notice(me, from.uid, t!(ctx, "\x02{account}\x02 will no longer expire.", account = account)),
|
||||
Ok(true) => ctx.notice(me, from.uid, t!(ctx, "\x02{account}\x02 can expire from inactivity again.", account = account)),
|
||||
Ok(false) => ctx.notice(me, from.uid, t!(ctx, "\x02{account}\x02 was already set that way.", account = account)),
|
||||
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use echo_api::{CodeKind, Store};
|
||||
use echo_api::{Sender, ServiceCtx};
|
||||
use echo_api::t;
|
||||
|
||||
// RESETPASS <account>: email a reset code to the address on file.
|
||||
// RESETPASS <account> <code> <newpassword>: complete the reset with that code.
|
||||
|
|
@ -11,7 +12,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
match (args.get(1), args.get(2), args.get(3)) {
|
||||
(Some(&name), None, None) => {
|
||||
let Some(canonical) = db.resolve_account(name).map(str::to_string) else {
|
||||
ctx.notice(me, from.uid, format!("\x02{name}\x02 isn't registered."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "\x02{name}\x02 isn't registered.", name = name));
|
||||
return;
|
||||
};
|
||||
let Some(email) = db.account(&canonical).and_then(|a| a.email.clone()) else {
|
||||
|
|
@ -20,17 +21,17 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
};
|
||||
let wait = db.code_issue_wait(&canonical);
|
||||
if wait > 0 {
|
||||
ctx.notice(me, from.uid, format!("A code was just sent — please wait \x02{wait}\x02s before requesting another."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "A code was just sent — please wait \x02{wait}\x02s before requesting another.", wait = wait));
|
||||
return;
|
||||
}
|
||||
let code = db.issue_code(&canonical, CodeKind::Reset);
|
||||
let mail = echo_api::email::reset(db.email_brand(), db.email_accent(), db.email_logo(), &canonical, &code);
|
||||
ctx.send_email(email, mail.subject, mail.text, Some(mail.html));
|
||||
ctx.notice(me, from.uid, format!("A reset code has been emailed to the address on file for \x02{canonical}\x02."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "A reset code has been emailed to the address on file for \x02{canonical}\x02.", canonical = canonical));
|
||||
}
|
||||
(Some(&name), Some(&code), Some(&newpass)) => {
|
||||
let Some(canonical) = db.resolve_account(name).map(str::to_string) else {
|
||||
ctx.notice(me, from.uid, format!("\x02{name}\x02 isn't registered."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "\x02{name}\x02 isn't registered.", name = name));
|
||||
return;
|
||||
};
|
||||
if !db.take_code(&canonical, CodeKind::Reset, code) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use echo_api::{Priv, Sender, ServiceCtx, Store};
|
||||
use echo_api::t;
|
||||
|
||||
// SASET <account> <option> [value]: the operator counterpart of SET, editing
|
||||
// another account's settings. Oper-only (Priv::Admin). Mirrors the self-service
|
||||
|
|
@ -13,7 +14,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
return;
|
||||
};
|
||||
let Some(account) = db.resolve_account(target).map(str::to_string) else {
|
||||
ctx.notice(me, from.uid, format!("\x02{target}\x02 isn't registered."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "\x02{target}\x02 isn't registered.", target = target));
|
||||
return;
|
||||
};
|
||||
// Credential/identity fields are owned by the website in external mode.
|
||||
|
|
@ -38,8 +39,8 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
let email = args.get(3).map(|s| s.to_string());
|
||||
let cleared = email.is_none();
|
||||
match db.set_email(&account, email) {
|
||||
Ok(()) if cleared => ctx.notice(me, from.uid, format!("Email for \x02{account}\x02 cleared.")),
|
||||
Ok(()) => ctx.notice(me, from.uid, format!("Email for \x02{account}\x02 updated.")),
|
||||
Ok(()) if cleared => ctx.notice(me, from.uid, t!(ctx, "Email for \x02{account}\x02 cleared.", account = account)),
|
||||
Ok(()) => ctx.notice(me, from.uid, t!(ctx, "Email for \x02{account}\x02 updated.", account = account)),
|
||||
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
||||
}
|
||||
}
|
||||
|
|
@ -47,8 +48,8 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
let greet = if args.len() > 3 { args[3..].join(" ") } else { String::new() };
|
||||
let cleared = greet.is_empty();
|
||||
match db.set_greet(&account, &greet) {
|
||||
Ok(()) if cleared => ctx.notice(me, from.uid, format!("Greet for \x02{account}\x02 cleared.")),
|
||||
Ok(()) => ctx.notice(me, from.uid, format!("Greet for \x02{account}\x02 is now: {greet}")),
|
||||
Ok(()) if cleared => ctx.notice(me, from.uid, t!(ctx, "Greet for \x02{account}\x02 cleared.", account = account)),
|
||||
Ok(()) => ctx.notice(me, from.uid, t!(ctx, "Greet for \x02{account}\x02 is now: {greet}", account = account, greet = greet)),
|
||||
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
}
|
||||
}
|
||||
match db.set_email(account, email) {
|
||||
Ok(()) if cleared => ctx.notice(me, from.uid, format!("Email for \x02{account}\x02 cleared.")),
|
||||
Ok(()) => ctx.notice(me, from.uid, format!("Email for \x02{account}\x02 updated.")),
|
||||
Ok(()) if cleared => ctx.notice(me, from.uid, t!(ctx, "Email for \x02{account}\x02 cleared.", account = account)),
|
||||
Ok(()) => ctx.notice(me, from.uid, t!(ctx, "Email for \x02{account}\x02 updated.", account = account)),
|
||||
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
||||
}
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
Some("STATUS") | Some("USERMASK") => {
|
||||
let Some(on) = args.get(3).and_then(|s| parse_toggle(s)) else {
|
||||
let state = if db.account_hides_status(account) { "ON" } else { "OFF" };
|
||||
ctx.notice(me, from.uid, format!("HIDE STATUS is \x02{state}\x02. Syntax: SET HIDE STATUS {{ON|OFF}}"));
|
||||
ctx.notice(me, from.uid, t!(ctx, "HIDE STATUS is \x02{state}\x02. Syntax: SET HIDE STATUS {ON|OFF}", state = state));
|
||||
return;
|
||||
};
|
||||
match db.set_account_hide_status(account, on) {
|
||||
|
|
@ -97,7 +97,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
};
|
||||
let Some(on) = on else {
|
||||
let state = if db.account_wants_protect(account) { "ON" } else { "OFF" };
|
||||
ctx.notice(me, from.uid, format!("KILL (nick protection) is \x02{state}\x02. Syntax: SET KILL {{ON|OFF}}"));
|
||||
ctx.notice(me, from.uid, t!(ctx, "KILL (nick protection) is \x02{state}\x02. Syntax: SET KILL {ON|OFF}", state = state));
|
||||
return;
|
||||
};
|
||||
match db.set_account_kill(account, on) {
|
||||
|
|
@ -109,7 +109,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
Some("AUTOOP") => {
|
||||
let Some(on) = args.get(2).and_then(|s| parse_toggle(s)) else {
|
||||
let state = if db.account_wants_autoop(account) { "ON" } else { "OFF" };
|
||||
ctx.notice(me, from.uid, format!("AUTOOP is \x02{state}\x02. Syntax: SET AUTOOP {{ON|OFF}}"));
|
||||
ctx.notice(me, from.uid, t!(ctx, "AUTOOP is \x02{state}\x02. Syntax: SET AUTOOP {ON|OFF}", state = state));
|
||||
return;
|
||||
};
|
||||
match db.set_account_autoop(account, on) {
|
||||
|
|
@ -121,7 +121,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
Some("SNOTICE") | Some("SNOTICES") => {
|
||||
let Some(on) = args.get(2).and_then(|s| parse_toggle(s)) else {
|
||||
let state = if db.account_wants_snotice(account) { "ON" } else { "OFF" };
|
||||
ctx.notice(me, from.uid, format!("SNOTICE is \x02{state}\x02. Syntax: SET SNOTICE {{ON|OFF}}"));
|
||||
ctx.notice(me, from.uid, t!(ctx, "SNOTICE is \x02{state}\x02. Syntax: SET SNOTICE {ON|OFF}", state = state));
|
||||
return;
|
||||
};
|
||||
match db.set_account_snotice(account, on) {
|
||||
|
|
@ -136,7 +136,7 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
let cleared = greet.is_empty();
|
||||
match db.set_greet(account, &greet) {
|
||||
Ok(()) if cleared => ctx.notice(me, from.uid, "Your greet has been cleared."),
|
||||
Ok(()) => ctx.notice(me, from.uid, format!("Your greet is now: {greet}")),
|
||||
Ok(()) => ctx.notice(me, from.uid, t!(ctx, "Your greet is now: {greet}", greet = greet)),
|
||||
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use echo_api::{parse_duration, NetView, Priv, Sender, ServiceCtx, Store};
|
||||
use echo_api::t;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
// SUSPEND <account> [+expiry] [reason] / UNSUSPEND <account>: freeze or unfreeze
|
||||
|
|
@ -15,14 +16,14 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net:
|
|||
return;
|
||||
};
|
||||
let Some(account) = db.resolve_account(target).map(str::to_string) else {
|
||||
ctx.notice(me, from.uid, format!("\x02{target}\x02 isn't registered."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "\x02{target}\x02 isn't registered.", target = target));
|
||||
return;
|
||||
};
|
||||
|
||||
if !suspending {
|
||||
match db.unsuspend_account(&account) {
|
||||
Ok(true) => ctx.notice(me, from.uid, format!("\x02{account}\x02 is no longer suspended.")),
|
||||
Ok(false) => ctx.notice(me, from.uid, format!("\x02{account}\x02 isn't suspended.")),
|
||||
Ok(true) => ctx.notice(me, from.uid, t!(ctx, "\x02{account}\x02 is no longer suspended.", account = account)),
|
||||
Ok(false) => ctx.notice(me, from.uid, t!(ctx, "\x02{account}\x02 isn't suspended.", account = account)),
|
||||
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
||||
}
|
||||
return;
|
||||
|
|
@ -42,8 +43,8 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, net:
|
|||
for uid in net.uids_logged_into(&account) {
|
||||
ctx.logout(&uid);
|
||||
}
|
||||
let expiry = if expires.is_some() { " (with expiry)" } else { "" };
|
||||
ctx.notice(me, from.uid, format!("\x02{account}\x02 is now suspended{expiry}."));
|
||||
let expiry = if expires.is_some() { t!(ctx, " (with expiry)") } else { String::new() };
|
||||
ctx.notice(me, from.uid, t!(ctx, "\x02{account}\x02 is now suspended{expiry}.", account = account, expiry = expiry));
|
||||
}
|
||||
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use echo_api::Store;
|
||||
use echo_api::{Sender, ServiceCtx};
|
||||
use echo_api::t;
|
||||
|
||||
// UNGROUP [nick]: remove a nick grouped to your account (defaults to your current nick).
|
||||
pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db: &mut dyn Store) {
|
||||
|
|
@ -13,12 +14,12 @@ pub fn handle(me: &str, from: &Sender, args: &[&str], ctx: &mut ServiceCtx, db:
|
|||
return;
|
||||
}
|
||||
if db.resolve_account(nick).is_none_or(|a| !a.eq_ignore_ascii_case(account)) {
|
||||
ctx.notice(me, from.uid, format!("\x02{nick}\x02 isn't grouped to your account."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "\x02{nick}\x02 isn't grouped to your account.", nick = nick));
|
||||
return;
|
||||
}
|
||||
match db.ungroup_nick(nick) {
|
||||
Ok(true) => ctx.notice(me, from.uid, format!("\x02{nick}\x02 is no longer grouped to \x02{account}\x02.")),
|
||||
Ok(false) => ctx.notice(me, from.uid, format!("\x02{nick}\x02 isn't a grouped nick.")),
|
||||
Ok(true) => ctx.notice(me, from.uid, t!(ctx, "\x02{nick}\x02 is no longer grouped to \x02{account}\x02.", nick = nick, account = account)),
|
||||
Ok(false) => ctx.notice(me, from.uid, t!(ctx, "\x02{nick}\x02 isn't a grouped nick.", nick = nick)),
|
||||
Err(_) => ctx.notice(me, from.uid, "Sorry, that didn't work. Please try again in a moment."),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use echo_api::{Sender, ServiceCtx, Store};
|
||||
use echo_api::t;
|
||||
|
||||
// UPDATE: re-apply your account's auto-joins and vhost, and re-check for waiting
|
||||
// memos, without having to re-identify.
|
||||
|
|
@ -15,7 +16,7 @@ pub fn handle(me: &str, from: &Sender, ctx: &mut ServiceCtx, db: &dyn Store) {
|
|||
}
|
||||
let unread = db.unread_memos(account);
|
||||
if unread > 0 {
|
||||
ctx.notice(me, from.uid, format!("You have \x02{unread}\x02 new memo(s). Read them with \x02/msg MemoServ READ NEW\x02."));
|
||||
ctx.notice(me, from.uid, t!(ctx, "You have \x02{unread}\x02 new memo(s). Read them with \x02/msg MemoServ READ NEW\x02.", unread = unread));
|
||||
}
|
||||
ctx.notice(me, from.uid, "Your status has been refreshed.");
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue