a tool to help your Letta AI agents navigate bluesky
at main 3.7 kB view raw
1import { 2 agentContext, 3 claimTaskThread, 4 releaseTaskThread, 5} from "../utils/agentContext.ts"; 6import { msFrom, msUntilNextWakeWindow } from "../utils/time.ts"; 7import { bsky } from "../utils/bsky.ts"; 8import { processNotification } from "../utils/processNotification.ts"; 9 10export const checkNotifications = async () => { 11 if (!claimTaskThread()) { 12 const newDelay = msFrom.minutes(2); 13 console.log( 14 `🔹 ${agentContext.agentBskyName} is busy, checking for notifications again in ${ 15 (newDelay * 1000) * 60 16 } minutes…`, 17 ); 18 // agentContext is busy, try to check notifications in 2 minutes. 19 setTimeout(checkNotifications, newDelay); 20 return; 21 } 22 23 const delay = msUntilNextWakeWindow( 24 msFrom.minutes(30), 25 msFrom.minutes(45), 26 ); 27 28 if (delay !== 0) { 29 setTimeout(checkNotifications, delay); 30 console.log( 31 `🔹 ${agentContext.agentBskyName} is currently asleep. scheduling next notification check for ${ 32 (delay / 1000 / 60 / 60).toFixed(2) 33 } hours from now…`, 34 ); 35 agentContext.notifDelayCurrent = agentContext.notifDelayMinimum; 36 releaseTaskThread(); 37 return; 38 } 39 40 try { 41 const allNotifications = await bsky.listNotifications({ 42 reasons: agentContext.supportedNotifTypes, 43 limit: 50, 44 }); 45 46 const startedProcessingTime: string = new Date().toISOString(); 47 48 const unreadNotifications = allNotifications.data.notifications.filter( 49 (notif) => !notif.isRead, 50 ); 51 52 if (unreadNotifications.length > 0) { 53 console.log( 54 `🔹 found ${unreadNotifications.length} notification(s), processing…`, 55 ); 56 57 // resets delay for future notification checks since 58 // it's likely agent actions might incur new ones 59 agentContext.notifDelayCurrent = Math.max( 60 agentContext.notifDelayCurrent / 4, 61 agentContext.notifDelayMinimum, 62 ); 63 64 // loop through all notifications until complete 65 // 66 let notificationCounter = 1; 67 for (const notification of unreadNotifications) { 68 console.log( 69 `🔹 processing notification #${notificationCounter} of #${unreadNotifications.length} [${notification.reason} from @${notification.author.handle}]`, 70 ); 71 await processNotification(notification); 72 notificationCounter++; 73 } 74 75 // marks all notifications that were processed as seen 76 // based on time from when retrieved instead of finished 77 await bsky.updateSeenNotifications(startedProcessingTime); 78 console.log( 79 `🔹 done processing ${unreadNotifications.length} notification${ 80 unreadNotifications.length > 1 ? "s" : "" 81 }`, 82 ); 83 // increases counter for notification processing session 84 agentContext.processingCount++; 85 } else { 86 // increases delay to check notifications again later 87 agentContext.notifDelayCurrent = Math.round(Math.min( 88 agentContext.notifDelayCurrent * 89 agentContext.notifDelayMultiplier, 90 agentContext.notifDelayMaximum, 91 )); 92 93 console.log( 94 "🔹 no notifications…", 95 `checking again in ${ 96 (agentContext.notifDelayCurrent / 1000).toFixed(2) 97 } seconds`, 98 ); 99 } 100 } catch (error) { 101 console.error("Error in checkNotifications:", error); 102 // since something went wrong, lets check for notifications again sooner 103 agentContext.notifDelayCurrent = agentContext.notifDelayMinimum; 104 } finally { 105 // increment check count 106 agentContext.checkCount++; 107 // actually schedules next time to check for notifications 108 setTimeout(checkNotifications, agentContext.notifDelayCurrent); 109 // ends work 110 releaseTaskThread(); 111 } 112};