@@ -24,7 +24,7 @@ import { IContentLoadedActionRequest } from "./interfaces/ContentLoadedAction";
2424import { WidgetApiFromWidgetAction , WidgetApiToWidgetAction } from "./interfaces/WidgetApiAction" ;
2525import { IWidgetApiErrorResponseData } from "./interfaces/IWidgetApiErrorResponse" ;
2626import { Capability } from "./interfaces/Capabilities" ;
27- import { WidgetDriver } from "./driver/WidgetDriver" ;
27+ import { ISendEventDetails , WidgetDriver } from "./driver/WidgetDriver" ;
2828import { ICapabilitiesActionResponseData } from "./interfaces/CapabilitiesAction" ;
2929import {
3030 ISupportedVersionsActionRequest ,
@@ -40,6 +40,13 @@ import {
4040 IModalWidgetOpenRequestDataButton ,
4141 IModalWidgetReturnData ,
4242} from "./interfaces/ModalWidgetActions" ;
43+ import {
44+ ISendEventFromWidgetActionRequest ,
45+ ISendEventFromWidgetResponseData ,
46+ ISendEventToWidgetRequestData ,
47+ } from "./interfaces/SendEventAction" ;
48+ import { EventDirection , WidgetEventCapability } from "./models/WidgetEventCapability" ;
49+ import { IRoomEvent } from "./interfaces/IRoomEvent" ;
4350
4451/**
4552 * API handler for the client side of widgets. This raises events
@@ -70,6 +77,7 @@ export class ClientWidgetApi extends EventEmitter {
7077
7178 private capabilitiesFinished = false ;
7279 private allowedCapabilities = new Set < Capability > ( ) ;
80+ private allowedEvents : WidgetEventCapability [ ] = [ ] ;
7381 private isStopped = false ;
7482
7583 /**
@@ -115,6 +123,26 @@ export class ClientWidgetApi extends EventEmitter {
115123 return this . allowedCapabilities . has ( capability ) ;
116124 }
117125
126+ public canSendRoomEvent ( eventType : string , msgtype : string = null ) : boolean {
127+ return this . allowedEvents . some ( e =>
128+ e . matchesAsRoomEvent ( eventType , msgtype ) && e . direction === EventDirection . Send ) ;
129+ }
130+
131+ public canSendStateEvent ( eventType : string , stateKey : string ) : boolean {
132+ return this . allowedEvents . some ( e =>
133+ e . matchesAsStateEvent ( eventType , stateKey ) && e . direction === EventDirection . Send ) ;
134+ }
135+
136+ public canReceiveRoomEvent ( eventType : string , msgtype : string = null ) : boolean {
137+ return this . allowedEvents . some ( e =>
138+ e . matchesAsRoomEvent ( eventType , msgtype ) && e . direction === EventDirection . Receive ) ;
139+ }
140+
141+ public canReceiveStateEvent ( eventType : string , stateKey : string ) : boolean {
142+ return this . allowedEvents . some ( e =>
143+ e . matchesAsStateEvent ( eventType , stateKey ) && e . direction === EventDirection . Receive ) ;
144+ }
145+
118146 public stop ( ) {
119147 this . isStopped = true ;
120148 this . transport . stop ( ) ;
@@ -140,7 +168,9 @@ export class ClientWidgetApi extends EventEmitter {
140168 ) . then ( caps => {
141169 return this . driver . validateCapabilities ( new Set ( caps . capabilities ) ) ;
142170 } ) . then ( allowedCaps => {
171+ console . log ( `Widget ${ this . widget . id } is allowed capabilities:` , Array . from ( allowedCaps ) ) ;
143172 this . allowedCapabilities = allowedCaps ;
173+ this . allowedEvents = WidgetEventCapability . findEventCapabilities ( allowedCaps ) ;
144174 this . capabilitiesFinished = true ;
145175 this . emit ( "ready" ) ;
146176 } ) ;
@@ -165,6 +195,63 @@ export class ClientWidgetApi extends EventEmitter {
165195 } ) ;
166196 }
167197
198+ private async handleSendEvent ( request : ISendEventFromWidgetActionRequest ) {
199+ if ( ! request . data . type ) {
200+ return this . transport . reply < IWidgetApiErrorResponseData > ( request , {
201+ error : { message : "Invalid request - missing event type" } ,
202+ } ) ;
203+ }
204+
205+ const isState = request . data . state_key !== null && request . data . state_key !== undefined ;
206+ let sentEvent : ISendEventDetails ;
207+ if ( isState ) {
208+ if ( ! this . canSendStateEvent ( request . data . type , request . data . state_key ) ) {
209+ return this . transport . reply < IWidgetApiErrorResponseData > ( request , {
210+ error : { message : "Cannot send state events of this type" } ,
211+ } ) ;
212+ }
213+
214+ try {
215+ sentEvent = await this . driver . sendEvent (
216+ request . data . type ,
217+ request . data . content || { } ,
218+ request . data . state_key ,
219+ ) ;
220+ } catch ( e ) {
221+ console . error ( "error sending event: " , e ) ;
222+ return this . transport . reply < IWidgetApiErrorResponseData > ( request , {
223+ error : { message : "Error sending event" } ,
224+ } ) ;
225+ }
226+ } else {
227+ const content = request . data . content || { } ;
228+ const msgtype = content [ 'msgtype' ] ;
229+ if ( ! this . canSendRoomEvent ( request . data . type , msgtype ) ) {
230+ return this . transport . reply < IWidgetApiErrorResponseData > ( request , {
231+ error : { message : "Cannot send room events of this type" } ,
232+ } ) ;
233+ }
234+
235+ try {
236+ sentEvent = await this . driver . sendEvent (
237+ request . data . type ,
238+ content ,
239+ null , // not sending a state event
240+ ) ;
241+ } catch ( e ) {
242+ console . error ( "error sending event: " , e ) ;
243+ return this . transport . reply < IWidgetApiErrorResponseData > ( request , {
244+ error : { message : "Error sending event" } ,
245+ } ) ;
246+ }
247+ }
248+
249+ return this . transport . reply < ISendEventFromWidgetResponseData > ( request , {
250+ room_id : sentEvent . roomId ,
251+ event_id : sentEvent . eventId ,
252+ } ) ;
253+ }
254+
168255 private handleMessage ( ev : CustomEvent < IWidgetApiRequest > ) {
169256 if ( this . isStopped ) return ;
170257 const actionEv = new CustomEvent ( `action:${ ev . detail . action } ` , {
@@ -178,6 +265,8 @@ export class ClientWidgetApi extends EventEmitter {
178265 return this . handleContentLoadedAction ( < IContentLoadedActionRequest > ev . detail ) ;
179266 case WidgetApiFromWidgetAction . SupportedApiVersions :
180267 return this . replyVersions ( < ISupportedVersionsActionRequest > ev . detail ) ;
268+ case WidgetApiFromWidgetAction . SendEvent :
269+ return this . handleSendEvent ( < ISendEventFromWidgetActionRequest > ev . detail ) ;
181270 default :
182271 return this . transport . reply ( ev . detail , < IWidgetApiErrorResponseData > {
183272 error : {
@@ -223,4 +312,31 @@ export class ClientWidgetApi extends EventEmitter {
223312 WidgetApiToWidgetAction . CloseModalWidget , data ,
224313 ) . then ( ) ;
225314 }
315+
316+ /**
317+ * Feeds an event to the widget. If the widget is not able to accept the event due to
318+ * permissions, this will no-op and return calmly. If the widget failed to handle the
319+ * event, this will raise an error.
320+ * @param {IRoomEvent } rawEvent The event to (try to) send to the widget.
321+ * @returns {Promise<void> } Resolves when complete, rejects if there was an error sending.
322+ */
323+ public feedEvent ( rawEvent : IRoomEvent ) : Promise < void > {
324+ if ( rawEvent . state_key !== undefined && rawEvent . state_key !== null ) {
325+ // state event
326+ if ( ! this . canReceiveStateEvent ( rawEvent . type , rawEvent . state_key ) ) {
327+ return Promise . resolve ( ) ; // no-op
328+ }
329+ } else {
330+ // message event
331+ if ( ! this . canReceiveRoomEvent ( rawEvent . type , ( rawEvent . content || { } ) [ 'msgtype' ] ) ) {
332+ return Promise . resolve ( ) ; // no-op
333+ }
334+ }
335+
336+ // Feed the event into the widget
337+ return this . transport . send < ISendEventToWidgetRequestData > (
338+ WidgetApiToWidgetAction . SendEvent ,
339+ rawEvent as ISendEventToWidgetRequestData , // it's compatible, but missing the index signature
340+ ) . then ( ) ;
341+ }
226342}
0 commit comments