2022-01-12 09:40:18 +00:00
|
|
|
/*
|
2024-09-09 14:57:16 +01:00
|
|
|
Copyright 2024 New Vector Ltd.
|
|
|
|
|
Copyright 2019-2022 The Matrix.org Foundation C.I.C.
|
2022-01-12 09:40:18 +00:00
|
|
|
|
2025-01-06 11:18:54 +00:00
|
|
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
2024-09-09 14:57:16 +01:00
|
|
|
Please see LICENSE files in the repository root for full details.
|
2022-01-12 09:40:18 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import React from "react";
|
|
|
|
|
import { logger } from "matrix-js-sdk/src/logger";
|
2024-03-25 12:48:48 +00:00
|
|
|
import { MatrixClient } from "matrix-js-sdk/src/matrix";
|
|
|
|
|
import { RoomMessageEventContent } from "matrix-js-sdk/src/types";
|
2022-01-12 09:40:18 +00:00
|
|
|
|
|
|
|
|
import EditorModel from "./model";
|
|
|
|
|
import { Type } from "./parts";
|
|
|
|
|
import { Command, CommandCategories, getCommand } from "../SlashCommands";
|
2023-03-31 02:30:43 -05:00
|
|
|
import { UserFriendlyError, _t, _td } from "../languageHandler";
|
2022-01-12 09:40:18 +00:00
|
|
|
import Modal from "../Modal";
|
|
|
|
|
import ErrorDialog from "../components/views/dialogs/ErrorDialog";
|
|
|
|
|
import QuestionDialog from "../components/views/dialogs/QuestionDialog";
|
|
|
|
|
|
|
|
|
|
export function isSlashCommand(model: EditorModel): boolean {
|
|
|
|
|
const parts = model.parts;
|
|
|
|
|
const firstPart = parts[0];
|
|
|
|
|
if (firstPart) {
|
|
|
|
|
if (firstPart.type === Type.Command && firstPart.text.startsWith("/") && !firstPart.text.startsWith("//")) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
firstPart.text.startsWith("/") &&
|
|
|
|
|
!firstPart.text.startsWith("//") &&
|
|
|
|
|
(firstPart.type === Type.Plain || firstPart.type === Type.PillCandidate)
|
|
|
|
|
) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-16 17:21:44 +00:00
|
|
|
export function getSlashCommand(model: EditorModel): [Command | undefined, string | undefined, string] {
|
2022-01-12 09:40:18 +00:00
|
|
|
const commandText = model.parts.reduce((text, part) => {
|
|
|
|
|
// use mxid to textify user pills in a command and room alias/id for room pills
|
|
|
|
|
if (part.type === Type.UserPill || part.type === Type.RoomPill) {
|
|
|
|
|
return text + part.resourceId;
|
|
|
|
|
}
|
|
|
|
|
return text + part.text;
|
|
|
|
|
}, "");
|
|
|
|
|
const { cmd, args } = getCommand(commandText);
|
|
|
|
|
return [cmd, args, commandText];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function runSlashCommand(
|
2023-05-25 16:29:48 +01:00
|
|
|
matrixClient: MatrixClient,
|
2022-01-12 09:40:18 +00:00
|
|
|
cmd: Command,
|
2023-02-24 15:28:40 +00:00
|
|
|
args: string | undefined,
|
2022-01-12 09:40:18 +00:00
|
|
|
roomId: string,
|
|
|
|
|
threadId: string | null,
|
2024-03-25 12:48:48 +00:00
|
|
|
): Promise<[content: RoomMessageEventContent | null, success: boolean]> {
|
2023-05-25 16:29:48 +01:00
|
|
|
const result = cmd.run(matrixClient, roomId, threadId, args);
|
2024-03-25 12:48:48 +00:00
|
|
|
let messageContent: RoomMessageEventContent | null = null;
|
2023-03-31 02:30:43 -05:00
|
|
|
let error: any = result.error;
|
2022-01-12 09:40:18 +00:00
|
|
|
if (result.promise) {
|
|
|
|
|
try {
|
2022-07-27 11:03:25 +01:00
|
|
|
if (cmd.category === CommandCategories.messages || cmd.category === CommandCategories.effects) {
|
2023-02-16 17:21:44 +00:00
|
|
|
messageContent = (await result.promise) ?? null;
|
2022-01-12 09:40:18 +00:00
|
|
|
} else {
|
|
|
|
|
await result.promise;
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
error = err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (error) {
|
2023-05-05 11:56:15 +01:00
|
|
|
logger.error(`Command failure: ${error}`);
|
2022-01-12 09:40:18 +00:00
|
|
|
// assume the error is a server error when the command is async
|
|
|
|
|
const isServerError = !!result.promise;
|
2023-09-19 17:16:38 +01:00
|
|
|
const title = isServerError ? _td("slash_command|server_error") : _td("slash_command|command_error");
|
2022-01-12 09:40:18 +00:00
|
|
|
|
|
|
|
|
let errText;
|
|
|
|
|
if (typeof error === "string") {
|
|
|
|
|
errText = error;
|
2023-03-31 02:30:43 -05:00
|
|
|
} else if (error instanceof UserFriendlyError) {
|
|
|
|
|
errText = error.translatedMessage;
|
2022-01-12 09:40:18 +00:00
|
|
|
} else if (error.message) {
|
|
|
|
|
errText = error.message;
|
|
|
|
|
} else {
|
2023-09-19 17:16:38 +01:00
|
|
|
errText = _t("slash_command|server_error_detail");
|
2022-01-12 09:40:18 +00:00
|
|
|
}
|
|
|
|
|
|
2022-06-14 17:51:51 +01:00
|
|
|
Modal.createDialog(ErrorDialog, {
|
2022-01-12 09:40:18 +00:00
|
|
|
title: _t(title),
|
|
|
|
|
description: errText,
|
|
|
|
|
});
|
2022-06-10 17:16:31 +01:00
|
|
|
return [null, false];
|
2022-01-12 09:40:18 +00:00
|
|
|
} else {
|
|
|
|
|
logger.log("Command success.");
|
2022-06-10 17:16:31 +01:00
|
|
|
return [messageContent, true];
|
2022-01-12 09:40:18 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export async function shouldSendAnyway(commandText: string): Promise<boolean> {
|
|
|
|
|
// ask the user if their unknown command should be sent as a message
|
2022-06-14 17:51:51 +01:00
|
|
|
const { finished } = Modal.createDialog(QuestionDialog, {
|
2023-09-19 17:16:38 +01:00
|
|
|
title: _t("slash_command|unknown_command"),
|
2022-01-12 09:40:18 +00:00
|
|
|
description: (
|
|
|
|
|
<div>
|
2023-09-19 17:16:38 +01:00
|
|
|
<p>{_t("slash_command|unknown_command_detail", { commandText })}</p>
|
2022-01-12 09:40:18 +00:00
|
|
|
<p>
|
|
|
|
|
{_t(
|
2023-09-19 17:16:38 +01:00
|
|
|
"slash_command|unknown_command_help",
|
2022-01-12 09:40:18 +00:00
|
|
|
{},
|
|
|
|
|
{
|
|
|
|
|
code: (t) => <code>{t}</code>,
|
|
|
|
|
},
|
|
|
|
|
)}
|
|
|
|
|
</p>
|
|
|
|
|
<p>
|
|
|
|
|
{_t(
|
2023-09-19 17:16:38 +01:00
|
|
|
"slash_command|unknown_command_hint",
|
2022-01-12 09:40:18 +00:00
|
|
|
{},
|
|
|
|
|
{
|
|
|
|
|
code: (t) => <code>{t}</code>,
|
|
|
|
|
},
|
|
|
|
|
)}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
),
|
2023-09-19 17:16:38 +01:00
|
|
|
button: _t("slash_command|unknown_command_button"),
|
2022-01-12 09:40:18 +00:00
|
|
|
});
|
|
|
|
|
const [sendAnyway] = await finished;
|
2023-03-23 14:35:55 +01:00
|
|
|
return sendAnyway || false;
|
2022-01-12 09:40:18 +00:00
|
|
|
}
|