@@ -47,6 +47,7 @@ import { join } from "@std/path/join";
4747import { AsyncLocalStorage } from "node:async_hooks" ;
4848import { detect } from "tinyld" ;
4949import { FilterXSS } from "xss" ;
50+ import { z } from "zod" ;
5051import metadata from "./deno.json" with { type : "json" } ;
5152
5253await configure ( {
@@ -118,13 +119,27 @@ bot.onFollow = async (session, followRequest) => {
118119 ) ;
119120} ;
120121
122+ const reactionResponseSchema = z . object ( {
123+ whetherToLike : z . boolean ( ) . describe (
124+ "Whether to press the like button on the message." ,
125+ ) ,
126+ } ) ;
127+
121128bot . onMention = async ( session , msg ) => {
122129 if ( msg . replyTarget != null ) return ;
123130 const actor = msg . actor ;
131+ const llmWithStruct = llm . withStructuredOutput ( reactionResponseSchema ) ;
132+ const whetherToLike = await llmWithStruct . invoke ( [
133+ getSystemMessage ( session ) ,
134+ await getIntroMessage ( session , actor , await getMentionPrompt ( actor ) ) ,
135+ await getHumanMessage ( msg , true ) ,
136+ ] ) ;
137+ logger . debug ( "Whether to like: {whetherToLike}" , whetherToLike ) ;
138+ if ( whetherToLike . whetherToLike ) await msg . like ( ) ;
124139 const response = await llm . invoke ( [
125140 getSystemMessage ( session ) ,
126141 await getIntroMessage ( session , actor , await getMentionPrompt ( actor ) ) ,
127- await getHumanMessage ( msg ) ,
142+ await getHumanMessage ( msg , false ) ,
128143 ] ) ;
129144 const message = response . content . toString ( ) ;
130145 const language = detect ( message ) ;
@@ -153,9 +168,16 @@ bot.onReply = async (session, msg) => {
153168 for ( const msg of thread ) {
154169 const message = msg . actor ?. id ?. href === session . actorId . href
155170 ? new AIMessage ( msg . text )
156- : await getHumanMessage ( msg ) ;
171+ : await getHumanMessage ( msg , false ) ;
157172 messages . push ( message ) ;
158173 }
174+ const llmWithStruct = llm . withStructuredOutput ( reactionResponseSchema ) ;
175+ const whetherToLike = await llmWithStruct . invoke ( [
176+ ...messages . slice ( 0 , messages . length - 1 ) ,
177+ await getHumanMessage ( thread [ thread . length - 1 ] , true ) ,
178+ ] ) ;
179+ if ( whetherToLike . whetherToLike ) await msg . like ( ) ;
180+ logger . debug ( "Whether to like: {whetherToLike}" , whetherToLike ) ;
159181 const response = await llm . invoke ( messages ) ;
160182 const message = response . content . toString ( ) ;
161183 const language = detect ( message ) ;
@@ -183,6 +205,9 @@ const FOLLOW_PROMPT_TEMPLATE = await Deno.readTextFile(
183205const MENTION_PROMPT_TEMPLATE = await Deno . readTextFile (
184206 join ( import . meta. dirname ! , "prompts" , "mention.txt" ) ,
185207) ;
208+ const REACTION_PROMPT_TEMPLATE = await Deno . readTextFile (
209+ join ( import . meta. dirname ! , "prompts" , "reaction.txt" ) ,
210+ ) ;
186211
187212const template = new Template ( { isEscape : false } ) ;
188213
@@ -224,6 +249,15 @@ async function getFollowPrompt(actor: Actor): Promise<string> {
224249 } ) ;
225250}
226251
252+ function getReactionPrompt ( message : Message < MessageClass , void > ) : string {
253+ const msg = message . text ;
254+ return template . render ( REACTION_PROMPT_TEMPLATE , {
255+ quotedMessage : msg == null
256+ ? "Not available."
257+ : `> ${ msg . replaceAll ( "\n" , "\n> " ) } ` ,
258+ } ) ;
259+ }
260+
227261async function getIntroMessage (
228262 session : Session < void > ,
229263 actor : Actor ,
@@ -281,6 +315,7 @@ async function getMentionPrompt(actor: Actor): Promise<string> {
281315
282316async function getHumanMessage < T extends MessageClass > (
283317 msg : Message < T , void > ,
318+ reaction : boolean ,
284319) : Promise < HumanMessage > {
285320 const attachments = msg . attachments . map ( async ( doc ) => {
286321 if ( ! doc . mediaType ?. startsWith ( "image/" ) ) return null ;
@@ -293,7 +328,7 @@ async function getHumanMessage<T extends MessageClass>(
293328 } ) ;
294329 return new HumanMessage ( {
295330 content : [
296- { type : "text" , text : msg . text } ,
331+ { type : "text" , text : reaction ? getReactionPrompt ( msg ) : msg . text } ,
297332 ...( await Promise . all ( attachments ) ) . filter ( ( a ) => a != null ) ,
298333 ] ,
299334 } ) ;
0 commit comments