1515// along with this program. If not, see <https://www.gnu.org/licenses/>.
1616import {
1717 type Actor ,
18+ Article ,
19+ ChatMessage ,
20+ Create ,
21+ Delete ,
1822 Document ,
1923 Hashtag ,
2024 isActor ,
25+ type KvKey ,
2126 LanguageString ,
2227 Mention ,
23- type Note ,
28+ Note ,
29+ type Object ,
2430 PUBLIC_COLLECTION ,
31+ Question ,
32+ Tombstone ,
2533} from "@fedify/fedify" ;
2634import type { LanguageTag } from "@phensley/language-tag" ;
2735import { unescape } from "@std/html/entities" ;
@@ -34,6 +42,8 @@ import type {
3442} from "./session.ts" ;
3543import type { Text } from "./text.ts" ;
3644
45+ const messageClasses = [ Article , ChatMessage , Note , Question ] ;
46+
3747export class MessageImpl < T extends MessageClass , TContextData >
3848 implements Message < T , TContextData > {
3949 readonly session : SessionImpl < TContextData > ;
@@ -50,7 +60,7 @@ export class MessageImpl<T extends MessageClass, TContextData>
5060
5161 constructor (
5262 session : SessionImpl < TContextData > ,
53- message : Omit < Message < T , TContextData > , "reply" > ,
63+ message : Omit < Message < T , TContextData > , "delete" | " reply"> ,
5464 ) {
5565 this . session = session ;
5666 this . raw = message . raw ;
@@ -65,6 +75,74 @@ export class MessageImpl<T extends MessageClass, TContextData>
6575 this . attachments = message . attachments ;
6676 }
6777
78+ async delete ( ) : Promise < void > {
79+ const parsed = this . session . context . parseUri ( this . id ) ;
80+ if (
81+ parsed ?. type !== "object" ||
82+ ! messageClasses . some ( ( cls ) => parsed . class === cls )
83+ ) {
84+ return ;
85+ }
86+ const { id } = parsed . values ;
87+ const kv = this . session . bot . kv ;
88+ const listKey : KvKey = this . session . bot . kvPrefixes . messages ;
89+ const lockKey : KvKey = [ ...listKey , "lock" ] ;
90+ const lockId = `${ id } :delete` ;
91+ do {
92+ await kv . set ( lockKey , lockId ) ;
93+ const set = new Set ( await kv . get < string [ ] > ( listKey ) ?? [ ] ) ;
94+ set . delete ( id ) ;
95+ const list = [ ...set ] ;
96+ list . sort ( ( a , b ) => a < b ? - 1 : a > b ? 1 : 0 ) ;
97+ await kv . set ( listKey , list ) ;
98+ } while ( await kv . get ( lockKey ) !== lockId ) ;
99+ const messageKey : KvKey = [ ...listKey , id ] ;
100+ const createJson = await kv . get ( messageKey ) ;
101+ if ( createJson == null ) return ;
102+ await kv . delete ( messageKey ) ;
103+ const create = await Create . fromJsonLd ( createJson , this . session . context ) ;
104+ const message = await create . getObject ( this . session . context ) ;
105+ if ( message == null ) return ;
106+ const mentionedActorIds : Set < string > = new Set ( ) ;
107+ for await ( const tag of message . getTags ( this . session . context ) ) {
108+ if ( tag instanceof Mention && tag . href != null ) {
109+ mentionedActorIds . add ( tag . href . href ) ;
110+ }
111+ }
112+ const promises : Promise < Object | null > [ ] = [ ] ;
113+ const documentLoader = await this . session . context . getDocumentLoader (
114+ this . session . bot ,
115+ ) ;
116+ for ( const uri of mentionedActorIds ) {
117+ promises . push ( this . session . context . lookupObject ( uri , { documentLoader } ) ) ;
118+ }
119+ const mentionedActors = ( await Promise . all ( promises ) ) . filter ( isActor ) ;
120+ const activity = new Delete ( {
121+ id : new URL ( "#delete" , this . id ) ,
122+ actor : this . session . context . getActorUri ( this . session . bot . identifier ) ,
123+ tos : create . toIds ,
124+ ccs : create . ccIds ,
125+ object : new Tombstone ( {
126+ id : this . id ,
127+ } ) ,
128+ } ) ;
129+ const excludeBaseUris = [ new URL ( this . session . context . origin ) ] ;
130+ await this . session . context . sendActivity (
131+ this . session . bot ,
132+ "followers" ,
133+ activity ,
134+ { preferSharedInbox : true , excludeBaseUris } ,
135+ ) ;
136+ for ( const actor of mentionedActors ) {
137+ await this . session . context . sendActivity (
138+ this . session . bot ,
139+ actor ,
140+ activity ,
141+ { preferSharedInbox : true , excludeBaseUris } ,
142+ ) ;
143+ }
144+ }
145+
68146 reply (
69147 text : Text < TContextData > ,
70148 options ?: SessionPublishOptions ,
0 commit comments