返回提交历史
Modified
src/controllers/custom/createMessageController.ts
+8
-6
Modified
src/services/loginService.ts
+4
-0
Modified
static/webui/index.html
+4
-12
Modified
static/webui/script.js
+89
-104
Modified
static/webui/translations/de.js
+3
-12
Modified
static/webui/translations/en.js
+3
-12
Modified
static/webui/translations/es.js
+3
-12
Modified
static/webui/translations/fr.js
+3
-12
Modified
static/webui/translations/ru.js
+3
-12
Modified
static/webui/translations/uk.js
+3
-12
Modified
static/webui/translations/zh.js
+2
-11
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
代码差异
11 个文件
+125
-205
@@ -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
}
@@ -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();
@@ -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>
@@ -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
}
@@ -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`,
@@ -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`,
@@ -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`,
@@ -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é`,
@@ -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: `Эффективность`,
@@ -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: `Ощадливість`,
@@ -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: `效率`,