XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFESpaceNinjaServer

A simple server for a small space ninja game

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFESpaceNinjaServer

feat(webui): send inbox message to multiple specific players (#4047)

Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/4047 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com>

696fb59c
Sainan <63328889+Sainan@users.noreply.github.com>
提交于

代码差异

11 个文件 +125 -205
Modified src/controllers/custom/createMessageController.ts +8 -6
@@ -4,19 +4,21 @@ import { getAccountForRequest, isAdministrator } from "../../services/loginServi
4 4 import type { RequestHandler } from "express";
5 5
6 6 export const createMessageController: RequestHandler = async (req, res) => {
7 const message = req.body as (IMessageCreationTemplate & { ownerId: string })[] | undefined;
8
9 7 const account = await getAccountForRequest(req);
10 8 if (!isAdministrator(account)) {
11 9 res.status(401).end();
12 10 return;
13 11 }
14 12
15 if (!Array.isArray(message) || message.length === 0) {
16 res.status(400).send("No message provided");
17 return;
13 const data = req.body as ICreateMessageRequest;
14 for (const targetId of data.targetIds) {
15 await createMessage(targetId, data.messages);
18 16 }
19 await createMessage(message[0].ownerId, message);
20 17
21 18 res.status(204).end();
22 19 };
20
21 interface ICreateMessageRequest {
22 targetIds: string[];
23 messages: IMessageCreationTemplate[];
24 }
Modified src/services/loginService.ts +4 -0
@@ -40,6 +40,10 @@ export const getUsernameFromEmail = async (email: string): Promise<string> => {
40 40 };
41 41
42 42 export const createAccount = async (accountData: IDatabaseAccountRequiredFields): Promise<IDatabaseAccountJson> => {
43 if (accountData.DisplayName == "all") {
44 throw new Error(`"${accountData.DisplayName}" is reserved and may not be used as a username`);
45 }
46
43 47 const account = new Account(accountData);
44 48 try {
45 49 await account.save();
Modified static/webui/index.html +4 -12
@@ -1734,18 +1734,9 @@
1734 1734 <div class="card mt-3">
1735 1735 <h5 class="card-header" data-loc="admin_broadcastInbox"></h5>
1736 1736 <div class="card-body">
1737 <div class="row g-2 mb-2">
1738 <div class="col-md-4">
1739 <label class="form-label" for="admin-message-target-mode" data-loc="admin_messageTargetMode"></label>
1740 <select class="form-select" id="admin-message-target-mode" onchange="onAdminMessageTargetModeChange();">
1741 <option value="all" data-loc="admin_messageTargetAll"></option>
1742 <option value="specific" data-loc="admin_messageTargetSpecific"></option>
1743 </select>
1744 </div>
1745 <div class="col-md-8 d-none" id="admin-message-target-value-group">
1746 <label class="form-label" for="admin-message-target-value" data-loc="admin_messageTargetValue"></label>
1747 <input class="form-control" id="admin-message-target-value" />
1748 </div>
1737 <div class="mb-2">
1738 <label class="form-label" for="admin-message-target" data-loc="admin_messageTarget"></label>
1739 <input class="form-control tags-input tags-input-unique" id="admin-message-target" list="datalist-usernames" />
1749 1740 </div>
1750 1741 <div class="mb-2">
1751 1742 <label class="form-label" for="admin-message-sender" data-loc="admin_messageSender"></label>
@@ -1845,6 +1836,7 @@
1845 1836 <option>Assassination</option>
1846 1837 <option>Alchemy</option>
1847 1838 </datalist>
1839 <datalist id="datalist-usernames"></datalist>
1848 1840 <script src="/webui/libs/jquery-3.6.0.min.js"></script>
1849 1841 <script src="/webui/libs/whirlpool-js.min.js"></script>
1850 1842 <script src="/webui/libs/single.js"></script>
Modified static/webui/script.js +89 -104
@@ -601,7 +601,9 @@ function fetchItemList() {
601 601 await dictPromise;
602 602
603 603 document.querySelectorAll('[id^="datalist-"]').forEach(datalist => {
604 datalist.innerHTML = "";
604 if (datalist.id != "datalist-circuitGameModes" && datalist.id != "datalist-usernames") {
605 datalist.innerHTML = "";
606 }
605 607 });
606 608
607 609 const syndicateNone = document.createElement("option");
@@ -2462,7 +2464,7 @@ function doAcquireEvolution() {
2462 2464 }
2463 2465 }
2464 2466
2465 $(document).on("input", "input", function () {
2467 $(document).on("input", "input, textarea", function () {
2466 2468 $(this).removeClass("is-invalid");
2467 2469 });
2468 2470
@@ -3293,10 +3295,6 @@ function doAcquireRiven() {
3293 3295 });
3294 3296 }
3295 3297
3296 $("#addriven-fingerprint").on("input", () => {
3297 $("#addriven-fingerprint").removeClass("is-invalid");
3298 });
3299
3300 3298 function setFingerprint(ItemType, ItemId, fingerprint) {
3301 3299 revalidateAuthz().then(() => {
3302 3300 $.post({
@@ -5197,8 +5195,18 @@ function setImportSample(key) {
5197 5195
5198 5196 document.querySelectorAll(".tags-input").forEach(input => {
5199 5197 const datalist = document.getElementById(input.getAttribute("list"));
5200 const options = [...datalist.querySelectorAll("option")].map(x => x.textContent);
5198 let shadowDatalist;
5199 const optionsAreUnique = input.classList.contains("tags-input-unique");
5201 5200 input.oninput = function () {
5201 const options = [...datalist.querySelectorAll("option")].map(x => x.textContent);
5202
5203 if (!shadowDatalist) {
5204 shadowDatalist = document.createElement("datalist");
5205 shadowDatalist.setAttribute("id", "shadow-" + datalist.id);
5206 input.setAttribute("list", "shadow-" + datalist.id);
5207 shadowDatalist = document.documentElement.appendChild(shadowDatalist);
5208 }
5209
5202 5210 const value = [];
5203 5211 for (const tag of this.value.split(",")) {
5204 5212 const index = options.map(x => x.toLowerCase()).indexOf(tag.trim().toLowerCase());
@@ -5209,11 +5217,21 @@ document.querySelectorAll(".tags-input").forEach(input => {
5209 5217
5210 5218 this.setAttribute("data-tags-value", value.join(", "));
5211 5219
5212 datalist.innerHTML = "";
5213 for (const option of options) {
5214 const elm = document.createElement("option");
5215 elm.textContent = [...value, option, ""].join(", ");
5216 datalist.appendChild(elm);
5220 if (value[0] == "all") {
5221 shadowDatalist.innerHTML = "<option>all</option>";
5222 } else {
5223 shadowDatalist.innerHTML = "";
5224 for (const option of options) {
5225 if (!optionsAreUnique || value.indexOf(option) == -1) {
5226 const elm = document.createElement("option");
5227 if (option == "all") {
5228 elm.textContent = "all";
5229 } else {
5230 elm.textContent = [...value, option, ""].join(", ");
5231 }
5232 shadowDatalist.appendChild(elm);
5233 }
5234 }
5217 5235 }
5218 5236 };
5219 5237 input.oninput();
@@ -5661,8 +5679,8 @@ single.getRoute("/webui/admin").on("beforeload", function () {
5661 5679 window.is_admin = true;
5662 5680 $(".admin-hide").addClass("d-none");
5663 5681 $(".admin-show").removeClass("d-none");
5664 onAdminMessageTargetModeChange();
5665 5682 document.getElementById("registered-losers").innerHTML = "";
5683 document.getElementById("datalist-usernames").innerHTML = "<option>all</option>";
5666 5684 for (const user of users) {
5667 5685 const tr = document.createElement("tr");
5668 5686 {
@@ -5691,6 +5709,10 @@ single.getRoute("/webui/admin").on("beforeload", function () {
5691 5709 tr.appendChild(td);
5692 5710 }
5693 5711 document.getElementById("registered-losers").appendChild(tr);
5712
5713 const option = document.createElement("option");
5714 option.textContent = user.DisplayName;
5715 document.getElementById("datalist-usernames").appendChild(option);
5694 5716 }
5695 5717 })
5696 5718 .fail(res => {
@@ -5713,69 +5735,8 @@ single.getRoute("/webui/admin").on("beforeload", function () {
5713 5735 });
5714 5736 });
5715 5737
5716 function onAdminMessageTargetModeChange() {
5717 const targetMode = document.getElementById("admin-message-target-mode").value;
5718 document.getElementById("admin-message-target-value-group").classList.toggle("d-none", targetMode !== "specific");
5719 }
5720
5721 function resolveAdminBroadcastTargets(users, targetMode, targetValue) {
5722 if (targetMode === "all") {
5723 return users;
5724 }
5725 const normalizedTargetValue = targetValue.toLowerCase();
5726 const idMatches = users.filter(user => user.id === targetValue);
5727 return idMatches.length > 0
5728 ? idMatches
5729 : users.filter(user => user.DisplayName.toLowerCase() === normalizedTargetValue);
5730 }
5731
5732 function buildAdminBroadcastConfirmMessage(targetMode, targetUsers) {
5733 if (targetMode === "all") {
5734 return loc("admin_sendToAllConfirm");
5735 }
5736 if (targetUsers.length === 1) {
5737 return loc("admin_sendToSpecificConfirmOne")
5738 .replaceAll("|NAME|", targetUsers[0].DisplayName)
5739 .replaceAll("|ID|", targetUsers[0].id);
5740 }
5741 return loc("admin_sendToSpecificConfirmMany").replaceAll("|COUNT|", String(targetUsers.length));
5742 }
5743
5744 function buildAdminInboxPayload(user, fields) {
5745 const payload = [
5746 {
5747 ownerId: user.id,
5748 sndr: fields.sender,
5749 sub: fields.subject,
5750 msg: fields.body,
5751 highPriority: fields.highPriority
5752 }
5753 ];
5754 if (fields.icon) {
5755 payload[0].icon = fields.icon;
5756 }
5757 if (fields.attachments.length > 0) {
5758 payload[0].att = fields.attachments;
5759 }
5760 if (fields.countedAttachments && fields.countedAttachments.length > 0) {
5761 payload[0].countedAtt = fields.countedAttachments;
5762 }
5763 return payload;
5764 }
5765
5766 async function sendAdminInboxMessage(payload) {
5767 await $.ajax({
5768 url: "/custom/createMessage?" + window.authz,
5769 method: "POST",
5770 contentType: "application/json",
5771 data: JSON.stringify(payload)
5772 });
5773 }
5774
5775 5738 async function doAdminBroadcastInbox() {
5776 5739 await revalidateAuthz();
5777 const targetMode = document.getElementById("admin-message-target-mode").value;
5778 const targetValue = document.getElementById("admin-message-target-value").value.trim();
5779 5740 const sender = document.getElementById("admin-message-sender").value.trim();
5780 5741 const subject = document.getElementById("admin-message-subject").value.trim();
5781 5742 const body = document.getElementById("admin-message-body").value.trim();
@@ -5786,11 +5747,15 @@ async function doAdminBroadcastInbox() {
5786 5747 const submitButton = document.getElementById("admin-broadcast-submit");
5787 5748
5788 5749 if (!sender || !subject || !body) {
5789 alert(loc("admin_messageRequiredFields"));
5790 return;
5791 }
5792 if (targetMode === "specific" && !targetValue) {
5793 alert(loc("admin_messageTargetRequired"));
5750 if (!sender) {
5751 document.getElementById("admin-message-sender").classList.add("is-invalid");
5752 }
5753 if (!subject) {
5754 document.getElementById("admin-message-subject").classList.add("is-invalid");
5755 }
5756 if (!body) {
5757 document.getElementById("admin-message-body").classList.add("is-invalid");
5758 }
5794 5759 return;
5795 5760 }
5796 5761
@@ -5798,12 +5763,10 @@ async function doAdminBroadcastInbox() {
5798 5763 if (countedAttRaw) {
5799 5764 try {
5800 5765 countedAttachments = JSON.parse(countedAttRaw);
5801 } catch (_error) {
5802 alert(loc("admin_messageCountedAttInvalid"));
5803 return;
5804 }
5766 } catch (_) {}
5805 5767 if (!Array.isArray(countedAttachments)) {
5806 alert(loc("admin_messageCountedAttInvalid"));
5768 document.getElementById("admin-message-counted-att").classList.add("is-invalid");
5769 document.getElementById("admin-message-counted-att").focus();
5807 5770 return;
5808 5771 }
5809 5772 }
@@ -5816,33 +5779,55 @@ async function doAdminBroadcastInbox() {
5816 5779 submitButton.disabled = true;
5817 5780 try {
5818 5781 const users = await $.get("/custom/getRegisteredLosers?" + window.authz);
5819 const targetUsers = resolveAdminBroadcastTargets(users, targetMode, targetValue);
5820 if (targetUsers.length === 0) {
5821 alert(loc("admin_messageTargetNotFound"));
5782 document.getElementById("admin-message-target").oninput();
5783 const targets = document
5784 .getElementById("admin-message-target")
5785 .getAttribute("data-tags-value")
5786 .split(", ")
5787 .filter(x => x);
5788 let targetIds;
5789 if (targets[0] == "all") {
5790 targetIds = users.map(user => user.id);
5791 } else {
5792 targetIds = users.filter(user => targets.indexOf(user.DisplayName) != -1).map(user => user.id);
5793 }
5794 if (targetIds.length == 0) {
5795 toast(loc("code_nothingToDo"));
5822 5796 return;
5823 5797 }
5824 5798
5825 const confirmMsg = buildAdminBroadcastConfirmMessage(targetMode, targetUsers);
5826 if (!window.confirm(confirmMsg)) {
5799 if (!window.confirm(loc("admin_sendConfirm").replaceAll("|COUNT|", String(targetIds.length)))) {
5827 5800 return;
5828 5801 }
5829 5802
5830 const payloadFields = {
5831 sender,
5832 subject,
5833 body,
5834 highPriority,
5835 icon,
5836 attachments,
5837 countedAttachments
5838 };
5839 for (const user of targetUsers) {
5840 const payload = buildAdminInboxPayload(user, payloadFields);
5841 await sendAdminInboxMessage(payload);
5803 const messages = [
5804 {
5805 sndr: sender,
5806 sub: subject,
5807 msg: body,
5808 highPriority: highPriority
5809 }
5810 ];
5811 if (icon) {
5812 messages[0].icon = icon;
5813 }
5814 if (attachments.length > 0) {
5815 messages[0].att = attachments;
5842 5816 }
5843 toast(loc("admin_sendToAllSuccess").replaceAll("|COUNT|", targetUsers.length));
5844 } catch (_error) {
5845 alert(loc("settings_changeFailed"));
5817 if (countedAttachments && countedAttachments.length > 0) {
5818 messages[0].countedAtt = countedAttachments;
5819 }
5820
5821 await $.ajax({
5822 url: "/custom/createMessage?" + window.authz,
5823 method: "POST",
5824 contentType: "application/json",
5825 data: JSON.stringify({ targetIds, messages })
5826 });
5827
5828 toast(loc("admin_sendToAllSuccess").replaceAll("|COUNT|", targetIds.length));
5829 } catch (error) {
5830 alert(error);
5846 5831 } finally {
5847 5832 submitButton.disabled = false;
5848 5833 }
Modified static/webui/translations/de.js +3 -12
@@ -546,27 +546,18 @@ dict = {
546 546 admin_users: `Benutzer`,
547 547 admin_possess: `Als Benutzer anmelden`,
548 548 admin_broadcastInbox: `[UNTRANSLATED] Broadcast Inbox Message`,
549 admin_messageTargetMode: `[UNTRANSLATED] Target`,
550 admin_messageTargetAll: `[UNTRANSLATED] All Users`,
551 admin_messageTargetSpecific: `[UNTRANSLATED] Specific User`,
552 admin_messageTargetValue: `[UNTRANSLATED] Username or Account ID`,
549 admin_messageTarget: `[UNTRANSLATED] Target ("all" or comma-separated list of names)`,
553 550 admin_messageSender: `[UNTRANSLATED] Sender`,
554 551 admin_messageSubject: `[UNTRANSLATED] Subject`,
555 552 admin_messageBody: `[UNTRANSLATED] Message`,
556 553 admin_messageIcon: `[UNTRANSLATED] Icon (optional)`,
557 554 admin_messageAtt: `[UNTRANSLATED] Attachments (optional, one item type per line)`,
558 admin_messageCountedAtt: `[UNTRANSLATED] Counted Attachments (optional JSON)`,
555 admin_messageCountedAtt: `[UNTRANSLATED] Counted Attachments (optional, JSON)`,
559 556 admin_messageHighPriority: `[UNTRANSLATED] High Priority`,
560 557 admin_messageHighPriorityHelp: `[UNTRANSLATED] High priority messages cause the inbox to open automatically.`,
561 558 admin_sendInboxMessage: `[UNTRANSLATED] Send Inbox Message`,
562 admin_sendToAllConfirm: `[UNTRANSLATED] Are you sure you want to send this inbox message to all users?`,
563 admin_sendToSpecificConfirmOne: `[UNTRANSLATED] Send this inbox message to "|NAME|" (|ID|)?`,
564 admin_sendToSpecificConfirmMany: `[UNTRANSLATED] Send this inbox message to |COUNT| matching accounts?`,
559 admin_sendConfirm: `[UNTRANSLATED] Send this inbox message to |COUNT| user(s)?`,
565 560 admin_sendToAllSuccess: `[UNTRANSLATED] Successfully sent inbox message to |COUNT| user(s).`,
566 admin_messageRequiredFields: `[UNTRANSLATED] Sender, subject, and message are required.`,
567 admin_messageCountedAttInvalid: `[UNTRANSLATED] Counted attachments must be valid JSON array.`,
568 admin_messageTargetRequired: `[UNTRANSLATED] Username or account id is required for Specific User.`,
569 admin_messageTargetNotFound: `[UNTRANSLATED] No user matched that username or account id.`,
570 561
571 562 AVATAR_ABILITY_DURATION: `Dauer`,
572 563 AVATAR_ABILITY_EFFICIENCY: `Effizienz`,
Modified static/webui/translations/en.js +3 -12
@@ -545,27 +545,18 @@ dict = {
545 545 admin_users: `Users`,
546 546 admin_possess: `Impersonate`,
547 547 admin_broadcastInbox: `Broadcast Inbox Message`,
548 admin_messageTargetMode: `Target`,
549 admin_messageTargetAll: `All Users`,
550 admin_messageTargetSpecific: `Specific User`,
551 admin_messageTargetValue: `Username or Account ID`,
548 admin_messageTarget: `Target ("all" or comma-separated list of names)`,
552 549 admin_messageSender: `Sender`,
553 550 admin_messageSubject: `Subject`,
554 551 admin_messageBody: `Message`,
555 552 admin_messageIcon: `Icon (optional)`,
556 553 admin_messageAtt: `Attachments (optional, one item type per line)`,
557 admin_messageCountedAtt: `Counted Attachments (optional JSON)`,
554 admin_messageCountedAtt: `Counted Attachments (optional, JSON)`,
558 555 admin_messageHighPriority: `High Priority`,
559 556 admin_messageHighPriorityHelp: `High priority messages cause the inbox to open automatically.`,
560 557 admin_sendInboxMessage: `Send Inbox Message`,
561 admin_sendToAllConfirm: `Are you sure you want to send this inbox message to all users?`,
562 admin_sendToSpecificConfirmOne: `Send this inbox message to "|NAME|" (|ID|)?`,
563 admin_sendToSpecificConfirmMany: `Send this inbox message to |COUNT| matching accounts?`,
558 admin_sendConfirm: `Send this inbox message to |COUNT| user(s)?`,
564 559 admin_sendToAllSuccess: `Successfully sent inbox message to |COUNT| user(s).`,
565 admin_messageRequiredFields: `Sender, subject, and message are required.`,
566 admin_messageCountedAttInvalid: `Counted attachments must be valid JSON array.`,
567 admin_messageTargetRequired: `Username or account id is required for Specific User.`,
568 admin_messageTargetNotFound: `No user matched that username or account id.`,
569 560
570 561 AVATAR_ABILITY_DURATION: `Duration`,
571 562 AVATAR_ABILITY_EFFICIENCY: `Efficiency`,
Modified static/webui/translations/es.js +3 -12
@@ -546,27 +546,18 @@ dict = {
546 546 admin_users: `[UNTRANSLATED] Users`,
547 547 admin_possess: `[UNTRANSLATED] Impersonate`,
548 548 admin_broadcastInbox: `[UNTRANSLATED] Broadcast Inbox Message`,
549 admin_messageTargetMode: `[UNTRANSLATED] Target`,
550 admin_messageTargetAll: `[UNTRANSLATED] All Users`,
551 admin_messageTargetSpecific: `[UNTRANSLATED] Specific User`,
552 admin_messageTargetValue: `[UNTRANSLATED] Username or Account ID`,
549 admin_messageTarget: `[UNTRANSLATED] Target ("all" or comma-separated list of names)`,
553 550 admin_messageSender: `[UNTRANSLATED] Sender`,
554 551 admin_messageSubject: `[UNTRANSLATED] Subject`,
555 552 admin_messageBody: `[UNTRANSLATED] Message`,
556 553 admin_messageIcon: `[UNTRANSLATED] Icon (optional)`,
557 554 admin_messageAtt: `[UNTRANSLATED] Attachments (optional, one item type per line)`,
558 admin_messageCountedAtt: `[UNTRANSLATED] Counted Attachments (optional JSON)`,
555 admin_messageCountedAtt: `[UNTRANSLATED] Counted Attachments (optional, JSON)`,
559 556 admin_messageHighPriority: `[UNTRANSLATED] High Priority`,
560 557 admin_messageHighPriorityHelp: `[UNTRANSLATED] High priority messages cause the inbox to open automatically.`,
561 558 admin_sendInboxMessage: `[UNTRANSLATED] Send Inbox Message`,
562 admin_sendToAllConfirm: `[UNTRANSLATED] Are you sure you want to send this inbox message to all users?`,
563 admin_sendToSpecificConfirmOne: `[UNTRANSLATED] Send this inbox message to "|NAME|" (|ID|)?`,
564 admin_sendToSpecificConfirmMany: `[UNTRANSLATED] Send this inbox message to |COUNT| matching accounts?`,
559 admin_sendConfirm: `[UNTRANSLATED] Send this inbox message to |COUNT| user(s)?`,
565 560 admin_sendToAllSuccess: `[UNTRANSLATED] Successfully sent inbox message to |COUNT| user(s).`,
566 admin_messageRequiredFields: `[UNTRANSLATED] Sender, subject, and message are required.`,
567 admin_messageCountedAttInvalid: `[UNTRANSLATED] Counted attachments must be valid JSON array.`,
568 admin_messageTargetRequired: `[UNTRANSLATED] Username or account id is required for Specific User.`,
569 admin_messageTargetNotFound: `[UNTRANSLATED] No user matched that username or account id.`,
570 561
571 562 AVATAR_ABILITY_DURATION: `Duración`,
572 563 AVATAR_ABILITY_EFFICIENCY: `Eficiencia`,
Modified static/webui/translations/fr.js +3 -12
@@ -546,27 +546,18 @@ dict = {
546 546 admin_users: `Utilisateurs`,
547 547 admin_possess: `[UNTRANSLATED] Impersonate`,
548 548 admin_broadcastInbox: `[UNTRANSLATED] Broadcast Inbox Message`,
549 admin_messageTargetMode: `[UNTRANSLATED] Target`,
550 admin_messageTargetAll: `[UNTRANSLATED] All Users`,
551 admin_messageTargetSpecific: `[UNTRANSLATED] Specific User`,
552 admin_messageTargetValue: `[UNTRANSLATED] Username or Account ID`,
549 admin_messageTarget: `[UNTRANSLATED] Target ("all" or comma-separated list of names)`,
553 550 admin_messageSender: `[UNTRANSLATED] Sender`,
554 551 admin_messageSubject: `[UNTRANSLATED] Subject`,
555 552 admin_messageBody: `[UNTRANSLATED] Message`,
556 553 admin_messageIcon: `[UNTRANSLATED] Icon (optional)`,
557 554 admin_messageAtt: `[UNTRANSLATED] Attachments (optional, one item type per line)`,
558 admin_messageCountedAtt: `[UNTRANSLATED] Counted Attachments (optional JSON)`,
555 admin_messageCountedAtt: `[UNTRANSLATED] Counted Attachments (optional, JSON)`,
559 556 admin_messageHighPriority: `[UNTRANSLATED] High Priority`,
560 557 admin_messageHighPriorityHelp: `[UNTRANSLATED] High priority messages cause the inbox to open automatically.`,
561 558 admin_sendInboxMessage: `[UNTRANSLATED] Send Inbox Message`,
562 admin_sendToAllConfirm: `[UNTRANSLATED] Are you sure you want to send this inbox message to all users?`,
563 admin_sendToSpecificConfirmOne: `[UNTRANSLATED] Send this inbox message to "|NAME|" (|ID|)?`,
564 admin_sendToSpecificConfirmMany: `[UNTRANSLATED] Send this inbox message to |COUNT| matching accounts?`,
559 admin_sendConfirm: `[UNTRANSLATED] Send this inbox message to |COUNT| user(s)?`,
565 560 admin_sendToAllSuccess: `[UNTRANSLATED] Successfully sent inbox message to |COUNT| user(s).`,
566 admin_messageRequiredFields: `[UNTRANSLATED] Sender, subject, and message are required.`,
567 admin_messageCountedAttInvalid: `[UNTRANSLATED] Counted attachments must be valid JSON array.`,
568 admin_messageTargetRequired: `[UNTRANSLATED] Username or account id is required for Specific User.`,
569 admin_messageTargetNotFound: `[UNTRANSLATED] No user matched that username or account id.`,
570 561
571 562 AVATAR_ABILITY_DURATION: `Durée`,
572 563 AVATAR_ABILITY_EFFICIENCY: `Efficacité`,
Modified static/webui/translations/ru.js +3 -12
@@ -546,27 +546,18 @@ dict = {
546 546 admin_users: `Пользователи`,
547 547 admin_possess: `Дейстовать от имени`,
548 548 admin_broadcastInbox: `[UNTRANSLATED] Broadcast Inbox Message`,
549 admin_messageTargetMode: `[UNTRANSLATED] Target`,
550 admin_messageTargetAll: `[UNTRANSLATED] All Users`,
551 admin_messageTargetSpecific: `[UNTRANSLATED] Specific User`,
552 admin_messageTargetValue: `[UNTRANSLATED] Username or Account ID`,
549 admin_messageTarget: `[UNTRANSLATED] Target ("all" or comma-separated list of names)`,
553 550 admin_messageSender: `[UNTRANSLATED] Sender`,
554 551 admin_messageSubject: `[UNTRANSLATED] Subject`,
555 552 admin_messageBody: `[UNTRANSLATED] Message`,
556 553 admin_messageIcon: `[UNTRANSLATED] Icon (optional)`,
557 554 admin_messageAtt: `[UNTRANSLATED] Attachments (optional, one item type per line)`,
558 admin_messageCountedAtt: `[UNTRANSLATED] Counted Attachments (optional JSON)`,
555 admin_messageCountedAtt: `[UNTRANSLATED] Counted Attachments (optional, JSON)`,
559 556 admin_messageHighPriority: `[UNTRANSLATED] High Priority`,
560 557 admin_messageHighPriorityHelp: `[UNTRANSLATED] High priority messages cause the inbox to open automatically.`,
561 558 admin_sendInboxMessage: `[UNTRANSLATED] Send Inbox Message`,
562 admin_sendToAllConfirm: `[UNTRANSLATED] Are you sure you want to send this inbox message to all users?`,
563 admin_sendToSpecificConfirmOne: `[UNTRANSLATED] Send this inbox message to "|NAME|" (|ID|)?`,
564 admin_sendToSpecificConfirmMany: `[UNTRANSLATED] Send this inbox message to |COUNT| matching accounts?`,
559 admin_sendConfirm: `[UNTRANSLATED] Send this inbox message to |COUNT| user(s)?`,
565 560 admin_sendToAllSuccess: `[UNTRANSLATED] Successfully sent inbox message to |COUNT| user(s).`,
566 admin_messageRequiredFields: `[UNTRANSLATED] Sender, subject, and message are required.`,
567 admin_messageCountedAttInvalid: `[UNTRANSLATED] Counted attachments must be valid JSON array.`,
568 admin_messageTargetRequired: `[UNTRANSLATED] Username or account id is required for Specific User.`,
569 admin_messageTargetNotFound: `[UNTRANSLATED] No user matched that username or account id.`,
570 561
571 562 AVATAR_ABILITY_DURATION: `Длительность`,
572 563 AVATAR_ABILITY_EFFICIENCY: `Эффективность`,
Modified static/webui/translations/uk.js +3 -12
@@ -546,27 +546,18 @@ dict = {
546 546 admin_users: `[UNTRANSLATED] Users`,
547 547 admin_possess: `[UNTRANSLATED] Impersonate`,
548 548 admin_broadcastInbox: `[UNTRANSLATED] Broadcast Inbox Message`,
549 admin_messageTargetMode: `[UNTRANSLATED] Target`,
550 admin_messageTargetAll: `[UNTRANSLATED] All Users`,
551 admin_messageTargetSpecific: `[UNTRANSLATED] Specific User`,
552 admin_messageTargetValue: `[UNTRANSLATED] Username or Account ID`,
549 admin_messageTarget: `[UNTRANSLATED] Target ("all" or comma-separated list of names)`,
553 550 admin_messageSender: `[UNTRANSLATED] Sender`,
554 551 admin_messageSubject: `[UNTRANSLATED] Subject`,
555 552 admin_messageBody: `[UNTRANSLATED] Message`,
556 553 admin_messageIcon: `[UNTRANSLATED] Icon (optional)`,
557 554 admin_messageAtt: `[UNTRANSLATED] Attachments (optional, one item type per line)`,
558 admin_messageCountedAtt: `[UNTRANSLATED] Counted Attachments (optional JSON)`,
555 admin_messageCountedAtt: `[UNTRANSLATED] Counted Attachments (optional, JSON)`,
559 556 admin_messageHighPriority: `[UNTRANSLATED] High Priority`,
560 557 admin_messageHighPriorityHelp: `[UNTRANSLATED] High priority messages cause the inbox to open automatically.`,
561 558 admin_sendInboxMessage: `[UNTRANSLATED] Send Inbox Message`,
562 admin_sendToAllConfirm: `[UNTRANSLATED] Are you sure you want to send this inbox message to all users?`,
563 admin_sendToSpecificConfirmOne: `[UNTRANSLATED] Send this inbox message to "|NAME|" (|ID|)?`,
564 admin_sendToSpecificConfirmMany: `[UNTRANSLATED] Send this inbox message to |COUNT| matching accounts?`,
559 admin_sendConfirm: `[UNTRANSLATED] Send this inbox message to |COUNT| user(s)?`,
565 560 admin_sendToAllSuccess: `[UNTRANSLATED] Successfully sent inbox message to |COUNT| user(s).`,
566 admin_messageRequiredFields: `[UNTRANSLATED] Sender, subject, and message are required.`,
567 admin_messageCountedAttInvalid: `[UNTRANSLATED] Counted attachments must be valid JSON array.`,
568 admin_messageTargetRequired: `[UNTRANSLATED] Username or account id is required for Specific User.`,
569 admin_messageTargetNotFound: `[UNTRANSLATED] No user matched that username or account id.`,
570 561
571 562 AVATAR_ABILITY_DURATION: `Тривалість`,
572 563 AVATAR_ABILITY_EFFICIENCY: `Ощадливість`,
Modified static/webui/translations/zh.js +2 -11
@@ -546,10 +546,7 @@ dict = {
546 546 admin_users: `用户`,
547 547 admin_possess: `管理此账户`,
548 548 admin_broadcastInbox: `广播收件箱消息`,
549 admin_messageTargetMode: `目标`,
550 admin_messageTargetAll: `所有用户`,
551 admin_messageTargetSpecific: `指定用户`,
552 admin_messageTargetValue: `用户名或账户ID`,
549 admin_messageTarget: `[UNTRANSLATED] Target ("all" or comma-separated list of names)`,
553 550 admin_messageSender: `发件人`,
554 551 admin_messageSubject: `主题`,
555 552 admin_messageBody: `消息内容`,
@@ -559,14 +556,8 @@ dict = {
559 556 admin_messageHighPriority: `高优先级`,
560 557 admin_messageHighPriorityHelp: `高优先级消息会自动打开收件箱.`,
561 558 admin_sendInboxMessage: `发送收件箱消息`,
562 admin_sendToAllConfirm: `确定要向所有用户发送此收件箱消息吗?`,
563 admin_sendToSpecificConfirmOne: `确定要向"|NAME|" (|ID|) 发送此收件箱消息吗?`,
564 admin_sendToSpecificConfirmMany: `确定要向 |COUNT| 个匹配的账户发送此收件箱消息吗?`,
559 admin_sendConfirm: `[UNTRANSLATED] Send this inbox message to |COUNT| user(s)?`,
565 560 admin_sendToAllSuccess: `已成功向 |COUNT| 位用户发送收件箱消息.`,
566 admin_messageRequiredFields: `发件人, 主题和消息内容为必填项.`,
567 admin_messageCountedAttInvalid: `计数附件必须是有效的 JSON 数组.`,
568 admin_messageTargetRequired: `发送给指定用户时, 必须填写用户名或账户ID.`,
569 admin_messageTargetNotFound: `未找到匹配该用户名或账户ID的用户.`,
570 561
571 562 AVATAR_ABILITY_DURATION: `持续时间`,
572 563 AVATAR_ABILITY_EFFICIENCY: `效率`,