Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit 038a6bc

Browse files
Make slash command errors translatable but also work in rageshakes (#7377)
See #7372 (comment) We want the error to be translated for the user but not in our rageshake logs. Also updates some error messages to give more info.
1 parent 53a72da commit 038a6bc

File tree

5 files changed

+91
-40
lines changed

5 files changed

+91
-40
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@
2929
"matrix_src_main": "./src/index.ts",
3030
"matrix_lib_main": "./lib/index.ts",
3131
"matrix_lib_typings": "./lib/index.d.ts",
32+
"matrix_i18n_extra_translation_funcs": [
33+
"newTranslatableError"
34+
],
3235
"scripts": {
3336
"prepublishOnly": "yarn build",
3437
"i18n": "matrix-gen-i18n",

src/SlashCommands.tsx

Lines changed: 74 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { logger } from "matrix-js-sdk/src/logger";
2727

2828
import { MatrixClientPeg } from './MatrixClientPeg';
2929
import dis from './dispatcher/dispatcher';
30-
import { _t, _td } from './languageHandler';
30+
import { _t, _td, newTranslatableError } from './languageHandler';
3131
import Modal from './Modal';
3232
import MultiInviter from './utils/MultiInviter';
3333
import { linkifyAndSanitizeHtml } from './HtmlUtils';
@@ -141,13 +141,26 @@ export class Command {
141141

142142
run(roomId: string, threadId: string, args: string) {
143143
// if it has no runFn then its an ignored/nop command (autocomplete only) e.g `/me`
144-
if (!this.runFn) return reject(_t("Command error"));
144+
if (!this.runFn) {
145+
reject(
146+
newTranslatableError(
147+
"Command error: Unable to handle slash command.",
148+
),
149+
);
150+
151+
return;
152+
}
145153

146154
const renderingType = threadId
147155
? TimelineRenderingType.Thread
148156
: TimelineRenderingType.Room;
149157
if (this.renderingTypes && !this.renderingTypes?.includes(renderingType)) {
150-
return reject(_t("Command error"));
158+
return reject(
159+
newTranslatableError(
160+
"Command error: Unable to find rendering type (%(renderingType)s)",
161+
{ renderingType },
162+
),
163+
);
151164
}
152165

153166
return this.runFn.bind(this)(roomId, args);
@@ -270,7 +283,9 @@ export const Commands = [
270283
const cli = MatrixClientPeg.get();
271284
const room = cli.getRoom(roomId);
272285
if (!room.currentState.mayClientSendStateEvent("m.room.tombstone", cli)) {
273-
return reject(_t("You do not have the required permissions to use this command."));
286+
return reject(
287+
newTranslatableError("You do not have the required permissions to use this command."),
288+
);
274289
}
275290

276291
const { finished } = Modal.createTrackedDialog('Slash Commands', 'upgrade room confirmation',
@@ -297,15 +312,10 @@ export const Commands = [
297312
return success((async () => {
298313
const unixTimestamp = Date.parse(args);
299314
if (!unixTimestamp) {
300-
throw new Error(
301-
// FIXME: Use newTranslatableError here instead
302-
// otherwise the rageshake error messages will be
303-
// translated too
304-
_t(
305-
// eslint-disable-next-line max-len
306-
'We were unable to understand the given date (%(inputDate)s). Try using the format YYYY-MM-DD.',
307-
{ inputDate: args },
308-
),
315+
throw newTranslatableError(
316+
'We were unable to understand the given date (%(inputDate)s). ' +
317+
'Try using the format YYYY-MM-DD.',
318+
{ inputDate: args },
309319
);
310320
}
311321

@@ -437,7 +447,11 @@ export const Commands = [
437447
return success(cli.setRoomTopic(roomId, args));
438448
}
439449
const room = cli.getRoom(roomId);
440-
if (!room) return reject(_t("Failed to set topic"));
450+
if (!room) {
451+
return reject(
452+
newTranslatableError("Failed to get room topic: Unable to find room (%(roomId)s", { roomId }),
453+
);
454+
}
441455

442456
const topicEvents = room.currentState.getStateEvents('m.room.topic', '');
443457
const topic = topicEvents && topicEvents.getContent().topic;
@@ -509,10 +523,16 @@ export const Commands = [
509523
useDefaultIdentityServer();
510524
return;
511525
}
512-
throw new Error(_t("Use an identity server to invite by email. Manage in Settings."));
526+
throw newTranslatableError(
527+
"Use an identity server to invite by email. Manage in Settings.",
528+
);
513529
});
514530
} else {
515-
return reject(_t("Use an identity server to invite by email. Manage in Settings."));
531+
return reject(
532+
newTranslatableError(
533+
"Use an identity server to invite by email. Manage in Settings.",
534+
),
535+
);
516536
}
517537
}
518538
const inviter = new MultiInviter(roomId);
@@ -680,7 +700,14 @@ export const Commands = [
680700
}
681701
if (targetRoomId) break;
682702
}
683-
if (!targetRoomId) return reject(_t('Unrecognised room address:') + ' ' + roomAlias);
703+
if (!targetRoomId) {
704+
return reject(
705+
newTranslatableError(
706+
'Unrecognised room address: %(roomAlias)s',
707+
{ roomAlias },
708+
),
709+
);
710+
}
684711
}
685712
}
686713

@@ -819,10 +846,14 @@ export const Commands = [
819846
if (!isNaN(powerLevel)) {
820847
const cli = MatrixClientPeg.get();
821848
const room = cli.getRoom(roomId);
822-
if (!room) return reject(_t("Command failed"));
849+
if (!room) {
850+
return reject(
851+
newTranslatableError("Command failed: Unable to find room (%(roomId)s", { roomId }),
852+
);
853+
}
823854
const member = room.getMember(userId);
824855
if (!member || getEffectiveMembership(member.membership) === EffectiveMembership.Leave) {
825-
return reject(_t("Could not find user in room"));
856+
return reject(newTranslatableError("Could not find user in room"));
826857
}
827858
const powerLevelEvent = room.currentState.getStateEvents('m.room.power_levels', '');
828859
return success(cli.setPowerLevel(roomId, userId, powerLevel, powerLevelEvent));
@@ -849,10 +880,16 @@ export const Commands = [
849880
if (matches) {
850881
const cli = MatrixClientPeg.get();
851882
const room = cli.getRoom(roomId);
852-
if (!room) return reject(_t("Command failed"));
883+
if (!room) {
884+
return reject(
885+
newTranslatableError("Command failed: Unable to find room (%(roomId)s", { roomId }),
886+
);
887+
}
853888

854889
const powerLevelEvent = room.currentState.getStateEvents('m.room.power_levels', '');
855-
if (!powerLevelEvent.getContent().users[args]) return reject(_t("Could not find user in room"));
890+
if (!powerLevelEvent.getContent().users[args]) {
891+
return reject(newTranslatableError("Could not find user in room"));
892+
}
856893
return success(cli.setPowerLevel(roomId, args, undefined, powerLevelEvent));
857894
}
858895
}
@@ -877,7 +914,7 @@ export const Commands = [
877914
isEnabled: () => SettingsStore.getValue(UIFeature.Widgets),
878915
runFn: function(roomId, widgetUrl) {
879916
if (!widgetUrl) {
880-
return reject(_t("Please supply a widget URL or embed code"));
917+
return reject(newTranslatableError("Please supply a widget URL or embed code"));
881918
}
882919

883920
// Try and parse out a widget URL from iframes
@@ -896,7 +933,7 @@ export const Commands = [
896933
}
897934

898935
if (!widgetUrl.startsWith("https://") && !widgetUrl.startsWith("http://")) {
899-
return reject(_t("Please supply a https:// or http:// widget URL"));
936+
return reject(newTranslatableError("Please supply a https:// or http:// widget URL"));
900937
}
901938
if (WidgetUtils.canUserModifyWidgets(roomId)) {
902939
const userId = MatrixClientPeg.get().getUserId();
@@ -918,7 +955,7 @@ export const Commands = [
918955

919956
return success(WidgetUtils.setRoomWidget(roomId, widgetId, type, widgetUrl, name, data));
920957
} else {
921-
return reject(_t("You cannot modify widgets in this room."));
958+
return reject(newTranslatableError("You cannot modify widgets in this room."));
922959
}
923960
},
924961
category: CommandCategories.admin,
@@ -941,30 +978,34 @@ export const Commands = [
941978
return success((async () => {
942979
const device = cli.getStoredDevice(userId, deviceId);
943980
if (!device) {
944-
throw new Error(_t('Unknown (user, session) pair:') + ` (${userId}, ${deviceId})`);
981+
throw newTranslatableError(
982+
'Unknown (user, session) pair: (%(userId)s, %(deviceId)s)',
983+
{ userId, deviceId },
984+
);
945985
}
946986
const deviceTrust = await cli.checkDeviceTrust(userId, deviceId);
947987

948988
if (deviceTrust.isVerified()) {
949989
if (device.getFingerprint() === fingerprint) {
950-
throw new Error(_t('Session already verified!'));
990+
throw newTranslatableError('Session already verified!');
951991
} else {
952-
throw new Error(_t('WARNING: Session already verified, but keys do NOT MATCH!'));
992+
throw newTranslatableError('WARNING: Session already verified, but keys do NOT MATCH!');
953993
}
954994
}
955995

956996
if (device.getFingerprint() !== fingerprint) {
957997
const fprint = device.getFingerprint();
958-
throw new Error(
959-
_t('WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session' +
998+
throw newTranslatableError(
999+
'WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session' +
9601000
' %(deviceId)s is "%(fprint)s" which does not match the provided key ' +
9611001
'"%(fingerprint)s". This could mean your communications are being intercepted!',
9621002
{
9631003
fprint,
9641004
userId,
9651005
deviceId,
9661006
fingerprint,
967-
}));
1007+
},
1008+
);
9681009
}
9691010

9701011
await cli.setDeviceVerified(userId, deviceId, true);
@@ -1083,7 +1124,7 @@ export const Commands = [
10831124
if (isPhoneNumber) {
10841125
const results = await CallHandler.instance.pstnLookup(this.state.value);
10851126
if (!results || results.length === 0 || !results[0].userid) {
1086-
throw new Error("Unable to find Matrix ID for phone number");
1127+
throw newTranslatableError("Unable to find Matrix ID for phone number");
10871128
}
10881129
userId = results[0].userid;
10891130
}
@@ -1135,7 +1176,7 @@ export const Commands = [
11351176
runFn: function(roomId, args) {
11361177
const call = CallHandler.instance.getCallForRoom(roomId);
11371178
if (!call) {
1138-
return reject("No active call in this room");
1179+
return reject(newTranslatableError("No active call in this room"));
11391180
}
11401181
call.setRemoteOnHold(true);
11411182
return success();
@@ -1149,7 +1190,7 @@ export const Commands = [
11491190
runFn: function(roomId, args) {
11501191
const call = CallHandler.instance.getCallForRoom(roomId);
11511192
if (!call) {
1152-
return reject("No active call in this room");
1193+
return reject(newTranslatableError("No active call in this room"));
11531194
}
11541195
call.setRemoteOnHold(false);
11551196
return success();

src/components/views/rooms/SendMessageComposer.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,9 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
378378
let errText;
379379
if (typeof error === 'string') {
380380
errText = error;
381+
} else if (error.translatedMessage) {
382+
// Check for translatable errors (newTranslatableError)
383+
errText = error.translatedMessage;
381384
} else if (error.message) {
382385
errText = error.message;
383386
} else {

src/i18n/strings/en_EN.json

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,8 @@
424424
"Advanced": "Advanced",
425425
"Effects": "Effects",
426426
"Other": "Other",
427-
"Command error": "Command error",
427+
"Command error: Unable to handle slash command.": "Command error: Unable to handle slash command.",
428+
"Command error: Unable to find rendering type (%(renderingType)s)": "Command error: Unable to find rendering type (%(renderingType)s)",
428429
"Usage": "Usage",
429430
"Sends the given message as a spoiler": "Sends the given message as a spoiler",
430431
"Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Prepends ¯\\_(ツ)_/¯ to a plain-text message",
@@ -443,7 +444,7 @@
443444
"Changes your avatar in this current room only": "Changes your avatar in this current room only",
444445
"Changes your avatar in all rooms": "Changes your avatar in all rooms",
445446
"Gets or sets the room topic": "Gets or sets the room topic",
446-
"Failed to set topic": "Failed to set topic",
447+
"Failed to get room topic: Unable to find room (%(roomId)s": "Failed to get room topic: Unable to find room (%(roomId)s",
447448
"This room has no topic.": "This room has no topic.",
448449
"Sets the room name": "Sets the room name",
449450
"Invites user with given id to current room": "Invites user with given id to current room",
@@ -452,7 +453,7 @@
452453
"Use an identity server to invite by email. Manage in Settings.": "Use an identity server to invite by email. Manage in Settings.",
453454
"Joins room with given address": "Joins room with given address",
454455
"Leave room": "Leave room",
455-
"Unrecognised room address:": "Unrecognised room address:",
456+
"Unrecognised room address: %(roomAlias)s": "Unrecognised room address: %(roomAlias)s",
456457
"Kicks user with given id": "Kicks user with given id",
457458
"Bans user with given id": "Bans user with given id",
458459
"Unbans user with given ID": "Unbans user with given ID",
@@ -463,7 +464,7 @@
463464
"Unignored user": "Unignored user",
464465
"You are no longer ignoring %(userId)s": "You are no longer ignoring %(userId)s",
465466
"Define the power level of a user": "Define the power level of a user",
466-
"Command failed": "Command failed",
467+
"Command failed: Unable to find room (%(roomId)s": "Command failed: Unable to find room (%(roomId)s",
467468
"Could not find user in room": "Could not find user in room",
468469
"Deops user with given id": "Deops user with given id",
469470
"Opens the Developer Tools dialog": "Opens the Developer Tools dialog",
@@ -472,7 +473,7 @@
472473
"Please supply a https:// or http:// widget URL": "Please supply a https:// or http:// widget URL",
473474
"You cannot modify widgets in this room.": "You cannot modify widgets in this room.",
474475
"Verifies a user, session, and pubkey tuple": "Verifies a user, session, and pubkey tuple",
475-
"Unknown (user, session) pair:": "Unknown (user, session) pair:",
476+
"Unknown (user, session) pair: (%(userId)s, %(deviceId)s)": "Unknown (user, session) pair: (%(userId)s, %(deviceId)s)",
476477
"Session already verified!": "Session already verified!",
477478
"WARNING: Session already verified, but keys do NOT MATCH!": "WARNING: Session already verified, but keys do NOT MATCH!",
478479
"WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!": "WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session %(deviceId)s is \"%(fprint)s\" which does not match the provided key \"%(fingerprint)s\". This could mean your communications are being intercepted!",
@@ -485,8 +486,10 @@
485486
"Displays information about a user": "Displays information about a user",
486487
"Send a bug report with logs": "Send a bug report with logs",
487488
"Opens chat with the given user": "Opens chat with the given user",
489+
"Unable to find Matrix ID for phone number": "Unable to find Matrix ID for phone number",
488490
"Sends a message to the given user": "Sends a message to the given user",
489491
"Places the call in the current room on hold": "Places the call in the current room on hold",
492+
"No active call in this room": "No active call in this room",
490493
"Takes the call in the current room off hold": "Takes the call in the current room off hold",
491494
"Converts the room to a DM": "Converts the room to a DM",
492495
"Converts the DM to a room": "Converts the DM to a room",
@@ -1630,6 +1633,7 @@
16301633
"This room is end-to-end encrypted": "This room is end-to-end encrypted",
16311634
"Everyone in this room is verified": "Everyone in this room is verified",
16321635
"Server error": "Server error",
1636+
"Command error": "Command error",
16331637
"Server unavailable, overloaded, or something else went wrong.": "Server unavailable, overloaded, or something else went wrong.",
16341638
"Unknown Command": "Unknown Command",
16351639
"Unrecognised command: %(commandText)s": "Unrecognised command: %(commandText)s",

src/languageHandler.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,9 @@ interface ITranslatableError extends Error {
5353
* @param {string} message Message to translate.
5454
* @returns {Error} The constructed error.
5555
*/
56-
export function newTranslatableError(message: string) {
56+
export function newTranslatableError(message: string, variables?: IVariables): ITranslatableError {
5757
const error = new Error(message) as ITranslatableError;
58-
error.translatedMessage = _t(message);
58+
error.translatedMessage = _t(message, variables);
5959
return error;
6060
}
6161

0 commit comments

Comments
 (0)