@@ -39,6 +39,7 @@ import { unescape } from "@std/html/entities";
3939import { generate as uuidv7 } from "@std/uuid/unstable-v7" ;
4040import { FilterXSS , getDefaultWhiteList } from "xss" ;
4141import type {
42+ AuthorizedMessage ,
4243 Message ,
4344 MessageClass ,
4445 MessageShareOptions ,
@@ -91,88 +92,20 @@ export class MessageImpl<T extends MessageClass, TContextData>
9192 this . updated = message . updated ;
9293 }
9394
94- async delete ( ) : Promise < void > {
95- const parsed = this . session . context . parseUri ( this . id ) ;
96- if (
97- parsed ?. type !== "object" ||
98- ! messageClasses . some ( ( cls ) => parsed . class === cls )
99- ) {
100- return ;
101- }
102- const { id } = parsed . values ;
103- const kv = this . session . bot . kv ;
104- const listKey : KvKey = this . session . bot . kvPrefixes . messages ;
105- const lockKey : KvKey = [ ...listKey , "lock" ] ;
106- const lockId = `${ id } :delete` ;
107- do {
108- await kv . set ( lockKey , lockId ) ;
109- const set = new Set ( await kv . get < string [ ] > ( listKey ) ?? [ ] ) ;
110- set . delete ( id ) ;
111- const list = [ ...set ] ;
112- list . sort ( ( a , b ) => a < b ? - 1 : a > b ? 1 : 0 ) ;
113- await kv . set ( listKey , list ) ;
114- } while ( await kv . get ( lockKey ) !== lockId ) ;
115- const messageKey : KvKey = [ ...listKey , id ] ;
116- const createJson = await kv . get ( messageKey ) ;
117- if ( createJson == null ) return ;
118- await kv . delete ( messageKey ) ;
119- const create = await Create . fromJsonLd ( createJson , this . session . context ) ;
120- const message = await create . getObject ( this . session . context ) ;
121- if ( message == null ) return ;
122- const mentionedActorIds : Set < string > = new Set ( ) ;
123- for await ( const tag of message . getTags ( this . session . context ) ) {
124- if ( tag instanceof Mention && tag . href != null ) {
125- mentionedActorIds . add ( tag . href . href ) ;
126- }
127- }
128- const promises : Promise < Object | null > [ ] = [ ] ;
129- const documentLoader = await this . session . context . getDocumentLoader (
130- this . session . bot ,
131- ) ;
132- for ( const uri of mentionedActorIds ) {
133- promises . push ( this . session . context . lookupObject ( uri , { documentLoader } ) ) ;
134- }
135- const mentionedActors = ( await Promise . all ( promises ) ) . filter ( isActor ) ;
136- const activity = new Delete ( {
137- id : new URL ( "#delete" , this . id ) ,
138- actor : this . session . context . getActorUri ( this . session . bot . identifier ) ,
139- tos : create . toIds ,
140- ccs : create . ccIds ,
141- object : new Tombstone ( {
142- id : this . id ,
143- } ) ,
144- } ) ;
145- const excludeBaseUris = [ new URL ( this . session . context . origin ) ] ;
146- await this . session . context . sendActivity (
147- this . session . bot ,
148- "followers" ,
149- activity ,
150- { preferSharedInbox : true , excludeBaseUris } ,
151- ) ;
152- for ( const actor of mentionedActors ) {
153- await this . session . context . sendActivity (
154- this . session . bot ,
155- actor ,
156- activity ,
157- { preferSharedInbox : true , excludeBaseUris } ,
158- ) ;
159- }
160- }
161-
16295 reply (
16396 text : Text < "block" , TContextData > ,
16497 options ?: SessionPublishOptions ,
165- ) : Promise < Message < Note , TContextData > > ;
98+ ) : Promise < AuthorizedMessage < Note , TContextData > > ;
16699 reply < T extends MessageClass > (
167100 text : Text < "block" , TContextData > ,
168101 options ?: SessionPublishOptionsWithClass < T > | undefined ,
169- ) : Promise < Message < T , TContextData > > ;
102+ ) : Promise < AuthorizedMessage < T , TContextData > > ;
170103 reply (
171104 text : Text < "block" , TContextData > ,
172105 options ?:
173106 | SessionPublishOptions
174107 | SessionPublishOptionsWithClass < MessageClass > ,
175- ) : Promise < Message < MessageClass , TContextData > > {
108+ ) : Promise < AuthorizedMessage < MessageClass , TContextData > > {
176109 return this . session . publish ( text , {
177110 visibility : this . visibility === "unknown" ? "direct" : this . visibility ,
178111 ...options ,
@@ -274,6 +207,78 @@ export class MessageImpl<T extends MessageClass, TContextData>
274207 }
275208}
276209
210+ export class AuthorizedMessageImpl < T extends MessageClass , TContextData >
211+ extends MessageImpl < T , TContextData >
212+ implements AuthorizedMessage < T , TContextData > {
213+ async delete ( ) : Promise < void > {
214+ const parsed = this . session . context . parseUri ( this . id ) ;
215+ if (
216+ parsed ?. type !== "object" ||
217+ ! messageClasses . some ( ( cls ) => parsed . class === cls )
218+ ) {
219+ return ;
220+ }
221+ const { id } = parsed . values ;
222+ const kv = this . session . bot . kv ;
223+ const listKey : KvKey = this . session . bot . kvPrefixes . messages ;
224+ const lockKey : KvKey = [ ...listKey , "lock" ] ;
225+ const lockId = `${ id } :delete` ;
226+ do {
227+ await kv . set ( lockKey , lockId ) ;
228+ const set = new Set ( await kv . get < string [ ] > ( listKey ) ?? [ ] ) ;
229+ set . delete ( id ) ;
230+ const list = [ ...set ] ;
231+ list . sort ( ( a , b ) => a < b ? - 1 : a > b ? 1 : 0 ) ;
232+ await kv . set ( listKey , list ) ;
233+ } while ( await kv . get ( lockKey ) !== lockId ) ;
234+ const messageKey : KvKey = [ ...listKey , id ] ;
235+ const createJson = await kv . get ( messageKey ) ;
236+ if ( createJson == null ) return ;
237+ await kv . delete ( messageKey ) ;
238+ const create = await Create . fromJsonLd ( createJson , this . session . context ) ;
239+ const message = await create . getObject ( this . session . context ) ;
240+ if ( message == null ) return ;
241+ const mentionedActorIds : Set < string > = new Set ( ) ;
242+ for await ( const tag of message . getTags ( this . session . context ) ) {
243+ if ( tag instanceof Mention && tag . href != null ) {
244+ mentionedActorIds . add ( tag . href . href ) ;
245+ }
246+ }
247+ const promises : Promise < Object | null > [ ] = [ ] ;
248+ const documentLoader = await this . session . context . getDocumentLoader (
249+ this . session . bot ,
250+ ) ;
251+ for ( const uri of mentionedActorIds ) {
252+ promises . push ( this . session . context . lookupObject ( uri , { documentLoader } ) ) ;
253+ }
254+ const mentionedActors = ( await Promise . all ( promises ) ) . filter ( isActor ) ;
255+ const activity = new Delete ( {
256+ id : new URL ( "#delete" , this . id ) ,
257+ actor : this . session . context . getActorUri ( this . session . bot . identifier ) ,
258+ tos : create . toIds ,
259+ ccs : create . ccIds ,
260+ object : new Tombstone ( {
261+ id : this . id ,
262+ } ) ,
263+ } ) ;
264+ const excludeBaseUris = [ new URL ( this . session . context . origin ) ] ;
265+ await this . session . context . sendActivity (
266+ this . session . bot ,
267+ "followers" ,
268+ activity ,
269+ { preferSharedInbox : true , excludeBaseUris } ,
270+ ) ;
271+ for ( const actor of mentionedActors ) {
272+ await this . session . context . sendActivity (
273+ this . session . bot ,
274+ actor ,
275+ activity ,
276+ { preferSharedInbox : true , excludeBaseUris } ,
277+ ) ;
278+ }
279+ }
280+ }
281+
277282const allowList = getDefaultWhiteList ( ) ;
278283const htmlXss = new FilterXSS ( {
279284 allowList : {
@@ -290,6 +295,19 @@ export async function createMessage<T extends MessageClass, TContextData>(
290295 raw : T ,
291296 session : SessionImpl < TContextData > ,
292297 replyTarget ?: Message < MessageClass , TContextData > ,
298+ authorized ?: true ,
299+ ) : Promise < AuthorizedMessage < T , TContextData > > ;
300+ export async function createMessage < T extends MessageClass , TContextData > (
301+ raw : T ,
302+ session : SessionImpl < TContextData > ,
303+ replyTarget ?: Message < MessageClass , TContextData > ,
304+ authorized ?: boolean ,
305+ ) : Promise < Message < T , TContextData > > ;
306+ export async function createMessage < T extends MessageClass , TContextData > (
307+ raw : T ,
308+ session : SessionImpl < TContextData > ,
309+ replyTarget ?: Message < MessageClass , TContextData > ,
310+ authorized : boolean = false ,
293311) : Promise < Message < T , TContextData > > {
294312 if ( raw . id == null ) throw new TypeError ( "The raw.id is required." ) ;
295313 else if ( raw . content == null ) {
@@ -352,7 +370,7 @@ export async function createMessage<T extends MessageClass, TContextData>(
352370 replyTarget = await createMessage ( rt , session ) ;
353371 }
354372 }
355- return new MessageImpl ( session , {
373+ return new ( authorized ? AuthorizedMessageImpl : MessageImpl ) ( session , {
356374 raw,
357375 id : raw . id ,
358376 actor,
0 commit comments