@@ -27,7 +27,7 @@ import {
2727 Hashtag ,
2828 isActor ,
2929 Like as RawLike ,
30- type Link ,
30+ Link ,
3131 Mention ,
3232 Note ,
3333 type Object ,
@@ -39,6 +39,7 @@ import {
3939} from "@fedify/fedify/vocab" ;
4040import type { LanguageTag } from "@phensley/language-tag" ;
4141import { unescape } from "@std/html/entities" ;
42+ import { parseMediaType } from "@std/media-types/parse-media-type" ;
4243import { generate as uuidv7 } from "@std/uuid/unstable-v7" ;
4344import { FilterXSS , getDefaultWhiteList } from "xss" ;
4445import type { DeferredCustomEmoji , Emoji } from "./emoji.ts" ;
@@ -90,6 +91,7 @@ export class MessageImpl<T extends MessageClass, TContextData>
9091 text : string ;
9192 html : string ;
9293 readonly replyTarget ?: Message < MessageClass , TContextData > | undefined ;
94+ readonly quoteTarget ?: Message < MessageClass , TContextData > | undefined ;
9395 mentions : readonly Actor [ ] ;
9496 hashtags : readonly Hashtag [ ] ;
9597 readonly attachments : readonly Document [ ] ;
@@ -112,6 +114,7 @@ export class MessageImpl<T extends MessageClass, TContextData>
112114 this . text = message . text ;
113115 this . html = message . html ;
114116 this . replyTarget = message . replyTarget ;
117+ this . quoteTarget = message . quoteTarget ;
115118 this . mentions = message . mentions ;
116119 this . hashtags = message . hashtags ;
117120 this . attachments = message . attachments ;
@@ -121,17 +124,17 @@ export class MessageImpl<T extends MessageClass, TContextData>
121124
122125 reply (
123126 text : Text < "block" , TContextData > ,
124- options ?: SessionPublishOptions ,
127+ options ?: SessionPublishOptions < TContextData > ,
125128 ) : Promise < AuthorizedMessage < Note , TContextData > > ;
126129 reply < T extends MessageClass > (
127130 text : Text < "block" , TContextData > ,
128- options ?: SessionPublishOptionsWithClass < T > | undefined ,
131+ options ?: SessionPublishOptionsWithClass < T , TContextData > | undefined ,
129132 ) : Promise < AuthorizedMessage < T , TContextData > > ;
130133 reply (
131134 text : Text < "block" , TContextData > ,
132135 options ?:
133- | SessionPublishOptions
134- | SessionPublishOptionsWithClass < MessageClass > ,
136+ | SessionPublishOptions < TContextData >
137+ | SessionPublishOptionsWithClass < MessageClass , TContextData > ,
135138 ) : Promise < AuthorizedMessage < MessageClass , TContextData > > {
136139 return this . session . publish ( text , {
137140 visibility : this . visibility === "unknown" ? "direct" : this . visibility ,
@@ -544,20 +547,23 @@ export async function createMessage<T extends MessageClass, TContextData>(
544547 session : SessionImpl < TContextData > ,
545548 cachedObjects : Record < string , Object > ,
546549 replyTarget ?: Message < MessageClass , TContextData > ,
550+ quote ?: Message < MessageClass , TContextData > ,
547551 authorized ?: true ,
548552) : Promise < AuthorizedMessage < T , TContextData > > ;
549553export async function createMessage < T extends MessageClass , TContextData > (
550554 raw : T ,
551555 session : SessionImpl < TContextData > ,
552556 cachedObjects : Record < string , Object > ,
553557 replyTarget ?: Message < MessageClass , TContextData > ,
558+ quote ?: Message < MessageClass , TContextData > ,
554559 authorized ?: boolean ,
555560) : Promise < Message < T , TContextData > > ;
556561export async function createMessage < T extends MessageClass , TContextData > (
557562 raw : T ,
558563 session : SessionImpl < TContextData > ,
559564 cachedObjects : Record < string , Object > ,
560565 replyTarget ?: Message < MessageClass , TContextData > ,
566+ quoteTarget ?: Message < MessageClass , TContextData > ,
561567 authorized : boolean = false ,
562568) : Promise < Message < T , TContextData > > {
563569 if ( raw . id == null ) throw new TypeError ( "The raw.id is required." ) ;
@@ -582,6 +588,7 @@ export async function createMessage<T extends MessageClass, TContextData>(
582588 const mentions : Actor [ ] = [ ] ;
583589 const mentionedActorIds = new Set < string > ( ) ;
584590 const hashtags : Hashtag [ ] = [ ] ;
591+ const quoteLinks : Link [ ] = [ ] ;
585592 for await ( const tag of raw . getTags ( options ) ) {
586593 if ( tag instanceof Mention && tag . href != null ) {
587594 const obj = tag . href . href === session . actorId ?. href
@@ -593,6 +600,18 @@ export async function createMessage<T extends MessageClass, TContextData>(
593600 mentionedActorIds . add ( tag . href . href ) ;
594601 } else if ( tag instanceof Hashtag ) {
595602 hashtags . push ( tag ) ;
603+ } else if ( tag instanceof Link ) {
604+ const mediaType = tag . mediaType == null
605+ ? null
606+ : parseMediaType ( tag . mediaType ) ;
607+ if (
608+ tag . rel === "https://misskey-hub.net/ns#_misskey_quote" ||
609+ mediaType ?. [ 0 ] === "application/activity+json" ||
610+ mediaType ?. [ 0 ] === "application/ld+json" &&
611+ mediaType [ 1 ] ?. profile === "https://www.w3.org/ns/activitystreams"
612+ ) {
613+ quoteLinks . push ( tag ) ;
614+ }
596615 }
597616 }
598617 const attachments : Document [ ] = [ ] ;
@@ -612,14 +631,46 @@ export async function createMessage<T extends MessageClass, TContextData>(
612631 session . context ,
613632 parsed . values . id ,
614633 ) ;
615- } else rt = await raw . getReplyTarget ( options ) ;
634+ } else {
635+ rt = await raw . getReplyTarget ( options ) ;
636+ }
616637 if (
617638 rt instanceof Article || rt instanceof ChatMessage ||
618639 rt instanceof Note || rt instanceof Question
619640 ) {
620641 replyTarget = await createMessage ( rt , session , cachedObjects ) ;
621642 }
622643 }
644+ if ( quoteTarget == null ) {
645+ let quoteUrl : URL | null = null ;
646+ for ( const quoteLink of quoteLinks ) {
647+ if ( quoteLink . href == null ) continue ;
648+ quoteUrl = quoteLink . href ;
649+ break ;
650+ }
651+ if ( quoteUrl == null ) quoteUrl = raw . quoteUrl ;
652+ let qt : Object | null = null ;
653+ const parsed = session . context . parseUri ( quoteUrl ) ;
654+ // @ts -ignore: The `class` property satisfies the `MessageClass` type.
655+ if ( parsed ?. type === "object" && messageClasses . includes ( parsed . class ) ) {
656+ // @ts -ignore: The `class` property satisfies the `MessageClass` type.
657+ // deno-lint-ignore no-explicit-any
658+ const cls : new ( values : any ) => T = parsed . class ;
659+ qt = await session . bot . dispatchMessage (
660+ cls ,
661+ session . context ,
662+ parsed . values . id ,
663+ ) ;
664+ } else if ( quoteUrl != null ) {
665+ qt = await session . context . lookupObject ( quoteUrl , options ) ;
666+ }
667+ if (
668+ qt instanceof Article || qt instanceof ChatMessage ||
669+ qt instanceof Note || qt instanceof Question
670+ ) {
671+ quoteTarget = await createMessage ( qt , session , cachedObjects ) ;
672+ }
673+ }
623674 return new ( authorized ? AuthorizedMessageImpl : MessageImpl ) ( session , {
624675 raw,
625676 id : raw . id ,
@@ -636,6 +687,7 @@ export async function createMessage<T extends MessageClass, TContextData>(
636687 text : unescape ( text ) ,
637688 html,
638689 replyTarget,
690+ quoteTarget,
639691 mentions,
640692 hashtags,
641693 attachments,
0 commit comments