Merge pull request #4302 from matrix-org/t3chguy/cmds
rework SlashCommands to better expose aliases
This commit is contained in:
commit
56dda3895c
4 changed files with 161 additions and 162 deletions
|
@ -2,6 +2,7 @@
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015, 2016 OpenMarket Ltd
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018 New Vector Ltd
|
||||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -17,7 +18,8 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
import React from 'react';
|
import * as React from 'react';
|
||||||
|
|
||||||
import {MatrixClientPeg} from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import dis from './dispatcher';
|
import dis from './dispatcher';
|
||||||
import * as sdk from './index';
|
import * as sdk from './index';
|
||||||
|
@ -34,11 +36,16 @@ import { getDefaultIdentityServerUrl, useDefaultIdentityServer } from './utils/I
|
||||||
import {isPermalinkHost, parsePermalink} from "./utils/permalinks/Permalinks";
|
import {isPermalinkHost, parsePermalink} from "./utils/permalinks/Permalinks";
|
||||||
import {inviteUsersToRoom} from "./RoomInvite";
|
import {inviteUsersToRoom} from "./RoomInvite";
|
||||||
|
|
||||||
const singleMxcUpload = async () => {
|
// XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
|
||||||
|
interface HTMLInputEvent extends Event {
|
||||||
|
target: HTMLInputElement & EventTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
const singleMxcUpload = async (): Promise<any> => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
const fileSelector = document.createElement('input');
|
const fileSelector = document.createElement('input');
|
||||||
fileSelector.setAttribute('type', 'file');
|
fileSelector.setAttribute('type', 'file');
|
||||||
fileSelector.onchange = (ev) => {
|
fileSelector.onchange = (ev: HTMLInputEvent) => {
|
||||||
const file = ev.target.files[0];
|
const file = ev.target.files[0];
|
||||||
|
|
||||||
const UploadConfirmDialog = sdk.getComponent("dialogs.UploadConfirmDialog");
|
const UploadConfirmDialog = sdk.getComponent("dialogs.UploadConfirmDialog");
|
||||||
|
@ -62,28 +69,49 @@ export const CommandCategories = {
|
||||||
"other": _td("Other"),
|
"other": _td("Other"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type RunFn = ((roomId: string, args: string, cmd: string) => {error: any} | {promise: Promise<any>});
|
||||||
|
|
||||||
|
interface ICommandOpts {
|
||||||
|
command: string;
|
||||||
|
aliases?: string[];
|
||||||
|
args?: string;
|
||||||
|
description: string;
|
||||||
|
runFn?: RunFn;
|
||||||
|
category: string;
|
||||||
|
hideCompletionAfterSpace?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
class Command {
|
class Command {
|
||||||
constructor({name, args='', description, runFn, category=CommandCategories.other, hideCompletionAfterSpace=false}) {
|
command: string;
|
||||||
this.command = '/' + name;
|
aliases: string[];
|
||||||
this.args = args;
|
args: undefined | string;
|
||||||
this.description = description;
|
description: string;
|
||||||
this.runFn = runFn;
|
runFn: undefined | RunFn;
|
||||||
this.category = category;
|
category: string;
|
||||||
this.hideCompletionAfterSpace = hideCompletionAfterSpace;
|
hideCompletionAfterSpace: boolean;
|
||||||
|
|
||||||
|
constructor(opts: ICommandOpts) {
|
||||||
|
this.command = opts.command;
|
||||||
|
this.aliases = opts.aliases || [];
|
||||||
|
this.args = opts.args || "";
|
||||||
|
this.description = opts.description;
|
||||||
|
this.runFn = opts.runFn;
|
||||||
|
this.category = opts.category || CommandCategories.other;
|
||||||
|
this.hideCompletionAfterSpace = opts.hideCompletionAfterSpace || false;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCommand() {
|
getCommand() {
|
||||||
return this.command;
|
return `/${this.command}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCommandWithArgs() {
|
getCommandWithArgs() {
|
||||||
return this.getCommand() + " " + this.args;
|
return this.getCommand() + " " + this.args;
|
||||||
}
|
}
|
||||||
|
|
||||||
run(roomId, args) {
|
run(roomId: string, args: string, cmd: 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;
|
if (!this.runFn) return;
|
||||||
return this.runFn.bind(this)(roomId, args);
|
return this.runFn.bind(this)(roomId, args, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
getUsage() {
|
getUsage() {
|
||||||
|
@ -95,7 +123,7 @@ function reject(error) {
|
||||||
return {error};
|
return {error};
|
||||||
}
|
}
|
||||||
|
|
||||||
function success(promise) {
|
function success(promise?: Promise<any>) {
|
||||||
return {promise};
|
return {promise};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,11 +131,9 @@ function success(promise) {
|
||||||
* functions are called with `this` bound to the Command instance.
|
* functions are called with `this` bound to the Command instance.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* eslint-disable babel/no-invalid-this */
|
export const Commands = [
|
||||||
|
new Command({
|
||||||
export const CommandMap = {
|
command: 'shrug',
|
||||||
shrug: new Command({
|
|
||||||
name: 'shrug',
|
|
||||||
args: '<message>',
|
args: '<message>',
|
||||||
description: _td('Prepends ¯\\_(ツ)_/¯ to a plain-text message'),
|
description: _td('Prepends ¯\\_(ツ)_/¯ to a plain-text message'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -119,8 +145,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.messages,
|
category: CommandCategories.messages,
|
||||||
}),
|
}),
|
||||||
plain: new Command({
|
new Command({
|
||||||
name: 'plain',
|
command: 'plain',
|
||||||
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) {
|
||||||
|
@ -128,8 +154,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.messages,
|
category: CommandCategories.messages,
|
||||||
}),
|
}),
|
||||||
html: new Command({
|
new Command({
|
||||||
name: 'html',
|
command: 'html',
|
||||||
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) {
|
||||||
|
@ -137,11 +163,11 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.messages,
|
category: CommandCategories.messages,
|
||||||
}),
|
}),
|
||||||
ddg: new Command({
|
new Command({
|
||||||
name: 'ddg',
|
command: 'ddg',
|
||||||
args: '<query>',
|
args: '<query>',
|
||||||
description: _td('Searches DuckDuckGo for results'),
|
description: _td('Searches DuckDuckGo for results'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function() {
|
||||||
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
|
const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog');
|
||||||
// TODO Don't explain this away, actually show a search UI here.
|
// TODO Don't explain this away, actually show a search UI here.
|
||||||
Modal.createTrackedDialog('Slash Commands', '/ddg is not a command', ErrorDialog, {
|
Modal.createTrackedDialog('Slash Commands', '/ddg is not a command', ErrorDialog, {
|
||||||
|
@ -153,9 +179,8 @@ export const CommandMap = {
|
||||||
category: CommandCategories.actions,
|
category: CommandCategories.actions,
|
||||||
hideCompletionAfterSpace: true,
|
hideCompletionAfterSpace: true,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
upgraderoom: new Command({
|
command: 'upgraderoom',
|
||||||
name: 'upgraderoom',
|
|
||||||
args: '<new_version>',
|
args: '<new_version>',
|
||||||
description: _td('Upgrades a room to a new version'),
|
description: _td('Upgrades a room to a new version'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -224,9 +249,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.admin,
|
category: CommandCategories.admin,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
nick: new Command({
|
command: 'nick',
|
||||||
name: 'nick',
|
|
||||||
args: '<display_name>',
|
args: '<display_name>',
|
||||||
description: _td('Changes your display nickname'),
|
description: _td('Changes your display nickname'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -237,9 +261,9 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.actions,
|
category: CommandCategories.actions,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
myroomnick: new Command({
|
command: 'myroomnick',
|
||||||
name: 'myroomnick',
|
aliases: ['roomnick'],
|
||||||
args: '<display_name>',
|
args: '<display_name>',
|
||||||
description: _td('Changes your display nickname in the current room only'),
|
description: _td('Changes your display nickname in the current room only'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -256,9 +280,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.actions,
|
category: CommandCategories.actions,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
roomavatar: new Command({
|
command: 'roomavatar',
|
||||||
name: 'roomavatar',
|
|
||||||
args: '[<mxc_url>]',
|
args: '[<mxc_url>]',
|
||||||
description: _td('Changes the avatar of the current room'),
|
description: _td('Changes the avatar of the current room'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -274,9 +297,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.actions,
|
category: CommandCategories.actions,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
myroomavatar: new Command({
|
command: 'myroomavatar',
|
||||||
name: 'myroomavatar',
|
|
||||||
args: '[<mxc_url>]',
|
args: '[<mxc_url>]',
|
||||||
description: _td('Changes your avatar in this current room only'),
|
description: _td('Changes your avatar in this current room only'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -301,9 +323,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.actions,
|
category: CommandCategories.actions,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
myavatar: new Command({
|
command: 'myavatar',
|
||||||
name: 'myavatar',
|
|
||||||
args: '[<mxc_url>]',
|
args: '[<mxc_url>]',
|
||||||
description: _td('Changes your avatar in all rooms'),
|
description: _td('Changes your avatar in all rooms'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -319,9 +340,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.actions,
|
category: CommandCategories.actions,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
topic: new Command({
|
command: 'topic',
|
||||||
name: 'topic',
|
|
||||||
args: '[<topic>]',
|
args: '[<topic>]',
|
||||||
description: _td('Gets or sets the room topic'),
|
description: _td('Gets or sets the room topic'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -345,9 +365,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.admin,
|
category: CommandCategories.admin,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
roomname: new Command({
|
command: 'roomname',
|
||||||
name: 'roomname',
|
|
||||||
args: '<name>',
|
args: '<name>',
|
||||||
description: _td('Sets the room name'),
|
description: _td('Sets the room name'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -358,9 +377,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.admin,
|
category: CommandCategories.admin,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
invite: new Command({
|
command: 'invite',
|
||||||
name: 'invite',
|
|
||||||
args: '<user-id>',
|
args: '<user-id>',
|
||||||
description: _td('Invites user with given id to current room'),
|
description: _td('Invites user with given id to current room'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -399,7 +417,7 @@ export const CommandMap = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const inviter = new MultiInviter(roomId);
|
const inviter = new MultiInviter(roomId);
|
||||||
return success(finished.then(([useDefault] = []) => {
|
return success(finished.then(([useDefault]: any) => {
|
||||||
if (useDefault) {
|
if (useDefault) {
|
||||||
useDefaultIdentityServer();
|
useDefaultIdentityServer();
|
||||||
} else if (useDefault === false) {
|
} else if (useDefault === false) {
|
||||||
|
@ -417,12 +435,12 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.actions,
|
category: CommandCategories.actions,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
join: new Command({
|
command: 'join',
|
||||||
name: 'join',
|
aliases: ['j', 'goto'],
|
||||||
args: '<room-alias>',
|
args: '<room-alias>',
|
||||||
description: _td('Joins room with given alias'),
|
description: _td('Joins room with given alias'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(_, args) {
|
||||||
if (args) {
|
if (args) {
|
||||||
// Note: we support 2 versions of this command. The first is
|
// Note: we support 2 versions of this command. The first is
|
||||||
// the public-facing one for most users and the other is a
|
// the public-facing one for most users and the other is a
|
||||||
|
@ -530,9 +548,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.actions,
|
category: CommandCategories.actions,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
part: new Command({
|
command: 'part',
|
||||||
name: 'part',
|
|
||||||
args: '[<room-alias>]',
|
args: '[<room-alias>]',
|
||||||
description: _td('Leave room'),
|
description: _td('Leave room'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -578,9 +595,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.actions,
|
category: CommandCategories.actions,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
kick: new Command({
|
command: 'kick',
|
||||||
name: 'kick',
|
|
||||||
args: '<user-id> [reason]',
|
args: '<user-id> [reason]',
|
||||||
description: _td('Kicks user with given id'),
|
description: _td('Kicks user with given id'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -594,10 +610,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.admin,
|
category: CommandCategories.admin,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
// Ban a user from the room with an optional reason
|
command: 'ban',
|
||||||
ban: new Command({
|
|
||||||
name: 'ban',
|
|
||||||
args: '<user-id> [reason]',
|
args: '<user-id> [reason]',
|
||||||
description: _td('Bans user with given id'),
|
description: _td('Bans user with given id'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -611,10 +625,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.admin,
|
category: CommandCategories.admin,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
// Unban a user from ythe room
|
command: 'unban',
|
||||||
unban: new Command({
|
|
||||||
name: 'unban',
|
|
||||||
args: '<user-id>',
|
args: '<user-id>',
|
||||||
description: _td('Unbans user with given ID'),
|
description: _td('Unbans user with given ID'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -629,9 +641,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.admin,
|
category: CommandCategories.admin,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
ignore: new Command({
|
command: 'ignore',
|
||||||
name: 'ignore',
|
|
||||||
args: '<user-id>',
|
args: '<user-id>',
|
||||||
description: _td('Ignores a user, hiding their messages from you'),
|
description: _td('Ignores a user, hiding their messages from you'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -660,9 +671,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.actions,
|
category: CommandCategories.actions,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
unignore: new Command({
|
command: 'unignore',
|
||||||
name: 'unignore',
|
|
||||||
args: '<user-id>',
|
args: '<user-id>',
|
||||||
description: _td('Stops ignoring a user, showing their messages going forward'),
|
description: _td('Stops ignoring a user, showing their messages going forward'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -692,10 +702,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.actions,
|
category: CommandCategories.actions,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
// Define the power level of a user
|
command: 'op',
|
||||||
op: new Command({
|
|
||||||
name: 'op',
|
|
||||||
args: '<user-id> [<power-level>]',
|
args: '<user-id> [<power-level>]',
|
||||||
description: _td('Define the power level of a user'),
|
description: _td('Define the power level of a user'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -705,7 +713,7 @@ export const CommandMap = {
|
||||||
if (matches) {
|
if (matches) {
|
||||||
const userId = matches[1];
|
const userId = matches[1];
|
||||||
if (matches.length === 4 && undefined !== matches[3]) {
|
if (matches.length === 4 && undefined !== matches[3]) {
|
||||||
powerLevel = parseInt(matches[3]);
|
powerLevel = parseInt(matches[3], 10);
|
||||||
}
|
}
|
||||||
if (!isNaN(powerLevel)) {
|
if (!isNaN(powerLevel)) {
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
|
@ -721,10 +729,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.admin,
|
category: CommandCategories.admin,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
// Reset the power level of a user
|
command: 'deop',
|
||||||
deop: new Command({
|
|
||||||
name: 'deop',
|
|
||||||
args: '<user-id>',
|
args: '<user-id>',
|
||||||
description: _td('Deops user with given id'),
|
description: _td('Deops user with given id'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -743,9 +749,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.admin,
|
category: CommandCategories.admin,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
devtools: new Command({
|
command: 'devtools',
|
||||||
name: 'devtools',
|
|
||||||
description: _td('Opens the Developer Tools dialog'),
|
description: _td('Opens the Developer Tools dialog'),
|
||||||
runFn: function(roomId) {
|
runFn: function(roomId) {
|
||||||
const DevtoolsDialog = sdk.getComponent('dialogs.DevtoolsDialog');
|
const DevtoolsDialog = sdk.getComponent('dialogs.DevtoolsDialog');
|
||||||
|
@ -754,9 +759,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.advanced,
|
category: CommandCategories.advanced,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
addwidget: new Command({
|
command: 'addwidget',
|
||||||
name: 'addwidget',
|
|
||||||
args: '<url>',
|
args: '<url>',
|
||||||
description: _td('Adds a custom widget by URL to the room'),
|
description: _td('Adds a custom widget by URL to the room'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -775,10 +779,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.admin,
|
category: CommandCategories.admin,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
// Verify a user, device, and pubkey tuple
|
command: 'verify',
|
||||||
verify: new Command({
|
|
||||||
name: 'verify',
|
|
||||||
args: '<user-id> <device-id> <device-signing-key>',
|
args: '<user-id> <device-id> <device-signing-key>',
|
||||||
description: _td('Verifies a user, session, and pubkey tuple'),
|
description: _td('Verifies a user, session, and pubkey tuple'),
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -843,20 +845,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.advanced,
|
category: CommandCategories.advanced,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
// Command definitions for autocompletion ONLY:
|
command: 'discardsession',
|
||||||
|
|
||||||
// /me is special because its not handled by SlashCommands.js and is instead done inside the Composer classes
|
|
||||||
me: new Command({
|
|
||||||
name: 'me',
|
|
||||||
args: '<message>',
|
|
||||||
description: _td('Displays action'),
|
|
||||||
category: CommandCategories.messages,
|
|
||||||
hideCompletionAfterSpace: true,
|
|
||||||
}),
|
|
||||||
|
|
||||||
discardsession: new Command({
|
|
||||||
name: 'discardsession',
|
|
||||||
description: _td('Forces the current outbound group session in an encrypted room to be discarded'),
|
description: _td('Forces the current outbound group session in an encrypted room to be discarded'),
|
||||||
runFn: function(roomId) {
|
runFn: function(roomId) {
|
||||||
try {
|
try {
|
||||||
|
@ -868,9 +858,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.advanced,
|
category: CommandCategories.advanced,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
rainbow: new Command({
|
command: "rainbow",
|
||||||
name: "rainbow",
|
|
||||||
description: _td("Sends the given message coloured as a rainbow"),
|
description: _td("Sends the given message coloured as a rainbow"),
|
||||||
args: '<message>',
|
args: '<message>',
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -879,9 +868,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.messages,
|
category: CommandCategories.messages,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
rainbowme: new Command({
|
command: "rainbowme",
|
||||||
name: "rainbowme",
|
|
||||||
description: _td("Sends the given emote coloured as a rainbow"),
|
description: _td("Sends the given emote coloured as a rainbow"),
|
||||||
args: '<message>',
|
args: '<message>',
|
||||||
runFn: function(roomId, args) {
|
runFn: function(roomId, args) {
|
||||||
|
@ -890,9 +878,8 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.messages,
|
category: CommandCategories.messages,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
help: new Command({
|
command: "help",
|
||||||
name: "help",
|
|
||||||
description: _td("Displays list of commands with usages and descriptions"),
|
description: _td("Displays list of commands with usages and descriptions"),
|
||||||
runFn: function() {
|
runFn: function() {
|
||||||
const SlashCommandHelpDialog = sdk.getComponent('dialogs.SlashCommandHelpDialog');
|
const SlashCommandHelpDialog = sdk.getComponent('dialogs.SlashCommandHelpDialog');
|
||||||
|
@ -902,18 +889,16 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.advanced,
|
category: CommandCategories.advanced,
|
||||||
}),
|
}),
|
||||||
|
new Command({
|
||||||
whois: new Command({
|
command: "whois",
|
||||||
name: "whois",
|
|
||||||
description: _td("Displays information about a user"),
|
description: _td("Displays information about a user"),
|
||||||
args: '<user-id>',
|
args: "<user-id>",
|
||||||
runFn: function(roomId, userId) {
|
runFn: function(roomId, userId) {
|
||||||
if (!userId || !userId.startsWith("@") || !userId.includes(":")) {
|
if (!userId || !userId.startsWith("@") || !userId.includes(":")) {
|
||||||
return reject(this.getUsage());
|
return reject(this.getUsage());
|
||||||
}
|
}
|
||||||
|
|
||||||
const member = MatrixClientPeg.get().getRoom(roomId).getMember(userId);
|
const member = MatrixClientPeg.get().getRoom(roomId).getMember(userId);
|
||||||
|
|
||||||
dis.dispatch({
|
dis.dispatch({
|
||||||
action: 'view_user',
|
action: 'view_user',
|
||||||
member: member || {userId},
|
member: member || {userId},
|
||||||
|
@ -922,17 +907,26 @@ export const CommandMap = {
|
||||||
},
|
},
|
||||||
category: CommandCategories.advanced,
|
category: CommandCategories.advanced,
|
||||||
}),
|
}),
|
||||||
};
|
|
||||||
/* eslint-enable babel/no-invalid-this */
|
|
||||||
|
|
||||||
|
// Command definitions for autocompletion ONLY:
|
||||||
|
// /me is special because its not handled by SlashCommands.js and is instead done inside the Composer classes
|
||||||
|
new Command({
|
||||||
|
command: 'me',
|
||||||
|
args: '<message>',
|
||||||
|
description: _td('Displays action'),
|
||||||
|
category: CommandCategories.messages,
|
||||||
|
hideCompletionAfterSpace: true,
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
// helpful aliases
|
// build a map from names and aliases to the Command objects.
|
||||||
const aliases = {
|
export const CommandMap = new Map();
|
||||||
j: "join",
|
Commands.forEach(cmd => {
|
||||||
newballsplease: "discardsession",
|
CommandMap.set(cmd.command, cmd);
|
||||||
goto: "join", // because it handles event permalinks magically
|
cmd.aliases.forEach(alias => {
|
||||||
roomnick: "myroomnick",
|
CommandMap.set(alias, cmd);
|
||||||
};
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -959,10 +953,7 @@ export function getCommand(roomId, input) {
|
||||||
cmd = input;
|
cmd = input;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aliases[cmd]) {
|
if (CommandMap.has(cmd)) {
|
||||||
cmd = aliases[cmd];
|
return () => CommandMap.get(cmd).run(roomId, args, cmd);
|
||||||
}
|
|
||||||
if (CommandMap[cmd]) {
|
|
||||||
return () => CommandMap[cmd].run(roomId, args);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -23,17 +23,16 @@ import AutocompleteProvider from './AutocompleteProvider';
|
||||||
import QueryMatcher from './QueryMatcher';
|
import QueryMatcher from './QueryMatcher';
|
||||||
import {TextualCompletion} from './Components';
|
import {TextualCompletion} from './Components';
|
||||||
import type {Completion, SelectionRange} from "./Autocompleter";
|
import type {Completion, SelectionRange} from "./Autocompleter";
|
||||||
import {CommandMap} from '../SlashCommands';
|
import {Commands, CommandMap} from '../SlashCommands';
|
||||||
|
|
||||||
const COMMANDS = Object.values(CommandMap);
|
|
||||||
|
|
||||||
const COMMAND_RE = /(^\/\w*)(?: .*)?/g;
|
const COMMAND_RE = /(^\/\w*)(?: .*)?/g;
|
||||||
|
|
||||||
export default class CommandProvider extends AutocompleteProvider {
|
export default class CommandProvider extends AutocompleteProvider {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(COMMAND_RE);
|
super(COMMAND_RE);
|
||||||
this.matcher = new QueryMatcher(COMMANDS, {
|
this.matcher = new QueryMatcher(Commands, {
|
||||||
keys: ['command', 'args', 'description'],
|
keys: ['command', 'args', 'description'],
|
||||||
|
funcs: [({aliases}) => aliases.join(" ")], // aliases
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,31 +45,40 @@ export default class CommandProvider extends AutocompleteProvider {
|
||||||
if (command[0] !== command[1]) {
|
if (command[0] !== command[1]) {
|
||||||
// The input looks like a command with arguments, perform exact match
|
// The input looks like a command with arguments, perform exact match
|
||||||
const name = command[1].substr(1); // strip leading `/`
|
const name = command[1].substr(1); // strip leading `/`
|
||||||
if (CommandMap[name]) {
|
if (CommandMap.has(name)) {
|
||||||
// some commands, namely `me` and `ddg` don't suit having the usage shown whilst typing their arguments
|
// some commands, namely `me` and `ddg` don't suit having the usage shown whilst typing their arguments
|
||||||
if (CommandMap[name].hideCompletionAfterSpace) return [];
|
if (CommandMap.get(name).hideCompletionAfterSpace) return [];
|
||||||
matches = [CommandMap[name]];
|
matches = [CommandMap.get(name)];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (query === '/') {
|
if (query === '/') {
|
||||||
// If they have just entered `/` show everything
|
// If they have just entered `/` show everything
|
||||||
matches = COMMANDS;
|
matches = Commands;
|
||||||
} else {
|
} else {
|
||||||
// otherwise fuzzy match against all of the fields
|
// otherwise fuzzy match against all of the fields
|
||||||
matches = this.matcher.match(command[1]);
|
matches = this.matcher.match(command[1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return matches.map((result) => ({
|
|
||||||
// If the command is the same as the one they entered, we don't want to discard their arguments
|
return matches.map((result) => {
|
||||||
completion: result.command === command[1] ? command[0] : (result.command + ' '),
|
let completion = result.getCommand() + ' ';
|
||||||
type: "command",
|
const usedAlias = result.aliases.find(alias => `/${alias}` === command[1]);
|
||||||
component: <TextualCompletion
|
// If the command (or an alias) is the same as the one they entered, we don't want to discard their arguments
|
||||||
title={result.command}
|
if (usedAlias || result.getCommand() === command[1]) {
|
||||||
subtitle={result.args}
|
completion = command[0];
|
||||||
description={_t(result.description)} />,
|
}
|
||||||
range,
|
|
||||||
}));
|
return {
|
||||||
|
completion,
|
||||||
|
type: "command",
|
||||||
|
component: <TextualCompletion
|
||||||
|
title={`/${usedAlias || result.command}`}
|
||||||
|
subtitle={result.args}
|
||||||
|
description={_t(result.description)} />,
|
||||||
|
range,
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getName() {
|
getName() {
|
||||||
|
|
|
@ -16,14 +16,14 @@ limitations under the License.
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {_t} from "../../../languageHandler";
|
import {_t} from "../../../languageHandler";
|
||||||
import {CommandCategories, CommandMap} from "../../../SlashCommands";
|
import {CommandCategories, Commands} from "../../../SlashCommands";
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
|
|
||||||
export default ({onFinished}) => {
|
export default ({onFinished}) => {
|
||||||
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
|
const InfoDialog = sdk.getComponent('dialogs.InfoDialog');
|
||||||
|
|
||||||
const categories = {};
|
const categories = {};
|
||||||
Object.values(CommandMap).forEach(cmd => {
|
Commands.forEach(cmd => {
|
||||||
if (!categories[cmd.category]) {
|
if (!categories[cmd.category]) {
|
||||||
categories[cmd.category] = [];
|
categories[cmd.category] = [];
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ export default ({onFinished}) => {
|
||||||
|
|
||||||
categories[category].forEach(cmd => {
|
categories[category].forEach(cmd => {
|
||||||
rows.push(<tr key={cmd.command}>
|
rows.push(<tr key={cmd.command}>
|
||||||
<td><strong>{cmd.command}</strong></td>
|
<td><strong>{cmd.getCommand()}</strong></td>
|
||||||
<td>{cmd.args}</td>
|
<td>{cmd.args}</td>
|
||||||
<td>{cmd.description}</td>
|
<td>{cmd.description}</td>
|
||||||
</tr>);
|
</tr>);
|
||||||
|
|
|
@ -198,12 +198,12 @@
|
||||||
"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!",
|
"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!",
|
||||||
"Verified key": "Verified key",
|
"Verified key": "Verified key",
|
||||||
"The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.",
|
"The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.": "The signing key you provided matches the signing key you received from %(userId)s's session %(deviceId)s. Session marked as verified.",
|
||||||
"Displays action": "Displays action",
|
|
||||||
"Forces the current outbound group session in an encrypted room to be discarded": "Forces the current outbound group session in an encrypted room to be discarded",
|
"Forces the current outbound group session in an encrypted room to be discarded": "Forces the current outbound group session in an encrypted room to be discarded",
|
||||||
"Sends the given message coloured as a rainbow": "Sends the given message coloured as a rainbow",
|
"Sends the given message coloured as a rainbow": "Sends the given message coloured as a rainbow",
|
||||||
"Sends the given emote coloured as a rainbow": "Sends the given emote coloured as a rainbow",
|
"Sends the given emote coloured as a rainbow": "Sends the given emote coloured as a rainbow",
|
||||||
"Displays list of commands with usages and descriptions": "Displays list of commands with usages and descriptions",
|
"Displays list of commands with usages and descriptions": "Displays list of commands with usages and descriptions",
|
||||||
"Displays information about a user": "Displays information about a user",
|
"Displays information about a user": "Displays information about a user",
|
||||||
|
"Displays action": "Displays action",
|
||||||
"Reason": "Reason",
|
"Reason": "Reason",
|
||||||
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.",
|
"%(targetName)s accepted the invitation for %(displayName)s.": "%(targetName)s accepted the invitation for %(displayName)s.",
|
||||||
"%(targetName)s accepted an invitation.": "%(targetName)s accepted an invitation.",
|
"%(targetName)s accepted an invitation.": "%(targetName)s accepted an invitation.",
|
||||||
|
|
Loading…
Reference in a new issue