@@ -26,8 +26,8 @@ import { logger } from "matrix-js-sdk/src/logger";
import { IContent } from "matrix-js-sdk/src/models/event" ;
import { MRoomTopicEventContent } from "matrix-js-sdk/src/@types/topic" ;
import { SlashCommand as SlashCommandEvent } from "@matrix-org/analytics-events/types/typescript/SlashCommand" ;
import { MatrixClient } from "matrix-js-sdk/src/matrix" ;
import { MatrixClientPeg } from "./MatrixClientPeg" ;
import dis from "./dispatcher/dispatcher" ;
import { _t , _td , UserFriendlyError } from "./languageHandler" ;
import Modal from "./Modal" ;
@@ -75,7 +75,7 @@ interface HTMLInputEvent extends Event {
target : HTMLInputElement & EventTarget ;
}
const singleMxcUpload = async ( ) : Promise < string | null > = > {
const singleMxcUpload = async ( cli : MatrixClient ) : Promise < string | null > = > {
return new Promise ( ( resolve ) = > {
const fileSelector = document . createElement ( "input" ) ;
fileSelector . setAttribute ( "type" , "file" ) ;
@@ -87,7 +87,7 @@ const singleMxcUpload = async (): Promise<string | null> => {
file ,
onFinished : async ( shouldContinue ) : Promise < void > = > {
if ( shouldContinue ) {
const { content_uri : uri } = await MatrixClientPeg . get ( ) . uploadContent ( file ) ;
const { content_uri : uri } = await cli . uploadContent ( file ) ;
resolve ( uri ) ;
} else {
resolve ( null ) ;
@@ -111,7 +111,7 @@ export const CommandCategories = {
export type RunResult = XOR < { error : Error } , { promise : Promise < IContent | undefined > } > ;
type RunFn = ( this : Command , roomId : string , args? : string ) = > RunResult ;
type RunFn = ( this : Command , matrixClient : MatrixClient , roomId : string , args? : string ) = > RunResult ;
interface ICommandOpts {
command : string ;
@@ -122,7 +122,7 @@ interface ICommandOpts {
runFn? : RunFn ;
category : string ;
hideCompletionAfterSpace? : boolean ;
isEnabled ? ( ) : boolean ;
isEnabled ? ( matrixClient? : MatrixClient ) : boolean ;
renderingTypes? : TimelineRenderingType [ ] ;
}
@@ -136,7 +136,7 @@ export class Command {
public readonly hideCompletionAfterSpace : boolean ;
public readonly renderingTypes? : TimelineRenderingType [ ] ;
public readonly analyticsName? : SlashCommandEvent [ "command" ] ;
private readonly _isEnabled ? : ( ) = > boolean ;
private readonly _isEnabled ? : ( matrixClient? : MatrixClient ) = > boolean ;
public constructor ( opts : ICommandOpts ) {
this . command = opts . command ;
@@ -159,7 +159,7 @@ export class Command {
return this . getCommand ( ) + " " + this . args ;
}
public run ( roomId : string , threadId : string | null , args? : string ) : RunResult {
public run ( matrixClient : MatrixClient , roomId : string , threadId : string | null , args? : string ) : RunResult {
// if it has no runFn then its an ignored/nop command (autocomplete only) e.g `/me`
if ( ! this . runFn ) {
return reject ( new UserFriendlyError ( "Command error: Unable to handle slash command." ) ) ;
@@ -182,15 +182,15 @@ export class Command {
} ) ;
}
return this . runFn ( roomId , args ) ;
return this . runFn ( matrixClient , roomId , args ) ;
}
public getUsage ( ) : string {
return _t ( "Usage" ) + ": " + this . getCommandWithArgs ( ) ;
}
public isEnabled ( ) : boolean {
return this . _isEnabled ? this . _isEnabled ( ) : true ;
public isEnabled ( cli? : MatrixClient ) : boolean {
return this . _isEnabled ? . ( cli ) ? ? true ;
}
}
@@ -206,15 +206,21 @@ function successSync(value: any): RunResult {
return success ( Promise . resolve ( value ) ) ;
}
const isCurrentLocalRoom = ( ) : boolean = > {
const cli = MatrixClientPeg . get ( ) ;
const isCurrentLocalRoom = ( cli? : MatrixClient ) : boolean = > {
const roomId = SdkContextClass . instance . roomViewStore . getRoomId ( ) ;
if ( ! roomId ) return false ;
const room = cli . getRoom ( roomId ) ;
const room = cli ? . getRoom ( roomId ) ;
if ( ! room ) return false ;
return isLocalRoom ( room ) ;
} ;
const canAffectPowerlevels = ( cli? : MatrixClient ) : boolean = > {
const roomId = SdkContextClass . instance . roomViewStore . getRoomId ( ) ;
if ( ! cli || ! roomId ) return false ;
const room = cli ? . getRoom ( roomId ) ;
return ! ! room ? . currentState . maySendStateEvent ( EventType . RoomPowerLevels , cli . getSafeUserId ( ) ) && ! isLocalRoom ( room ) ;
} ;
/* Disable the "unexpected this" error for these commands - all of the run
* functions are called with `this` bound to the Command instance.
*/
@@ -224,7 +230,7 @@ export const Commands = [
command : "spoiler" ,
args : "<message>" ,
description : _td ( "Sends the given message as a spoiler" ) ,
runFn : function ( roomId , message = "" ) {
runFn : function ( cli , roomId , message = "" ) {
return successSync ( ContentHelpers . makeHtmlMessage ( message , ` <span data-mx-spoiler> ${ message } </span> ` ) ) ;
} ,
category : CommandCategories.messages ,
@@ -233,7 +239,7 @@ export const Commands = [
command : "shrug" ,
args : "<message>" ,
description : _td ( "Prepends ¯\\_(ツ)_/¯ to a plain-text message" ) ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
let message = "¯\\_(ツ)_/¯" ;
if ( args ) {
message = message + " " + args ;
@@ -246,7 +252,7 @@ export const Commands = [
command : "tableflip" ,
args : "<message>" ,
description : _td ( "Prepends (╯°□°)╯︵ ┻━┻ to a plain-text message" ) ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
let message = "(╯°□°)╯︵ ┻━┻" ;
if ( args ) {
message = message + " " + args ;
@@ -259,7 +265,7 @@ export const Commands = [
command : "unflip" ,
args : "<message>" ,
description : _td ( "Prepends ┬──┬ ノ ( ゜-゜ノ) to a plain-text message" ) ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
let message = "┬──┬ ノ ( ゜-゜ノ)" ;
if ( args ) {
message = message + " " + args ;
@@ -272,7 +278,7 @@ export const Commands = [
command : "lenny" ,
args : "<message>" ,
description : _td ( "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message" ) ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
let message = "( ͡° ͜ʖ ͡°)" ;
if ( args ) {
message = message + " " + args ;
@@ -285,7 +291,7 @@ export const Commands = [
command : "plain" ,
args : "<message>" ,
description : _td ( "Sends a message as plain text, without interpreting it as markdown" ) ,
runFn : function ( roomId , messages = "" ) {
runFn : function ( cli , roomId , messages = "" ) {
return successSync ( ContentHelpers . makeTextMessage ( messages ) ) ;
} ,
category : CommandCategories.messages ,
@@ -294,7 +300,7 @@ export const Commands = [
command : "html" ,
args : "<message>" ,
description : _td ( "Sends a message as html, without interpreting it as markdown" ) ,
runFn : function ( roomId , messages = "" ) {
runFn : function ( cli , roomId , messages = "" ) {
return successSync ( ContentHelpers . makeHtmlMessage ( messages , messages ) ) ;
} ,
category : CommandCategories.messages ,
@@ -303,10 +309,9 @@ export const Commands = [
command : "upgraderoom" ,
args : "<new_version>" ,
description : _td ( "Upgrades a room to a new version" ) ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , args ) {
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId , args ) {
if ( args ) {
const cli = MatrixClientPeg . get ( ) ;
const room = cli . getRoom ( roomId ) ;
if ( ! room ? . currentState . mayClientSendStateEvent ( "m.room.tombstone" , cli ) ) {
return reject (
@@ -339,7 +344,7 @@ export const Commands = [
args : "<YYYY-MM-DD>" ,
description : _td ( "Jump to the given date in the timeline" ) ,
isEnabled : ( ) = > SettingsStore . getValue ( "feature_jump_to_date" ) ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
if ( args ) {
return success (
( async ( ) : Promise < void > = > {
@@ -352,7 +357,6 @@ export const Commands = [
) ;
}
const cli = MatrixClientPeg . get ( ) ;
const { event_id : eventId , origin_server_ts : originServerTs } = await cli . timestampToEvent (
roomId ,
unixTimestamp ,
@@ -381,9 +385,9 @@ export const Commands = [
command : "nick" ,
args : "<display_name>" ,
description : _td ( "Changes your display nickname" ) ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
if ( args ) {
return success ( MatrixClientPeg . get ( ) . setDisplayName ( args ) ) ;
return success ( cli . setDisplayName ( args ) ) ;
}
return reject ( this . getUsage ( ) ) ;
} ,
@@ -395,16 +399,15 @@ export const Commands = [
aliases : [ "roomnick" ] ,
args : "<display_name>" ,
description : _td ( "Changes your display nickname in the current room only" ) ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , args ) {
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId , args ) {
if ( args ) {
const cli = MatrixClientPeg . get ( ) ;
const ev = cli . getRoom ( roomId ) ? . currentState . getStateEvents ( "m.room.member" , cli . getUserId ( ) ! ) ;
const ev = cli . getRoom ( roomId ) ? . currentState . getStateEvents ( "m.room.member" , cli . getSafeUserId ( ) ) ;
const content = {
. . . ( ev ? ev . getContent ( ) : { membership : "join" } ) ,
displayname : args ,
} ;
return success ( cli . sendStateEvent ( roomId , "m.room.member" , content , cli . getUserId ( ) ! ) ) ;
return success ( cli . sendStateEvent ( roomId , "m.room.member" , content , cli . getSafeUserId ( ) ) ) ;
}
return reject ( this . getUsage ( ) ) ;
} ,
@@ -415,17 +418,17 @@ export const Commands = [
command : "roomavatar" ,
args : "[<mxc_url>]" ,
description : _td ( "Changes the avatar of the current room" ) ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , args ) {
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId , args ) {
let promise = Promise . resolve ( args ? ? null ) ;
if ( ! args ) {
promise = singleMxcUpload ( ) ;
promise = singleMxcUpload ( cli ) ;
}
return success (
promise . then ( ( url ) = > {
if ( ! url ) return ;
return MatrixClientPeg . get ( ) . sendStateEvent ( roomId , "m.room.avatar" , { url } , "" ) ;
return cli . sendStateEvent ( roomId , "m.room.avatar" , { url } , "" ) ;
} ) ,
) ;
} ,
@@ -436,15 +439,14 @@ export const Commands = [
command : "myroomavatar" ,
args : "[<mxc_url>]" ,
description : _td ( "Changes your avatar in this current room only" ) ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , args ) {
const cli = MatrixClientPeg . get ( ) ;
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId , args ) {
const room = cli . getRoom ( roomId ) ;
const userId = cli . getUserId ( ) ! ;
const userId = cli . getSafeUserId ( ) ;
let promise = Promise . resolve ( args ? ? null ) ;
if ( ! args ) {
promise = singleMxcUpload ( ) ;
promise = singleMxcUpload ( cli ) ;
}
return success (
@@ -466,16 +468,16 @@ export const Commands = [
command : "myavatar" ,
args : "[<mxc_url>]" ,
description : _td ( "Changes your avatar in all rooms" ) ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
let promise = Promise . resolve ( args ? ? null ) ;
if ( ! args ) {
promise = singleMxcUpload ( ) ;
promise = singleMxcUpload ( cli ) ;
}
return success (
promise . then ( ( url ) = > {
if ( ! url ) return ;
return MatrixClientPeg . get ( ) . setAvatarUrl ( url ) ;
return cli . setAvatarUrl ( url ) ;
} ) ,
) ;
} ,
@@ -486,9 +488,8 @@ export const Commands = [
command : "topic" ,
args : "[<topic>]" ,
description : _td ( "Gets or sets the room topic" ) ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , args ) {
const cli = MatrixClientPeg . get ( ) ;
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId , args ) {
if ( args ) {
const html = htmlSerializeFromMdIfNeeded ( args , { forceHTML : false } ) ;
return success ( cli . setRoomTopic ( roomId , args , html ) ) ;
@@ -525,10 +526,10 @@ export const Commands = [
command : "roomname" ,
args : "<name>" ,
description : _td ( "Sets the room name" ) ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , args ) {
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId , args ) {
if ( args ) {
return success ( MatrixClientPeg . get ( ) . setRoomName ( roomId , args ) ) ;
return success ( cli . setRoomName ( roomId , args ) ) ;
}
return reject ( this . getUsage ( ) ) ;
} ,
@@ -540,8 +541,8 @@ export const Commands = [
args : "<user-id> [<reason>]" ,
description : _td ( "Invites user with given id to current room" ) ,
analyticsName : "Invite" ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) && shouldShowComponent ( UIComponent . InviteUsers ) ,
runFn : function ( roomId , args ) {
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) && shouldShowComponent ( UIComponent . InviteUsers ) ,
runFn : function ( cli , roomId , args ) {
if ( args ) {
const [ address , reason ] = args . split ( /\s+(.+)/ ) ;
if ( address ) {
@@ -551,10 +552,7 @@ export const Commands = [
// get a bit more complex here, but we try to show something
// meaningful.
let prom = Promise . resolve ( ) ;
if (
getAddressType ( address ) === AddressType . Email &&
! MatrixClientPeg . get ( ) . getIdentityServerUrl ( )
) {
if ( getAddressType ( address ) === AddressType . Email && ! cli . getIdentityServerUrl ( ) ) {
const defaultIdentityServerUrl = getDefaultIdentityServerUrl ( ) ;
if ( defaultIdentityServerUrl ) {
const { finished } = Modal . createDialog ( QuestionDialog , {
@@ -576,7 +574,7 @@ export const Commands = [
prom = finished . then ( ( [ useDefault ] ) = > {
if ( useDefault ) {
setToDefaultIdentityServer ( MatrixClientPeg . get ( ) ) ;
setToDefaultIdentityServer ( cli ) ;
return ;
}
throw new UserFriendlyError (
@@ -589,7 +587,7 @@ export const Commands = [
) ;
}
}
const inviter = new MultiInviter ( MatrixClientPeg . get ( ) , roomId ) ;
const inviter = new MultiInviter ( cli , roomId ) ;
return success (
prom
. then ( ( ) = > {
@@ -621,7 +619,7 @@ export const Commands = [
aliases : [ "j" , "goto" ] ,
args : "<room-address>" ,
description : _td ( "Joins room with given address" ) ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
if ( args ) {
// Note: we support 2 versions of this command. The first is
// the public-facing one for most users and the other is a
@@ -654,7 +652,7 @@ export const Commands = [
if ( params [ 0 ] [ 0 ] === "#" ) {
let roomAlias = params [ 0 ] ;
if ( ! roomAlias . includes ( ":" ) ) {
roomAlias += ":" + MatrixClientPeg . get ( ) . getDomain ( ) ;
roomAlias += ":" + cli . getDomain ( ) ;
}
dis . dispatch < ViewRoomPayload > ( {
@@ -733,10 +731,8 @@ export const Commands = [
args : "[<room-address>]" ,
description : _td ( "Leave room" ) ,
analyticsName : "Part" ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , args ) {
const cli = MatrixClientPeg . get ( ) ;
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId , args ) {
let targetRoomId : string | undefined ;
if ( args ) {
const matches = args . match ( /^(\S+)$/ ) ;
@@ -775,12 +771,12 @@ export const Commands = [
aliases : [ "kick" ] ,
args : "<user-id> [reason]" ,
description : _td ( "Removes user with given id from this room" ) ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , args ) {
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId , args ) {
if ( args ) {
const matches = args . match ( /^(\S+?)( +(.*))?$/ ) ;
if ( matches ) {
return success ( MatrixClientPeg . get ( ) . kick ( roomId , matches [ 1 ] , matches [ 3 ] ) ) ;
return success ( cli . kick ( roomId , matches [ 1 ] , matches [ 3 ] ) ) ;
}
}
return reject ( this . getUsage ( ) ) ;
@@ -792,12 +788,12 @@ export const Commands = [
command : "ban" ,
args : "<user-id> [reason]" ,
description : _td ( "Bans user with given id" ) ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , args ) {
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId , args ) {
if ( args ) {
const matches = args . match ( /^(\S+?)( +(.*))?$/ ) ;
if ( matches ) {
return success ( MatrixClientPeg . get ( ) . ban ( roomId , matches [ 1 ] , matches [ 3 ] ) ) ;
return success ( cli . ban ( roomId , matches [ 1 ] , matches [ 3 ] ) ) ;
}
}
return reject ( this . getUsage ( ) ) ;
@@ -809,13 +805,13 @@ export const Commands = [
command : "unban" ,
args : "<user-id>" ,
description : _td ( "Unbans user with given ID" ) ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , args ) {
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , 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 success ( cli . unban ( roomId , matches [ 1 ] ) ) ;
}
}
return reject ( this . getUsage ( ) ) ;
@@ -827,10 +823,8 @@ export const Commands = [
command : "ignore" ,
args : "<user-id>" ,
description : _td ( "Ignores a user, hiding their messages from you" ) ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
if ( args ) {
const cli = MatrixClientPeg . get ( ) ;
const matches = args . match ( /^(@[^:]+:\S+)$/ ) ;
if ( matches ) {
const userId = matches [ 1 ] ;
@@ -858,10 +852,8 @@ export const Commands = [
command : "unignore" ,
args : "<user-id>" ,
description : _td ( "Stops ignoring a user, showing their messages going forward" ) ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
if ( args ) {
const cli = MatrixClientPeg . get ( ) ;
const matches = args . match ( /(^@[^:]+:\S+$)/ ) ;
if ( matches ) {
const userId = matches [ 1 ] ;
@@ -890,17 +882,8 @@ export const Commands = [
command : "op" ,
args : "<user-id> [<power-level>]" ,
description : _td ( "Define the power level of a user" ) ,
isEnabled ( ) : boolean {
const cli = MatrixClientPeg . get ( ) ;
const roomId = SdkContextClass . instance . roomViewStore . getRoomId ( ) ;
if ( ! roomId ) return false ;
const room = cli . getRoom ( roomId ) ;
return (
! ! room ? . currentState . maySendStateEvent ( EventType . RoomPowerLevels , cli . getUserId ( ) ! ) &&
! isLocalRoom ( room )
) ;
} ,
runFn : function ( roomId , args ) {
isEnabled : canAffectPowerlevels ,
runFn : function ( cli , roomId , args ) {
if ( args ) {
const matches = args . match ( /^(\S+?)( +(-?\d+))?$/ ) ;
let powerLevel = 50 ; // default power level for op
@@ -910,7 +893,6 @@ export const Commands = [
powerLevel = parseInt ( matches [ 3 ] , 10 ) ;
}
if ( ! isNaN ( powerLevel ) ) {
const cli = MatrixClientPeg . get ( ) ;
const room = cli . getRoom ( roomId ) ;
if ( ! room ) {
return reject (
@@ -941,21 +923,11 @@ export const Commands = [
command : "deop" ,
args : "<user-id>" ,
description : _td ( "Deops user with given id" ) ,
isEnabled ( ) : boolean {
const cli = MatrixClientPeg . get ( ) ;
const roomId = SdkContextClass . instance . roomViewStore . getRoomId ( ) ;
if ( ! roomId ) return false ;
const room = cli . getRoom ( roomId ) ;
return (
! ! room ? . currentState . maySendStateEvent ( EventType . RoomPowerLevels , cli . getUserId ( ) ! ) &&
! isLocalRoom ( room )
) ;
} ,
runFn : function ( roomId , args ) {
isEnabled : canAffectPowerlevels ,
runFn : function ( cli , roomId , args ) {
if ( args ) {
const matches = args . match ( /^(\S+)$/ ) ;
if ( matches ) {
const cli = MatrixClientPeg . get ( ) ;
const room = cli . getRoom ( roomId ) ;
if ( ! room ) {
return reject (
@@ -981,7 +953,7 @@ export const Commands = [
new Command ( {
command : "devtools" ,
description : _td ( "Opens the Developer Tools dialog" ) ,
runFn : function ( roomId ) {
runFn : function ( cli , roomId ) {
Modal . createDialog ( DevtoolsDialog , { roomId } , "mx_DevtoolsDialog_wrapper" ) ;
return success ( ) ;
} ,
@@ -991,11 +963,11 @@ export const Commands = [
command : "addwidget" ,
args : "<url | embed code | Jitsi url>" ,
description : _td ( "Adds a custom widget by URL to the room" ) ,
isEnabled : ( ) = >
isEnabled : ( cli ) = >
SettingsStore . getValue ( UIFeature . Widgets ) &&
shouldShowComponent ( UIComponent . AddIntegrations ) &&
! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , widgetUrl ) {
! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId , widgetUrl ) {
if ( ! widgetUrl ) {
return reject ( new UserFriendlyError ( "Please supply a widget URL or embed code" ) ) ;
}
@@ -1005,12 +977,12 @@ export const Commands = [
const embed = new DOMParser ( ) . parseFromString ( widgetUrl , "text/html" ) . body ;
if ( embed ? . childNodes ? . length === 1 ) {
const iframe = embed . firstElementChild ;
if ( iframe . tagName . toLowerCase ( ) === "iframe" ) {
if ( iframe ? . tagName . toLowerCase ( ) === "iframe" ) {
logger . log ( "Pulling URL out of iframe (embed code)" ) ;
if ( ! iframe . hasAttribute ( "src" ) ) {
return reject ( new UserFriendlyError ( "iframe has no src attribute" ) ) ;
}
widgetUrl = iframe . getAttribute ( "src" ) ;
widgetUrl = iframe . getAttribute ( "src" ) ! ;
}
}
}
@@ -1018,8 +990,8 @@ export const Commands = [
if ( ! widgetUrl . startsWith ( "https://" ) && ! widgetUrl . startsWith ( "http://" ) ) {
return reject ( new UserFriendlyError ( "Please supply a https:// or http:// widget URL" ) ) ;
}
if ( WidgetUtils . canUserModifyWidgets ( MatrixClientPeg . get ( ) , roomId ) ) {
const userId = MatrixClientPeg . get ( ) . getUserId ( ) ;
if ( WidgetUtils . canUserModifyWidgets ( cli , roomId ) ) {
const userId = cli . getUserId ( ) ;
const nowMs = new Date ( ) . getTime ( ) ;
const widgetId = encodeURIComponent ( ` ${ roomId } _ ${ userId } _ ${ nowMs } ` ) ;
let type = WidgetType . CUSTOM ;
@@ -1036,9 +1008,7 @@ export const Commands = [
widgetUrl = WidgetUtils . getLocalJitsiWrapperUrl ( ) ;
}
return success (
WidgetUtils . setRoomWidget ( MatrixClientPeg . get ( ) , roomId , widgetId , type , widgetUrl , name , data ) ,
) ;
return success ( WidgetUtils . setRoomWidget ( cli , roomId , widgetId , type , widgetUrl , name , data ) ) ;
} else {
return reject ( new UserFriendlyError ( "You cannot modify widgets in this room." ) ) ;
}
@@ -1050,12 +1020,10 @@ export const Commands = [
command : "verify" ,
args : "<user-id> <device-id> <device-signing-key>" ,
description : _td ( "Verifies a user, session, and pubkey tuple" ) ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
if ( args ) {
const matches = args . match ( /^(\S+) +(\S+) +(\S+)$/ ) ;
if ( matches ) {
const cli = MatrixClientPeg . get ( ) ;
const userId = matches [ 1 ] ;
const deviceId = matches [ 2 ] ;
const fingerprint = matches [ 3 ] ;
@@ -1130,10 +1098,10 @@ export const Commands = [
new Command ( {
command : "discardsession" ,
description : _td ( "Forces the current outbound group session in an encrypted room to be discarded" ) ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId ) {
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId ) {
try {
MatrixClientPeg . get ( ) . forceDiscardSession ( roomId ) ;
cli . forceDiscardSession ( roomId ) ;
} catch ( e ) {
return reject ( e . message ) ;
}
@@ -1145,19 +1113,19 @@ export const Commands = [
new Command ( {
command : "remakeolm" ,
description : _td ( "Developer command: Discards the current outbound group session and sets up new Olm sessions" ) ,
isEnabled : ( ) = > {
return SettingsStore . getValue ( "developerMode" ) && ! isCurrentLocalRoom ( ) ;
isEnabled : ( cli ) = > {
return SettingsStore . getValue ( "developerMode" ) && ! isCurrentLocalRoom ( cli ) ;
} ,
runFn : ( roomId ) = > {
runFn : ( cli , roomId ) = > {
try {
const room = MatrixClientPeg . get ( ) . getRoom ( roomId ) ;
const room = cli . getRoom ( roomId ) ;
MatrixClientPeg . get ( ) . forceDiscardSession ( roomId ) ;
cli . forceDiscardSession ( roomId ) ;
return success (
room ? . getEncryptionTargetMembers ( ) . then ( ( members ) = > {
// noinspection JSIgnoredPromiseFromCall
MatrixClientPeg . get ( ) . crypto ? . ensureOlmSessionsForUsers (
cli . crypto ? . ensureOlmSessionsForUsers (
members . map ( ( m ) = > m . userId ) ,
true ,
) ;
@@ -1174,7 +1142,7 @@ export const Commands = [
command : "rainbow" ,
description : _td ( "Sends the given message coloured as a rainbow" ) ,
args : "<message>" ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
if ( ! args ) return reject ( this . getUsage ( ) ) ;
return successSync ( ContentHelpers . makeHtmlMessage ( args , textToHtmlRainbow ( args ) ) ) ;
} ,
@@ -1184,7 +1152,7 @@ export const Commands = [
command : "rainbowme" ,
description : _td ( "Sends the given emote coloured as a rainbow" ) ,
args : "<message>" ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
if ( ! args ) return reject ( this . getUsage ( ) ) ;
return successSync ( ContentHelpers . makeHtmlEmote ( args , textToHtmlRainbow ( args ) ) ) ;
} ,
@@ -1203,13 +1171,13 @@ export const Commands = [
command : "whois" ,
description : _td ( "Displays information about a user" ) ,
args : "<user-id>" ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , userId ) {
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId , userId ) {
if ( ! userId || ! userId . startsWith ( "@" ) || ! userId . includes ( ":" ) ) {
return reject ( this . getUsage ( ) ) ;
}
const member = MatrixClientPeg . get ( ) . getRoom ( roomId ) ? . getMember ( userId ) ;
const member = cli . getRoom ( roomId ) ? . getMember ( userId ) ;
dis . dispatch < ViewUserPayload > ( {
action : Action.ViewUser ,
// XXX: We should be using a real member object and not assuming what the receiver wants.
@@ -1225,7 +1193,7 @@ export const Commands = [
description : _td ( "Send a bug report with logs" ) ,
isEnabled : ( ) = > ! ! SdkConfig . get ( ) . bug_report_endpoint_url ,
args : "<description>" ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
return success (
Modal . createDialog ( BugReportDialog , {
initialText : args ,
@@ -1238,10 +1206,10 @@ export const Commands = [
command : "tovirtual" ,
description : _td ( "Switches to this room's virtual room, if it has one" ) ,
category : CommandCategories.advanced ,
isEnabled ( ) : boolean {
return ! ! LegacyCallHandler . instance . getSupportsVirtualRooms ( ) && ! isCurrentLocalRoom ( ) ;
isEnabled ( cli ) : boolean {
return ! ! LegacyCallHandler . instance . getSupportsVirtualRooms ( ) && ! isCurrentLocalRoom ( cli ) ;
} ,
runFn : ( roomId ) = > {
runFn : ( cli , roomId ) = > {
return success (
( async ( ) : Promise < void > = > {
const room = await VoipUserMapper . sharedInstance ( ) . getVirtualRoomForRoom ( roomId ) ;
@@ -1260,7 +1228,7 @@ export const Commands = [
command : "query" ,
description : _td ( "Opens chat with the given user" ) ,
args : "<user-id>" ,
runFn : function ( roomId , userId ) {
runFn : function ( cli , roomId , userId ) {
// easter-egg for now: look up phone numbers through the thirdparty API
// (very dumb phone number detection...)
const isPhoneNumber = userId && /^\+?[0123456789]+$/ . test ( userId ) ;
@@ -1278,7 +1246,7 @@ export const Commands = [
userId = results [ 0 ] . userid ;
}
const roomId = await ensureDMExists ( MatrixClientPeg . get ( ) , userId ) ;
const roomId = await ensureDMExists ( cli , userId ) ;
if ( ! roomId ) throw new Error ( "Failed to ensure DM exists" ) ;
dis . dispatch < ViewRoomPayload > ( {
@@ -1296,7 +1264,7 @@ export const Commands = [
command : "msg" ,
description : _td ( "Sends a message to the given user" ) ,
args : "<user-id> [<message>]" ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
if ( args ) {
// matches the first whitespace delimited group and then the rest of the string
const matches = args . match ( / ^ ( \ S + ? ) ( ? : + ( . * ) ) ? $ / s ) ;
@@ -1305,7 +1273,6 @@ export const Commands = [
if ( userId && userId . startsWith ( "@" ) && userId . includes ( ":" ) ) {
return success (
( async ( ) : Promise < void > = > {
const cli = MatrixClientPeg . get ( ) ;
const roomId = await ensureDMExists ( cli , userId ) ;
if ( ! roomId ) throw new Error ( "Failed to ensure DM exists" ) ;
@@ -1332,8 +1299,8 @@ export const Commands = [
command : "holdcall" ,
description : _td ( "Places the call in the current room on hold" ) ,
category : CommandCategories.other ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , args ) {
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId , args ) {
const call = LegacyCallHandler . instance . getCallForRoom ( roomId ) ;
if ( ! call ) {
return reject ( new UserFriendlyError ( "No active call in this room" ) ) ;
@@ -1347,8 +1314,8 @@ export const Commands = [
command : "unholdcall" ,
description : _td ( "Takes the call in the current room off hold" ) ,
category : CommandCategories.other ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , args ) {
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId , args ) {
const call = LegacyCallHandler . instance . getCallForRoom ( roomId ) ;
if ( ! call ) {
return reject ( new UserFriendlyError ( "No active call in this room" ) ) ;
@@ -1362,9 +1329,9 @@ export const Commands = [
command : "converttodm" ,
description : _td ( "Converts the room to a DM" ) ,
category : CommandCategories.other ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , args ) {
const room = MatrixClientPeg . get ( ) . getRoom ( roomId ) ;
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId , args ) {
const room = cli . getRoom ( roomId ) ;
if ( ! room ) return reject ( new UserFriendlyError ( "Could not find room" ) ) ;
return success ( guessAndSetDMRoom ( room , true ) ) ;
} ,
@@ -1374,9 +1341,9 @@ export const Commands = [
command : "converttoroom" ,
description : _td ( "Converts the DM to a room" ) ,
category : CommandCategories.other ,
isEnabled : ( ) = > ! isCurrentLocalRoom ( ) ,
runFn : function ( roomId , args ) {
const room = MatrixClientPeg . get ( ) . getRoom ( roomId ) ;
isEnabled : ( cli ) = > ! isCurrentLocalRoom ( cli ) ,
runFn : function ( cli , roomId , args ) {
const room = cli . getRoom ( roomId ) ;
if ( ! room ) return reject ( new UserFriendlyError ( "Could not find room" ) ) ;
return success ( guessAndSetDMRoom ( room , false ) ) ;
} ,
@@ -1398,7 +1365,7 @@ export const Commands = [
command : effect.command ,
description : effect.description ( ) ,
args : "<message>" ,
runFn : function ( roomId , args ) {
runFn : function ( cli , roomId , args ) {
let content : IContent ;
if ( ! args ) {
content = ContentHelpers . makeEmoteMessage ( effect . fallbackMessage ( ) ) ;