@elizaos/plugin-bluesky 2.0.0-alpha.6 → 2.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +203 -0
- package/dist/browser/index.browser.js +18 -16
- package/dist/browser/index.browser.js.map +3 -3
- package/dist/cjs/index.node.cjs +959 -151
- package/dist/cjs/index.node.js.map +14 -12
- package/dist/node/index.node.js +937 -142
- package/dist/node/index.node.js.map +14 -12
- package/package.json +18 -12
package/dist/node/index.node.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
// index.ts
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
// client.ts
|
|
8
|
-
import { BskyAgent, RichText } from "@atproto/api";
|
|
9
|
-
import { logger } from "@elizaos/core";
|
|
10
|
-
import { LRUCache } from "lru-cache";
|
|
2
|
+
import {
|
|
3
|
+
getConnectorAccountManager,
|
|
4
|
+
logger as logger4
|
|
5
|
+
} from "@elizaos/core";
|
|
11
6
|
|
|
12
7
|
// types/index.ts
|
|
13
|
-
import
|
|
8
|
+
import * as zod from "zod";
|
|
9
|
+
var z2 = zod.z ?? zod;
|
|
14
10
|
var BLUESKY_SERVICE_URL = "https://bsky.social";
|
|
15
11
|
var BLUESKY_MAX_POST_LENGTH = 300;
|
|
16
12
|
var BLUESKY_POLL_INTERVAL = 60;
|
|
@@ -35,20 +31,20 @@ var CACHE_SIZE = {
|
|
|
35
31
|
NOTIFICATIONS: 1000,
|
|
36
32
|
CONVERSATIONS: 100
|
|
37
33
|
};
|
|
38
|
-
var BlueSkyConfigSchema =
|
|
39
|
-
handle:
|
|
40
|
-
password:
|
|
41
|
-
service:
|
|
42
|
-
dryRun:
|
|
43
|
-
pollInterval:
|
|
44
|
-
enablePost:
|
|
45
|
-
postIntervalMin:
|
|
46
|
-
postIntervalMax:
|
|
47
|
-
enableActionProcessing:
|
|
48
|
-
actionInterval:
|
|
49
|
-
postImmediately:
|
|
50
|
-
maxActionsProcessing:
|
|
51
|
-
enableDMs:
|
|
34
|
+
var BlueSkyConfigSchema = z2.object({
|
|
35
|
+
handle: z2.string().regex(AT_PROTOCOL_HANDLE_REGEX, "Invalid handle format"),
|
|
36
|
+
password: z2.string().min(1),
|
|
37
|
+
service: z2.string().url().default(BLUESKY_SERVICE_URL),
|
|
38
|
+
dryRun: z2.boolean().default(false),
|
|
39
|
+
pollInterval: z2.number().positive().default(BLUESKY_POLL_INTERVAL),
|
|
40
|
+
enablePost: z2.boolean().default(true),
|
|
41
|
+
postIntervalMin: z2.number().positive().default(BLUESKY_POST_INTERVAL_MIN),
|
|
42
|
+
postIntervalMax: z2.number().positive().default(BLUESKY_POST_INTERVAL_MAX),
|
|
43
|
+
enableActionProcessing: z2.boolean().default(true),
|
|
44
|
+
actionInterval: z2.number().positive().default(BLUESKY_ACTION_INTERVAL),
|
|
45
|
+
postImmediately: z2.boolean().default(false),
|
|
46
|
+
maxActionsProcessing: z2.number().positive().default(BLUESKY_MAX_ACTIONS),
|
|
47
|
+
enableDMs: z2.boolean().default(true)
|
|
52
48
|
});
|
|
53
49
|
|
|
54
50
|
class BlueSkyError extends Error {
|
|
@@ -62,7 +58,254 @@ class BlueSkyError extends Error {
|
|
|
62
58
|
}
|
|
63
59
|
}
|
|
64
60
|
|
|
61
|
+
// utils/config.ts
|
|
62
|
+
var DEFAULT_BLUESKY_ACCOUNT_ID = "default";
|
|
63
|
+
function getApiKeyOptional(runtime, key) {
|
|
64
|
+
const value = runtime.getSetting(key);
|
|
65
|
+
return typeof value === "string" ? value : undefined;
|
|
66
|
+
}
|
|
67
|
+
function stringSetting(runtime, key) {
|
|
68
|
+
const value = runtime.getSetting(key);
|
|
69
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
70
|
+
}
|
|
71
|
+
function characterConfig(runtime) {
|
|
72
|
+
const settings = runtime.character?.settings;
|
|
73
|
+
const raw = settings?.bluesky;
|
|
74
|
+
return raw && typeof raw === "object" ? raw : {};
|
|
75
|
+
}
|
|
76
|
+
function parseAccountsJson(runtime) {
|
|
77
|
+
const raw = stringSetting(runtime, "BLUESKY_ACCOUNTS");
|
|
78
|
+
if (!raw)
|
|
79
|
+
return {};
|
|
80
|
+
try {
|
|
81
|
+
const parsed = JSON.parse(raw);
|
|
82
|
+
if (Array.isArray(parsed)) {
|
|
83
|
+
return Object.fromEntries(parsed.filter((item) => Boolean(item) && typeof item === "object").map((item) => [
|
|
84
|
+
normalizeBlueSkyAccountId(item.accountId ?? item.id),
|
|
85
|
+
item
|
|
86
|
+
]));
|
|
87
|
+
}
|
|
88
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
89
|
+
} catch {
|
|
90
|
+
return {};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function allAccountConfigs(runtime) {
|
|
94
|
+
return {
|
|
95
|
+
...characterConfig(runtime).accounts ?? {},
|
|
96
|
+
...parseAccountsJson(runtime)
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function accountConfig(runtime, accountId) {
|
|
100
|
+
const accounts = allAccountConfigs(runtime);
|
|
101
|
+
return accounts[accountId] ?? accounts[normalizeBlueSkyAccountId(accountId)] ?? {};
|
|
102
|
+
}
|
|
103
|
+
function readRawField(record, keys) {
|
|
104
|
+
if (!record)
|
|
105
|
+
return;
|
|
106
|
+
for (const key of keys) {
|
|
107
|
+
const value = record[key];
|
|
108
|
+
if (typeof value === "string" && value.trim())
|
|
109
|
+
return value.trim();
|
|
110
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
111
|
+
return String(value);
|
|
112
|
+
}
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
function boolValue(value, fallback = false) {
|
|
116
|
+
if (typeof value === "boolean")
|
|
117
|
+
return value;
|
|
118
|
+
if (typeof value === "string")
|
|
119
|
+
return value.trim().toLowerCase() === "true";
|
|
120
|
+
return fallback;
|
|
121
|
+
}
|
|
122
|
+
function intValue(value, fallback) {
|
|
123
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
124
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
125
|
+
}
|
|
126
|
+
function normalizeBlueSkyAccountId(accountId) {
|
|
127
|
+
if (typeof accountId !== "string")
|
|
128
|
+
return DEFAULT_BLUESKY_ACCOUNT_ID;
|
|
129
|
+
const trimmed = accountId.trim();
|
|
130
|
+
return trimmed || DEFAULT_BLUESKY_ACCOUNT_ID;
|
|
131
|
+
}
|
|
132
|
+
function listBlueSkyAccountIds(runtime) {
|
|
133
|
+
const ids = new Set;
|
|
134
|
+
const config = characterConfig(runtime);
|
|
135
|
+
if (stringSetting(runtime, "BLUESKY_HANDLE") || config.handle && config.password) {
|
|
136
|
+
ids.add(DEFAULT_BLUESKY_ACCOUNT_ID);
|
|
137
|
+
}
|
|
138
|
+
for (const id of Object.keys(allAccountConfigs(runtime))) {
|
|
139
|
+
ids.add(normalizeBlueSkyAccountId(id));
|
|
140
|
+
}
|
|
141
|
+
return Array.from(ids.size ? ids : new Set([DEFAULT_BLUESKY_ACCOUNT_ID])).sort((a, b) => a.localeCompare(b));
|
|
142
|
+
}
|
|
143
|
+
function resolveDefaultBlueSkyAccountId(runtime) {
|
|
144
|
+
const requested = stringSetting(runtime, "BLUESKY_DEFAULT_ACCOUNT_ID") ?? stringSetting(runtime, "BLUESKY_ACCOUNT_ID");
|
|
145
|
+
if (requested)
|
|
146
|
+
return normalizeBlueSkyAccountId(requested);
|
|
147
|
+
const ids = listBlueSkyAccountIds(runtime);
|
|
148
|
+
return ids.includes(DEFAULT_BLUESKY_ACCOUNT_ID) ? DEFAULT_BLUESKY_ACCOUNT_ID : ids[0] ?? DEFAULT_BLUESKY_ACCOUNT_ID;
|
|
149
|
+
}
|
|
150
|
+
function readBlueSkyAccountId(...sources) {
|
|
151
|
+
for (const source of sources) {
|
|
152
|
+
if (!source || typeof source !== "object")
|
|
153
|
+
continue;
|
|
154
|
+
const record = source;
|
|
155
|
+
const parameters = record.parameters && typeof record.parameters === "object" ? record.parameters : {};
|
|
156
|
+
const data = record.data && typeof record.data === "object" ? record.data : {};
|
|
157
|
+
const metadata = record.metadata && typeof record.metadata === "object" ? record.metadata : {};
|
|
158
|
+
const bluesky = data.bluesky && typeof data.bluesky === "object" ? data.bluesky : {};
|
|
159
|
+
const value = record.accountId ?? parameters.accountId ?? data.accountId ?? bluesky.accountId ?? metadata.accountId;
|
|
160
|
+
if (typeof value === "string" && value.trim())
|
|
161
|
+
return normalizeBlueSkyAccountId(value);
|
|
162
|
+
}
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
function hasBlueSkyEnabled(runtime, accountId) {
|
|
166
|
+
const normalizedAccountId = normalizeBlueSkyAccountId(accountId ?? resolveDefaultBlueSkyAccountId(runtime));
|
|
167
|
+
const base = characterConfig(runtime);
|
|
168
|
+
const account = accountConfig(runtime, normalizedAccountId);
|
|
169
|
+
const allowEnv = normalizedAccountId === DEFAULT_BLUESKY_ACCOUNT_ID;
|
|
170
|
+
const enabled = readRawField(account, ["enabled", "BLUESKY_ENABLED"]) ?? readRawField(base, ["enabled", "BLUESKY_ENABLED"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLED") : undefined);
|
|
171
|
+
if (enabled)
|
|
172
|
+
return String(enabled).toLowerCase() === "true";
|
|
173
|
+
return Boolean((readRawField(account, ["handle", "BLUESKY_HANDLE"]) ?? readRawField(base, ["handle", "BLUESKY_HANDLE"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_HANDLE") : undefined)) && (readRawField(account, ["password", "BLUESKY_PASSWORD"]) ?? readRawField(base, ["password", "BLUESKY_PASSWORD"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_PASSWORD") : undefined)));
|
|
174
|
+
}
|
|
175
|
+
function validateBlueSkyConfig(runtime, accountId) {
|
|
176
|
+
const normalizedAccountId = normalizeBlueSkyAccountId(accountId ?? resolveDefaultBlueSkyAccountId(runtime));
|
|
177
|
+
const base = characterConfig(runtime);
|
|
178
|
+
const account = accountConfig(runtime, normalizedAccountId);
|
|
179
|
+
const allowEnv = normalizedAccountId === DEFAULT_BLUESKY_ACCOUNT_ID;
|
|
180
|
+
const result = BlueSkyConfigSchema.safeParse({
|
|
181
|
+
handle: readRawField(account, ["handle", "BLUESKY_HANDLE"]) ?? readRawField(base, ["handle", "BLUESKY_HANDLE"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_HANDLE") : undefined) ?? "",
|
|
182
|
+
password: readRawField(account, ["password", "BLUESKY_PASSWORD"]) ?? readRawField(base, ["password", "BLUESKY_PASSWORD"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_PASSWORD") : undefined) ?? "",
|
|
183
|
+
service: readRawField(account, ["service", "BLUESKY_SERVICE"]) ?? readRawField(base, ["service", "BLUESKY_SERVICE"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_SERVICE") : undefined) ?? BLUESKY_SERVICE_URL,
|
|
184
|
+
dryRun: boolValue(readRawField(account, ["dryRun", "BLUESKY_DRY_RUN"]) ?? readRawField(base, ["dryRun", "BLUESKY_DRY_RUN"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_DRY_RUN") : undefined)),
|
|
185
|
+
pollInterval: intValue(readRawField(account, ["pollInterval", "BLUESKY_POLL_INTERVAL"]) ?? readRawField(base, ["pollInterval", "BLUESKY_POLL_INTERVAL"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_POLL_INTERVAL") : undefined), BLUESKY_POLL_INTERVAL),
|
|
186
|
+
enablePost: String(readRawField(account, ["enablePost", "BLUESKY_ENABLE_POSTING"]) ?? readRawField(base, ["enablePost", "BLUESKY_ENABLE_POSTING"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLE_POSTING") : undefined) ?? "true").toLowerCase() !== "false",
|
|
187
|
+
postIntervalMin: intValue(readRawField(account, ["postIntervalMin", "BLUESKY_POST_INTERVAL_MIN"]) ?? readRawField(base, ["postIntervalMin", "BLUESKY_POST_INTERVAL_MIN"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_POST_INTERVAL_MIN") : undefined), BLUESKY_POST_INTERVAL_MIN),
|
|
188
|
+
postIntervalMax: intValue(readRawField(account, ["postIntervalMax", "BLUESKY_POST_INTERVAL_MAX"]) ?? readRawField(base, ["postIntervalMax", "BLUESKY_POST_INTERVAL_MAX"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_POST_INTERVAL_MAX") : undefined), BLUESKY_POST_INTERVAL_MAX),
|
|
189
|
+
enableActionProcessing: String(readRawField(account, [
|
|
190
|
+
"enableActionProcessing",
|
|
191
|
+
"BLUESKY_ENABLE_ACTION_PROCESSING"
|
|
192
|
+
]) ?? readRawField(base, [
|
|
193
|
+
"enableActionProcessing",
|
|
194
|
+
"BLUESKY_ENABLE_ACTION_PROCESSING"
|
|
195
|
+
]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLE_ACTION_PROCESSING") : undefined) ?? "true").toLowerCase() !== "false",
|
|
196
|
+
actionInterval: intValue(readRawField(account, ["actionInterval", "BLUESKY_ACTION_INTERVAL"]) ?? readRawField(base, ["actionInterval", "BLUESKY_ACTION_INTERVAL"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ACTION_INTERVAL") : undefined), BLUESKY_ACTION_INTERVAL),
|
|
197
|
+
postImmediately: boolValue(readRawField(account, ["postImmediately", "BLUESKY_POST_IMMEDIATELY"]) ?? readRawField(base, ["postImmediately", "BLUESKY_POST_IMMEDIATELY"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_POST_IMMEDIATELY") : undefined)),
|
|
198
|
+
maxActionsProcessing: intValue(readRawField(account, [
|
|
199
|
+
"maxActionsProcessing",
|
|
200
|
+
"BLUESKY_MAX_ACTIONS_PROCESSING"
|
|
201
|
+
]) ?? readRawField(base, [
|
|
202
|
+
"maxActionsProcessing",
|
|
203
|
+
"BLUESKY_MAX_ACTIONS_PROCESSING"
|
|
204
|
+
]) ?? (allowEnv ? runtime.getSetting("BLUESKY_MAX_ACTIONS_PROCESSING") : undefined), BLUESKY_MAX_ACTIONS),
|
|
205
|
+
enableDMs: String(readRawField(account, ["enableDMs", "BLUESKY_ENABLE_DMS"]) ?? readRawField(base, ["enableDMs", "BLUESKY_ENABLE_DMS"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLE_DMS") : undefined) ?? "true").toLowerCase() !== "false"
|
|
206
|
+
});
|
|
207
|
+
if (!result.success) {
|
|
208
|
+
const errors = result.error.errors?.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ") || result.error.toString();
|
|
209
|
+
throw new Error(`Invalid BlueSky configuration: ${errors}`);
|
|
210
|
+
}
|
|
211
|
+
return { ...result.data, accountId: normalizedAccountId };
|
|
212
|
+
}
|
|
213
|
+
function getPollInterval(runtime, accountId) {
|
|
214
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
215
|
+
return config.pollInterval * 1000;
|
|
216
|
+
}
|
|
217
|
+
function getActionInterval(runtime, accountId) {
|
|
218
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
219
|
+
return config.actionInterval * 1000;
|
|
220
|
+
}
|
|
221
|
+
function getMaxActionsProcessing(runtime, accountId) {
|
|
222
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
223
|
+
return config.maxActionsProcessing;
|
|
224
|
+
}
|
|
225
|
+
function isPostingEnabled(runtime, accountId) {
|
|
226
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
227
|
+
return config.enablePost;
|
|
228
|
+
}
|
|
229
|
+
function shouldPostImmediately(runtime, accountId) {
|
|
230
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
231
|
+
return config.postImmediately;
|
|
232
|
+
}
|
|
233
|
+
function getPostIntervalRange(runtime, accountId) {
|
|
234
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
235
|
+
return {
|
|
236
|
+
min: config.postIntervalMin * 1000,
|
|
237
|
+
max: config.postIntervalMax * 1000
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// connector-account-provider.ts
|
|
242
|
+
var BLUESKY_PROVIDER_ID = "bluesky";
|
|
243
|
+
function toConnectorAccount(runtime, accountId) {
|
|
244
|
+
let connected = false;
|
|
245
|
+
let handle = "";
|
|
246
|
+
let service = "";
|
|
247
|
+
try {
|
|
248
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
249
|
+
connected = Boolean(config.handle && config.password);
|
|
250
|
+
handle = config.handle;
|
|
251
|
+
service = config.service;
|
|
252
|
+
} catch {
|
|
253
|
+
connected = false;
|
|
254
|
+
}
|
|
255
|
+
const now = Date.now();
|
|
256
|
+
return {
|
|
257
|
+
id: accountId,
|
|
258
|
+
provider: BLUESKY_PROVIDER_ID,
|
|
259
|
+
label: handle || accountId,
|
|
260
|
+
role: "OWNER",
|
|
261
|
+
purpose: ["posting", "reading"],
|
|
262
|
+
accessGate: "open",
|
|
263
|
+
status: connected ? "connected" : "disabled",
|
|
264
|
+
externalId: handle || undefined,
|
|
265
|
+
displayHandle: handle || undefined,
|
|
266
|
+
createdAt: now,
|
|
267
|
+
updatedAt: now,
|
|
268
|
+
metadata: {
|
|
269
|
+
service
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
function createBlueSkyConnectorAccountProvider(runtime) {
|
|
274
|
+
return {
|
|
275
|
+
provider: BLUESKY_PROVIDER_ID,
|
|
276
|
+
label: "BlueSky",
|
|
277
|
+
listAccounts: async (_manager) => {
|
|
278
|
+
const ids = listBlueSkyAccountIds(runtime);
|
|
279
|
+
return ids.map((id) => toConnectorAccount(runtime, id));
|
|
280
|
+
},
|
|
281
|
+
createAccount: async (input, _manager) => {
|
|
282
|
+
return {
|
|
283
|
+
...input,
|
|
284
|
+
provider: BLUESKY_PROVIDER_ID,
|
|
285
|
+
role: input.role ?? "OWNER",
|
|
286
|
+
purpose: input.purpose ?? ["posting", "reading"],
|
|
287
|
+
accessGate: input.accessGate ?? "open",
|
|
288
|
+
status: input.status ?? "pending"
|
|
289
|
+
};
|
|
290
|
+
},
|
|
291
|
+
patchAccount: async (_accountId, patch, _manager) => {
|
|
292
|
+
return { ...patch, provider: BLUESKY_PROVIDER_ID };
|
|
293
|
+
},
|
|
294
|
+
deleteAccount: async (_accountId, _manager) => {}
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// services/bluesky.ts
|
|
299
|
+
import {
|
|
300
|
+
ChannelType as ChannelType3,
|
|
301
|
+
logger as logger3,
|
|
302
|
+
Service
|
|
303
|
+
} from "@elizaos/core";
|
|
304
|
+
|
|
65
305
|
// client.ts
|
|
306
|
+
import { BskyAgent, RichText } from "@atproto/api";
|
|
307
|
+
import { logger } from "@elizaos/core";
|
|
308
|
+
import { LRUCache } from "lru-cache";
|
|
66
309
|
function isPostView(item) {
|
|
67
310
|
return typeof item === "object" && item !== null && "uri" in item && "cid" in item && "author" in item && "record" in item && "indexedAt" in item && typeof item.uri === "string" && typeof item.cid === "string";
|
|
68
311
|
}
|
|
@@ -180,6 +423,18 @@ class BlueSkyClient {
|
|
|
180
423
|
})
|
|
181
424
|
};
|
|
182
425
|
}
|
|
426
|
+
async searchPosts(params) {
|
|
427
|
+
const api = this.agent;
|
|
428
|
+
const response = await api.app.bsky.feed.searchPosts({
|
|
429
|
+
q: params.query,
|
|
430
|
+
limit: params.limit ?? 25,
|
|
431
|
+
cursor: params.cursor
|
|
432
|
+
});
|
|
433
|
+
return {
|
|
434
|
+
posts: response.data.posts.map(adaptPostView),
|
|
435
|
+
cursor: response.data.cursor
|
|
436
|
+
};
|
|
437
|
+
}
|
|
183
438
|
async sendPost(request) {
|
|
184
439
|
if (this.config.dryRun) {
|
|
185
440
|
logger.info(`Dry run: would create post with text: ${request.content.text}`);
|
|
@@ -199,7 +454,20 @@ class BlueSkyClient {
|
|
|
199
454
|
if (request.content.embed) {
|
|
200
455
|
record.embed = request.content.embed;
|
|
201
456
|
}
|
|
457
|
+
logger.info({
|
|
458
|
+
src: "plugin:bluesky",
|
|
459
|
+
op: "atproto:post",
|
|
460
|
+
textLength: rt.text.length,
|
|
461
|
+
hasReply: Boolean(request.replyTo),
|
|
462
|
+
hasEmbed: Boolean(request.content.embed)
|
|
463
|
+
}, "Publishing Bluesky post via atproto");
|
|
202
464
|
const response = await this.agent.post(record);
|
|
465
|
+
logger.info({
|
|
466
|
+
src: "plugin:bluesky",
|
|
467
|
+
op: "atproto:post",
|
|
468
|
+
uri: response.uri,
|
|
469
|
+
cid: response.cid
|
|
470
|
+
}, "Bluesky post published");
|
|
203
471
|
const thread = await this.agent.getPostThread({
|
|
204
472
|
uri: response.uri,
|
|
205
473
|
depth: 0
|
|
@@ -295,65 +563,15 @@ class BlueSkyClient {
|
|
|
295
563
|
}
|
|
296
564
|
|
|
297
565
|
// managers/agent.ts
|
|
298
|
-
import {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
function hasBlueSkyEnabled(runtime) {
|
|
306
|
-
const enabled = runtime.getSetting("BLUESKY_ENABLED");
|
|
307
|
-
if (enabled)
|
|
308
|
-
return String(enabled).toLowerCase() === "true";
|
|
309
|
-
return Boolean(runtime.getSetting("BLUESKY_HANDLE") && runtime.getSetting("BLUESKY_PASSWORD"));
|
|
310
|
-
}
|
|
311
|
-
function validateBlueSkyConfig(runtime) {
|
|
312
|
-
const result = BlueSkyConfigSchema.safeParse({
|
|
313
|
-
handle: String(runtime.getSetting("BLUESKY_HANDLE") ?? ""),
|
|
314
|
-
password: String(runtime.getSetting("BLUESKY_PASSWORD") ?? ""),
|
|
315
|
-
service: String(runtime.getSetting("BLUESKY_SERVICE") ?? BLUESKY_SERVICE_URL),
|
|
316
|
-
dryRun: runtime.getSetting("BLUESKY_DRY_RUN") === "true",
|
|
317
|
-
pollInterval: parseInt(String(runtime.getSetting("BLUESKY_POLL_INTERVAL") ?? ""), 10) || BLUESKY_POLL_INTERVAL,
|
|
318
|
-
enablePost: runtime.getSetting("BLUESKY_ENABLE_POSTING") !== "false",
|
|
319
|
-
postIntervalMin: parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MIN") ?? ""), 10) || BLUESKY_POST_INTERVAL_MIN,
|
|
320
|
-
postIntervalMax: parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MAX") ?? ""), 10) || BLUESKY_POST_INTERVAL_MAX,
|
|
321
|
-
enableActionProcessing: runtime.getSetting("BLUESKY_ENABLE_ACTION_PROCESSING") !== "false",
|
|
322
|
-
actionInterval: parseInt(String(runtime.getSetting("BLUESKY_ACTION_INTERVAL") ?? ""), 10) || BLUESKY_ACTION_INTERVAL,
|
|
323
|
-
postImmediately: runtime.getSetting("BLUESKY_POST_IMMEDIATELY") === "true",
|
|
324
|
-
maxActionsProcessing: parseInt(String(runtime.getSetting("BLUESKY_MAX_ACTIONS_PROCESSING") ?? ""), 10) || BLUESKY_MAX_ACTIONS,
|
|
325
|
-
enableDMs: runtime.getSetting("BLUESKY_ENABLE_DMS") !== "false"
|
|
326
|
-
});
|
|
327
|
-
if (!result.success) {
|
|
328
|
-
const errors = result.error.errors?.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ") || result.error.toString();
|
|
329
|
-
throw new Error(`Invalid BlueSky configuration: ${errors}`);
|
|
330
|
-
}
|
|
331
|
-
return result.data;
|
|
332
|
-
}
|
|
333
|
-
function getPollInterval(runtime) {
|
|
334
|
-
const seconds = parseInt(String(runtime.getSetting("BLUESKY_POLL_INTERVAL") ?? ""), 10) || BLUESKY_POLL_INTERVAL;
|
|
335
|
-
return seconds * 1000;
|
|
336
|
-
}
|
|
337
|
-
function getActionInterval(runtime) {
|
|
338
|
-
const seconds = parseInt(String(runtime.getSetting("BLUESKY_ACTION_INTERVAL") ?? ""), 10) || BLUESKY_ACTION_INTERVAL;
|
|
339
|
-
return seconds * 1000;
|
|
340
|
-
}
|
|
341
|
-
function getMaxActionsProcessing(runtime) {
|
|
342
|
-
return parseInt(String(runtime.getSetting("BLUESKY_MAX_ACTIONS_PROCESSING") ?? ""), 10) || BLUESKY_MAX_ACTIONS;
|
|
343
|
-
}
|
|
344
|
-
function isPostingEnabled(runtime) {
|
|
345
|
-
return runtime.getSetting("BLUESKY_ENABLE_POSTING") !== "false";
|
|
346
|
-
}
|
|
347
|
-
function shouldPostImmediately(runtime) {
|
|
348
|
-
return runtime.getSetting("BLUESKY_POST_IMMEDIATELY") === "true";
|
|
349
|
-
}
|
|
350
|
-
function getPostIntervalRange(runtime) {
|
|
351
|
-
const min = parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MIN") ?? ""), 10) || BLUESKY_POST_INTERVAL_MIN;
|
|
352
|
-
const max = parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MAX") ?? ""), 10) || BLUESKY_POST_INTERVAL_MAX;
|
|
353
|
-
return { min: min * 1000, max: max * 1000 };
|
|
566
|
+
import {
|
|
567
|
+
logger as logger2,
|
|
568
|
+
setTrajectoryPurpose,
|
|
569
|
+
withStandaloneTrajectory
|
|
570
|
+
} from "@elizaos/core";
|
|
571
|
+
function cursorCacheKey(agentId, accountId) {
|
|
572
|
+
return `bluesky:cursor:${agentId}:${accountId}`;
|
|
354
573
|
}
|
|
355
574
|
|
|
356
|
-
// managers/agent.ts
|
|
357
575
|
class BlueSkyAgentManager {
|
|
358
576
|
runtime;
|
|
359
577
|
config;
|
|
@@ -363,24 +581,33 @@ class BlueSkyAgentManager {
|
|
|
363
581
|
postTimer = null;
|
|
364
582
|
running = false;
|
|
365
583
|
lastSeenAt = null;
|
|
584
|
+
accountId;
|
|
366
585
|
constructor(runtime, config, client) {
|
|
367
586
|
this.runtime = runtime;
|
|
368
587
|
this.config = config;
|
|
369
588
|
this.client = client;
|
|
589
|
+
this.accountId = normalizeBlueSkyAccountId(config.accountId ?? DEFAULT_BLUESKY_ACCOUNT_ID);
|
|
590
|
+
}
|
|
591
|
+
getAccountId() {
|
|
592
|
+
return this.accountId;
|
|
370
593
|
}
|
|
371
594
|
async start() {
|
|
372
595
|
if (this.running)
|
|
373
596
|
return;
|
|
374
597
|
await this.client.authenticate();
|
|
375
598
|
this.running = true;
|
|
599
|
+
const cached = await this.runtime.getCache(cursorCacheKey(this.runtime.agentId, this.accountId));
|
|
600
|
+
if (typeof cached === "string" && cached) {
|
|
601
|
+
this.lastSeenAt = cached;
|
|
602
|
+
}
|
|
376
603
|
this.startNotificationPolling();
|
|
377
604
|
if (this.config.enableActionProcessing) {
|
|
378
605
|
this.startActionProcessing();
|
|
379
606
|
}
|
|
380
|
-
if (isPostingEnabled(this.runtime)) {
|
|
607
|
+
if (isPostingEnabled(this.runtime, this.accountId)) {
|
|
381
608
|
this.startAutomatedPosting();
|
|
382
609
|
}
|
|
383
|
-
logger2.success({ agentId: this.runtime.agentId }, "BlueSky agent manager started");
|
|
610
|
+
logger2.success({ agentId: this.runtime.agentId, accountId: this.accountId }, "BlueSky agent manager started");
|
|
384
611
|
}
|
|
385
612
|
async stop() {
|
|
386
613
|
this.running = false;
|
|
@@ -394,10 +621,10 @@ class BlueSkyAgentManager {
|
|
|
394
621
|
this.actionTimer = null;
|
|
395
622
|
this.postTimer = null;
|
|
396
623
|
await this.client.cleanup();
|
|
397
|
-
logger2.info({ agentId: this.runtime.agentId }, "BlueSky agent manager stopped");
|
|
624
|
+
logger2.info({ agentId: this.runtime.agentId, accountId: this.accountId }, "BlueSky agent manager stopped");
|
|
398
625
|
}
|
|
399
626
|
startNotificationPolling() {
|
|
400
|
-
const interval = getPollInterval(this.runtime);
|
|
627
|
+
const interval = getPollInterval(this.runtime, this.accountId);
|
|
401
628
|
this.pollNotifications();
|
|
402
629
|
this.pollTimer = setInterval(() => this.pollNotifications(), interval);
|
|
403
630
|
}
|
|
@@ -413,6 +640,7 @@ class BlueSkyAgentManager {
|
|
|
413
640
|
}) : notifications;
|
|
414
641
|
if (newNotifications.length > 0) {
|
|
415
642
|
this.lastSeenAt = notifications[0].indexedAt;
|
|
643
|
+
await this.runtime.setCache(cursorCacheKey(this.runtime.agentId, this.accountId), this.lastSeenAt);
|
|
416
644
|
for (const notification of newNotifications) {
|
|
417
645
|
this.emitNotificationEvent(notification);
|
|
418
646
|
}
|
|
@@ -433,26 +661,28 @@ class BlueSkyAgentManager {
|
|
|
433
661
|
const payload = {
|
|
434
662
|
runtime: this.runtime,
|
|
435
663
|
source: "bluesky",
|
|
664
|
+
accountId: this.accountId,
|
|
436
665
|
notification
|
|
437
666
|
};
|
|
438
667
|
this.runtime.emitEvent(event, payload);
|
|
439
668
|
}
|
|
440
669
|
}
|
|
441
670
|
startActionProcessing() {
|
|
442
|
-
const interval = getActionInterval(this.runtime);
|
|
443
|
-
this.
|
|
444
|
-
this.actionTimer = setInterval(() => this.
|
|
671
|
+
const interval = getActionInterval(this.runtime, this.accountId);
|
|
672
|
+
this.processQueuedActions();
|
|
673
|
+
this.actionTimer = setInterval(() => this.processQueuedActions(), interval);
|
|
445
674
|
}
|
|
446
|
-
async
|
|
675
|
+
async processQueuedActions() {
|
|
447
676
|
if (!this.running)
|
|
448
677
|
return;
|
|
449
|
-
const max = getMaxActionsProcessing(this.runtime);
|
|
678
|
+
const max = getMaxActionsProcessing(this.runtime, this.accountId);
|
|
450
679
|
const { notifications } = await this.client.getNotifications(max);
|
|
451
680
|
for (const notification of notifications) {
|
|
452
681
|
if (notification.reason === "mention" || notification.reason === "reply") {
|
|
453
682
|
const payload = {
|
|
454
683
|
runtime: this.runtime,
|
|
455
684
|
source: "bluesky",
|
|
685
|
+
accountId: this.accountId,
|
|
456
686
|
notification
|
|
457
687
|
};
|
|
458
688
|
this.runtime.emitEvent("bluesky.should_respond", payload);
|
|
@@ -460,47 +690,93 @@ class BlueSkyAgentManager {
|
|
|
460
690
|
}
|
|
461
691
|
}
|
|
462
692
|
startAutomatedPosting() {
|
|
463
|
-
if (shouldPostImmediately(this.runtime)) {
|
|
693
|
+
if (shouldPostImmediately(this.runtime, this.accountId)) {
|
|
464
694
|
this.createAutomatedPost();
|
|
465
695
|
}
|
|
466
696
|
this.scheduleNextPost();
|
|
467
697
|
}
|
|
468
698
|
scheduleNextPost() {
|
|
469
|
-
const { min, max } = getPostIntervalRange(this.runtime);
|
|
699
|
+
const { min, max } = getPostIntervalRange(this.runtime, this.accountId);
|
|
470
700
|
const interval = Math.random() * (max - min) + min;
|
|
471
701
|
this.postTimer = setTimeout(() => {
|
|
472
702
|
if (this.running) {
|
|
473
|
-
this.createAutomatedPost();
|
|
474
|
-
this.scheduleNextPost();
|
|
703
|
+
this.createAutomatedPost().finally(() => this.scheduleNextPost());
|
|
475
704
|
}
|
|
476
705
|
}, interval);
|
|
477
706
|
}
|
|
478
|
-
createAutomatedPost() {
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
707
|
+
async createAutomatedPost() {
|
|
708
|
+
await withStandaloneTrajectory(this.runtime, {
|
|
709
|
+
source: "plugin-bluesky:auto-post",
|
|
710
|
+
metadata: {
|
|
711
|
+
platform: "bluesky",
|
|
712
|
+
kind: "public_post_generation",
|
|
713
|
+
automated: true,
|
|
714
|
+
accountId: this.accountId
|
|
715
|
+
}
|
|
716
|
+
}, async () => {
|
|
717
|
+
setTrajectoryPurpose("background");
|
|
718
|
+
const payload = {
|
|
719
|
+
runtime: this.runtime,
|
|
720
|
+
source: "bluesky",
|
|
721
|
+
automated: true,
|
|
722
|
+
accountId: this.accountId
|
|
723
|
+
};
|
|
724
|
+
await this.runtime.emitEvent("bluesky.create_post", payload);
|
|
725
|
+
});
|
|
485
726
|
}
|
|
486
727
|
}
|
|
487
728
|
|
|
488
729
|
// services/message.ts
|
|
489
|
-
import {
|
|
730
|
+
import {
|
|
731
|
+
ChannelType,
|
|
732
|
+
composePrompt,
|
|
733
|
+
createUniqueUuid,
|
|
734
|
+
ModelType
|
|
735
|
+
} from "@elizaos/core";
|
|
490
736
|
|
|
491
|
-
//
|
|
737
|
+
// prompts.ts
|
|
492
738
|
var generateDmTemplate = `Generate a friendly direct message response under 200 characters.`;
|
|
493
739
|
var generatePostTemplate = `Generate an engaging BlueSky post under {{maxLength}} characters.`;
|
|
494
740
|
var truncatePostTemplate = `Shorten to under {{maxLength}} characters: "{{text}}"`;
|
|
495
741
|
|
|
496
742
|
// services/message.ts
|
|
743
|
+
var BLUESKY_CONNECTOR_CONTEXTS = ["social", "connectors"];
|
|
744
|
+
function normalizeBlueSkyQuery(value) {
|
|
745
|
+
return value.trim().replace(/^@/, "").toLowerCase();
|
|
746
|
+
}
|
|
747
|
+
function scoreBlueSkyMatch(query, id, labels) {
|
|
748
|
+
if (!query)
|
|
749
|
+
return 0.45;
|
|
750
|
+
if (id.toLowerCase() === query)
|
|
751
|
+
return 1;
|
|
752
|
+
let bestScore = 0;
|
|
753
|
+
for (const label of labels) {
|
|
754
|
+
const normalized = label?.trim().replace(/^@/, "").toLowerCase();
|
|
755
|
+
if (!normalized)
|
|
756
|
+
continue;
|
|
757
|
+
if (normalized === query) {
|
|
758
|
+
bestScore = Math.max(bestScore, 0.95);
|
|
759
|
+
} else if (normalized.startsWith(query)) {
|
|
760
|
+
bestScore = Math.max(bestScore, 0.85);
|
|
761
|
+
} else if (normalized.includes(query)) {
|
|
762
|
+
bestScore = Math.max(bestScore, 0.7);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
return bestScore;
|
|
766
|
+
}
|
|
767
|
+
|
|
497
768
|
class BlueSkyMessageService {
|
|
498
769
|
client;
|
|
499
770
|
runtime;
|
|
771
|
+
accountId;
|
|
500
772
|
static serviceType = "IMessageService";
|
|
501
|
-
constructor(client, runtime) {
|
|
773
|
+
constructor(client, runtime, accountId = "default") {
|
|
502
774
|
this.client = client;
|
|
503
775
|
this.runtime = runtime;
|
|
776
|
+
this.accountId = accountId;
|
|
777
|
+
}
|
|
778
|
+
getAccountId() {
|
|
779
|
+
return normalizeBlueSkyAccountId(this.accountId);
|
|
504
780
|
}
|
|
505
781
|
async getMessages(convoId, limit = 50) {
|
|
506
782
|
const response = await this.client.getMessages(convoId, limit);
|
|
@@ -514,6 +790,215 @@ class BlueSkyMessageService {
|
|
|
514
790
|
const response = await this.client.getConversations(limit);
|
|
515
791
|
return response.conversations;
|
|
516
792
|
}
|
|
793
|
+
async handleSendMessage(runtime, target, content) {
|
|
794
|
+
const requestedAccountId = normalizeBlueSkyAccountId(target.accountId ?? readBlueSkyAccountId(content, target) ?? this.getAccountId());
|
|
795
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
796
|
+
throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
|
|
797
|
+
}
|
|
798
|
+
const text = typeof content.text === "string" ? content.text.trim() : "";
|
|
799
|
+
if (!text) {
|
|
800
|
+
throw new Error("BlueSky DM connector requires non-empty text content.");
|
|
801
|
+
}
|
|
802
|
+
let convoId = target.channelId ?? target.threadId;
|
|
803
|
+
if (!convoId && target.roomId) {
|
|
804
|
+
const room = await runtime.getRoom(target.roomId);
|
|
805
|
+
convoId = room?.channelId;
|
|
806
|
+
}
|
|
807
|
+
if (!convoId) {
|
|
808
|
+
throw new Error("BlueSky DM connector requires a conversation target.");
|
|
809
|
+
}
|
|
810
|
+
await this.sendMessage(convoId, text);
|
|
811
|
+
}
|
|
812
|
+
async fetchConnectorMessages(context, params = {}) {
|
|
813
|
+
const requestedAccountId = normalizeBlueSkyAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
|
|
814
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
815
|
+
throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
|
|
816
|
+
}
|
|
817
|
+
const target = params.target ?? context.target;
|
|
818
|
+
let convoId = target?.channelId ?? target?.threadId;
|
|
819
|
+
if (!convoId && target?.roomId) {
|
|
820
|
+
const room = await context.runtime.getRoom(target.roomId);
|
|
821
|
+
convoId = room?.channelId;
|
|
822
|
+
}
|
|
823
|
+
if (convoId) {
|
|
824
|
+
const messages = await this.getMessages(convoId, clampLimit(params.limit, 25, 100));
|
|
825
|
+
return messages.map((message) => this.messageToMemory(context.runtime, message, convoId));
|
|
826
|
+
}
|
|
827
|
+
const conversations = await this.getConversations(clampLimit(params.limit, 25, 50));
|
|
828
|
+
const memories = [];
|
|
829
|
+
for (const conversation of conversations) {
|
|
830
|
+
const messages = await this.getMessages(conversation.id, 1);
|
|
831
|
+
memories.push(...messages.map((message) => this.messageToMemory(context.runtime, message, conversation.id)));
|
|
832
|
+
}
|
|
833
|
+
return memories.sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0));
|
|
834
|
+
}
|
|
835
|
+
async resolveConnectorTargets(query, _context) {
|
|
836
|
+
const normalizedQuery = normalizeBlueSkyQuery(query);
|
|
837
|
+
const conversations = await this.getConversations(50);
|
|
838
|
+
return conversations.map((conversation) => {
|
|
839
|
+
const score = scoreBlueSkyMatch(normalizedQuery, conversation.id, [
|
|
840
|
+
...conversation.members.flatMap((member) => [
|
|
841
|
+
member.handle,
|
|
842
|
+
member.displayName,
|
|
843
|
+
member.did
|
|
844
|
+
])
|
|
845
|
+
]);
|
|
846
|
+
return score > 0 ? this.buildConversationTarget(conversation, score) : null;
|
|
847
|
+
}).filter((target) => Boolean(target)).slice(0, 25);
|
|
848
|
+
}
|
|
849
|
+
async listConnectorRooms(_context) {
|
|
850
|
+
const conversations = await this.getConversations(50);
|
|
851
|
+
return conversations.map((conversation) => this.buildConversationTarget(conversation, 0.5));
|
|
852
|
+
}
|
|
853
|
+
async listRecentConnectorTargets(context) {
|
|
854
|
+
const targets = [];
|
|
855
|
+
const room = context.roomId && typeof context.runtime.getRoom === "function" ? await context.runtime.getRoom(context.roomId) : null;
|
|
856
|
+
const convoId = context.target?.channelId ?? room?.channelId;
|
|
857
|
+
if (convoId) {
|
|
858
|
+
targets.push({
|
|
859
|
+
target: {
|
|
860
|
+
source: "bluesky",
|
|
861
|
+
accountId: this.getAccountId(),
|
|
862
|
+
channelId: convoId
|
|
863
|
+
},
|
|
864
|
+
label: `BlueSky conversation ${convoId}`,
|
|
865
|
+
kind: "thread",
|
|
866
|
+
score: 0.95,
|
|
867
|
+
contexts: [...BLUESKY_CONNECTOR_CONTEXTS],
|
|
868
|
+
metadata: { accountId: this.getAccountId(), blueskyConvoId: convoId }
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
targets.push(...await this.listConnectorRooms(context));
|
|
872
|
+
const seen = new Set;
|
|
873
|
+
return targets.filter((target) => {
|
|
874
|
+
const channelId = target.target.channelId;
|
|
875
|
+
if (!channelId || seen.has(channelId))
|
|
876
|
+
return false;
|
|
877
|
+
seen.add(channelId);
|
|
878
|
+
return true;
|
|
879
|
+
}).slice(0, 25);
|
|
880
|
+
}
|
|
881
|
+
async getConnectorChatContext(target, context) {
|
|
882
|
+
let convoId = target.channelId ?? target.threadId;
|
|
883
|
+
if (!convoId && target.roomId) {
|
|
884
|
+
const room = await context.runtime.getRoom(target.roomId);
|
|
885
|
+
convoId = room?.channelId;
|
|
886
|
+
}
|
|
887
|
+
if (!convoId)
|
|
888
|
+
return null;
|
|
889
|
+
const messages = await this.getMessages(convoId, 25);
|
|
890
|
+
return {
|
|
891
|
+
target: {
|
|
892
|
+
source: "bluesky",
|
|
893
|
+
accountId: this.getAccountId(),
|
|
894
|
+
channelId: convoId
|
|
895
|
+
},
|
|
896
|
+
label: `BlueSky conversation ${convoId}`,
|
|
897
|
+
recentMessages: messages.map((message) => ({
|
|
898
|
+
name: message.sender.did,
|
|
899
|
+
text: message.text ?? "",
|
|
900
|
+
timestamp: Date.parse(message.sentAt),
|
|
901
|
+
metadata: {
|
|
902
|
+
accountId: this.getAccountId(),
|
|
903
|
+
blueskyMessageId: message.id,
|
|
904
|
+
blueskySenderDid: message.sender.did
|
|
905
|
+
}
|
|
906
|
+
})),
|
|
907
|
+
metadata: { accountId: this.getAccountId(), blueskyConvoId: convoId }
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
async getConnectorUserContext(entityId, _context) {
|
|
911
|
+
const normalizedEntity = entityId.trim().replace(/^@/, "");
|
|
912
|
+
if (!normalizedEntity)
|
|
913
|
+
return null;
|
|
914
|
+
const conversations = await this.getConversations(50);
|
|
915
|
+
for (const conversation of conversations) {
|
|
916
|
+
const member = conversation.members.find((candidate) => candidate.did === entityId || candidate.handle === normalizedEntity || candidate.displayName === entityId);
|
|
917
|
+
if (!member)
|
|
918
|
+
continue;
|
|
919
|
+
return {
|
|
920
|
+
entityId,
|
|
921
|
+
label: member.displayName || member.handle || member.did,
|
|
922
|
+
aliases: [member.handle, member.displayName, member.did].filter((value) => Boolean(value)),
|
|
923
|
+
handles: {
|
|
924
|
+
bluesky: member.handle ?? member.did
|
|
925
|
+
},
|
|
926
|
+
metadata: {
|
|
927
|
+
accountId: this.getAccountId(),
|
|
928
|
+
blueskyDid: member.did,
|
|
929
|
+
blueskyHandle: member.handle,
|
|
930
|
+
avatar: member.avatar
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
return null;
|
|
935
|
+
}
|
|
936
|
+
buildConversationTarget(conversation, score) {
|
|
937
|
+
const sessionDid = this.client.getSession()?.did;
|
|
938
|
+
const otherMembers = conversation.members.filter((member) => member.did !== sessionDid);
|
|
939
|
+
const label = otherMembers.map((member) => member.displayName || member.handle || member.did).filter(Boolean).join(", ") || `BlueSky conversation ${conversation.id}`;
|
|
940
|
+
return {
|
|
941
|
+
target: {
|
|
942
|
+
source: "bluesky",
|
|
943
|
+
accountId: this.getAccountId(),
|
|
944
|
+
channelId: conversation.id
|
|
945
|
+
},
|
|
946
|
+
label,
|
|
947
|
+
kind: "thread",
|
|
948
|
+
description: "BlueSky direct message conversation",
|
|
949
|
+
score,
|
|
950
|
+
contexts: [...BLUESKY_CONNECTOR_CONTEXTS],
|
|
951
|
+
metadata: {
|
|
952
|
+
accountId: this.getAccountId(),
|
|
953
|
+
blueskyConvoId: conversation.id,
|
|
954
|
+
unreadCount: conversation.unreadCount,
|
|
955
|
+
muted: conversation.muted,
|
|
956
|
+
members: conversation.members.map((member) => ({
|
|
957
|
+
did: member.did,
|
|
958
|
+
handle: member.handle,
|
|
959
|
+
displayName: member.displayName
|
|
960
|
+
}))
|
|
961
|
+
}
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
messageToMemory(runtime, message, convoId) {
|
|
965
|
+
const senderDid = message.sender.did || "unknown";
|
|
966
|
+
const createdAt = Date.parse(message.sentAt) || Date.now();
|
|
967
|
+
const entityId = senderDid === runtime.agentId ? runtime.agentId : createUniqueUuid(runtime, `bluesky:user:${senderDid}`);
|
|
968
|
+
const roomId = createUniqueUuid(runtime, `bluesky:dm:${convoId}`);
|
|
969
|
+
return {
|
|
970
|
+
id: createUniqueUuid(runtime, `bluesky:dm:${message.id}`),
|
|
971
|
+
agentId: runtime.agentId,
|
|
972
|
+
entityId,
|
|
973
|
+
roomId,
|
|
974
|
+
createdAt,
|
|
975
|
+
content: {
|
|
976
|
+
text: message.text ?? "",
|
|
977
|
+
source: "bluesky",
|
|
978
|
+
channelType: ChannelType.DM
|
|
979
|
+
},
|
|
980
|
+
metadata: {
|
|
981
|
+
type: "message",
|
|
982
|
+
source: "bluesky",
|
|
983
|
+
accountId: this.getAccountId(),
|
|
984
|
+
provider: "bluesky",
|
|
985
|
+
timestamp: createdAt,
|
|
986
|
+
fromBot: entityId === runtime.agentId,
|
|
987
|
+
messageIdFull: message.id,
|
|
988
|
+
chatType: ChannelType.DM,
|
|
989
|
+
sender: {
|
|
990
|
+
id: senderDid
|
|
991
|
+
},
|
|
992
|
+
bluesky: {
|
|
993
|
+
accountId: this.getAccountId(),
|
|
994
|
+
messageId: message.id,
|
|
995
|
+
convoId,
|
|
996
|
+
rev: message.rev,
|
|
997
|
+
senderDid
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
517
1002
|
async generateReply() {
|
|
518
1003
|
const prompt = composePrompt({
|
|
519
1004
|
state: {},
|
|
@@ -526,16 +1011,46 @@ class BlueSkyMessageService {
|
|
|
526
1011
|
return response;
|
|
527
1012
|
}
|
|
528
1013
|
}
|
|
1014
|
+
function clampLimit(value, fallback, max) {
|
|
1015
|
+
if (!Number.isFinite(value))
|
|
1016
|
+
return fallback;
|
|
1017
|
+
return Math.min(Math.max(1, Math.floor(value)), max);
|
|
1018
|
+
}
|
|
529
1019
|
|
|
530
1020
|
// services/post.ts
|
|
531
|
-
import {
|
|
1021
|
+
import {
|
|
1022
|
+
ChannelType as ChannelType2,
|
|
1023
|
+
composePrompt as composePrompt2,
|
|
1024
|
+
createUniqueUuid as createUniqueUuid2,
|
|
1025
|
+
ModelType as ModelType2
|
|
1026
|
+
} from "@elizaos/core";
|
|
1027
|
+
function clampLimit2(value, fallback, max) {
|
|
1028
|
+
if (!Number.isFinite(value))
|
|
1029
|
+
return fallback;
|
|
1030
|
+
return Math.min(Math.max(1, Math.floor(value)), max);
|
|
1031
|
+
}
|
|
1032
|
+
function readContentString(content, keys) {
|
|
1033
|
+
const record = content;
|
|
1034
|
+
for (const key of keys) {
|
|
1035
|
+
const value = record[key];
|
|
1036
|
+
if (typeof value === "string" && value.trim())
|
|
1037
|
+
return value.trim();
|
|
1038
|
+
}
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
532
1042
|
class BlueSkyPostService {
|
|
533
1043
|
client;
|
|
534
1044
|
runtime;
|
|
1045
|
+
accountId;
|
|
535
1046
|
static serviceType = "IPostService";
|
|
536
|
-
constructor(client, runtime) {
|
|
1047
|
+
constructor(client, runtime, accountId = "default") {
|
|
537
1048
|
this.client = client;
|
|
538
1049
|
this.runtime = runtime;
|
|
1050
|
+
this.accountId = accountId;
|
|
1051
|
+
}
|
|
1052
|
+
getAccountId() {
|
|
1053
|
+
return normalizeBlueSkyAccountId(this.accountId);
|
|
539
1054
|
}
|
|
540
1055
|
async getPosts(limit = 50, cursor) {
|
|
541
1056
|
const response = await this.client.getTimeline({ limit, cursor });
|
|
@@ -552,9 +1067,91 @@ class BlueSkyPostService {
|
|
|
552
1067
|
};
|
|
553
1068
|
return this.client.sendPost(request);
|
|
554
1069
|
}
|
|
1070
|
+
async handleSendPost(runtime, content) {
|
|
1071
|
+
const requestedAccountId = normalizeBlueSkyAccountId(readBlueSkyAccountId(content) ?? this.getAccountId());
|
|
1072
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
1073
|
+
throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
|
|
1074
|
+
}
|
|
1075
|
+
const replyUri = readContentString(content, ["replyToUri", "replyTo"]);
|
|
1076
|
+
const replyCid = readContentString(content, ["replyToCid"]);
|
|
1077
|
+
const post = await this.createPost(content.text ?? "", replyUri && replyCid ? { uri: replyUri, cid: replyCid } : undefined);
|
|
1078
|
+
return this.postToMemory(runtime, post);
|
|
1079
|
+
}
|
|
1080
|
+
async fetchFeed(context, params = {}) {
|
|
1081
|
+
const requestedAccountId = normalizeBlueSkyAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
|
|
1082
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
1083
|
+
throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
|
|
1084
|
+
}
|
|
1085
|
+
const response = await this.client.getTimeline({
|
|
1086
|
+
limit: clampLimit2(params.limit, 25, 100),
|
|
1087
|
+
cursor: params.cursor
|
|
1088
|
+
});
|
|
1089
|
+
return response.feed.map((item) => this.postToMemory(context.runtime, item.post));
|
|
1090
|
+
}
|
|
1091
|
+
async searchPosts(context, params) {
|
|
1092
|
+
const requestedAccountId = normalizeBlueSkyAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
|
|
1093
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
1094
|
+
throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
|
|
1095
|
+
}
|
|
1096
|
+
const query = params.query?.trim();
|
|
1097
|
+
if (!query) {
|
|
1098
|
+
throw new Error("BlueSky searchPosts connector requires a query.");
|
|
1099
|
+
}
|
|
1100
|
+
const response = await this.client.searchPosts({
|
|
1101
|
+
query,
|
|
1102
|
+
limit: clampLimit2(params.limit, 25, 100),
|
|
1103
|
+
cursor: params.cursor
|
|
1104
|
+
});
|
|
1105
|
+
return response.posts.map((post) => this.postToMemory(context.runtime, post));
|
|
1106
|
+
}
|
|
555
1107
|
async deletePost(uri) {
|
|
556
1108
|
await this.client.deletePost(uri);
|
|
557
1109
|
}
|
|
1110
|
+
postToMemory(runtime, post) {
|
|
1111
|
+
const createdAt = Date.parse(post.indexedAt || post.record.createdAt) || Date.now();
|
|
1112
|
+
const authorId = post.author.did || post.author.handle || "unknown";
|
|
1113
|
+
const entityId = authorId === runtime.agentId ? runtime.agentId : createUniqueUuid2(runtime, `bluesky:user:${authorId}`);
|
|
1114
|
+
const roomId = createUniqueUuid2(runtime, `bluesky:feed:${authorId}`);
|
|
1115
|
+
return {
|
|
1116
|
+
id: createUniqueUuid2(runtime, `bluesky:post:${post.uri}`),
|
|
1117
|
+
agentId: runtime.agentId,
|
|
1118
|
+
entityId,
|
|
1119
|
+
roomId,
|
|
1120
|
+
createdAt,
|
|
1121
|
+
content: {
|
|
1122
|
+
text: post.record.text,
|
|
1123
|
+
source: "bluesky",
|
|
1124
|
+
url: `https://bsky.app/profile/${post.author.handle}/post/${post.uri.split("/").pop()}`,
|
|
1125
|
+
channelType: ChannelType2.FEED
|
|
1126
|
+
},
|
|
1127
|
+
metadata: {
|
|
1128
|
+
type: "message",
|
|
1129
|
+
source: "bluesky",
|
|
1130
|
+
accountId: this.getAccountId(),
|
|
1131
|
+
provider: "bluesky",
|
|
1132
|
+
timestamp: createdAt,
|
|
1133
|
+
fromBot: entityId === runtime.agentId,
|
|
1134
|
+
messageIdFull: post.uri,
|
|
1135
|
+
chatType: ChannelType2.FEED,
|
|
1136
|
+
sender: {
|
|
1137
|
+
id: authorId,
|
|
1138
|
+
name: post.author.displayName,
|
|
1139
|
+
username: post.author.handle
|
|
1140
|
+
},
|
|
1141
|
+
bluesky: {
|
|
1142
|
+
accountId: this.getAccountId(),
|
|
1143
|
+
uri: post.uri,
|
|
1144
|
+
cid: post.cid,
|
|
1145
|
+
authorDid: post.author.did,
|
|
1146
|
+
authorHandle: post.author.handle,
|
|
1147
|
+
replyCount: post.replyCount,
|
|
1148
|
+
repostCount: post.repostCount,
|
|
1149
|
+
likeCount: post.likeCount,
|
|
1150
|
+
quoteCount: post.quoteCount
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
};
|
|
1154
|
+
}
|
|
558
1155
|
async generateContent() {
|
|
559
1156
|
const prompt = composePrompt2({
|
|
560
1157
|
state: {
|
|
@@ -588,9 +1185,7 @@ class BlueSkyPostService {
|
|
|
588
1185
|
// services/bluesky.ts
|
|
589
1186
|
class BlueSkyService extends Service {
|
|
590
1187
|
static instance;
|
|
591
|
-
|
|
592
|
-
messageServices = new Map;
|
|
593
|
-
postServices = new Map;
|
|
1188
|
+
agents = new Map;
|
|
594
1189
|
static serviceType = BLUESKY_SERVICE_NAME;
|
|
595
1190
|
capabilityDescription = "Send and receive messages on BlueSky";
|
|
596
1191
|
static getInstance() {
|
|
@@ -599,48 +1194,230 @@ class BlueSkyService extends Service {
|
|
|
599
1194
|
}
|
|
600
1195
|
static async start(runtime) {
|
|
601
1196
|
const service = BlueSkyService.getInstance();
|
|
602
|
-
if (service.
|
|
1197
|
+
if (service.agents.has(runtime.agentId)) {
|
|
603
1198
|
return service;
|
|
604
1199
|
}
|
|
605
1200
|
if (!hasBlueSkyEnabled(runtime)) {
|
|
606
1201
|
return service;
|
|
607
1202
|
}
|
|
608
|
-
const
|
|
609
|
-
const
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
service.
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
1203
|
+
const accountIds = listBlueSkyAccountIds(runtime);
|
|
1204
|
+
const defaultAccountId = normalizeBlueSkyAccountId(resolveDefaultBlueSkyAccountId(runtime));
|
|
1205
|
+
const accounts = {
|
|
1206
|
+
defaultAccountId,
|
|
1207
|
+
managers: new Map,
|
|
1208
|
+
messageServices: new Map,
|
|
1209
|
+
postServices: new Map
|
|
1210
|
+
};
|
|
1211
|
+
service.agents.set(runtime.agentId, accounts);
|
|
1212
|
+
for (const accountId of accountIds) {
|
|
1213
|
+
if (!hasBlueSkyEnabled(runtime, accountId)) {
|
|
1214
|
+
continue;
|
|
1215
|
+
}
|
|
1216
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
1217
|
+
if (!config.handle || !config.password) {
|
|
1218
|
+
logger3.warn({ agentId: runtime.agentId, accountId }, "Skipping BlueSky account without handle/password");
|
|
1219
|
+
continue;
|
|
1220
|
+
}
|
|
1221
|
+
const client = new BlueSkyClient({
|
|
1222
|
+
service: config.service,
|
|
1223
|
+
handle: config.handle,
|
|
1224
|
+
password: config.password,
|
|
1225
|
+
dryRun: config.dryRun
|
|
1226
|
+
});
|
|
1227
|
+
const manager = new BlueSkyAgentManager(runtime, config, client);
|
|
1228
|
+
accounts.managers.set(accountId, manager);
|
|
1229
|
+
accounts.messageServices.set(accountId, new BlueSkyMessageService(client, runtime, accountId));
|
|
1230
|
+
accounts.postServices.set(accountId, new BlueSkyPostService(client, runtime, accountId));
|
|
1231
|
+
await manager.start();
|
|
1232
|
+
logger3.success({ agentId: runtime.agentId, accountId }, "BlueSky client started");
|
|
1233
|
+
}
|
|
621
1234
|
return service;
|
|
622
1235
|
}
|
|
623
1236
|
static async stop(runtime) {
|
|
624
1237
|
const service = BlueSkyService.getInstance();
|
|
625
|
-
const
|
|
626
|
-
if (!
|
|
1238
|
+
const accounts = service.agents.get(runtime.agentId);
|
|
1239
|
+
if (!accounts)
|
|
627
1240
|
return;
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
service.
|
|
1241
|
+
for (const manager of accounts.managers.values()) {
|
|
1242
|
+
await manager.stop();
|
|
1243
|
+
}
|
|
1244
|
+
service.agents.delete(runtime.agentId);
|
|
632
1245
|
logger3.info({ agentId: runtime.agentId }, "BlueSky client stopped");
|
|
633
1246
|
}
|
|
1247
|
+
static registerSendHandlers(runtime, serviceInstance) {
|
|
1248
|
+
const accounts = serviceInstance?.agents.get(runtime.agentId);
|
|
1249
|
+
if (!accounts) {
|
|
1250
|
+
runtime.logger.warn({ src: "plugin:bluesky", agentId: runtime.agentId }, "Cannot register BlueSky connectors; service is not initialized");
|
|
1251
|
+
return;
|
|
1252
|
+
}
|
|
1253
|
+
for (const postService of accounts.postServices.values()) {
|
|
1254
|
+
BlueSkyService.registerPostConnector(runtime, postService);
|
|
1255
|
+
}
|
|
1256
|
+
if (typeof runtime.registerMessageConnector === "function" && accounts.messageServices.size > 0) {
|
|
1257
|
+
for (const messageService2 of accounts.messageServices.values()) {
|
|
1258
|
+
BlueSkyService.registerMessageConnector(runtime, messageService2);
|
|
1259
|
+
}
|
|
1260
|
+
return;
|
|
1261
|
+
}
|
|
1262
|
+
const defaultAccountId = normalizeBlueSkyAccountId(accounts.defaultAccountId);
|
|
1263
|
+
const messageService = accounts.messageServices.get(defaultAccountId);
|
|
1264
|
+
if (!messageService) {
|
|
1265
|
+
runtime.logger.warn({ src: "plugin:bluesky", agentId: runtime.agentId }, "Cannot register legacy BlueSky DM send handler; default message service is not initialized");
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
runtime.registerSendHandler("bluesky", messageService.handleSendMessage.bind(messageService));
|
|
1269
|
+
}
|
|
1270
|
+
static registerMessageConnector(runtime, messageService) {
|
|
1271
|
+
const accountId = messageService.getAccountId();
|
|
1272
|
+
const registration = {
|
|
1273
|
+
source: "bluesky",
|
|
1274
|
+
accountId,
|
|
1275
|
+
label: "BlueSky",
|
|
1276
|
+
description: "BlueSky DM connector for sending private messages to conversations.",
|
|
1277
|
+
capabilities: [
|
|
1278
|
+
"send_message",
|
|
1279
|
+
"fetch_messages",
|
|
1280
|
+
"resolve_targets",
|
|
1281
|
+
"list_rooms",
|
|
1282
|
+
"chat_context",
|
|
1283
|
+
"user_context"
|
|
1284
|
+
],
|
|
1285
|
+
supportedTargetKinds: ["thread", "user"],
|
|
1286
|
+
contexts: ["social", "connectors"],
|
|
1287
|
+
metadata: {
|
|
1288
|
+
accountId,
|
|
1289
|
+
service: BLUESKY_SERVICE_NAME
|
|
1290
|
+
},
|
|
1291
|
+
resolveTargets: messageService.resolveConnectorTargets.bind(messageService),
|
|
1292
|
+
listRecentTargets: messageService.listRecentConnectorTargets.bind(messageService),
|
|
1293
|
+
listRooms: messageService.listConnectorRooms.bind(messageService),
|
|
1294
|
+
getChatContext: messageService.getConnectorChatContext.bind(messageService),
|
|
1295
|
+
getUserContext: messageService.getConnectorUserContext.bind(messageService),
|
|
1296
|
+
fetchMessages: messageService.fetchConnectorMessages.bind(messageService),
|
|
1297
|
+
contentShaping: {
|
|
1298
|
+
systemPromptFragment: "For BlueSky DMs, keep messages direct and conversational. Avoid public-feed conventions like hashtags unless the user asked.",
|
|
1299
|
+
constraints: {
|
|
1300
|
+
supportsMarkdown: false,
|
|
1301
|
+
channelType: ChannelType3.DM
|
|
1302
|
+
}
|
|
1303
|
+
},
|
|
1304
|
+
sendHandler: messageService.handleSendMessage.bind(messageService)
|
|
1305
|
+
};
|
|
1306
|
+
runtime.registerMessageConnector(registration);
|
|
1307
|
+
runtime.logger.info({ src: "plugin:bluesky", agentId: runtime.agentId, accountId }, "Registered BlueSky DM connector");
|
|
1308
|
+
}
|
|
1309
|
+
static registerPostConnector(runtime, postService) {
|
|
1310
|
+
const withPostConnector = runtime;
|
|
1311
|
+
if (typeof withPostConnector.registerPostConnector !== "function") {
|
|
1312
|
+
return;
|
|
1313
|
+
}
|
|
1314
|
+
const accountId = postService.getAccountId?.() ?? DEFAULT_BLUESKY_ACCOUNT_ID;
|
|
1315
|
+
withPostConnector.registerPostConnector({
|
|
1316
|
+
source: "bluesky",
|
|
1317
|
+
accountId,
|
|
1318
|
+
label: "BlueSky",
|
|
1319
|
+
description: "BlueSky public feed connector for publishing posts, reading the timeline, and searching posts.",
|
|
1320
|
+
capabilities: ["post", "fetch_feed", "search_posts"],
|
|
1321
|
+
contexts: ["social", "social_posting", "connectors"],
|
|
1322
|
+
metadata: {
|
|
1323
|
+
accountId,
|
|
1324
|
+
service: BLUESKY_SERVICE_NAME
|
|
1325
|
+
},
|
|
1326
|
+
postHandler: postService.handleSendPost.bind(postService),
|
|
1327
|
+
fetchFeed: postService.fetchFeed.bind(postService),
|
|
1328
|
+
searchPosts: postService.searchPosts.bind(postService),
|
|
1329
|
+
contentShaping: {
|
|
1330
|
+
systemPromptFragment: "For BlueSky posts, write a public post under 300 characters. Handles, links, and facets are supported by the connector; do not exceed the platform limit.",
|
|
1331
|
+
constraints: {
|
|
1332
|
+
maxLength: 300,
|
|
1333
|
+
supportsMarkdown: false,
|
|
1334
|
+
channelType: ChannelType3.FEED
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
});
|
|
1338
|
+
runtime.logger.info({ src: "plugin:bluesky", agentId: runtime.agentId, accountId }, "Registered BlueSky post connector");
|
|
1339
|
+
}
|
|
634
1340
|
async stop() {
|
|
635
|
-
for (const
|
|
636
|
-
|
|
1341
|
+
for (const [agentId, accounts] of this.agents) {
|
|
1342
|
+
for (const manager of accounts.managers.values()) {
|
|
1343
|
+
await manager.stop();
|
|
1344
|
+
}
|
|
1345
|
+
this.agents.delete(agentId);
|
|
637
1346
|
}
|
|
638
1347
|
}
|
|
639
|
-
|
|
640
|
-
|
|
1348
|
+
getMessageServiceForAccount(accountId, agentId) {
|
|
1349
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
1350
|
+
if (!resolvedAgentId)
|
|
1351
|
+
return;
|
|
1352
|
+
const accounts = this.agents.get(resolvedAgentId);
|
|
1353
|
+
if (!accounts)
|
|
1354
|
+
return;
|
|
1355
|
+
const id = accountId ? normalizeBlueSkyAccountId(accountId) : accounts.defaultAccountId;
|
|
1356
|
+
return accounts.messageServices.get(id);
|
|
1357
|
+
}
|
|
1358
|
+
getPostServiceForAccount(accountId, agentId) {
|
|
1359
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
1360
|
+
if (!resolvedAgentId)
|
|
1361
|
+
return;
|
|
1362
|
+
const accounts = this.agents.get(resolvedAgentId);
|
|
1363
|
+
if (!accounts)
|
|
1364
|
+
return;
|
|
1365
|
+
const id = accountId ? normalizeBlueSkyAccountId(accountId) : accounts.defaultAccountId;
|
|
1366
|
+
return accounts.postServices.get(id);
|
|
1367
|
+
}
|
|
1368
|
+
getMessageService(agentId, accountId) {
|
|
1369
|
+
return this.getMessageServiceForAccount(accountId, agentId);
|
|
1370
|
+
}
|
|
1371
|
+
getPostService(agentId, accountId) {
|
|
1372
|
+
return this.getPostServiceForAccount(accountId, agentId);
|
|
1373
|
+
}
|
|
1374
|
+
getDefaultAccountId(agentId) {
|
|
1375
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
1376
|
+
return resolvedAgentId ? this.agents.get(resolvedAgentId)?.defaultAccountId : undefined;
|
|
641
1377
|
}
|
|
642
|
-
|
|
643
|
-
|
|
1378
|
+
listAccountIds(agentId) {
|
|
1379
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
1380
|
+
const accounts = resolvedAgentId ? this.agents.get(resolvedAgentId) : undefined;
|
|
1381
|
+
return accounts ? Array.from(accounts.managers.keys()) : [];
|
|
1382
|
+
}
|
|
1383
|
+
firstAgentId() {
|
|
1384
|
+
return this.agents.keys().next().value;
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
// workflow-credential-provider.ts
|
|
1389
|
+
import { Service as Service2 } from "@elizaos/core";
|
|
1390
|
+
var WORKFLOW_CREDENTIAL_PROVIDER_TYPE = "workflow_credential_provider";
|
|
1391
|
+
var SUPPORTED = ["httpHeaderAuth"];
|
|
1392
|
+
|
|
1393
|
+
class BlueskyWorkflowCredentialProvider extends Service2 {
|
|
1394
|
+
static serviceType = WORKFLOW_CREDENTIAL_PROVIDER_TYPE;
|
|
1395
|
+
capabilityDescription = "Supplies Bluesky credentials to the workflow plugin.";
|
|
1396
|
+
static async start(runtime) {
|
|
1397
|
+
return new BlueskyWorkflowCredentialProvider(runtime);
|
|
1398
|
+
}
|
|
1399
|
+
async stop() {}
|
|
1400
|
+
async resolve(_userId, credType) {
|
|
1401
|
+
if (credType !== "httpHeaderAuth")
|
|
1402
|
+
return null;
|
|
1403
|
+
const handle = this.runtime.getSetting("BLUESKY_HANDLE");
|
|
1404
|
+
const password = this.runtime.getSetting("BLUESKY_PASSWORD");
|
|
1405
|
+
if (!handle?.trim() || !password?.trim())
|
|
1406
|
+
return null;
|
|
1407
|
+
return {
|
|
1408
|
+
status: "credential_data",
|
|
1409
|
+
data: {
|
|
1410
|
+
name: "X-Bluesky-Handle",
|
|
1411
|
+
value: handle.trim(),
|
|
1412
|
+
appPassword: password.trim()
|
|
1413
|
+
}
|
|
1414
|
+
};
|
|
1415
|
+
}
|
|
1416
|
+
checkCredentialTypes(credTypes) {
|
|
1417
|
+
return {
|
|
1418
|
+
supported: credTypes.filter((t) => SUPPORTED.includes(t)),
|
|
1419
|
+
unsupported: credTypes.filter((t) => !SUPPORTED.includes(t))
|
|
1420
|
+
};
|
|
644
1421
|
}
|
|
645
1422
|
}
|
|
646
1423
|
|
|
@@ -696,20 +1473,38 @@ var blueSkyPlugin = {
|
|
|
696
1473
|
BLUESKY_ACTION_INTERVAL: process.env.BLUESKY_ACTION_INTERVAL ?? null,
|
|
697
1474
|
BLUESKY_POST_IMMEDIATELY: process.env.BLUESKY_POST_IMMEDIATELY ?? null,
|
|
698
1475
|
BLUESKY_MAX_ACTIONS_PROCESSING: process.env.BLUESKY_MAX_ACTIONS_PROCESSING ?? null,
|
|
699
|
-
BLUESKY_MAX_POST_LENGTH: process.env.BLUESKY_MAX_POST_LENGTH ?? null
|
|
1476
|
+
BLUESKY_MAX_POST_LENGTH: process.env.BLUESKY_MAX_POST_LENGTH ?? null,
|
|
1477
|
+
BLUESKY_ACCOUNTS: process.env.BLUESKY_ACCOUNTS ?? null,
|
|
1478
|
+
BLUESKY_DEFAULT_ACCOUNT_ID: process.env.BLUESKY_DEFAULT_ACCOUNT_ID ?? null
|
|
700
1479
|
},
|
|
701
|
-
async init(_config,
|
|
1480
|
+
async init(_config, runtime) {
|
|
1481
|
+
try {
|
|
1482
|
+
const manager = getConnectorAccountManager(runtime);
|
|
1483
|
+
manager.registerProvider(createBlueSkyConnectorAccountProvider(runtime));
|
|
1484
|
+
} catch (err) {
|
|
1485
|
+
logger4.warn({
|
|
1486
|
+
src: "plugin:bluesky",
|
|
1487
|
+
err: err instanceof Error ? err.message : String(err)
|
|
1488
|
+
}, "Failed to register BlueSky provider with ConnectorAccountManager");
|
|
1489
|
+
}
|
|
702
1490
|
logger4.log("BlueSky plugin initialized");
|
|
703
1491
|
},
|
|
704
|
-
|
|
1492
|
+
actions: [],
|
|
1493
|
+
services: [BlueSkyService, BlueskyWorkflowCredentialProvider],
|
|
705
1494
|
tests: pluginTests
|
|
706
1495
|
};
|
|
707
|
-
var typescript_default = blueSkyPlugin;
|
|
708
1496
|
export {
|
|
709
|
-
|
|
1497
|
+
resolveDefaultBlueSkyAccountId,
|
|
1498
|
+
readBlueSkyAccountId,
|
|
1499
|
+
normalizeBlueSkyAccountId,
|
|
1500
|
+
listBlueSkyAccountIds,
|
|
1501
|
+
default2 as default,
|
|
1502
|
+
createBlueSkyConnectorAccountProvider,
|
|
710
1503
|
blueSkyPlugin,
|
|
1504
|
+
DEFAULT_BLUESKY_ACCOUNT_ID,
|
|
711
1505
|
BlueSkyService,
|
|
712
|
-
BlueSkyClient
|
|
1506
|
+
BlueSkyClient,
|
|
1507
|
+
BLUESKY_PROVIDER_ID
|
|
713
1508
|
};
|
|
714
1509
|
|
|
715
|
-
//# debugId=
|
|
1510
|
+
//# debugId=ABC5C845CC8C11C664756E2164756E21
|