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

Commit

Permalink
Make slash command errors translatable but also work in rageshakes (#…
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
MadLittleMods authored Jan 11, 2022
1 parent 53a72da commit 038a6bc
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 40 deletions.
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
"matrix_src_main": "./src/index.ts",
"matrix_lib_main": "./lib/index.ts",
"matrix_lib_typings": "./lib/index.d.ts",
"matrix_i18n_extra_translation_funcs": [
"newTranslatableError"
],
"scripts": {
"prepublishOnly": "yarn build",
"i18n": "matrix-gen-i18n",
Expand Down
107 changes: 74 additions & 33 deletions src/SlashCommands.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { logger } from "matrix-js-sdk/src/logger";

import { MatrixClientPeg } from './MatrixClientPeg';
import dis from './dispatcher/dispatcher';
import { _t, _td } from './languageHandler';
import { _t, _td, newTranslatableError } from './languageHandler';
import Modal from './Modal';
import MultiInviter from './utils/MultiInviter';
import { linkifyAndSanitizeHtml } from './HtmlUtils';
Expand Down Expand Up @@ -141,13 +141,26 @@ export class Command {

run(roomId: string, threadId: string, args: string) {
// if it has no runFn then its an ignored/nop command (autocomplete only) e.g `/me`
if (!this.runFn) return reject(_t("Command error"));
if (!this.runFn) {
reject(
newTranslatableError(
"Command error: Unable to handle slash command.",
),
);

return;
}

const renderingType = threadId
? TimelineRenderingType.Thread
: TimelineRenderingType.Room;
if (this.renderingTypes && !this.renderingTypes?.includes(renderingType)) {
return reject(_t("Command error"));
return reject(
newTranslatableError(
"Command error: Unable to find rendering type (%(renderingType)s)",
{ renderingType },
),
);
}

return this.runFn.bind(this)(roomId, args);
Expand Down Expand Up @@ -270,7 +283,9 @@ export const Commands = [
const cli = MatrixClientPeg.get();
const room = cli.getRoom(roomId);
if (!room.currentState.mayClientSendStateEvent("m.room.tombstone", cli)) {
return reject(_t("You do not have the required permissions to use this command."));
return reject(
newTranslatableError("You do not have the required permissions to use this command."),
);
}

const { finished } = Modal.createTrackedDialog('Slash Commands', 'upgrade room confirmation',
Expand All @@ -297,15 +312,10 @@ export const Commands = [
return success((async () => {
const unixTimestamp = Date.parse(args);
if (!unixTimestamp) {
throw new Error(
// FIXME: Use newTranslatableError here instead
// otherwise the rageshake error messages will be
// translated too
_t(
// eslint-disable-next-line max-len
'We were unable to understand the given date (%(inputDate)s). Try using the format YYYY-MM-DD.',
{ inputDate: args },
),
throw newTranslatableError(
'We were unable to understand the given date (%(inputDate)s). ' +
'Try using the format YYYY-MM-DD.',
{ inputDate: args },
);
}

Expand Down Expand Up @@ -437,7 +447,11 @@ export const Commands = [
return success(cli.setRoomTopic(roomId, args));
}
const room = cli.getRoom(roomId);
if (!room) return reject(_t("Failed to set topic"));
if (!room) {
return reject(
newTranslatableError("Failed to get room topic: Unable to find room (%(roomId)s", { roomId }),
);
}

const topicEvents = room.currentState.getStateEvents('m.room.topic', '');
const topic = topicEvents && topicEvents.getContent().topic;
Expand Down Expand Up @@ -509,10 +523,16 @@ export const Commands = [
useDefaultIdentityServer();
return;
}
throw new Error(_t("Use an identity server to invite by email. Manage in Settings."));
throw newTranslatableError(
"Use an identity server to invite by email. Manage in Settings.",
);
});
} else {
return reject(_t("Use an identity server to invite by email. Manage in Settings."));
return reject(
newTranslatableError(
"Use an identity server to invite by email. Manage in Settings.",
),
);
}
}
const inviter = new MultiInviter(roomId);
Expand Down Expand Up @@ -680,7 +700,14 @@ export const Commands = [
}
if (targetRoomId) break;
}
if (!targetRoomId) return reject(_t('Unrecognised room address:') + ' ' + roomAlias);
if (!targetRoomId) {
return reject(
newTranslatableError(
'Unrecognised room address: %(roomAlias)s',
{ roomAlias },
),
);
}
}
}

Expand Down Expand Up @@ -819,10 +846,14 @@ export const Commands = [
if (!isNaN(powerLevel)) {
const cli = MatrixClientPeg.get();
const room = cli.getRoom(roomId);
if (!room) return reject(_t("Command failed"));
if (!room) {
return reject(
newTranslatableError("Command failed: Unable to find room (%(roomId)s", { roomId }),
);
}
const member = room.getMember(userId);
if (!member || getEffectiveMembership(member.membership) === EffectiveMembership.Leave) {
return reject(_t("Could not find user in room"));
return reject(newTranslatableError("Could not find user in room"));
}
const powerLevelEvent = room.currentState.getStateEvents('m.room.power_levels', '');
return success(cli.setPowerLevel(roomId, userId, powerLevel, powerLevelEvent));
Expand All @@ -849,10 +880,16 @@ export const Commands = [
if (matches) {
const cli = MatrixClientPeg.get();
const room = cli.getRoom(roomId);
if (!room) return reject(_t("Command failed"));
if (!room) {
return reject(
newTranslatableError("Command failed: Unable to find room (%(roomId)s", { roomId }),
);
}

const powerLevelEvent = room.currentState.getStateEvents('m.room.power_levels', '');
if (!powerLevelEvent.getContent().users[args]) return reject(_t("Could not find user in room"));
if (!powerLevelEvent.getContent().users[args]) {
return reject(newTranslatableError("Could not find user in room"));
}
return success(cli.setPowerLevel(roomId, args, undefined, powerLevelEvent));
}
}
Expand All @@ -877,7 +914,7 @@ export const Commands = [
isEnabled: () => SettingsStore.getValue(UIFeature.Widgets),
runFn: function(roomId, widgetUrl) {
if (!widgetUrl) {
return reject(_t("Please supply a widget URL or embed code"));
return reject(newTranslatableError("Please supply a widget URL or embed code"));
}

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

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

return success(WidgetUtils.setRoomWidget(roomId, widgetId, type, widgetUrl, name, data));
} else {
return reject(_t("You cannot modify widgets in this room."));
return reject(newTranslatableError("You cannot modify widgets in this room."));
}
},
category: CommandCategories.admin,
Expand All @@ -941,30 +978,34 @@ export const Commands = [
return success((async () => {
const device = cli.getStoredDevice(userId, deviceId);
if (!device) {
throw new Error(_t('Unknown (user, session) pair:') + ` (${userId}, ${deviceId})`);
throw newTranslatableError(
'Unknown (user, session) pair: (%(userId)s, %(deviceId)s)',
{ userId, deviceId },
);
}
const deviceTrust = await cli.checkDeviceTrust(userId, deviceId);

if (deviceTrust.isVerified()) {
if (device.getFingerprint() === fingerprint) {
throw new Error(_t('Session already verified!'));
throw newTranslatableError('Session already verified!');
} else {
throw new Error(_t('WARNING: Session already verified, but keys do NOT MATCH!'));
throw newTranslatableError('WARNING: Session already verified, but keys do NOT MATCH!');
}
}

if (device.getFingerprint() !== fingerprint) {
const fprint = device.getFingerprint();
throw new Error(
_t('WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and session' +
throw newTranslatableError(
'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!',
{
fprint,
userId,
deviceId,
fingerprint,
}));
},
);
}

await cli.setDeviceVerified(userId, deviceId, true);
Expand Down Expand Up @@ -1083,7 +1124,7 @@ export const Commands = [
if (isPhoneNumber) {
const results = await CallHandler.instance.pstnLookup(this.state.value);
if (!results || results.length === 0 || !results[0].userid) {
throw new Error("Unable to find Matrix ID for phone number");
throw newTranslatableError("Unable to find Matrix ID for phone number");
}
userId = results[0].userid;
}
Expand Down Expand Up @@ -1135,7 +1176,7 @@ export const Commands = [
runFn: function(roomId, args) {
const call = CallHandler.instance.getCallForRoom(roomId);
if (!call) {
return reject("No active call in this room");
return reject(newTranslatableError("No active call in this room"));
}
call.setRemoteOnHold(true);
return success();
Expand All @@ -1149,7 +1190,7 @@ export const Commands = [
runFn: function(roomId, args) {
const call = CallHandler.instance.getCallForRoom(roomId);
if (!call) {
return reject("No active call in this room");
return reject(newTranslatableError("No active call in this room"));
}
call.setRemoteOnHold(false);
return success();
Expand Down
3 changes: 3 additions & 0 deletions src/components/views/rooms/SendMessageComposer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,9 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
let errText;
if (typeof error === 'string') {
errText = error;
} else if (error.translatedMessage) {
// Check for translatable errors (newTranslatableError)
errText = error.translatedMessage;
} else if (error.message) {
errText = error.message;
} else {
Expand Down
14 changes: 9 additions & 5 deletions src/i18n/strings/en_EN.json
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,8 @@
"Advanced": "Advanced",
"Effects": "Effects",
"Other": "Other",
"Command error": "Command error",
"Command error: Unable to handle slash command.": "Command error: Unable to handle slash command.",
"Command error: Unable to find rendering type (%(renderingType)s)": "Command error: Unable to find rendering type (%(renderingType)s)",
"Usage": "Usage",
"Sends the given message as a spoiler": "Sends the given message as a spoiler",
"Prepends ¯\\_(ツ)_/¯ to a plain-text message": "Prepends ¯\\_(ツ)_/¯ to a plain-text message",
Expand All @@ -443,7 +444,7 @@
"Changes your avatar in this current room only": "Changes your avatar in this current room only",
"Changes your avatar in all rooms": "Changes your avatar in all rooms",
"Gets or sets the room topic": "Gets or sets the room topic",
"Failed to set topic": "Failed to set topic",
"Failed to get room topic: Unable to find room (%(roomId)s": "Failed to get room topic: Unable to find room (%(roomId)s",
"This room has no topic.": "This room has no topic.",
"Sets the room name": "Sets the room name",
"Invites user with given id to current room": "Invites user with given id to current room",
Expand All @@ -452,7 +453,7 @@
"Use an identity server to invite by email. Manage in Settings.": "Use an identity server to invite by email. Manage in Settings.",
"Joins room with given address": "Joins room with given address",
"Leave room": "Leave room",
"Unrecognised room address:": "Unrecognised room address:",
"Unrecognised room address: %(roomAlias)s": "Unrecognised room address: %(roomAlias)s",
"Kicks user with given id": "Kicks user with given id",
"Bans user with given id": "Bans user with given id",
"Unbans user with given ID": "Unbans user with given ID",
Expand All @@ -463,7 +464,7 @@
"Unignored user": "Unignored user",
"You are no longer ignoring %(userId)s": "You are no longer ignoring %(userId)s",
"Define the power level of a user": "Define the power level of a user",
"Command failed": "Command failed",
"Command failed: Unable to find room (%(roomId)s": "Command failed: Unable to find room (%(roomId)s",
"Could not find user in room": "Could not find user in room",
"Deops user with given id": "Deops user with given id",
"Opens the Developer Tools dialog": "Opens the Developer Tools dialog",
Expand All @@ -472,7 +473,7 @@
"Please supply a https:// or http:// widget URL": "Please supply a https:// or http:// widget URL",
"You cannot modify widgets in this room.": "You cannot modify widgets in this room.",
"Verifies a user, session, and pubkey tuple": "Verifies a user, session, and pubkey tuple",
"Unknown (user, session) pair:": "Unknown (user, session) pair:",
"Unknown (user, session) pair: (%(userId)s, %(deviceId)s)": "Unknown (user, session) pair: (%(userId)s, %(deviceId)s)",
"Session already verified!": "Session already verified!",
"WARNING: Session already verified, but keys do NOT MATCH!": "WARNING: Session already verified, but keys do NOT MATCH!",
"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!",
Expand All @@ -485,8 +486,10 @@
"Displays information about a user": "Displays information about a user",
"Send a bug report with logs": "Send a bug report with logs",
"Opens chat with the given user": "Opens chat with the given user",
"Unable to find Matrix ID for phone number": "Unable to find Matrix ID for phone number",
"Sends a message to the given user": "Sends a message to the given user",
"Places the call in the current room on hold": "Places the call in the current room on hold",
"No active call in this room": "No active call in this room",
"Takes the call in the current room off hold": "Takes the call in the current room off hold",
"Converts the room to a DM": "Converts the room to a DM",
"Converts the DM to a room": "Converts the DM to a room",
Expand Down Expand Up @@ -1630,6 +1633,7 @@
"This room is end-to-end encrypted": "This room is end-to-end encrypted",
"Everyone in this room is verified": "Everyone in this room is verified",
"Server error": "Server error",
"Command error": "Command error",
"Server unavailable, overloaded, or something else went wrong.": "Server unavailable, overloaded, or something else went wrong.",
"Unknown Command": "Unknown Command",
"Unrecognised command: %(commandText)s": "Unrecognised command: %(commandText)s",
Expand Down
4 changes: 2 additions & 2 deletions src/languageHandler.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@ interface ITranslatableError extends Error {
* @param {string} message Message to translate.
* @returns {Error} The constructed error.
*/
export function newTranslatableError(message: string) {
export function newTranslatableError(message: string, variables?: IVariables): ITranslatableError {
const error = new Error(message) as ITranslatableError;
error.translatedMessage = _t(message);
error.translatedMessage = _t(message, variables);
return error;
}

Expand Down

0 comments on commit 038a6bc

Please sign in to comment.