consolidate the two locations commands are defined, as it was a MESS
Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>pull/21833/head
							parent
							
								
									51560c516d
								
							
						
					
					
						commit
						5bf3e5b00a
					
				|  | @ -14,28 +14,31 @@ See the License for the specific language governing permissions and | |||
| limitations under the License. | ||||
| */ | ||||
| 
 | ||||
| import MatrixClientPeg from "./MatrixClientPeg"; | ||||
| import dis from "./dispatcher"; | ||||
| import Tinter from "./Tinter"; | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import MatrixClientPeg from './MatrixClientPeg'; | ||||
| import dis from './dispatcher'; | ||||
| import Tinter from './Tinter'; | ||||
| import sdk from './index'; | ||||
| import { _t } from './languageHandler'; | ||||
| import {_t, _td} from './languageHandler'; | ||||
| import Modal from './Modal'; | ||||
| import SettingsStore, {SettingLevel} from "./settings/SettingsStore"; | ||||
| import SettingsStore, {SettingLevel} from './settings/SettingsStore'; | ||||
| 
 | ||||
| 
 | ||||
| class Command { | ||||
|     constructor(name, paramArgs, runFn) { | ||||
|         this.name = name; | ||||
|         this.paramArgs = paramArgs; | ||||
|     constructor({name, args='', description, runFn}) { | ||||
|         this.command = name; | ||||
|         this.args = args; | ||||
|         this.description = description; | ||||
|         this.runFn = runFn; | ||||
|     } | ||||
| 
 | ||||
|     getCommand() { | ||||
|         return "/" + this.name; | ||||
|         return "/" + this.command; | ||||
|     } | ||||
| 
 | ||||
|     getCommandWithArgs() { | ||||
|         return this.getCommand() + " " + this.paramArgs; | ||||
|         return this.getCommand() + " " + this.args; | ||||
|     } | ||||
| 
 | ||||
|     run(roomId, args) { | ||||
|  | @ -47,16 +50,12 @@ class Command { | |||
|     } | ||||
| } | ||||
| 
 | ||||
| function reject(msg) { | ||||
|     return { | ||||
|         error: msg, | ||||
|     }; | ||||
| function reject(error) { | ||||
|     return {error}; | ||||
| } | ||||
| 
 | ||||
| function success(promise) { | ||||
|     return { | ||||
|         promise: promise, | ||||
|     }; | ||||
|     return {promise}; | ||||
| } | ||||
| 
 | ||||
| /* Disable the "unexpected this" error for these commands - all of the run | ||||
|  | @ -65,352 +64,408 @@ function success(promise) { | |||
| 
 | ||||
| /* eslint-disable babel/no-invalid-this */ | ||||
| 
 | ||||
| const commands = { | ||||
|     ddg: new Command("ddg", "<query>", function(roomId, args) { | ||||
|         const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); | ||||
|         // TODO Don't explain this away, actually show a search UI here.
 | ||||
|         Modal.createTrackedDialog('Slash Commands', '/ddg is not a command', ErrorDialog, { | ||||
|             title: _t('/ddg is not a command'), | ||||
|             description: _t('To use it, just wait for autocomplete results to load and tab through them.'), | ||||
|         }); | ||||
|         return success(); | ||||
| export const CommandMap = { | ||||
|     ddg: new Command({ | ||||
|         name: 'ddg', | ||||
|         args: '<query>', | ||||
|         description: _td('Searches DuckDuckGo for results'), | ||||
|         runFn: function(roomId, args) { | ||||
|             const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); | ||||
|             // TODO Don't explain this away, actually show a search UI here.
 | ||||
|             Modal.createTrackedDialog('Slash Commands', '/ddg is not a command', ErrorDialog, { | ||||
|                 title: _t('/ddg is not a command'), | ||||
|                 description: _t('To use it, just wait for autocomplete results to load and tab through them.'), | ||||
|             }); | ||||
|             return success(); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     // Change your nickname
 | ||||
|     nick: new Command("nick", "<display_name>", function(roomId, args) { | ||||
|         if (args) { | ||||
|             return success( | ||||
|                 MatrixClientPeg.get().setDisplayName(args), | ||||
|             ); | ||||
|         } | ||||
|         return reject(this.getUsage()); | ||||
|     }), | ||||
| 
 | ||||
|     // Changes the colorscheme of your current room
 | ||||
|     tint: new Command("tint", "<color1> [<color2>]", function(roomId, args) { | ||||
|         if (args) { | ||||
|             const matches = args.match(/^(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}))( +(#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})))?$/); | ||||
|             if (matches) { | ||||
|                 Tinter.tint(matches[1], matches[4]); | ||||
|                 const colorScheme = {}; | ||||
|                 colorScheme.primary_color = matches[1]; | ||||
|                 if (matches[4]) { | ||||
|                     colorScheme.secondary_color = matches[4]; | ||||
|                 } else { | ||||
|                     colorScheme.secondary_color = colorScheme.primary_color; | ||||
|                 } | ||||
|                 return success( | ||||
|                     SettingsStore.setValue("roomColor", roomId, SettingLevel.ROOM_ACCOUNT, colorScheme), | ||||
|                 ); | ||||
|     nick: new Command({ | ||||
|         name: 'nick', | ||||
|         args: '<display_name>', | ||||
|         description: _td('Changes your display nickname'), | ||||
|         runFn: function(roomId, args) { | ||||
|             if (args) { | ||||
|                 return success(MatrixClientPeg.get().setDisplayName(args)); | ||||
|             } | ||||
|         } | ||||
|         return reject(this.getUsage()); | ||||
|             return reject(this.getUsage()); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     // Change the room topic
 | ||||
|     topic: new Command("topic", "<topic>", function(roomId, args) { | ||||
|         if (args) { | ||||
|             return success( | ||||
|                 MatrixClientPeg.get().setRoomTopic(roomId, args), | ||||
|             ); | ||||
|         } | ||||
|         return reject(this.getUsage()); | ||||
|     }), | ||||
| 
 | ||||
|     // Invite a user
 | ||||
|     invite: new Command("invite", "<userId>", function(roomId, args) { | ||||
|         if (args) { | ||||
|             const matches = args.match(/^(\S+)$/); | ||||
|             if (matches) { | ||||
|                 return success( | ||||
|                     MatrixClientPeg.get().invite(roomId, matches[1]), | ||||
|                 ); | ||||
|             } | ||||
|         } | ||||
|         return reject(this.getUsage()); | ||||
|     }), | ||||
| 
 | ||||
|     // Join a room
 | ||||
|     join: new Command("join", "#alias:domain", function(roomId, args) { | ||||
|         if (args) { | ||||
|             const matches = args.match(/^(\S+)$/); | ||||
|             if (matches) { | ||||
|                 let roomAlias = matches[1]; | ||||
|                 if (roomAlias[0] !== '#') { | ||||
|                     return reject(this.getUsage()); | ||||
|                 } | ||||
|                 if (!roomAlias.match(/:/)) { | ||||
|                     roomAlias += ':' + MatrixClientPeg.get().getDomain(); | ||||
|                 } | ||||
| 
 | ||||
|                 dis.dispatch({ | ||||
|                     action: 'view_room', | ||||
|                     room_alias: roomAlias, | ||||
|                     auto_join: true, | ||||
|                 }); | ||||
| 
 | ||||
|                 return success(); | ||||
|             } | ||||
|         } | ||||
|         return reject(this.getUsage()); | ||||
|     }), | ||||
| 
 | ||||
|     part: new Command("part", "[#alias:domain]", function(roomId, args) { | ||||
|         let targetRoomId; | ||||
|         if (args) { | ||||
|             const matches = args.match(/^(\S+)$/); | ||||
|             if (matches) { | ||||
|                 let roomAlias = matches[1]; | ||||
|                 if (roomAlias[0] !== '#') { | ||||
|                     return reject(this.getUsage()); | ||||
|                 } | ||||
|                 if (!roomAlias.match(/:/)) { | ||||
|                     roomAlias += ':' + MatrixClientPeg.get().getDomain(); | ||||
|                 } | ||||
| 
 | ||||
|                 // Try to find a room with this alias
 | ||||
|                 const rooms = MatrixClientPeg.get().getRooms(); | ||||
|                 for (let i = 0; i < rooms.length; i++) { | ||||
|                     const aliasEvents = rooms[i].currentState.getStateEvents( | ||||
|                         "m.room.aliases", | ||||
|                     ); | ||||
|                     for (let j = 0; j < aliasEvents.length; j++) { | ||||
|                         const aliases = aliasEvents[j].getContent().aliases || []; | ||||
|                         for (let k = 0; k < aliases.length; k++) { | ||||
|                             if (aliases[k] === roomAlias) { | ||||
|                                 targetRoomId = rooms[i].roomId; | ||||
|                                 break; | ||||
|                             } | ||||
|                         } | ||||
|                         if (targetRoomId) { break; } | ||||
|     tint: new Command({ | ||||
|         name: 'tint', | ||||
|         args: '<color1> [<color2>]', | ||||
|         description: _td('Changes colour scheme of current room'), | ||||
|         runFn: function(roomId, args) { | ||||
|             if (args) { | ||||
|                 const matches = args.match(/^(#([\da-fA-F]{3}|[\da-fA-F]{6}))( +(#([\da-fA-F]{3}|[\da-fA-F]{6})))?$/); | ||||
|                 if (matches) { | ||||
|                     Tinter.tint(matches[1], matches[4]); | ||||
|                     const colorScheme = {}; | ||||
|                     colorScheme.primary_color = matches[1]; | ||||
|                     if (matches[4]) { | ||||
|                         colorScheme.secondary_color = matches[4]; | ||||
|                     } else { | ||||
|                         colorScheme.secondary_color = colorScheme.primary_color; | ||||
|                     } | ||||
|                     if (targetRoomId) { break; } | ||||
|                 } | ||||
|                 if (!targetRoomId) { | ||||
|                     return reject(_t("Unrecognised room alias:") + ' ' + roomAlias); | ||||
|                     return success( | ||||
|                         SettingsStore.setValue('roomColor', roomId, SettingLevel.ROOM_ACCOUNT, colorScheme), | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         if (!targetRoomId) targetRoomId = roomId; | ||||
|         return success( | ||||
|             MatrixClientPeg.get().leave(targetRoomId).then( | ||||
|                 function() { | ||||
|                     dis.dispatch({action: 'view_next_room'}); | ||||
|                 }, | ||||
|             ), | ||||
|         ); | ||||
|             return reject(this.getUsage()); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     // Kick a user from the room with an optional reason
 | ||||
|     kick: new Command("kick", "<userId> [<reason>]", function(roomId, args) { | ||||
|         if (args) { | ||||
|             const matches = args.match(/^(\S+?)( +(.*))?$/); | ||||
|             if (matches) { | ||||
|                 return success( | ||||
|                     MatrixClientPeg.get().kick(roomId, matches[1], matches[3]), | ||||
|                 ); | ||||
|     topic: new Command({ | ||||
|         name: 'topic', | ||||
|         args: '<topic>', | ||||
|         description: _td('Sets the room topic'), | ||||
|         runFn: function(roomId, args) { | ||||
|             if (args) { | ||||
|                 return success(MatrixClientPeg.get().setRoomTopic(roomId, args)); | ||||
|             } | ||||
|         } | ||||
|         return reject(this.getUsage()); | ||||
|             return reject(this.getUsage()); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     invite: new Command({ | ||||
|         name: 'invite', | ||||
|         args: '<user-id>', | ||||
|         description: _td('Invites user with given id to current room'), | ||||
|         runFn: function(roomId, args) { | ||||
|             if (args) { | ||||
|                 const matches = args.match(/^(\S+)$/); | ||||
|                 if (matches) { | ||||
|                     return success(MatrixClientPeg.get().invite(roomId, matches[1])); | ||||
|                 } | ||||
|             } | ||||
|             return reject(this.getUsage()); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     join: new Command({ | ||||
|         name: 'join', | ||||
|         args: '<room-alias>', | ||||
|         description: _td('Joins room with given alias'), | ||||
|         runFn: function(roomId, args) { | ||||
|             if (args) { | ||||
|                 const matches = args.match(/^(\S+)$/); | ||||
|                 if (matches) { | ||||
|                     let roomAlias = matches[1]; | ||||
|                     if (roomAlias[0] !== '#') return reject(this.getUsage()); | ||||
| 
 | ||||
|                     if (!roomAlias.includes(':')) { | ||||
|                         roomAlias += ':' + MatrixClientPeg.get().getDomain(); | ||||
|                     } | ||||
| 
 | ||||
|                     dis.dispatch({ | ||||
|                         action: 'view_room', | ||||
|                         room_alias: roomAlias, | ||||
|                         auto_join: true, | ||||
|                     }); | ||||
| 
 | ||||
|                     return success(); | ||||
|                 } | ||||
|             } | ||||
|             return reject(this.getUsage()); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     part: new Command({ | ||||
|         name: 'part', | ||||
|         args: '[<room-alias>]', | ||||
|         description: _td('Leave room'), | ||||
|         runFn: function(roomId, args) { | ||||
|             const cli = MatrixClientPeg.get(); | ||||
| 
 | ||||
|             let targetRoomId; | ||||
|             if (args) { | ||||
|                 const matches = args.match(/^(\S+)$/); | ||||
|                 if (matches) { | ||||
|                     let roomAlias = matches[1]; | ||||
|                     if (roomAlias[0] !== '#') return reject(this.getUsage()); | ||||
| 
 | ||||
|                     if (!roomAlias.includes(':')) { | ||||
|                         roomAlias += ':' + cli.getDomain(); | ||||
|                     } | ||||
| 
 | ||||
|                     // Try to find a room with this alias
 | ||||
|                     const rooms = cli.getRooms(); | ||||
|                     for (let i = 0; i < rooms.length; i++) { | ||||
|                         const aliasEvents = rooms[i].currentState.getStateEvents('m.room.aliases'); | ||||
|                         for (let j = 0; j < aliasEvents.length; j++) { | ||||
|                             const aliases = aliasEvents[j].getContent().aliases || []; | ||||
|                             for (let k = 0; k < aliases.length; k++) { | ||||
|                                 if (aliases[k] === roomAlias) { | ||||
|                                     targetRoomId = rooms[i].roomId; | ||||
|                                     break; | ||||
|                                 } | ||||
|                             } | ||||
|                             if (targetRoomId) break; | ||||
|                         } | ||||
|                         if (targetRoomId) break; | ||||
|                     } | ||||
|                     if (!targetRoomId) return reject(_t('Unrecognised room alias:') + ' ' + roomAlias); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (!targetRoomId) targetRoomId = roomId; | ||||
|             return success( | ||||
|                 cli.leave(targetRoomId).then(function() { | ||||
|                     dis.dispatch({action: 'view_next_room'}); | ||||
|                 }), | ||||
|             ); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     kick: new Command({ | ||||
|         name: 'kick', | ||||
|         args: '<user-id> [reason]', | ||||
|         description: _td('Kicks user with given id'), | ||||
|         runFn: function(roomId, args) { | ||||
|             if (args) { | ||||
|                 const matches = args.match(/^(\S+?)( +(.*))?$/); | ||||
|                 if (matches) { | ||||
|                     return success(MatrixClientPeg.get().kick(roomId, matches[1], matches[3])); | ||||
|                 } | ||||
|             } | ||||
|             return reject(this.getUsage()); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     // Ban a user from the room with an optional reason
 | ||||
|     ban: new Command("ban", "<userId> [<reason>]", function(roomId, args) { | ||||
|         if (args) { | ||||
|             const matches = args.match(/^(\S+?)( +(.*))?$/); | ||||
|             if (matches) { | ||||
|                 return success( | ||||
|                     MatrixClientPeg.get().ban(roomId, matches[1], matches[3]), | ||||
|                 ); | ||||
|     ban: new Command({ | ||||
|         name: 'ban', | ||||
|         args: '<user-id> [reason]', | ||||
|         description: _td('Bans user with given id'), | ||||
|         runFn: function(roomId, args) { | ||||
|             if (args) { | ||||
|                 const matches = args.match(/^(\S+?)( +(.*))?$/); | ||||
|                 if (matches) { | ||||
|                     return success(MatrixClientPeg.get().ban(roomId, matches[1], matches[3])); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return reject(this.getUsage()); | ||||
|             return reject(this.getUsage()); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     // Unban a user from the room
 | ||||
|     unban: new Command("unban", "<userId>", function(roomId, args) { | ||||
|         if (args) { | ||||
|             const matches = args.match(/^(\S+)$/); | ||||
|             if (matches) { | ||||
|                 // Reset the user membership to "leave" to unban him
 | ||||
|                 return success( | ||||
|                     MatrixClientPeg.get().unban(roomId, matches[1]), | ||||
|                 ); | ||||
|     // Unban a user from ythe room
 | ||||
|     unban: new Command({ | ||||
|         name: 'unban', | ||||
|         args: '<user-id>', | ||||
|         description: _td('Unbans user with given id'), | ||||
|         runFn: function(roomId, args) { | ||||
|             if (args) { | ||||
|                 const matches = args.match(/^(\S+)$/); | ||||
|                 if (matches) { | ||||
|                     // Reset the user membership to "leave" to unban him
 | ||||
|                     return success(MatrixClientPeg.get().unban(roomId, matches[1])); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return reject(this.getUsage()); | ||||
|             return reject(this.getUsage()); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     ignore: new Command("ignore", "<userId>", function(roomId, args) { | ||||
|         if (args) { | ||||
|             const matches = args.match(/^(\S+)$/); | ||||
|             if (matches) { | ||||
|                 const userId = matches[1]; | ||||
|                 const ignoredUsers = MatrixClientPeg.get().getIgnoredUsers(); | ||||
|                 ignoredUsers.push(userId); // de-duped internally in the js-sdk
 | ||||
|                 return success( | ||||
|                     MatrixClientPeg.get().setIgnoredUsers(ignoredUsers).then(() => { | ||||
|                         const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); | ||||
|                         Modal.createTrackedDialog('Slash Commands', 'User ignored', QuestionDialog, { | ||||
|                             title: _t("Ignored user"), | ||||
|                             description: ( | ||||
|                                 <div> | ||||
|                                     <p>{ _t("You are now ignoring %(userId)s", {userId: userId}) }</p> | ||||
|                                 </div> | ||||
|                             ), | ||||
|                             hasCancelButton: false, | ||||
|                         }); | ||||
|                     }), | ||||
|                 ); | ||||
|     ignore: new Command({ | ||||
|         name: 'ignore', | ||||
|         args: '<user-id>', | ||||
|         description: _td('Ignores a user, hiding their messages from you'), | ||||
|         runFn: function(roomId, args) { | ||||
|             if (args) { | ||||
|                 const cli = MatrixClientPeg.get(); | ||||
| 
 | ||||
|                 const matches = args.match(/^(\S+)$/); | ||||
|                 if (matches) { | ||||
|                     const userId = matches[1]; | ||||
|                     const ignoredUsers = cli.getIgnoredUsers(); | ||||
|                     ignoredUsers.push(userId); // de-duped internally in the js-sdk
 | ||||
|                     return success( | ||||
|                         cli.setIgnoredUsers(ignoredUsers).then(() => { | ||||
|                             const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog'); | ||||
|                             Modal.createTrackedDialog('Slash Commands', 'User ignored', QuestionDialog, { | ||||
|                                 title: _t('Ignored user'), | ||||
|                                 description: <div> | ||||
|                                     <p>{ _t('You are now ignoring %(userId)s', {userId}) }</p> | ||||
|                                 </div>, | ||||
|                                 hasCancelButton: false, | ||||
|                             }); | ||||
|                         }), | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return reject(this.getUsage()); | ||||
|             return reject(this.getUsage()); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     unignore: new Command("unignore", "<userId>", function(roomId, args) { | ||||
|         if (args) { | ||||
|             const matches = args.match(/^(\S+)$/); | ||||
|             if (matches) { | ||||
|                 const userId = matches[1]; | ||||
|                 const ignoredUsers = MatrixClientPeg.get().getIgnoredUsers(); | ||||
|                 const index = ignoredUsers.indexOf(userId); | ||||
|                 if (index !== -1) ignoredUsers.splice(index, 1); | ||||
|                 return success( | ||||
|                     MatrixClientPeg.get().setIgnoredUsers(ignoredUsers).then(() => { | ||||
|                         const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); | ||||
|                         Modal.createTrackedDialog('Slash Commands', 'User unignored', QuestionDialog, { | ||||
|                             title: _t("Unignored user"), | ||||
|                             description: ( | ||||
|                                 <div> | ||||
|                                     <p>{ _t("You are no longer ignoring %(userId)s", {userId: userId}) }</p> | ||||
|                                 </div> | ||||
|                             ), | ||||
|                             hasCancelButton: false, | ||||
|                         }); | ||||
|                     }), | ||||
|                 ); | ||||
|     unignore: new Command({ | ||||
|         name: 'unignore', | ||||
|         args: '<user-id>', | ||||
|         description: _td('Stops ignoring a user, showing their messages going forward'), | ||||
|         runFn: function(roomId, args) { | ||||
|             if (args) { | ||||
|                 const cli = MatrixClientPeg.get(); | ||||
| 
 | ||||
|                 const matches = args.match(/^(\S+)$/); | ||||
|                 if (matches) { | ||||
|                     const userId = matches[1]; | ||||
|                     const ignoredUsers = cli.getIgnoredUsers(); | ||||
|                     const index = ignoredUsers.indexOf(userId); | ||||
|                     if (index !== -1) ignoredUsers.splice(index, 1); | ||||
|                     return success( | ||||
|                         cli.setIgnoredUsers(ignoredUsers).then(() => { | ||||
|                             const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog'); | ||||
|                             Modal.createTrackedDialog('Slash Commands', 'User unignored', QuestionDialog, { | ||||
|                                 title: _t('Unignored user'), | ||||
|                                 description: <div> | ||||
|                                     <p>{ _t('You are no longer ignoring %(userId)s', {userId}) }</p> | ||||
|                                 </div>, | ||||
|                                 hasCancelButton: false, | ||||
|                             }); | ||||
|                         }), | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return reject(this.getUsage()); | ||||
|             return reject(this.getUsage()); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     // Define the power level of a user
 | ||||
|     op: new Command("op", "<userId> [<power level>]", function(roomId, args) { | ||||
|         if (args) { | ||||
|             const matches = args.match(/^(\S+?)( +(-?\d+))?$/); | ||||
|             let powerLevel = 50; // default power level for op
 | ||||
|             if (matches) { | ||||
|                 const userId = matches[1]; | ||||
|                 if (matches.length === 4 && undefined !== matches[3]) { | ||||
|                     powerLevel = parseInt(matches[3]); | ||||
|                 } | ||||
|                 if (!isNaN(powerLevel)) { | ||||
|                     const room = MatrixClientPeg.get().getRoom(roomId); | ||||
|                     if (!room) { | ||||
|                         return reject("Bad room ID: " + roomId); | ||||
|     op: new Command({ | ||||
|         name: 'op', | ||||
|         args: '<user-id> [<power-level>]', | ||||
|         description: _td('Define the power level of a user'), | ||||
|         runFn: function(roomId, args) { | ||||
|             if (args) { | ||||
|                 const matches = args.match(/^(\S+?)( +(-?\d+))?$/); | ||||
|                 let powerLevel = 50; // default power level for op
 | ||||
|                 if (matches) { | ||||
|                     const userId = matches[1]; | ||||
|                     if (matches.length === 4 && undefined !== matches[3]) { | ||||
|                         powerLevel = parseInt(matches[3]); | ||||
|                     } | ||||
|                     if (!isNaN(powerLevel)) { | ||||
|                         const cli = MatrixClientPeg.get(); | ||||
|                         const room = cli.getRoom(roomId); | ||||
|                         if (!room) return reject('Bad room ID: ' + roomId); | ||||
| 
 | ||||
|                         const powerLevelEvent = room.currentState.getStateEvents('m.room.power_levels', ''); | ||||
|                         return success(cli.setPowerLevel(roomId, userId, powerLevel, powerLevelEvent)); | ||||
|                     } | ||||
|                     const powerLevelEvent = room.currentState.getStateEvents( | ||||
|                         "m.room.power_levels", "", | ||||
|                     ); | ||||
|                     return success( | ||||
|                         MatrixClientPeg.get().setPowerLevel( | ||||
|                             roomId, userId, powerLevel, powerLevelEvent, | ||||
|                         ), | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return reject(this.getUsage()); | ||||
|             return reject(this.getUsage()); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     // Reset the power level of a user
 | ||||
|     deop: new Command("deop", "<userId>", function(roomId, args) { | ||||
|         if (args) { | ||||
|             const matches = args.match(/^(\S+)$/); | ||||
|             if (matches) { | ||||
|                 const room = MatrixClientPeg.get().getRoom(roomId); | ||||
|                 if (!room) { | ||||
|                     return reject("Bad room ID: " + roomId); | ||||
|                 } | ||||
|     deop: new Command({ | ||||
|         name: 'deop', | ||||
|         args: '<user-id>', | ||||
|         description: _td('Deops user with given id'), | ||||
|         runFn: function(roomId, args) { | ||||
|             if (args) { | ||||
|                 const matches = args.match(/^(\S+)$/); | ||||
|                 if (matches) { | ||||
|                     const cli = MatrixClientPeg.get(); | ||||
|                     const room = cli.getRoom(roomId); | ||||
|                     if (!room) return reject('Bad room ID: ' + roomId); | ||||
| 
 | ||||
|                 const powerLevelEvent = room.currentState.getStateEvents( | ||||
|                     "m.room.power_levels", "", | ||||
|                 ); | ||||
|                 return success( | ||||
|                     MatrixClientPeg.get().setPowerLevel( | ||||
|                         roomId, args, undefined, powerLevelEvent, | ||||
|                     ), | ||||
|                 ); | ||||
|                     const powerLevelEvent = room.currentState.getStateEvents('m.room.power_levels', ''); | ||||
|                     return success(cli.setPowerLevel(roomId, args, undefined, powerLevelEvent)); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return reject(this.getUsage()); | ||||
|             return reject(this.getUsage()); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     // Open developer tools
 | ||||
|     devtools: new Command("devtools", "", function(roomId) { | ||||
|         const DevtoolsDialog = sdk.getComponent("dialogs.DevtoolsDialog"); | ||||
|         Modal.createDialog(DevtoolsDialog, { roomId }); | ||||
|         return success(); | ||||
|     devtools: new Command({ | ||||
|         name: 'devtools', | ||||
|         description: _td('Opens the Developer Tools dialog'), | ||||
|         runFn: function(roomId) { | ||||
|             const DevtoolsDialog = sdk.getComponent('dialogs.DevtoolsDialog'); | ||||
|             Modal.createDialog(DevtoolsDialog, {roomId}); | ||||
|             return success(); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     // Verify a user, device, and pubkey tuple
 | ||||
|     verify: new Command("verify", "<userId> <deviceId> <deviceSigningKey>", function(roomId, args) { | ||||
|         if (args) { | ||||
|             const matches = args.match(/^(\S+) +(\S+) +(\S+)$/); | ||||
|             if (matches) { | ||||
|                 const userId = matches[1]; | ||||
|                 const deviceId = matches[2]; | ||||
|                 const fingerprint = matches[3]; | ||||
|     verify: new Command({ | ||||
|         name: 'verify', | ||||
|         args: '<user-id> <device-id> <device-signing-key>', | ||||
|         description: _td('Verifies a user, device, and pubkey tuple'), | ||||
|         runFn: function(roomId, args) { | ||||
|             if (args) { | ||||
|                 const matches = args.match(/^(\S+) +(\S+) +(\S+)$/); | ||||
|                 if (matches) { | ||||
|                     const cli = MatrixClientPeg.get(); | ||||
| 
 | ||||
|                 return success( | ||||
|                     // Promise.resolve to handle transition from static result to promise; can be removed
 | ||||
|                     // in future
 | ||||
|                     Promise.resolve(MatrixClientPeg.get().getStoredDevice(userId, deviceId)).then((device) => { | ||||
|                         if (!device) { | ||||
|                             throw new Error(_t(`Unknown (user, device) pair:`) + ` (${userId}, ${deviceId})`); | ||||
|                         } | ||||
|                     const userId = matches[1]; | ||||
|                     const deviceId = matches[2]; | ||||
|                     const fingerprint = matches[3]; | ||||
| 
 | ||||
|                         if (device.isVerified()) { | ||||
|                             if (device.getFingerprint() === fingerprint) { | ||||
|                                 throw new Error(_t(`Device already verified!`)); | ||||
|                             } else { | ||||
|                                 throw new Error(_t(`WARNING: Device already verified, but keys do NOT MATCH!`)); | ||||
|                     return success( | ||||
|                         // Promise.resolve to handle transition from static result to promise; can be removed
 | ||||
|                         // in future
 | ||||
|                         Promise.resolve(cli.getStoredDevice(userId, deviceId)).then((device) => { | ||||
|                             if (!device) { | ||||
|                                 throw new Error(_t('Unknown (user, device) pair:') + ` (${userId}, ${deviceId})`); | ||||
|                             } | ||||
|                         } | ||||
| 
 | ||||
|                         if (device.getFingerprint() !== fingerprint) { | ||||
|                             const fprint = device.getFingerprint(); | ||||
|                             throw new Error( | ||||
|                                 _t('WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device' + | ||||
|                                    ' %(deviceId)s is "%(fprint)s" which does not match the provided key' + | ||||
|                                    ' "%(fingerprint)s". This could mean your communications are being intercepted!', | ||||
|                                    {deviceId: deviceId, fprint: fprint, userId: userId, fingerprint: fingerprint})); | ||||
|                         } | ||||
|                             if (device.isVerified()) { | ||||
|                                 if (device.getFingerprint() === fingerprint) { | ||||
|                                     throw new Error(_t('Device already verified!')); | ||||
|                                 } else { | ||||
|                                     throw new Error(_t('WARNING: Device already verified, but keys do NOT MATCH!')); | ||||
|                                 } | ||||
|                             } | ||||
| 
 | ||||
|                         return MatrixClientPeg.get().setDeviceVerified(userId, deviceId, true); | ||||
|                     }).then(() => { | ||||
|                         // Tell the user we verified everything
 | ||||
|                         const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); | ||||
|                         Modal.createTrackedDialog('Slash Commands', 'Verified key', QuestionDialog, { | ||||
|                             title: _t("Verified key"), | ||||
|                             description: ( | ||||
|                                 <div> | ||||
|                                   <p> | ||||
|                                     { | ||||
|                                         _t("The signing key you provided matches the signing key you received " + | ||||
|                                            "from %(userId)s's device %(deviceId)s. Device marked as verified.", | ||||
|                                            {userId: userId, deviceId: deviceId}) | ||||
|                                     } | ||||
|                                   </p> | ||||
|                                 </div> | ||||
|                             ), | ||||
|                             hasCancelButton: false, | ||||
|                         }); | ||||
|                     }), | ||||
|                 ); | ||||
|                             if (device.getFingerprint() !== fingerprint) { | ||||
|                                 const fprint = device.getFingerprint(); | ||||
|                                 throw new Error( | ||||
|                                     _t('WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device' + | ||||
|                                         ' %(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, | ||||
|                                         })); | ||||
|                             } | ||||
| 
 | ||||
|                             return cli.setDeviceVerified(userId, deviceId, true); | ||||
|                         }).then(() => { | ||||
|                             // Tell the user we verified everything
 | ||||
|                             const QuestionDialog = sdk.getComponent('dialogs.QuestionDialog'); | ||||
|                             Modal.createTrackedDialog('Slash Commands', 'Verified key', QuestionDialog, { | ||||
|                                 title: _t('Verified key'), | ||||
|                                 description: <div> | ||||
|                                     <p> | ||||
|                                         { | ||||
|                                             _t('The signing key you provided matches the signing key you received ' + | ||||
|                                                 'from %(userId)s\'s device %(deviceId)s. Device marked as verified.', | ||||
|                                                 {userId, deviceId}) | ||||
|                                         } | ||||
|                                     </p> | ||||
|                                 </div>, | ||||
|                                 hasCancelButton: false, | ||||
|                             }); | ||||
|                         }), | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return reject(this.getUsage()); | ||||
|             return reject(this.getUsage()); | ||||
|         }, | ||||
|     }), | ||||
| 
 | ||||
|     // Command definitions for autocompletion ONLY:
 | ||||
| 
 | ||||
|     // /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'), | ||||
|     }), | ||||
| }; | ||||
| /* eslint-enable babel/no-invalid-this */ | ||||
|  | @ -421,50 +476,39 @@ const aliases = { | |||
|     j: "join", | ||||
| }; | ||||
| 
 | ||||
| module.exports = { | ||||
|     /** | ||||
|      * Process the given text for /commands and perform them. | ||||
|      * @param {string} roomId The room in which the command was performed. | ||||
|      * @param {string} input The raw text input by the user. | ||||
|      * @return {Object|null} An object with the property 'error' if there was an error | ||||
|      * processing the command, or 'promise' if a request was sent out. | ||||
|      * Returns null if the input didn't match a command. | ||||
|      */ | ||||
|     processInput: function(roomId, input) { | ||||
|         // trim any trailing whitespace, as it can confuse the parser for
 | ||||
|         // IRC-style commands
 | ||||
|         input = input.replace(/\s+$/, ""); | ||||
|         if (input[0] === "/" && input[1] !== "/") { | ||||
|             const bits = input.match(/^(\S+?)( +((.|\n)*))?$/); | ||||
|             let cmd; | ||||
|             let args; | ||||
|             if (bits) { | ||||
|                 cmd = bits[1].substring(1).toLowerCase(); | ||||
|                 args = bits[3]; | ||||
|             } else { | ||||
|                 cmd = input; | ||||
|             } | ||||
|             if (cmd === "me") return null; | ||||
|             if (aliases[cmd]) { | ||||
|                 cmd = aliases[cmd]; | ||||
|             } | ||||
|             if (commands[cmd]) { | ||||
|                 return commands[cmd].run(roomId, args); | ||||
|             } else { | ||||
|                 return reject(_t("Unrecognised command:") + ' ' + input); | ||||
|             } | ||||
|         } | ||||
|         return null; // not a command
 | ||||
|     }, | ||||
| /** | ||||
|  * Process the given text for /commands and perform them. | ||||
|  * @param {string} roomId The room in which the command was performed. | ||||
|  * @param {string} input The raw text input by the user. | ||||
|  * @return {Object|null} An object with the property 'error' if there was an error | ||||
|  * processing the command, or 'promise' if a request was sent out. | ||||
|  * Returns null if the input didn't match a command. | ||||
|  */ | ||||
| export function processCommandInput(roomId, input) { | ||||
|     // trim any trailing whitespace, as it can confuse the parser for
 | ||||
|     // IRC-style commands
 | ||||
|     input = input.replace(/\s+$/, ''); | ||||
|     if (input[0] !== '/' || input[1] === '/') return null; // not a command
 | ||||
| 
 | ||||
|     getCommandList: function() { | ||||
|         // Return all the commands plus /me and /markdown which aren't handled like normal commands
 | ||||
|         const cmds = Object.keys(commands).sort().map(function(cmdKey) { | ||||
|             return commands[cmdKey]; | ||||
|         }); | ||||
|         cmds.push(new Command("me", "<action>", function() {})); | ||||
|         cmds.push(new Command("markdown", "<on|off>", function() {})); | ||||
|     const bits = input.match(/^(\S+?)( +((.|\n)*))?$/); | ||||
|     let cmd; | ||||
|     let args; | ||||
|     if (bits) { | ||||
|         cmd = bits[1].substring(1).toLowerCase(); | ||||
|         args = bits[3]; | ||||
|     } else { | ||||
|         cmd = input; | ||||
|     } | ||||
| 
 | ||||
|         return cmds; | ||||
|     }, | ||||
| }; | ||||
|     if (aliases[cmd]) { | ||||
|         cmd = aliases[cmd]; | ||||
|     } | ||||
|     if (CommandMap[cmd]) { | ||||
|         // if it has no runFn then its an ignored/nop command (autocomplete only) e.g `/me`
 | ||||
|         if (!CommandMap[cmd].runFn) return null; | ||||
| 
 | ||||
|         return CommandMap[cmd].run(roomId, args); | ||||
|     } else { | ||||
|         return reject(_t('Unrecognised command:') + ' ' + input); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -18,101 +18,14 @@ limitations under the License. | |||
| */ | ||||
| 
 | ||||
| import React from 'react'; | ||||
| import { _t, _td } from '../languageHandler'; | ||||
| import {_t} from '../languageHandler'; | ||||
| import AutocompleteProvider from './AutocompleteProvider'; | ||||
| import FuzzyMatcher from './FuzzyMatcher'; | ||||
| import {TextualCompletion} from './Components'; | ||||
| import {CommandMap} from '../SlashCommands'; | ||||
| import type {SelectionRange} from "./Autocompleter"; | ||||
| 
 | ||||
| // TODO merge this with the factory mechanics of SlashCommands?
 | ||||
| // Warning: Since the description string will be translated in _t(result.description), all these strings below must be in i18n/strings/en_EN.json file
 | ||||
| const COMMANDS = [ | ||||
|     { | ||||
|         command: '/me', | ||||
|         args: '<message>', | ||||
|         description: _td('Displays action'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/ban', | ||||
|         args: '<user-id> [reason]', | ||||
|         description: _td('Bans user with given id'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/unban', | ||||
|         args: '<user-id>', | ||||
|         description: _td('Unbans user with given id'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/op', | ||||
|         args: '<user-id> [<power-level>]', | ||||
|         description: _td('Define the power level of a user'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/deop', | ||||
|         args: '<user-id>', | ||||
|         description: _td('Deops user with given id'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/invite', | ||||
|         args: '<user-id>', | ||||
|         description: _td('Invites user with given id to current room'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/join', | ||||
|         args: '<room-alias>', | ||||
|         description: _td('Joins room with given alias'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/part', | ||||
|         args: '[<room-alias>]', | ||||
|         description: _td('Leave room'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/topic', | ||||
|         args: '<topic>', | ||||
|         description: _td('Sets the room topic'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/kick', | ||||
|         args: '<user-id> [reason]', | ||||
|         description: _td('Kicks user with given id'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/nick', | ||||
|         args: '<display-name>', | ||||
|         description: _td('Changes your display nickname'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/ddg', | ||||
|         args: '<query>', | ||||
|         description: _td('Searches DuckDuckGo for results'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/tint', | ||||
|         args: '<color1> [<color2>]', | ||||
|         description: _td('Changes colour scheme of current room'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/verify', | ||||
|         args: '<user-id> <device-id> <device-signing-key>', | ||||
|         description: _td('Verifies a user, device, and pubkey tuple'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/ignore', | ||||
|         args: '<user-id>', | ||||
|         description: _td('Ignores a user, hiding their messages from you'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/unignore', | ||||
|         args: '<user-id>', | ||||
|         description: _td('Stops ignoring a user, showing their messages going forward'), | ||||
|     }, | ||||
|     { | ||||
|         command: '/devtools', | ||||
|         args: '', | ||||
|         description: _td('Opens the Developer Tools dialog'), | ||||
|     }, | ||||
| ]; | ||||
| const COMMANDS = Object.values(CommandMap); | ||||
| 
 | ||||
| const COMMAND_RE = /(^\/\w*)(?: .*)?/g; | ||||
| 
 | ||||
|  | @ -134,11 +47,10 @@ export default class CommandProvider extends AutocompleteProvider { | |||
|             return { | ||||
|                 // If the command is the same as the one they entered, we don't want to discard their arguments
 | ||||
|                 completion: result.command === command[1] ? command[0] : (result.command + ' '), | ||||
|                 component: (<TextualCompletion | ||||
|                 component: <TextualCompletion | ||||
|                     title={result.command} | ||||
|                     subtitle={result.args} | ||||
|                     description={_t(result.description)} | ||||
|                 />), | ||||
|                     description={_t(result.description)} />, | ||||
|                 range, | ||||
|             }; | ||||
|         }); | ||||
|  |  | |||
|  | @ -28,7 +28,7 @@ import Promise from 'bluebird'; | |||
| 
 | ||||
| import MatrixClientPeg from '../../../MatrixClientPeg'; | ||||
| import type {MatrixClient} from 'matrix-js-sdk/lib/matrix'; | ||||
| import SlashCommands from '../../../SlashCommands'; | ||||
| import {processCommandInput} from '../../../SlashCommands'; | ||||
| import { KeyCode, isOnlyCtrlOrCmdKeyEvent } from '../../../Keyboard'; | ||||
| import Modal from '../../../Modal'; | ||||
| import sdk from '../../../index'; | ||||
|  | @ -721,7 +721,7 @@ export default class MessageComposerInput extends React.Component { | |||
| 
 | ||||
|         // Some commands (/join) require pills to be replaced with their text content
 | ||||
|         const commandText = this.removeMDLinks(contentState, ['#']); | ||||
|         const cmd = SlashCommands.processInput(this.props.room.roomId, commandText); | ||||
|         const cmd = processCommandInput(this.props.room.roomId, commandText); | ||||
|         if (cmd) { | ||||
|             if (!cmd.error) { | ||||
|                 this.historyManager.save(contentState, this.state.isRichtextEnabled ? 'html' : 'markdown'); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Michael Telatynski
						Michael Telatynski