Merge pull request #5686 from robintown/reply-commands

Support replying with a message command
This commit is contained in:
Travis Ralston 2021-03-24 09:56:56 -06:00 committed by GitHub
commit da7d31aeb6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 25 deletions

View file

@ -20,6 +20,7 @@ limitations under the License.
import * as React from 'react'; import * as React from 'react';
import { ContentHelpers } from 'matrix-js-sdk';
import {MatrixClientPeg} from './MatrixClientPeg'; import {MatrixClientPeg} from './MatrixClientPeg';
import dis from './dispatcher/dispatcher'; import dis from './dispatcher/dispatcher';
import * as sdk from './index'; import * as sdk from './index';
@ -126,10 +127,10 @@ export class Command {
return this.getCommand() + " " + this.args; return this.getCommand() + " " + this.args;
} }
run(roomId: string, args: string, cmd: string) { run(roomId: string, args: string) {
// if it has no runFn then its an ignored/nop command (autocomplete only) e.g `/me` // 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) return reject(_t("Command error"));
return this.runFn.bind(this)(roomId, args, cmd); return this.runFn.bind(this)(roomId, args);
} }
getUsage() { getUsage() {
@ -163,7 +164,7 @@ export const Commands = [
if (args) { if (args) {
message = message + ' ' + args; message = message + ' ' + args;
} }
return success(MatrixClientPeg.get().sendTextMessage(roomId, message)); return success(ContentHelpers.makeTextMessage(message));
}, },
category: CommandCategories.messages, category: CommandCategories.messages,
}), }),
@ -176,7 +177,7 @@ export const Commands = [
if (args) { if (args) {
message = message + ' ' + args; message = message + ' ' + args;
} }
return success(MatrixClientPeg.get().sendTextMessage(roomId, message)); return success(ContentHelpers.makeTextMessage(message));
}, },
category: CommandCategories.messages, category: CommandCategories.messages,
}), }),
@ -189,7 +190,7 @@ export const Commands = [
if (args) { if (args) {
message = message + ' ' + args; message = message + ' ' + args;
} }
return success(MatrixClientPeg.get().sendTextMessage(roomId, message)); return success(ContentHelpers.makeTextMessage(message));
}, },
category: CommandCategories.messages, category: CommandCategories.messages,
}), }),
@ -202,7 +203,7 @@ export const Commands = [
if (args) { if (args) {
message = message + ' ' + args; message = message + ' ' + args;
} }
return success(MatrixClientPeg.get().sendTextMessage(roomId, message)); return success(ContentHelpers.makeTextMessage(message));
}, },
category: CommandCategories.messages, category: CommandCategories.messages,
}), }),
@ -211,7 +212,7 @@ export const Commands = [
args: '<message>', args: '<message>',
description: _td('Sends a message as plain text, without interpreting it as markdown'), description: _td('Sends a message as plain text, without interpreting it as markdown'),
runFn: function(roomId, messages) { runFn: function(roomId, messages) {
return success(MatrixClientPeg.get().sendTextMessage(roomId, messages)); return success(ContentHelpers.makeTextMessage(messages));
}, },
category: CommandCategories.messages, category: CommandCategories.messages,
}), }),
@ -220,7 +221,7 @@ export const Commands = [
args: '<message>', args: '<message>',
description: _td('Sends a message as html, without interpreting it as markdown'), description: _td('Sends a message as html, without interpreting it as markdown'),
runFn: function(roomId, messages) { runFn: function(roomId, messages) {
return success(MatrixClientPeg.get().sendHtmlMessage(roomId, messages, messages)); return success(ContentHelpers.makeHtmlMessage(messages, messages));
}, },
category: CommandCategories.messages, category: CommandCategories.messages,
}), }),
@ -965,7 +966,7 @@ export const Commands = [
args: '<message>', args: '<message>',
runFn: function(roomId, args) { runFn: function(roomId, args) {
if (!args) return reject(this.getUserId()); if (!args) return reject(this.getUserId());
return success(MatrixClientPeg.get().sendHtmlMessage(roomId, args, textToHtmlRainbow(args))); return success(ContentHelpers.makeHtmlMessage(args, textToHtmlRainbow(args)));
}, },
category: CommandCategories.messages, category: CommandCategories.messages,
}), }),
@ -975,7 +976,7 @@ export const Commands = [
args: '<message>', args: '<message>',
runFn: function(roomId, args) { runFn: function(roomId, args) {
if (!args) return reject(this.getUserId()); if (!args) return reject(this.getUserId());
return success(MatrixClientPeg.get().sendHtmlEmote(roomId, args, textToHtmlRainbow(args))); return success(ContentHelpers.makeHtmlEmote(args, textToHtmlRainbow(args)));
}, },
category: CommandCategories.messages, category: CommandCategories.messages,
}), }),
@ -1200,10 +1201,13 @@ export function parseCommandString(input: string) {
* processing the command, or 'promise' if a request was sent out. * processing the command, or 'promise' if a request was sent out.
* Returns null if the input didn't match a command. * Returns null if the input didn't match a command.
*/ */
export function getCommand(roomId: string, input: string) { export function getCommand(input: string) {
const {cmd, args} = parseCommandString(input); const {cmd, args} = parseCommandString(input);
if (CommandMap.has(cmd) && CommandMap.get(cmd).isEnabled()) { if (CommandMap.has(cmd) && CommandMap.get(cmd).isEnabled()) {
return () => CommandMap.get(cmd).run(roomId, args, cmd); return {
cmd: CommandMap.get(cmd),
args,
};
} }
} }

View file

@ -33,7 +33,7 @@ import ReplyThread from "../elements/ReplyThread";
import {parseEvent} from '../../../editor/deserialize'; import {parseEvent} from '../../../editor/deserialize';
import {findEditableEvent} from '../../../utils/EventUtils'; import {findEditableEvent} from '../../../utils/EventUtils';
import SendHistoryManager from "../../../SendHistoryManager"; import SendHistoryManager from "../../../SendHistoryManager";
import {getCommand} from '../../../SlashCommands'; import {CommandCategories, getCommand} from '../../../SlashCommands';
import * as sdk from '../../../index'; import * as sdk from '../../../index';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
import {_t, _td} from '../../../languageHandler'; import {_t, _td} from '../../../languageHandler';
@ -291,15 +291,22 @@ export default class SendMessageComposer extends React.Component {
} }
return text + part.text; return text + part.text;
}, ""); }, "");
return [getCommand(this.props.room.roomId, commandText), commandText]; const {cmd, args} = getCommand(commandText);
return [cmd, args, commandText];
} }
async _runSlashCommand(fn) { async _runSlashCommand(cmd, args) {
const cmd = fn(); const result = cmd.run(this.props.room.roomId, args);
let error = cmd.error; let messageContent;
if (cmd.promise) { let error = result.error;
if (result.promise) {
try { try {
await cmd.promise; if (cmd.category === CommandCategories.messages) {
// The command returns a modified message that we need to pass on
messageContent = await result.promise;
} else {
await result.promise;
}
} catch (err) { } catch (err) {
error = err; error = err;
} }
@ -308,7 +315,7 @@ export default class SendMessageComposer extends React.Component {
console.error("Command failure: %s", error); console.error("Command failure: %s", error);
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
// assume the error is a server error when the command is async // assume the error is a server error when the command is async
const isServerError = !!cmd.promise; const isServerError = !!result.promise;
const title = isServerError ? _td("Server error") : _td("Command error"); const title = isServerError ? _td("Server error") : _td("Command error");
let errText; let errText;
@ -326,6 +333,7 @@ export default class SendMessageComposer extends React.Component {
}); });
} else { } else {
console.log("Command success."); console.log("Command success.");
if (messageContent) return messageContent;
} }
} }
@ -334,13 +342,22 @@ export default class SendMessageComposer extends React.Component {
return; return;
} }
const replyToEvent = this.props.replyToEvent;
let shouldSend = true; let shouldSend = true;
let content;
if (!containsEmote(this.model) && this._isSlashCommand()) { if (!containsEmote(this.model) && this._isSlashCommand()) {
const [cmd, commandText] = this._getSlashCommand(); const [cmd, args, commandText] = this._getSlashCommand();
if (cmd) { if (cmd) {
shouldSend = false; if (cmd.category === CommandCategories.messages) {
this._runSlashCommand(cmd); content = await this._runSlashCommand(cmd, args);
if (replyToEvent) {
addReplyToMessageContent(content, replyToEvent, this.props.permalinkCreator);
}
} else {
this._runSlashCommand(cmd, args);
shouldSend = false;
}
} else { } else {
// ask the user if their unknown command should be sent as a message // ask the user if their unknown command should be sent as a message
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
@ -375,11 +392,12 @@ export default class SendMessageComposer extends React.Component {
this._sendQuickReaction(); this._sendQuickReaction();
} }
const replyToEvent = this.props.replyToEvent;
if (shouldSend) { if (shouldSend) {
const startTime = CountlyAnalytics.getTimestamp(); const startTime = CountlyAnalytics.getTimestamp();
const {roomId} = this.props.room; const {roomId} = this.props.room;
const content = createMessageContent(this.model, this.props.permalinkCreator, replyToEvent); if (!content) {
content = createMessageContent(this.model, this.props.permalinkCreator, replyToEvent);
}
// don't bother sending an empty message // don't bother sending an empty message
if (!content.body.trim()) return; if (!content.body.trim()) return;