···31 pub legacy_login_body: &'static str,
32 pub migration_verification_subject: &'static str,
33 pub migration_verification_body: &'static str,
0034}
3536pub fn get_strings(locale: &str) -> &'static NotificationStrings {
···66 legacy_login_body: "Hello @{handle},\n\nA login to your account was detected using a legacy app (like Bluesky) that doesn't support TOTP verification.\n\nDetails:\n- Time: {timestamp}\n- IP Address: {ip}\n\nYour TOTP protection was bypassed for this login. The session has limited permissions for sensitive operations.\n\nIf this wasn't you, please:\n1. Change your password immediately\n2. Review your active sessions\n3. Consider disabling legacy app logins in your security settings\n\nStay safe,\n{hostname}",
67 migration_verification_subject: "Verify your email - {hostname}",
68 migration_verification_body: "Welcome to {hostname}!\n\nYour account has been migrated successfully. To complete the setup, please verify your email address.\n\nYour verification code is:\n{code}\n\nCopy the code above and enter it at:\n{verify_page}\n\nThis code will expire in 48 hours.\n\nOr if you like to live dangerously:\n{verify_link}\n\nIf you did not migrate your account, please ignore this email.",
0069};
7071static STRINGS_ZH: NotificationStrings = NotificationStrings {
···90 legacy_login_body: "您好 @{handle},\n\n检测到使用不支持 TOTP 验证的传统应用(如 Bluesky)登录您的账户。\n\n详细信息:\n- 时间:{timestamp}\n- IP 地址:{ip}\n\n此次登录绕过了 TOTP 保护。该会话对敏感操作的权限有限。\n\n如果这不是您的操作,请:\n1. 立即更改密码\n2. 检查您的活跃会话\n3. 考虑在安全设置中禁用传统应用登录\n\n请注意安全,\n{hostname}",
91 migration_verification_subject: "验证您的邮箱 - {hostname}",
92 migration_verification_body: "欢迎来到 {hostname}!\n\n您的账户已成功迁移。要完成设置,请验证您的邮箱地址。\n\n您的验证码是:\n{code}\n\n复制上述验证码并在此输入:\n{verify_page}\n\n此验证码将在 48 小时后过期。\n\n或者直接点击链接:\n{verify_link}\n\n如果您没有迁移账户,请忽略此邮件。",
0093};
9495static STRINGS_JA: NotificationStrings = NotificationStrings {
···114 legacy_login_body: "@{handle} 様\n\nTOTP 認証に対応していないレガシーアプリ(Bluesky など)からのログインが検出されました。\n\n詳細:\n- 時刻:{timestamp}\n- IP アドレス:{ip}\n\nこのログインでは TOTP 保護がバイパスされました。このセッションは機密操作に対する権限が制限されています。\n\n心当たりがない場合は:\n1. 直ちにパスワードを変更してください\n2. アクティブなセッションを確認してください\n3. セキュリティ設定でレガシーアプリのログインを無効にすることを検討してください\n\nご注意ください。\n{hostname}",
115 migration_verification_subject: "メールアドレスの認証 - {hostname}",
116 migration_verification_body: "{hostname} へようこそ!\n\nアカウントの移行が完了しました。設定を完了するには、メールアドレスを認証してください。\n\n認証コードは:\n{code}\n\n上記のコードをコピーして、こちらで入力してください:\n{verify_page}\n\nこのコードは48時間後に期限切れとなります。\n\n自己責任でワンクリック認証:\n{verify_link}\n\nアカウントを移行していない場合は、このメールを無視してください。",
00117};
118119static STRINGS_KO: NotificationStrings = NotificationStrings {
···138 legacy_login_body: "안녕하세요 @{handle}님,\n\nTOTP 인증을 지원하지 않는 레거시 앱(예: Bluesky)을 사용한 로그인이 감지되었습니다.\n\n세부 정보:\n- 시간: {timestamp}\n- IP 주소: {ip}\n\n이 로그인에서 TOTP 보호가 우회되었습니다. 이 세션은 민감한 작업에 대한 권한이 제한됩니다.\n\n본인이 아닌 경우:\n1. 즉시 비밀번호를 변경하세요\n2. 활성 세션을 검토하세요\n3. 보안 설정에서 레거시 앱 로그인 비활성화를 고려하세요\n\n{hostname} 드림",
139 migration_verification_subject: "이메일 인증 - {hostname}",
140 migration_verification_body: "{hostname}에 오신 것을 환영합니다!\n\n계정 마이그레이션이 완료되었습니다. 설정을 완료하려면 이메일 주소를 인증하세요.\n\n인증 코드는:\n{code}\n\n위 코드를 복사하여 여기에 입력하세요:\n{verify_page}\n\n이 코드는 48시간 후에 만료됩니다.\n\n위험을 감수하고 원클릭 인증:\n{verify_link}\n\n계정을 마이그레이션하지 않았다면 이 이메일을 무시하세요.",
00141};
142143static STRINGS_SV: NotificationStrings = NotificationStrings {
···162 legacy_login_body: "Hej @{handle},\n\nEn inloggning till ditt konto upptäcktes med en äldre app (som Bluesky) som inte stöder TOTP-verifiering.\n\nDetaljer:\n- Tid: {timestamp}\n- IP-adress: {ip}\n\nDitt TOTP-skydd kringgicks för denna inloggning. Sessionen har begränsade behörigheter för känsliga operationer.\n\nOm detta inte var du:\n1. Ändra ditt lösenord omedelbart\n2. Granska dina aktiva sessioner\n3. Överväg att inaktivera äldre appinloggningar i dina säkerhetsinställningar\n\nVar försiktig,\n{hostname}",
163 migration_verification_subject: "Verifiera din e-post - {hostname}",
164 migration_verification_body: "Välkommen till {hostname}!\n\nDitt konto har migrerats framgångsrikt. För att slutföra installationen, verifiera din e-postadress.\n\nDin verifieringskod är:\n{code}\n\nKopiera koden ovan och ange den på:\n{verify_page}\n\nDenna kod upphör om 48 timmar.\n\nEller om du gillar att leva farligt:\n{verify_link}\n\nOm du inte migrerade ditt konto kan du ignorera detta meddelande.",
00165};
166167static STRINGS_FI: NotificationStrings = NotificationStrings {
···186 legacy_login_body: "Hei @{handle},\n\nTilillesi havaittiin kirjautuminen vanhalla sovelluksella (kuten Bluesky), joka ei tue TOTP-vahvistusta.\n\nTiedot:\n- Aika: {timestamp}\n- IP-osoite: {ip}\n\nTOTP-suojauksesi ohitettiin tässä kirjautumisessa. Istunnolla on rajoitetut oikeudet arkaluontoisiin toimintoihin.\n\nJos tämä et ollut sinä:\n1. Vaihda salasanasi välittömästi\n2. Tarkista aktiiviset istuntosi\n3. Harkitse vanhojen sovellusten kirjautumisen poistamista käytöstä turvallisuusasetuksissa\n\nOle varovainen,\n{hostname}",
187 migration_verification_subject: "Vahvista sähköpostisi - {hostname}",
188 migration_verification_body: "Tervetuloa palveluun {hostname}!\n\nTilisi on siirretty onnistuneesti. Viimeistele asennus vahvistamalla sähköpostiosoitteesi.\n\nVahvistuskoodisi on:\n{code}\n\nKopioi koodi yllä ja syötä se osoitteessa:\n{verify_page}\n\nTämä koodi vanhenee 48 tunnissa.\n\nTai jos pidät vaarallisesta elämästä:\n{verify_link}\n\nJos et siirtänyt tiliäsi, voit jättää tämän viestin huomiotta.",
00189};
190191pub fn format_message(template: &str, vars: &[(&str, &str)]) -> String {
···31 pub legacy_login_body: &'static str,
32 pub migration_verification_subject: &'static str,
33 pub migration_verification_body: &'static str,
34+ pub channel_verified_subject: &'static str,
35+ pub channel_verified_body: &'static str,
36}
3738pub fn get_strings(locale: &str) -> &'static NotificationStrings {
···68 legacy_login_body: "Hello @{handle},\n\nA login to your account was detected using a legacy app (like Bluesky) that doesn't support TOTP verification.\n\nDetails:\n- Time: {timestamp}\n- IP Address: {ip}\n\nYour TOTP protection was bypassed for this login. The session has limited permissions for sensitive operations.\n\nIf this wasn't you, please:\n1. Change your password immediately\n2. Review your active sessions\n3. Consider disabling legacy app logins in your security settings\n\nStay safe,\n{hostname}",
69 migration_verification_subject: "Verify your email - {hostname}",
70 migration_verification_body: "Welcome to {hostname}!\n\nYour account has been migrated successfully. To complete the setup, please verify your email address.\n\nYour verification code is:\n{code}\n\nCopy the code above and enter it at:\n{verify_page}\n\nThis code will expire in 48 hours.\n\nOr if you like to live dangerously:\n{verify_link}\n\nIf you did not migrate your account, please ignore this email.",
71+ channel_verified_subject: "Channel verified - {hostname}",
72+ channel_verified_body: "Hello {handle},\n\n{channel} has been verified as a notification channel for your account on {hostname}.",
73};
7475static STRINGS_ZH: NotificationStrings = NotificationStrings {
···94 legacy_login_body: "您好 @{handle},\n\n检测到使用不支持 TOTP 验证的传统应用(如 Bluesky)登录您的账户。\n\n详细信息:\n- 时间:{timestamp}\n- IP 地址:{ip}\n\n此次登录绕过了 TOTP 保护。该会话对敏感操作的权限有限。\n\n如果这不是您的操作,请:\n1. 立即更改密码\n2. 检查您的活跃会话\n3. 考虑在安全设置中禁用传统应用登录\n\n请注意安全,\n{hostname}",
95 migration_verification_subject: "验证您的邮箱 - {hostname}",
96 migration_verification_body: "欢迎来到 {hostname}!\n\n您的账户已成功迁移。要完成设置,请验证您的邮箱地址。\n\n您的验证码是:\n{code}\n\n复制上述验证码并在此输入:\n{verify_page}\n\n此验证码将在 48 小时后过期。\n\n或者直接点击链接:\n{verify_link}\n\n如果您没有迁移账户,请忽略此邮件。",
97+ channel_verified_subject: "通知渠道已验证 - {hostname}",
98+ channel_verified_body: "您好 {handle},\n\n{channel} 已被验证为您在 {hostname} 上的通知渠道。",
99};
100101static STRINGS_JA: NotificationStrings = NotificationStrings {
···120 legacy_login_body: "@{handle} 様\n\nTOTP 認証に対応していないレガシーアプリ(Bluesky など)からのログインが検出されました。\n\n詳細:\n- 時刻:{timestamp}\n- IP アドレス:{ip}\n\nこのログインでは TOTP 保護がバイパスされました。このセッションは機密操作に対する権限が制限されています。\n\n心当たりがない場合は:\n1. 直ちにパスワードを変更してください\n2. アクティブなセッションを確認してください\n3. セキュリティ設定でレガシーアプリのログインを無効にすることを検討してください\n\nご注意ください。\n{hostname}",
121 migration_verification_subject: "メールアドレスの認証 - {hostname}",
122 migration_verification_body: "{hostname} へようこそ!\n\nアカウントの移行が完了しました。設定を完了するには、メールアドレスを認証してください。\n\n認証コードは:\n{code}\n\n上記のコードをコピーして、こちらで入力してください:\n{verify_page}\n\nこのコードは48時間後に期限切れとなります。\n\n自己責任でワンクリック認証:\n{verify_link}\n\nアカウントを移行していない場合は、このメールを無視してください。",
123+ channel_verified_subject: "通知チャンネル認証完了 - {hostname}",
124+ channel_verified_body: "{handle} 様\n\n{channel} が {hostname} の通知チャンネルとして認証されました。",
125};
126127static STRINGS_KO: NotificationStrings = NotificationStrings {
···146 legacy_login_body: "안녕하세요 @{handle}님,\n\nTOTP 인증을 지원하지 않는 레거시 앱(예: Bluesky)을 사용한 로그인이 감지되었습니다.\n\n세부 정보:\n- 시간: {timestamp}\n- IP 주소: {ip}\n\n이 로그인에서 TOTP 보호가 우회되었습니다. 이 세션은 민감한 작업에 대한 권한이 제한됩니다.\n\n본인이 아닌 경우:\n1. 즉시 비밀번호를 변경하세요\n2. 활성 세션을 검토하세요\n3. 보안 설정에서 레거시 앱 로그인 비활성화를 고려하세요\n\n{hostname} 드림",
147 migration_verification_subject: "이메일 인증 - {hostname}",
148 migration_verification_body: "{hostname}에 오신 것을 환영합니다!\n\n계정 마이그레이션이 완료되었습니다. 설정을 완료하려면 이메일 주소를 인증하세요.\n\n인증 코드는:\n{code}\n\n위 코드를 복사하여 여기에 입력하세요:\n{verify_page}\n\n이 코드는 48시간 후에 만료됩니다.\n\n위험을 감수하고 원클릭 인증:\n{verify_link}\n\n계정을 마이그레이션하지 않았다면 이 이메일을 무시하세요.",
149+ channel_verified_subject: "알림 채널 인증 완료 - {hostname}",
150+ channel_verified_body: "안녕하세요 {handle}님,\n\n{channel}이(가) {hostname}의 알림 채널로 인증되었습니다.",
151};
152153static STRINGS_SV: NotificationStrings = NotificationStrings {
···172 legacy_login_body: "Hej @{handle},\n\nEn inloggning till ditt konto upptäcktes med en äldre app (som Bluesky) som inte stöder TOTP-verifiering.\n\nDetaljer:\n- Tid: {timestamp}\n- IP-adress: {ip}\n\nDitt TOTP-skydd kringgicks för denna inloggning. Sessionen har begränsade behörigheter för känsliga operationer.\n\nOm detta inte var du:\n1. Ändra ditt lösenord omedelbart\n2. Granska dina aktiva sessioner\n3. Överväg att inaktivera äldre appinloggningar i dina säkerhetsinställningar\n\nVar försiktig,\n{hostname}",
173 migration_verification_subject: "Verifiera din e-post - {hostname}",
174 migration_verification_body: "Välkommen till {hostname}!\n\nDitt konto har migrerats framgångsrikt. För att slutföra installationen, verifiera din e-postadress.\n\nDin verifieringskod är:\n{code}\n\nKopiera koden ovan och ange den på:\n{verify_page}\n\nDenna kod upphör om 48 timmar.\n\nEller om du gillar att leva farligt:\n{verify_link}\n\nOm du inte migrerade ditt konto kan du ignorera detta meddelande.",
175+ channel_verified_subject: "Aviseringskanal verifierad - {hostname}",
176+ channel_verified_body: "Hej {handle},\n\n{channel} har verifierats som aviseringskanal för ditt konto på {hostname}.",
177};
178179static STRINGS_FI: NotificationStrings = NotificationStrings {
···198 legacy_login_body: "Hei @{handle},\n\nTilillesi havaittiin kirjautuminen vanhalla sovelluksella (kuten Bluesky), joka ei tue TOTP-vahvistusta.\n\nTiedot:\n- Aika: {timestamp}\n- IP-osoite: {ip}\n\nTOTP-suojauksesi ohitettiin tässä kirjautumisessa. Istunnolla on rajoitetut oikeudet arkaluontoisiin toimintoihin.\n\nJos tämä et ollut sinä:\n1. Vaihda salasanasi välittömästi\n2. Tarkista aktiiviset istuntosi\n3. Harkitse vanhojen sovellusten kirjautumisen poistamista käytöstä turvallisuusasetuksissa\n\nOle varovainen,\n{hostname}",
199 migration_verification_subject: "Vahvista sähköpostisi - {hostname}",
200 migration_verification_body: "Tervetuloa palveluun {hostname}!\n\nTilisi on siirretty onnistuneesti. Viimeistele asennus vahvistamalla sähköpostiosoitteesi.\n\nVahvistuskoodisi on:\n{code}\n\nKopioi koodi yllä ja syötä se osoitteessa:\n{verify_page}\n\nTämä koodi vanhenee 48 tunnissa.\n\nTai jos pidät vaarallisesta elämästä:\n{verify_link}\n\nJos et siirtänyt tiliäsi, voit jättää tämän viestin huomiotta.",
201+ channel_verified_subject: "Ilmoituskanava vahvistettu - {hostname}",
202+ channel_verified_body: "Hei {handle},\n\n{channel} on vahvistettu ilmoituskanavaksi tilillesi palvelussa {hostname}.",
203};
204205pub fn format_message(template: &str, vars: &[(&str, &str)]) -> String {
+64-3
crates/tranquil-comms/src/sender.rs
···67 }
68}
6900000070pub fn is_valid_phone_number(number: &str) -> bool {
71 if number.len() < 2 || number.len() > 20 {
72 return false;
···244 let bot_token = std::env::var("TELEGRAM_BOT_TOKEN").ok()?;
245 Some(Self::new(bot_token))
246 }
000000000000000000000000000000000000000000000000000000247}
248249#[async_trait]
···254255 async fn send(&self, notification: &QueuedComms) -> Result<(), SendError> {
256 let chat_id = ¬ification.recipient;
257- let subject = notification.subject.as_deref().unwrap_or("Notification");
258- let text = format!("*{}*\n\n{}", subject, notification.body);
0259 let url = format!("https://api.telegram.org/bot{}/sendMessage", self.bot_token);
260 let payload = json!({
261 "chat_id": chat_id,
262 "text": text,
263- "parse_mode": "Markdown"
264 });
265 let mut last_error = None;
266 for attempt in 0..MAX_RETRIES {
···67 }
68}
6970+pub fn escape_html(text: &str) -> String {
71+ text.replace('&', "&")
72+ .replace('<', "<")
73+ .replace('>', ">")
74+}
75+76pub fn is_valid_phone_number(number: &str) -> bool {
77 if number.len() < 2 || number.len() > 20 {
78 return false;
···250 let bot_token = std::env::var("TELEGRAM_BOT_TOKEN").ok()?;
251 Some(Self::new(bot_token))
252 }
253+254+ pub async fn set_webhook(
255+ &self,
256+ webhook_url: &str,
257+ secret_token: Option<&str>,
258+ ) -> Result<(), SendError> {
259+ let url = format!("https://api.telegram.org/bot{}/setWebhook", self.bot_token);
260+ let mut payload = json!({ "url": webhook_url });
261+ if let Some(secret) = secret_token {
262+ payload["secret_token"] = json!(secret);
263+ }
264+ let response = self
265+ .http_client
266+ .post(&url)
267+ .json(&payload)
268+ .send()
269+ .await
270+ .map_err(|e| SendError::ExternalService(format!("setWebhook request failed: {}", e)))?;
271+ if !response.status().is_success() {
272+ let body = response.text().await.unwrap_or_default();
273+ return Err(SendError::ExternalService(format!(
274+ "setWebhook returned error: {}",
275+ body
276+ )));
277+ }
278+ Ok(())
279+ }
280+281+ pub async fn resolve_bot_username(&self) -> Result<String, SendError> {
282+ let url = format!("https://api.telegram.org/bot{}/getMe", self.bot_token);
283+ let response = self.http_client.get(&url).send().await.map_err(|e| {
284+ SendError::ExternalService(format!("Telegram getMe request failed: {}", e))
285+ })?;
286+287+ if !response.status().is_success() {
288+ let body = response.text().await.unwrap_or_default();
289+ return Err(SendError::ExternalService(format!(
290+ "Telegram getMe returned error: {}",
291+ body
292+ )));
293+ }
294+295+ let data: serde_json::Value = response.json().await.map_err(|e| {
296+ SendError::ExternalService(format!("Failed to parse getMe response: {}", e))
297+ })?;
298+299+ data.get("result")
300+ .and_then(|r| r.get("username"))
301+ .and_then(|u| u.as_str())
302+ .map(|s| s.to_string())
303+ .ok_or_else(|| {
304+ SendError::ExternalService("getMe response missing username".to_string())
305+ })
306+ }
307}
308309#[async_trait]
···314315 async fn send(&self, notification: &QueuedComms) -> Result<(), SendError> {
316 let chat_id = ¬ification.recipient;
317+ let subject = escape_html(notification.subject.as_deref().unwrap_or("Notification"));
318+ let body = escape_html(¬ification.body);
319+ let text = format!("<b>{}</b>\n\n{}", subject, body);
320 let url = format!("https://api.telegram.org/bot{}/sendMessage", self.bot_token);
321 let payload = json!({
322 "chat_id": chat_id,
323 "text": text,
324+ "parse_mode": "HTML"
325 });
326 let mut last_error = None;
327 for attempt in 0..MAX_RETRIES {
···439 "noMessages": "No messages found.",
440 "discordInUseWarning": "This Discord ID is already associated with another account.",
441 "telegramInUseWarning": "This Telegram username is already associated with another account.",
442- "signalInUseWarning": "This Signal number is already associated with another account."
00443 },
444 "repoExplorer": {
445 "collections": "Collections",
···439 "noMessages": "No messages found.",
440 "discordInUseWarning": "This Discord ID is already associated with another account.",
441 "telegramInUseWarning": "This Telegram username is already associated with another account.",
442+ "signalInUseWarning": "This Signal number is already associated with another account.",
443+ "telegramStartBot": "Or send /start {handle} to @{botUsername} manually",
444+ "telegramOpenLink": "Open Telegram to verify"
445 },
446 "repoExplorer": {
447 "collections": "Collections",
+2
frontend/src/locales/fi.json
···436 "discordInUseWarning": "Tämä Discord-tunnus on jo yhdistetty toiseen tiliin.",
437 "telegramInUseWarning": "Tämä Telegram-käyttäjänimi on jo yhdistetty toiseen tiliin.",
438 "signalInUseWarning": "Tämä Signal-numero on jo yhdistetty toiseen tiliin.",
00439 "failedToLoad": "Asetusten lataus epäonnistui",
440 "failedToSave": "Asetusten tallennus epäonnistui",
441 "failedToVerify": "Vahvistus epäonnistui",
···436 "discordInUseWarning": "Tämä Discord-tunnus on jo yhdistetty toiseen tiliin.",
437 "telegramInUseWarning": "Tämä Telegram-käyttäjänimi on jo yhdistetty toiseen tiliin.",
438 "signalInUseWarning": "Tämä Signal-numero on jo yhdistetty toiseen tiliin.",
439+ "telegramStartBot": "Tai lähetä /start {handle} käyttäjälle @{botUsername} manuaalisesti",
440+ "telegramOpenLink": "Avaa Telegram vahvistaaksesi",
441 "failedToLoad": "Asetusten lataus epäonnistui",
442 "failedToSave": "Asetusten tallennus epäonnistui",
443 "failedToVerify": "Vahvistus epäonnistui",
···436 "discordInUseWarning": "이 Discord ID는 이미 다른 계정과 연결되어 있습니다.",
437 "telegramInUseWarning": "이 Telegram 사용자 이름은 이미 다른 계정과 연결되어 있습니다.",
438 "signalInUseWarning": "이 Signal 번호는 이미 다른 계정과 연결되어 있습니다.",
00439 "failedToLoad": "설정 로딩 실패",
440 "failedToSave": "설정 저장 실패",
441 "failedToVerify": "인증 실패",
···436 "discordInUseWarning": "이 Discord ID는 이미 다른 계정과 연결되어 있습니다.",
437 "telegramInUseWarning": "이 Telegram 사용자 이름은 이미 다른 계정과 연결되어 있습니다.",
438 "signalInUseWarning": "이 Signal 번호는 이미 다른 계정과 연결되어 있습니다.",
439+ "telegramStartBot": "또는 @{botUsername}에게 /start {handle}을 직접 보내세요",
440+ "telegramOpenLink": "Telegram에서 인증하기",
441 "failedToLoad": "설정 로딩 실패",
442 "failedToSave": "설정 저장 실패",
443 "failedToVerify": "인증 실패",
+2
frontend/src/locales/sv.json
···436 "discordInUseWarning": "Detta Discord-ID är redan kopplat till ett annat konto.",
437 "telegramInUseWarning": "Detta Telegram-användarnamn är redan kopplat till ett annat konto.",
438 "signalInUseWarning": "Detta Signal-nummer är redan kopplat till ett annat konto.",
00439 "failedToLoad": "Kunde inte ladda inställningar",
440 "failedToSave": "Kunde inte spara inställningar",
441 "failedToVerify": "Verifiering misslyckades",
···436 "discordInUseWarning": "Detta Discord-ID är redan kopplat till ett annat konto.",
437 "telegramInUseWarning": "Detta Telegram-användarnamn är redan kopplat till ett annat konto.",
438 "signalInUseWarning": "Detta Signal-nummer är redan kopplat till ett annat konto.",
439+ "telegramStartBot": "Eller skicka /start {handle} till @{botUsername} manuellt",
440+ "telegramOpenLink": "Öppna Telegram för att verifiera",
441 "failedToLoad": "Kunde inte ladda inställningar",
442 "failedToSave": "Kunde inte spara inställningar",
443 "failedToVerify": "Verifiering misslyckades",