+102
-30
server.ts
+102
-30
server.ts
···
1
import { bsky } from "./util/bsky.ts";
2
-
import { session } from "./util/sessionCount.ts";
3
import { processNotification } from "./util/processNotification.ts";
4
5
-
const checkNotifications = async () => {
6
-
session.checkCount++;
7
-
8
-
const statusMsg =
9
-
`(${session.checkCount} notif checks, ${session.mentionCount} mentions, ${session.replyCount} replies, ${session.likeCount} likes, ${session.followCount} follows})`;
10
-
11
-
if (session.isProcessing) {
12
console.log(
13
-
`pausing notification check while processing notifications… [current session: ${session.processingCount}] ${statusMsg}`,
14
);
15
return;
16
}
17
18
-
const allNotifications = await bsky.listNotifications({
19
-
reasons: ["like", "repost", "follow", "mention", "reply", "quote"],
20
-
limit: 50,
21
-
});
22
-
23
-
const unreadNotifications = allNotifications.data.notifications.filter(
24
-
(notif) => !notif.isRead,
25
-
);
26
27
-
if (unreadNotifications.length > 0) {
28
-
session.isProcessing = true;
29
const startedProcessingTime: string = new Date().toISOString();
30
31
-
for (const notification of unreadNotifications) {
32
-
await processNotification(notification);
33
}
34
35
-
await bsky.updateSeenNotifications(startedProcessingTime);
36
37
-
session.processingCount++;
38
-
session.isProcessing = false;
39
-
} else {
40
console.log(
41
-
`no notifications, checking in ${session.delaySeconds} second${
42
-
session.delaySeconds > 1 ? "s" : ""
43
-
} ${statusMsg}…`,
44
);
45
}
46
};
47
48
-
setInterval(checkNotifications, session.delay);
49
-
checkNotifications();
···
1
import { bsky } from "./util/bsky.ts";
2
+
import { claimTaskThread, releaseTaskThread, session } from "./util/session.ts";
3
+
import { messageAgent } from "./util/messageAgent.ts";
4
+
import { createReflectionPrompt } from "./util/promptsAndLogs.ts";
5
import { processNotification } from "./util/processNotification.ts";
6
+
import { randomDelayMs } from "./util/time.ts";
7
8
+
const notificationCheck = async () => {
9
+
// check if agent is busy before attempting
10
+
// to give it new tasks
11
+
if (!claimTaskThread()) {
12
+
const newDelay = randomDelayMs(5, 10);
13
console.log(
14
+
`${session.agentName} is busy, checking for notifications again in ${
15
+
newDelay * 60 * 1000
16
+
} minutes…`,
17
);
18
+
// session is busy, try to check notifications in 5~10 minutes.
19
+
setTimeout(notificationCheck, newDelay);
20
return;
21
}
22
23
+
try {
24
+
// gets ALL notifications, limited to the last 50
25
+
const allNotifications = await bsky.listNotifications({
26
+
reasons: session.notificationTypes,
27
+
limit: 50,
28
+
});
29
30
+
// setting time when receiving all notifications
31
+
// this is to possibly mark the notifications as read later
32
const startedProcessingTime: string = new Date().toISOString();
33
34
+
const unreadNotifications = allNotifications.data.notifications.filter(
35
+
(notif) => !notif.isRead,
36
+
);
37
+
38
+
if (unreadNotifications.length > 0) {
39
+
console.log(
40
+
`found ${unreadNotifications.length} notification(s), processing…`,
41
+
);
42
+
43
+
// resets delay for future notification checks since
44
+
// it's likely agent actions might incur new ones
45
+
session.currentNotifDelaySeconds = session.minNotifDelaySeconds;
46
+
47
+
// loop through all notifications until complete
48
+
for (const notification of unreadNotifications) {
49
+
let notificationCounter = 1;
50
+
console.log(
51
+
`processing notification #${notificationCounter} [${notification.reason}]`,
52
+
);
53
+
await processNotification(notification);
54
+
notificationCounter++;
55
+
}
56
+
57
+
// marks all notifications that were processed as seen
58
+
// based on time from when retrieved instead of finished
59
+
await bsky.updateSeenNotifications(startedProcessingTime);
60
+
61
+
// increases counter for notification processing sessions
62
+
session.processingCount++;
63
+
} else {
64
+
// increases delay to check notifications again later
65
+
session.currentNotifDelaySeconds = Math.round(Math.min(
66
+
session.currentNotifDelaySeconds * session.notifDelayMultiplier,
67
+
session.maxNotifDelaySeconds,
68
+
));
69
+
70
+
console.log(
71
+
"no notifications…",
72
+
`checking again in ${session.currentNotifDelaySeconds / 1000} seconds`,
73
+
);
74
}
75
+
} catch (error) {
76
+
console.error("Error in notificationCheck:", error);
77
+
// since something went wrong, lets check for notifications again sooner
78
+
session.currentNotifDelaySeconds = session.minNotifDelaySeconds;
79
+
} finally {
80
+
// actually schedules next time to check for notifications
81
+
setTimeout(notificationCheck, session.currentNotifDelaySeconds);
82
+
// ends work
83
+
session.busy = false;
84
+
}
85
+
};
86
87
+
const reflectionPrompt = async () => {
88
+
if (!claimTaskThread()) {
89
+
const newDelay = randomDelayMs(5, 10);
90
91
+
console.log(
92
+
`${session.agentName} is busy, will try reflecting again in ${
93
+
newDelay * 60 * 1000
94
+
} minutes…`,
95
+
);
96
+
// session is busy, try to check notifications in 5~10 minutes.
97
+
setTimeout(notificationCheck, newDelay);
98
+
return;
99
+
}
100
+
101
+
try {
102
+
const prompt = createReflectionPrompt();
103
+
console.log("starting reflection prompt…");
104
+
await messageAgent(prompt);
105
+
} catch (error) {
106
+
console.error("Error in reflectionCheck:", error);
107
+
} finally {
108
console.log(
109
+
"finished reflection prompt. returning to checking for notifications…",
110
);
111
+
setTimeout(
112
+
reflectionPrompt,
113
+
session.currentReflectDelayMinutes + randomDelayMs(1, 90),
114
+
);
115
+
releaseTaskThread();
116
+
session.currentNotifDelaySeconds = session.minNotifDelaySeconds;
117
}
118
};
119
120
+
await notificationCheck();
121
+
setTimeout(reflectionPrompt, session.minReflectDelayMinutes);
+1
-1
util/getCleanThread.ts
+1
-1
util/getCleanThread.ts
···
1
import { bsky } from "./bsky.ts";
2
-
import type { AppBskyFeedDefs } from "npm:@atproto/api";
3
4
type threadPost = {
5
authorHandle: string;
···
22
23
if (thread) {
24
postsThread.push({
25
authorHandle: `@${thread.post.author.handle}`,
26
message: thread.post.record.text,
27
uri: thread.post.uri,
+1
-1
util/messageAgent.ts
+1
-1
util/messageAgent.ts
+21
-9
util/processNotification.ts
+21
-9
util/processNotification.ts
···
1
import type { Notification } from "./types.ts";
2
-
// import { getCleanThread } from "./getCleanThread.ts";
3
-
import { session } from "./sessionCount.ts";
4
import { messageAgent } from "./messageAgent.ts";
5
6
import {
···
10
createQuotePrompt,
11
createReplyPrompt,
12
createRepostPrompt,
13
-
} from "./prompts.ts";
14
15
export const processNotification = async (notification: Notification) => {
16
-
const agentProject = Deno.env.get("LETTA_PROJECT_SLUG");
17
const kind = notification.reason;
18
-
19
// const referencePostThread = await getCleanThread(notification.uri);
20
// console.log(referencePostThread[0]);
21
···
24
25
const prompt = await createLikePrompt(notification);
26
await messageAgent(prompt);
27
} else if (kind == "repost") {
28
session.repostCount++;
29
30
const prompt = await createRepostPrompt(notification);
31
await messageAgent(prompt);
32
} else if (kind == "follow") {
33
session.followCount++;
34
35
const prompt = createNewFollowerPrompt(notification);
36
await messageAgent(prompt);
37
-
} else if (kind == "mention") {
38
-
session.mentionCount++;
39
console.log(
40
-
`initiating message to ${agentProject} [kind: ${kind}]…`,
41
);
42
-
43
const prompt = await createMentionPrompt(notification);
44
45
await messageAgent(prompt);
46
} else if (kind == "reply") {
47
session.replyCount++;
48
49
const prompt = await createReplyPrompt(notification);
50
await messageAgent(prompt);
51
} else if (kind == "quote") {
52
session.quoteCount++;
53
54
const prompt = await createQuotePrompt(notification);
55
await messageAgent(prompt);
56
} else {
57
console.log(
58
`kind "${kind} does not have a system prompt associated with it, moving on…`,
···
1
import type { Notification } from "./types.ts";
2
+
import { session } from "./session.ts";
3
import { messageAgent } from "./messageAgent.ts";
4
5
import {
···
9
createQuotePrompt,
10
createReplyPrompt,
11
createRepostPrompt,
12
+
} from "./promptsAndLogs.ts";
13
14
export const processNotification = async (notification: Notification) => {
15
+
const agentProject = Deno.env.get("LETTA_PROJECT_NAME");
16
const kind = notification.reason;
17
+
console.log(`pausing notif checks, received ${kind} notification…`);
18
// const referencePostThread = await getCleanThread(notification.uri);
19
// console.log(referencePostThread[0]);
20
···
23
24
const prompt = await createLikePrompt(notification);
25
await messageAgent(prompt);
26
+
console.log(
27
+
`sent ${kind} notification to ${agentProject}, waiting for response…`,
28
+
);
29
} else if (kind == "repost") {
30
session.repostCount++;
31
32
const prompt = await createRepostPrompt(notification);
33
await messageAgent(prompt);
34
+
console.log(
35
+
`sent ${kind} notification to ${agentProject}, waiting for response…`,
36
+
);
37
} else if (kind == "follow") {
38
session.followCount++;
39
40
const prompt = createNewFollowerPrompt(notification);
41
await messageAgent(prompt);
42
console.log(
43
+
`sent ${kind} notification to ${agentProject}, waiting for response…`,
44
);
45
+
} else if (kind == "mention") {
46
const prompt = await createMentionPrompt(notification);
47
48
await messageAgent(prompt);
49
+
console.log(
50
+
`sent ${kind} notification to ${agentProject}, waiting for response…`,
51
+
);
52
} else if (kind == "reply") {
53
session.replyCount++;
54
55
const prompt = await createReplyPrompt(notification);
56
await messageAgent(prompt);
57
+
console.log(
58
+
`sent ${kind} notification to ${agentProject}, waiting for response…`,
59
+
);
60
} else if (kind == "quote") {
61
session.quoteCount++;
62
63
const prompt = await createQuotePrompt(notification);
64
await messageAgent(prompt);
65
+
console.log(
66
+
`sent ${kind} notification to ${agentProject}, waiting for response…`,
67
+
);
68
} else {
69
console.log(
70
`kind "${kind} does not have a system prompt associated with it, moving on…`,