Add support for sending/receiving events from widgets
Part of MSC2762: https://github.com/matrix-org/matrix-doc/pull/2762 Requires: https://github.com/matrix-org/matrix-widget-api/pull/9 This is the bare minimum required to send an event to a widget and receive events from widgets. Like the view_room action, this is controlled by a well-known permission key. **Danger**: This allows widgets to potentially modify room state. Use the permissions with care.
This commit is contained in:
parent
e15041bd53
commit
f5cd079a16
2 changed files with 57 additions and 1 deletions
|
@ -55,6 +55,8 @@ import ThemeWatcher from "../../settings/watchers/ThemeWatcher";
|
||||||
import {getCustomTheme} from "../../theme";
|
import {getCustomTheme} from "../../theme";
|
||||||
import CountlyAnalytics from "../../CountlyAnalytics";
|
import CountlyAnalytics from "../../CountlyAnalytics";
|
||||||
import { ElementWidgetCapabilities } from "./ElementWidgetCapabilities";
|
import { ElementWidgetCapabilities } from "./ElementWidgetCapabilities";
|
||||||
|
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
import ActiveRoomObserver from "../../ActiveRoomObserver";
|
||||||
|
|
||||||
// TODO: Destroy all of this code
|
// TODO: Destroy all of this code
|
||||||
|
|
||||||
|
@ -329,6 +331,10 @@ export class StopGapWidget extends EventEmitter {
|
||||||
this.messaging.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{});
|
this.messaging.transport.reply(ev.detail, <IWidgetApiRequestEmptyData>{});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Attach listeners for feeding events - the underlying widget classes handle permissions for us
|
||||||
|
MatrixClientPeg.get().on('event', this.onEvent);
|
||||||
|
MatrixClientPeg.get().on('Event.decrypted', this.onEventDecrypted);
|
||||||
|
|
||||||
if (WidgetType.JITSI.matches(this.mockWidget.type)) {
|
if (WidgetType.JITSI.matches(this.mockWidget.type)) {
|
||||||
this.messaging.on("action:set_always_on_screen",
|
this.messaging.on("action:set_always_on_screen",
|
||||||
(ev: CustomEvent<IStickyActionRequest>) => {
|
(ev: CustomEvent<IStickyActionRequest>) => {
|
||||||
|
@ -422,5 +428,31 @@ export class StopGapWidget extends EventEmitter {
|
||||||
if (!this.started) return;
|
if (!this.started) return;
|
||||||
WidgetMessagingStore.instance.stopMessaging(this.mockWidget);
|
WidgetMessagingStore.instance.stopMessaging(this.mockWidget);
|
||||||
ActiveWidgetStore.delRoomId(this.mockWidget.id);
|
ActiveWidgetStore.delRoomId(this.mockWidget.id);
|
||||||
|
|
||||||
|
if (MatrixClientPeg.get()) {
|
||||||
|
MatrixClientPeg.get().off('event', this.onEvent);
|
||||||
|
MatrixClientPeg.get().off('Event.decrypted', this.onEventDecrypted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onEvent = (ev: MatrixEvent) => {
|
||||||
|
if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return;
|
||||||
|
if (ev.getRoomId() !== ActiveRoomObserver.activeRoomId) return;
|
||||||
|
this.feedEvent(ev);
|
||||||
|
};
|
||||||
|
|
||||||
|
private onEventDecrypted = (ev: MatrixEvent) => {
|
||||||
|
if (ev.isDecryptionFailure()) return;
|
||||||
|
if (ev.getRoomId() !== ActiveRoomObserver.activeRoomId) return;
|
||||||
|
this.feedEvent(ev);
|
||||||
|
};
|
||||||
|
|
||||||
|
private feedEvent(ev: MatrixEvent) {
|
||||||
|
if (!this.messaging) return;
|
||||||
|
|
||||||
|
const raw = ev.event;
|
||||||
|
this.messaging.feedEvent(raw).catch(e => {
|
||||||
|
console.error("Error sending event to widget: ", e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,12 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Capability, WidgetDriver, WidgetType } from "matrix-widget-api";
|
import { Capability, ISendEventDetails, WidgetDriver, WidgetEventCapability, WidgetType } from "matrix-widget-api";
|
||||||
import { iterableUnion } from "../../utils/iterables";
|
import { iterableUnion } from "../../utils/iterables";
|
||||||
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
import { MatrixClientPeg } from "../../MatrixClientPeg";
|
||||||
import { arrayFastClone } from "../../utils/arrays";
|
import { arrayFastClone } from "../../utils/arrays";
|
||||||
import { ElementWidgetCapabilities } from "./ElementWidgetCapabilities";
|
import { ElementWidgetCapabilities } from "./ElementWidgetCapabilities";
|
||||||
|
import ActiveRoomObserver from "../../ActiveRoomObserver";
|
||||||
|
|
||||||
// TODO: Purge this from the universe
|
// TODO: Purge this from the universe
|
||||||
|
|
||||||
|
@ -47,7 +48,30 @@ export class StopGapWidgetDriver extends WidgetDriver {
|
||||||
allowedCaps.push(ElementWidgetCapabilities.CanChangeViewedRoom);
|
allowedCaps.push(ElementWidgetCapabilities.CanChangeViewedRoom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (Array.isArray(wkPerms["event_actions"])) {
|
||||||
|
if (wkPerms["event_actions"].includes(this.forType)) {
|
||||||
|
allowedCaps.push(...WidgetEventCapability.findEventCapabilities(requested).map(c => c.raw));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return new Set(iterableUnion(requested, allowedCaps));
|
return new Set(iterableUnion(requested, allowedCaps));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async sendEvent(eventType: string, content: any, stateKey: string = null): Promise<ISendEventDetails> {
|
||||||
|
const client = MatrixClientPeg.get();
|
||||||
|
const roomId = ActiveRoomObserver.activeRoomId;
|
||||||
|
|
||||||
|
if (!client || !roomId) throw new Error("Not in a room or not attached to a client");
|
||||||
|
|
||||||
|
let r: {event_id: string} = null;
|
||||||
|
if (stateKey !== null) {
|
||||||
|
// state event
|
||||||
|
r = await client.sendStateEvent(roomId, eventType, content, stateKey);
|
||||||
|
} else {
|
||||||
|
// message event
|
||||||
|
r = await client.sendEvent(roomId, eventType, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {roomId, eventId: r.event_id};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue