@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/cjs/index.node.cjs
CHANGED
|
@@ -1,54 +1,88 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
1
3
|
var __defProp = Object.defineProperty;
|
|
2
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
6
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
-
|
|
7
|
+
function __accessProp(key) {
|
|
8
|
+
return this[key];
|
|
9
|
+
}
|
|
10
|
+
var __toESMCache_node;
|
|
11
|
+
var __toESMCache_esm;
|
|
12
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
13
|
+
var canCache = mod != null && typeof mod === "object";
|
|
14
|
+
if (canCache) {
|
|
15
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
16
|
+
var cached = cache.get(mod);
|
|
17
|
+
if (cached)
|
|
18
|
+
return cached;
|
|
19
|
+
}
|
|
20
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
21
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
22
|
+
for (let key of __getOwnPropNames(mod))
|
|
23
|
+
if (!__hasOwnProp.call(to, key))
|
|
24
|
+
__defProp(to, key, {
|
|
25
|
+
get: __accessProp.bind(mod, key),
|
|
26
|
+
enumerable: true
|
|
27
|
+
});
|
|
28
|
+
if (canCache)
|
|
29
|
+
cache.set(mod, to);
|
|
30
|
+
return to;
|
|
31
|
+
};
|
|
6
32
|
var __toCommonJS = (from) => {
|
|
7
|
-
var entry = __moduleCache.get(from), desc;
|
|
33
|
+
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
8
34
|
if (entry)
|
|
9
35
|
return entry;
|
|
10
36
|
entry = __defProp({}, "__esModule", { value: true });
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function")
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
37
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
38
|
+
for (var key of __getOwnPropNames(from))
|
|
39
|
+
if (!__hasOwnProp.call(entry, key))
|
|
40
|
+
__defProp(entry, key, {
|
|
41
|
+
get: __accessProp.bind(from, key),
|
|
42
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
43
|
+
});
|
|
44
|
+
}
|
|
16
45
|
__moduleCache.set(from, entry);
|
|
17
46
|
return entry;
|
|
18
47
|
};
|
|
48
|
+
var __moduleCache;
|
|
49
|
+
var __returnValue = (v) => v;
|
|
50
|
+
function __exportSetter(name, newValue) {
|
|
51
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
52
|
+
}
|
|
19
53
|
var __export = (target, all) => {
|
|
20
54
|
for (var name in all)
|
|
21
55
|
__defProp(target, name, {
|
|
22
56
|
get: all[name],
|
|
23
57
|
enumerable: true,
|
|
24
58
|
configurable: true,
|
|
25
|
-
set: (
|
|
59
|
+
set: __exportSetter.bind(all, name)
|
|
26
60
|
});
|
|
27
61
|
};
|
|
28
62
|
|
|
29
63
|
// index.node.ts
|
|
30
64
|
var exports_index_node = {};
|
|
31
65
|
__export(exports_index_node, {
|
|
32
|
-
|
|
66
|
+
resolveDefaultBlueSkyAccountId: () => resolveDefaultBlueSkyAccountId,
|
|
67
|
+
readBlueSkyAccountId: () => readBlueSkyAccountId,
|
|
68
|
+
normalizeBlueSkyAccountId: () => normalizeBlueSkyAccountId,
|
|
69
|
+
listBlueSkyAccountIds: () => listBlueSkyAccountIds,
|
|
70
|
+
default: () => import__.default,
|
|
71
|
+
createBlueSkyConnectorAccountProvider: () => createBlueSkyConnectorAccountProvider,
|
|
33
72
|
blueSkyPlugin: () => blueSkyPlugin,
|
|
73
|
+
DEFAULT_BLUESKY_ACCOUNT_ID: () => DEFAULT_BLUESKY_ACCOUNT_ID,
|
|
34
74
|
BlueSkyService: () => BlueSkyService,
|
|
35
|
-
BlueSkyClient: () => BlueSkyClient
|
|
75
|
+
BlueSkyClient: () => BlueSkyClient,
|
|
76
|
+
BLUESKY_PROVIDER_ID: () => BLUESKY_PROVIDER_ID
|
|
36
77
|
});
|
|
37
78
|
module.exports = __toCommonJS(exports_index_node);
|
|
38
79
|
|
|
39
80
|
// index.ts
|
|
40
|
-
var
|
|
41
|
-
|
|
42
|
-
// services/bluesky.ts
|
|
43
|
-
var import_core5 = require("@elizaos/core");
|
|
44
|
-
|
|
45
|
-
// client.ts
|
|
46
|
-
var import_api = require("@atproto/api");
|
|
47
|
-
var import_core = require("@elizaos/core");
|
|
48
|
-
var import_lru_cache = require("lru-cache");
|
|
81
|
+
var import_core7 = require("@elizaos/core");
|
|
49
82
|
|
|
50
83
|
// types/index.ts
|
|
51
|
-
var
|
|
84
|
+
var zod = __toESM(require("zod"));
|
|
85
|
+
var z2 = zod.z ?? zod;
|
|
52
86
|
var BLUESKY_SERVICE_URL = "https://bsky.social";
|
|
53
87
|
var BLUESKY_MAX_POST_LENGTH = 300;
|
|
54
88
|
var BLUESKY_POLL_INTERVAL = 60;
|
|
@@ -73,20 +107,20 @@ var CACHE_SIZE = {
|
|
|
73
107
|
NOTIFICATIONS: 1000,
|
|
74
108
|
CONVERSATIONS: 100
|
|
75
109
|
};
|
|
76
|
-
var BlueSkyConfigSchema =
|
|
77
|
-
handle:
|
|
78
|
-
password:
|
|
79
|
-
service:
|
|
80
|
-
dryRun:
|
|
81
|
-
pollInterval:
|
|
82
|
-
enablePost:
|
|
83
|
-
postIntervalMin:
|
|
84
|
-
postIntervalMax:
|
|
85
|
-
enableActionProcessing:
|
|
86
|
-
actionInterval:
|
|
87
|
-
postImmediately:
|
|
88
|
-
maxActionsProcessing:
|
|
89
|
-
enableDMs:
|
|
110
|
+
var BlueSkyConfigSchema = z2.object({
|
|
111
|
+
handle: z2.string().regex(AT_PROTOCOL_HANDLE_REGEX, "Invalid handle format"),
|
|
112
|
+
password: z2.string().min(1),
|
|
113
|
+
service: z2.string().url().default(BLUESKY_SERVICE_URL),
|
|
114
|
+
dryRun: z2.boolean().default(false),
|
|
115
|
+
pollInterval: z2.number().positive().default(BLUESKY_POLL_INTERVAL),
|
|
116
|
+
enablePost: z2.boolean().default(true),
|
|
117
|
+
postIntervalMin: z2.number().positive().default(BLUESKY_POST_INTERVAL_MIN),
|
|
118
|
+
postIntervalMax: z2.number().positive().default(BLUESKY_POST_INTERVAL_MAX),
|
|
119
|
+
enableActionProcessing: z2.boolean().default(true),
|
|
120
|
+
actionInterval: z2.number().positive().default(BLUESKY_ACTION_INTERVAL),
|
|
121
|
+
postImmediately: z2.boolean().default(false),
|
|
122
|
+
maxActionsProcessing: z2.number().positive().default(BLUESKY_MAX_ACTIONS),
|
|
123
|
+
enableDMs: z2.boolean().default(true)
|
|
90
124
|
});
|
|
91
125
|
|
|
92
126
|
class BlueSkyError extends Error {
|
|
@@ -100,7 +134,250 @@ class BlueSkyError extends Error {
|
|
|
100
134
|
}
|
|
101
135
|
}
|
|
102
136
|
|
|
137
|
+
// utils/config.ts
|
|
138
|
+
var DEFAULT_BLUESKY_ACCOUNT_ID = "default";
|
|
139
|
+
function getApiKeyOptional(runtime, key) {
|
|
140
|
+
const value = runtime.getSetting(key);
|
|
141
|
+
return typeof value === "string" ? value : undefined;
|
|
142
|
+
}
|
|
143
|
+
function stringSetting(runtime, key) {
|
|
144
|
+
const value = runtime.getSetting(key);
|
|
145
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
146
|
+
}
|
|
147
|
+
function characterConfig(runtime) {
|
|
148
|
+
const settings = runtime.character?.settings;
|
|
149
|
+
const raw = settings?.bluesky;
|
|
150
|
+
return raw && typeof raw === "object" ? raw : {};
|
|
151
|
+
}
|
|
152
|
+
function parseAccountsJson(runtime) {
|
|
153
|
+
const raw = stringSetting(runtime, "BLUESKY_ACCOUNTS");
|
|
154
|
+
if (!raw)
|
|
155
|
+
return {};
|
|
156
|
+
try {
|
|
157
|
+
const parsed = JSON.parse(raw);
|
|
158
|
+
if (Array.isArray(parsed)) {
|
|
159
|
+
return Object.fromEntries(parsed.filter((item) => Boolean(item) && typeof item === "object").map((item) => [
|
|
160
|
+
normalizeBlueSkyAccountId(item.accountId ?? item.id),
|
|
161
|
+
item
|
|
162
|
+
]));
|
|
163
|
+
}
|
|
164
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
165
|
+
} catch {
|
|
166
|
+
return {};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function allAccountConfigs(runtime) {
|
|
170
|
+
return {
|
|
171
|
+
...characterConfig(runtime).accounts ?? {},
|
|
172
|
+
...parseAccountsJson(runtime)
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function accountConfig(runtime, accountId) {
|
|
176
|
+
const accounts = allAccountConfigs(runtime);
|
|
177
|
+
return accounts[accountId] ?? accounts[normalizeBlueSkyAccountId(accountId)] ?? {};
|
|
178
|
+
}
|
|
179
|
+
function readRawField(record, keys) {
|
|
180
|
+
if (!record)
|
|
181
|
+
return;
|
|
182
|
+
for (const key of keys) {
|
|
183
|
+
const value = record[key];
|
|
184
|
+
if (typeof value === "string" && value.trim())
|
|
185
|
+
return value.trim();
|
|
186
|
+
if (typeof value === "number" || typeof value === "boolean")
|
|
187
|
+
return String(value);
|
|
188
|
+
}
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
function boolValue(value, fallback = false) {
|
|
192
|
+
if (typeof value === "boolean")
|
|
193
|
+
return value;
|
|
194
|
+
if (typeof value === "string")
|
|
195
|
+
return value.trim().toLowerCase() === "true";
|
|
196
|
+
return fallback;
|
|
197
|
+
}
|
|
198
|
+
function intValue(value, fallback) {
|
|
199
|
+
const parsed = Number.parseInt(String(value ?? ""), 10);
|
|
200
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
201
|
+
}
|
|
202
|
+
function normalizeBlueSkyAccountId(accountId) {
|
|
203
|
+
if (typeof accountId !== "string")
|
|
204
|
+
return DEFAULT_BLUESKY_ACCOUNT_ID;
|
|
205
|
+
const trimmed = accountId.trim();
|
|
206
|
+
return trimmed || DEFAULT_BLUESKY_ACCOUNT_ID;
|
|
207
|
+
}
|
|
208
|
+
function listBlueSkyAccountIds(runtime) {
|
|
209
|
+
const ids = new Set;
|
|
210
|
+
const config = characterConfig(runtime);
|
|
211
|
+
if (stringSetting(runtime, "BLUESKY_HANDLE") || config.handle && config.password) {
|
|
212
|
+
ids.add(DEFAULT_BLUESKY_ACCOUNT_ID);
|
|
213
|
+
}
|
|
214
|
+
for (const id of Object.keys(allAccountConfigs(runtime))) {
|
|
215
|
+
ids.add(normalizeBlueSkyAccountId(id));
|
|
216
|
+
}
|
|
217
|
+
return Array.from(ids.size ? ids : new Set([DEFAULT_BLUESKY_ACCOUNT_ID])).sort((a, b) => a.localeCompare(b));
|
|
218
|
+
}
|
|
219
|
+
function resolveDefaultBlueSkyAccountId(runtime) {
|
|
220
|
+
const requested = stringSetting(runtime, "BLUESKY_DEFAULT_ACCOUNT_ID") ?? stringSetting(runtime, "BLUESKY_ACCOUNT_ID");
|
|
221
|
+
if (requested)
|
|
222
|
+
return normalizeBlueSkyAccountId(requested);
|
|
223
|
+
const ids = listBlueSkyAccountIds(runtime);
|
|
224
|
+
return ids.includes(DEFAULT_BLUESKY_ACCOUNT_ID) ? DEFAULT_BLUESKY_ACCOUNT_ID : ids[0] ?? DEFAULT_BLUESKY_ACCOUNT_ID;
|
|
225
|
+
}
|
|
226
|
+
function readBlueSkyAccountId(...sources) {
|
|
227
|
+
for (const source of sources) {
|
|
228
|
+
if (!source || typeof source !== "object")
|
|
229
|
+
continue;
|
|
230
|
+
const record = source;
|
|
231
|
+
const parameters = record.parameters && typeof record.parameters === "object" ? record.parameters : {};
|
|
232
|
+
const data = record.data && typeof record.data === "object" ? record.data : {};
|
|
233
|
+
const metadata = record.metadata && typeof record.metadata === "object" ? record.metadata : {};
|
|
234
|
+
const bluesky = data.bluesky && typeof data.bluesky === "object" ? data.bluesky : {};
|
|
235
|
+
const value = record.accountId ?? parameters.accountId ?? data.accountId ?? bluesky.accountId ?? metadata.accountId;
|
|
236
|
+
if (typeof value === "string" && value.trim())
|
|
237
|
+
return normalizeBlueSkyAccountId(value);
|
|
238
|
+
}
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
function hasBlueSkyEnabled(runtime, accountId) {
|
|
242
|
+
const normalizedAccountId = normalizeBlueSkyAccountId(accountId ?? resolveDefaultBlueSkyAccountId(runtime));
|
|
243
|
+
const base = characterConfig(runtime);
|
|
244
|
+
const account = accountConfig(runtime, normalizedAccountId);
|
|
245
|
+
const allowEnv = normalizedAccountId === DEFAULT_BLUESKY_ACCOUNT_ID;
|
|
246
|
+
const enabled = readRawField(account, ["enabled", "BLUESKY_ENABLED"]) ?? readRawField(base, ["enabled", "BLUESKY_ENABLED"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLED") : undefined);
|
|
247
|
+
if (enabled)
|
|
248
|
+
return String(enabled).toLowerCase() === "true";
|
|
249
|
+
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)));
|
|
250
|
+
}
|
|
251
|
+
function validateBlueSkyConfig(runtime, accountId) {
|
|
252
|
+
const normalizedAccountId = normalizeBlueSkyAccountId(accountId ?? resolveDefaultBlueSkyAccountId(runtime));
|
|
253
|
+
const base = characterConfig(runtime);
|
|
254
|
+
const account = accountConfig(runtime, normalizedAccountId);
|
|
255
|
+
const allowEnv = normalizedAccountId === DEFAULT_BLUESKY_ACCOUNT_ID;
|
|
256
|
+
const result = BlueSkyConfigSchema.safeParse({
|
|
257
|
+
handle: readRawField(account, ["handle", "BLUESKY_HANDLE"]) ?? readRawField(base, ["handle", "BLUESKY_HANDLE"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_HANDLE") : undefined) ?? "",
|
|
258
|
+
password: readRawField(account, ["password", "BLUESKY_PASSWORD"]) ?? readRawField(base, ["password", "BLUESKY_PASSWORD"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_PASSWORD") : undefined) ?? "",
|
|
259
|
+
service: readRawField(account, ["service", "BLUESKY_SERVICE"]) ?? readRawField(base, ["service", "BLUESKY_SERVICE"]) ?? (allowEnv ? stringSetting(runtime, "BLUESKY_SERVICE") : undefined) ?? BLUESKY_SERVICE_URL,
|
|
260
|
+
dryRun: boolValue(readRawField(account, ["dryRun", "BLUESKY_DRY_RUN"]) ?? readRawField(base, ["dryRun", "BLUESKY_DRY_RUN"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_DRY_RUN") : undefined)),
|
|
261
|
+
pollInterval: intValue(readRawField(account, ["pollInterval", "BLUESKY_POLL_INTERVAL"]) ?? readRawField(base, ["pollInterval", "BLUESKY_POLL_INTERVAL"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_POLL_INTERVAL") : undefined), BLUESKY_POLL_INTERVAL),
|
|
262
|
+
enablePost: String(readRawField(account, ["enablePost", "BLUESKY_ENABLE_POSTING"]) ?? readRawField(base, ["enablePost", "BLUESKY_ENABLE_POSTING"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLE_POSTING") : undefined) ?? "true").toLowerCase() !== "false",
|
|
263
|
+
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),
|
|
264
|
+
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),
|
|
265
|
+
enableActionProcessing: String(readRawField(account, [
|
|
266
|
+
"enableActionProcessing",
|
|
267
|
+
"BLUESKY_ENABLE_ACTION_PROCESSING"
|
|
268
|
+
]) ?? readRawField(base, [
|
|
269
|
+
"enableActionProcessing",
|
|
270
|
+
"BLUESKY_ENABLE_ACTION_PROCESSING"
|
|
271
|
+
]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLE_ACTION_PROCESSING") : undefined) ?? "true").toLowerCase() !== "false",
|
|
272
|
+
actionInterval: intValue(readRawField(account, ["actionInterval", "BLUESKY_ACTION_INTERVAL"]) ?? readRawField(base, ["actionInterval", "BLUESKY_ACTION_INTERVAL"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ACTION_INTERVAL") : undefined), BLUESKY_ACTION_INTERVAL),
|
|
273
|
+
postImmediately: boolValue(readRawField(account, ["postImmediately", "BLUESKY_POST_IMMEDIATELY"]) ?? readRawField(base, ["postImmediately", "BLUESKY_POST_IMMEDIATELY"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_POST_IMMEDIATELY") : undefined)),
|
|
274
|
+
maxActionsProcessing: intValue(readRawField(account, [
|
|
275
|
+
"maxActionsProcessing",
|
|
276
|
+
"BLUESKY_MAX_ACTIONS_PROCESSING"
|
|
277
|
+
]) ?? readRawField(base, [
|
|
278
|
+
"maxActionsProcessing",
|
|
279
|
+
"BLUESKY_MAX_ACTIONS_PROCESSING"
|
|
280
|
+
]) ?? (allowEnv ? runtime.getSetting("BLUESKY_MAX_ACTIONS_PROCESSING") : undefined), BLUESKY_MAX_ACTIONS),
|
|
281
|
+
enableDMs: String(readRawField(account, ["enableDMs", "BLUESKY_ENABLE_DMS"]) ?? readRawField(base, ["enableDMs", "BLUESKY_ENABLE_DMS"]) ?? (allowEnv ? runtime.getSetting("BLUESKY_ENABLE_DMS") : undefined) ?? "true").toLowerCase() !== "false"
|
|
282
|
+
});
|
|
283
|
+
if (!result.success) {
|
|
284
|
+
const errors = result.error.errors?.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ") || result.error.toString();
|
|
285
|
+
throw new Error(`Invalid BlueSky configuration: ${errors}`);
|
|
286
|
+
}
|
|
287
|
+
return { ...result.data, accountId: normalizedAccountId };
|
|
288
|
+
}
|
|
289
|
+
function getPollInterval(runtime, accountId) {
|
|
290
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
291
|
+
return config.pollInterval * 1000;
|
|
292
|
+
}
|
|
293
|
+
function getActionInterval(runtime, accountId) {
|
|
294
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
295
|
+
return config.actionInterval * 1000;
|
|
296
|
+
}
|
|
297
|
+
function getMaxActionsProcessing(runtime, accountId) {
|
|
298
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
299
|
+
return config.maxActionsProcessing;
|
|
300
|
+
}
|
|
301
|
+
function isPostingEnabled(runtime, accountId) {
|
|
302
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
303
|
+
return config.enablePost;
|
|
304
|
+
}
|
|
305
|
+
function shouldPostImmediately(runtime, accountId) {
|
|
306
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
307
|
+
return config.postImmediately;
|
|
308
|
+
}
|
|
309
|
+
function getPostIntervalRange(runtime, accountId) {
|
|
310
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
311
|
+
return {
|
|
312
|
+
min: config.postIntervalMin * 1000,
|
|
313
|
+
max: config.postIntervalMax * 1000
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// connector-account-provider.ts
|
|
318
|
+
var BLUESKY_PROVIDER_ID = "bluesky";
|
|
319
|
+
function toConnectorAccount(runtime, accountId) {
|
|
320
|
+
let connected = false;
|
|
321
|
+
let handle = "";
|
|
322
|
+
let service = "";
|
|
323
|
+
try {
|
|
324
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
325
|
+
connected = Boolean(config.handle && config.password);
|
|
326
|
+
handle = config.handle;
|
|
327
|
+
service = config.service;
|
|
328
|
+
} catch {
|
|
329
|
+
connected = false;
|
|
330
|
+
}
|
|
331
|
+
const now = Date.now();
|
|
332
|
+
return {
|
|
333
|
+
id: accountId,
|
|
334
|
+
provider: BLUESKY_PROVIDER_ID,
|
|
335
|
+
label: handle || accountId,
|
|
336
|
+
role: "OWNER",
|
|
337
|
+
purpose: ["posting", "reading"],
|
|
338
|
+
accessGate: "open",
|
|
339
|
+
status: connected ? "connected" : "disabled",
|
|
340
|
+
externalId: handle || undefined,
|
|
341
|
+
displayHandle: handle || undefined,
|
|
342
|
+
createdAt: now,
|
|
343
|
+
updatedAt: now,
|
|
344
|
+
metadata: {
|
|
345
|
+
service
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
function createBlueSkyConnectorAccountProvider(runtime) {
|
|
350
|
+
return {
|
|
351
|
+
provider: BLUESKY_PROVIDER_ID,
|
|
352
|
+
label: "BlueSky",
|
|
353
|
+
listAccounts: async (_manager) => {
|
|
354
|
+
const ids = listBlueSkyAccountIds(runtime);
|
|
355
|
+
return ids.map((id) => toConnectorAccount(runtime, id));
|
|
356
|
+
},
|
|
357
|
+
createAccount: async (input, _manager) => {
|
|
358
|
+
return {
|
|
359
|
+
...input,
|
|
360
|
+
provider: BLUESKY_PROVIDER_ID,
|
|
361
|
+
role: input.role ?? "OWNER",
|
|
362
|
+
purpose: input.purpose ?? ["posting", "reading"],
|
|
363
|
+
accessGate: input.accessGate ?? "open",
|
|
364
|
+
status: input.status ?? "pending"
|
|
365
|
+
};
|
|
366
|
+
},
|
|
367
|
+
patchAccount: async (_accountId, patch, _manager) => {
|
|
368
|
+
return { ...patch, provider: BLUESKY_PROVIDER_ID };
|
|
369
|
+
},
|
|
370
|
+
deleteAccount: async (_accountId, _manager) => {}
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// services/bluesky.ts
|
|
375
|
+
var import_core5 = require("@elizaos/core");
|
|
376
|
+
|
|
103
377
|
// client.ts
|
|
378
|
+
var import_api = require("@atproto/api");
|
|
379
|
+
var import_core = require("@elizaos/core");
|
|
380
|
+
var import_lru_cache = require("lru-cache");
|
|
104
381
|
function isPostView(item) {
|
|
105
382
|
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";
|
|
106
383
|
}
|
|
@@ -218,6 +495,18 @@ class BlueSkyClient {
|
|
|
218
495
|
})
|
|
219
496
|
};
|
|
220
497
|
}
|
|
498
|
+
async searchPosts(params) {
|
|
499
|
+
const api = this.agent;
|
|
500
|
+
const response = await api.app.bsky.feed.searchPosts({
|
|
501
|
+
q: params.query,
|
|
502
|
+
limit: params.limit ?? 25,
|
|
503
|
+
cursor: params.cursor
|
|
504
|
+
});
|
|
505
|
+
return {
|
|
506
|
+
posts: response.data.posts.map(adaptPostView),
|
|
507
|
+
cursor: response.data.cursor
|
|
508
|
+
};
|
|
509
|
+
}
|
|
221
510
|
async sendPost(request) {
|
|
222
511
|
if (this.config.dryRun) {
|
|
223
512
|
import_core.logger.info(`Dry run: would create post with text: ${request.content.text}`);
|
|
@@ -237,7 +526,20 @@ class BlueSkyClient {
|
|
|
237
526
|
if (request.content.embed) {
|
|
238
527
|
record.embed = request.content.embed;
|
|
239
528
|
}
|
|
529
|
+
import_core.logger.info({
|
|
530
|
+
src: "plugin:bluesky",
|
|
531
|
+
op: "atproto:post",
|
|
532
|
+
textLength: rt.text.length,
|
|
533
|
+
hasReply: Boolean(request.replyTo),
|
|
534
|
+
hasEmbed: Boolean(request.content.embed)
|
|
535
|
+
}, "Publishing Bluesky post via atproto");
|
|
240
536
|
const response = await this.agent.post(record);
|
|
537
|
+
import_core.logger.info({
|
|
538
|
+
src: "plugin:bluesky",
|
|
539
|
+
op: "atproto:post",
|
|
540
|
+
uri: response.uri,
|
|
541
|
+
cid: response.cid
|
|
542
|
+
}, "Bluesky post published");
|
|
241
543
|
const thread = await this.agent.getPostThread({
|
|
242
544
|
uri: response.uri,
|
|
243
545
|
depth: 0
|
|
@@ -334,64 +636,10 @@ class BlueSkyClient {
|
|
|
334
636
|
|
|
335
637
|
// managers/agent.ts
|
|
336
638
|
var import_core2 = require("@elizaos/core");
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
function getApiKeyOptional(runtime, key) {
|
|
340
|
-
const value = runtime.getSetting(key);
|
|
341
|
-
return typeof value === "string" ? value : undefined;
|
|
342
|
-
}
|
|
343
|
-
function hasBlueSkyEnabled(runtime) {
|
|
344
|
-
const enabled = runtime.getSetting("BLUESKY_ENABLED");
|
|
345
|
-
if (enabled)
|
|
346
|
-
return String(enabled).toLowerCase() === "true";
|
|
347
|
-
return Boolean(runtime.getSetting("BLUESKY_HANDLE") && runtime.getSetting("BLUESKY_PASSWORD"));
|
|
348
|
-
}
|
|
349
|
-
function validateBlueSkyConfig(runtime) {
|
|
350
|
-
const result = BlueSkyConfigSchema.safeParse({
|
|
351
|
-
handle: String(runtime.getSetting("BLUESKY_HANDLE") ?? ""),
|
|
352
|
-
password: String(runtime.getSetting("BLUESKY_PASSWORD") ?? ""),
|
|
353
|
-
service: String(runtime.getSetting("BLUESKY_SERVICE") ?? BLUESKY_SERVICE_URL),
|
|
354
|
-
dryRun: runtime.getSetting("BLUESKY_DRY_RUN") === "true",
|
|
355
|
-
pollInterval: parseInt(String(runtime.getSetting("BLUESKY_POLL_INTERVAL") ?? ""), 10) || BLUESKY_POLL_INTERVAL,
|
|
356
|
-
enablePost: runtime.getSetting("BLUESKY_ENABLE_POSTING") !== "false",
|
|
357
|
-
postIntervalMin: parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MIN") ?? ""), 10) || BLUESKY_POST_INTERVAL_MIN,
|
|
358
|
-
postIntervalMax: parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MAX") ?? ""), 10) || BLUESKY_POST_INTERVAL_MAX,
|
|
359
|
-
enableActionProcessing: runtime.getSetting("BLUESKY_ENABLE_ACTION_PROCESSING") !== "false",
|
|
360
|
-
actionInterval: parseInt(String(runtime.getSetting("BLUESKY_ACTION_INTERVAL") ?? ""), 10) || BLUESKY_ACTION_INTERVAL,
|
|
361
|
-
postImmediately: runtime.getSetting("BLUESKY_POST_IMMEDIATELY") === "true",
|
|
362
|
-
maxActionsProcessing: parseInt(String(runtime.getSetting("BLUESKY_MAX_ACTIONS_PROCESSING") ?? ""), 10) || BLUESKY_MAX_ACTIONS,
|
|
363
|
-
enableDMs: runtime.getSetting("BLUESKY_ENABLE_DMS") !== "false"
|
|
364
|
-
});
|
|
365
|
-
if (!result.success) {
|
|
366
|
-
const errors = result.error.errors?.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ") || result.error.toString();
|
|
367
|
-
throw new Error(`Invalid BlueSky configuration: ${errors}`);
|
|
368
|
-
}
|
|
369
|
-
return result.data;
|
|
370
|
-
}
|
|
371
|
-
function getPollInterval(runtime) {
|
|
372
|
-
const seconds = parseInt(String(runtime.getSetting("BLUESKY_POLL_INTERVAL") ?? ""), 10) || BLUESKY_POLL_INTERVAL;
|
|
373
|
-
return seconds * 1000;
|
|
374
|
-
}
|
|
375
|
-
function getActionInterval(runtime) {
|
|
376
|
-
const seconds = parseInt(String(runtime.getSetting("BLUESKY_ACTION_INTERVAL") ?? ""), 10) || BLUESKY_ACTION_INTERVAL;
|
|
377
|
-
return seconds * 1000;
|
|
378
|
-
}
|
|
379
|
-
function getMaxActionsProcessing(runtime) {
|
|
380
|
-
return parseInt(String(runtime.getSetting("BLUESKY_MAX_ACTIONS_PROCESSING") ?? ""), 10) || BLUESKY_MAX_ACTIONS;
|
|
381
|
-
}
|
|
382
|
-
function isPostingEnabled(runtime) {
|
|
383
|
-
return runtime.getSetting("BLUESKY_ENABLE_POSTING") !== "false";
|
|
384
|
-
}
|
|
385
|
-
function shouldPostImmediately(runtime) {
|
|
386
|
-
return runtime.getSetting("BLUESKY_POST_IMMEDIATELY") === "true";
|
|
387
|
-
}
|
|
388
|
-
function getPostIntervalRange(runtime) {
|
|
389
|
-
const min = parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MIN") ?? ""), 10) || BLUESKY_POST_INTERVAL_MIN;
|
|
390
|
-
const max = parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MAX") ?? ""), 10) || BLUESKY_POST_INTERVAL_MAX;
|
|
391
|
-
return { min: min * 1000, max: max * 1000 };
|
|
639
|
+
function cursorCacheKey(agentId, accountId) {
|
|
640
|
+
return `bluesky:cursor:${agentId}:${accountId}`;
|
|
392
641
|
}
|
|
393
642
|
|
|
394
|
-
// managers/agent.ts
|
|
395
643
|
class BlueSkyAgentManager {
|
|
396
644
|
runtime;
|
|
397
645
|
config;
|
|
@@ -401,24 +649,33 @@ class BlueSkyAgentManager {
|
|
|
401
649
|
postTimer = null;
|
|
402
650
|
running = false;
|
|
403
651
|
lastSeenAt = null;
|
|
652
|
+
accountId;
|
|
404
653
|
constructor(runtime, config, client) {
|
|
405
654
|
this.runtime = runtime;
|
|
406
655
|
this.config = config;
|
|
407
656
|
this.client = client;
|
|
657
|
+
this.accountId = normalizeBlueSkyAccountId(config.accountId ?? DEFAULT_BLUESKY_ACCOUNT_ID);
|
|
658
|
+
}
|
|
659
|
+
getAccountId() {
|
|
660
|
+
return this.accountId;
|
|
408
661
|
}
|
|
409
662
|
async start() {
|
|
410
663
|
if (this.running)
|
|
411
664
|
return;
|
|
412
665
|
await this.client.authenticate();
|
|
413
666
|
this.running = true;
|
|
667
|
+
const cached = await this.runtime.getCache(cursorCacheKey(this.runtime.agentId, this.accountId));
|
|
668
|
+
if (typeof cached === "string" && cached) {
|
|
669
|
+
this.lastSeenAt = cached;
|
|
670
|
+
}
|
|
414
671
|
this.startNotificationPolling();
|
|
415
672
|
if (this.config.enableActionProcessing) {
|
|
416
673
|
this.startActionProcessing();
|
|
417
674
|
}
|
|
418
|
-
if (isPostingEnabled(this.runtime)) {
|
|
675
|
+
if (isPostingEnabled(this.runtime, this.accountId)) {
|
|
419
676
|
this.startAutomatedPosting();
|
|
420
677
|
}
|
|
421
|
-
import_core2.logger.success({ agentId: this.runtime.agentId }, "BlueSky agent manager started");
|
|
678
|
+
import_core2.logger.success({ agentId: this.runtime.agentId, accountId: this.accountId }, "BlueSky agent manager started");
|
|
422
679
|
}
|
|
423
680
|
async stop() {
|
|
424
681
|
this.running = false;
|
|
@@ -432,10 +689,10 @@ class BlueSkyAgentManager {
|
|
|
432
689
|
this.actionTimer = null;
|
|
433
690
|
this.postTimer = null;
|
|
434
691
|
await this.client.cleanup();
|
|
435
|
-
import_core2.logger.info({ agentId: this.runtime.agentId }, "BlueSky agent manager stopped");
|
|
692
|
+
import_core2.logger.info({ agentId: this.runtime.agentId, accountId: this.accountId }, "BlueSky agent manager stopped");
|
|
436
693
|
}
|
|
437
694
|
startNotificationPolling() {
|
|
438
|
-
const interval = getPollInterval(this.runtime);
|
|
695
|
+
const interval = getPollInterval(this.runtime, this.accountId);
|
|
439
696
|
this.pollNotifications();
|
|
440
697
|
this.pollTimer = setInterval(() => this.pollNotifications(), interval);
|
|
441
698
|
}
|
|
@@ -451,6 +708,7 @@ class BlueSkyAgentManager {
|
|
|
451
708
|
}) : notifications;
|
|
452
709
|
if (newNotifications.length > 0) {
|
|
453
710
|
this.lastSeenAt = notifications[0].indexedAt;
|
|
711
|
+
await this.runtime.setCache(cursorCacheKey(this.runtime.agentId, this.accountId), this.lastSeenAt);
|
|
454
712
|
for (const notification of newNotifications) {
|
|
455
713
|
this.emitNotificationEvent(notification);
|
|
456
714
|
}
|
|
@@ -471,26 +729,28 @@ class BlueSkyAgentManager {
|
|
|
471
729
|
const payload = {
|
|
472
730
|
runtime: this.runtime,
|
|
473
731
|
source: "bluesky",
|
|
732
|
+
accountId: this.accountId,
|
|
474
733
|
notification
|
|
475
734
|
};
|
|
476
735
|
this.runtime.emitEvent(event, payload);
|
|
477
736
|
}
|
|
478
737
|
}
|
|
479
738
|
startActionProcessing() {
|
|
480
|
-
const interval = getActionInterval(this.runtime);
|
|
481
|
-
this.
|
|
482
|
-
this.actionTimer = setInterval(() => this.
|
|
739
|
+
const interval = getActionInterval(this.runtime, this.accountId);
|
|
740
|
+
this.processQueuedActions();
|
|
741
|
+
this.actionTimer = setInterval(() => this.processQueuedActions(), interval);
|
|
483
742
|
}
|
|
484
|
-
async
|
|
743
|
+
async processQueuedActions() {
|
|
485
744
|
if (!this.running)
|
|
486
745
|
return;
|
|
487
|
-
const max = getMaxActionsProcessing(this.runtime);
|
|
746
|
+
const max = getMaxActionsProcessing(this.runtime, this.accountId);
|
|
488
747
|
const { notifications } = await this.client.getNotifications(max);
|
|
489
748
|
for (const notification of notifications) {
|
|
490
749
|
if (notification.reason === "mention" || notification.reason === "reply") {
|
|
491
750
|
const payload = {
|
|
492
751
|
runtime: this.runtime,
|
|
493
752
|
source: "bluesky",
|
|
753
|
+
accountId: this.accountId,
|
|
494
754
|
notification
|
|
495
755
|
};
|
|
496
756
|
this.runtime.emitEvent("bluesky.should_respond", payload);
|
|
@@ -498,47 +758,88 @@ class BlueSkyAgentManager {
|
|
|
498
758
|
}
|
|
499
759
|
}
|
|
500
760
|
startAutomatedPosting() {
|
|
501
|
-
if (shouldPostImmediately(this.runtime)) {
|
|
761
|
+
if (shouldPostImmediately(this.runtime, this.accountId)) {
|
|
502
762
|
this.createAutomatedPost();
|
|
503
763
|
}
|
|
504
764
|
this.scheduleNextPost();
|
|
505
765
|
}
|
|
506
766
|
scheduleNextPost() {
|
|
507
|
-
const { min, max } = getPostIntervalRange(this.runtime);
|
|
767
|
+
const { min, max } = getPostIntervalRange(this.runtime, this.accountId);
|
|
508
768
|
const interval = Math.random() * (max - min) + min;
|
|
509
769
|
this.postTimer = setTimeout(() => {
|
|
510
770
|
if (this.running) {
|
|
511
|
-
this.createAutomatedPost();
|
|
512
|
-
this.scheduleNextPost();
|
|
771
|
+
this.createAutomatedPost().finally(() => this.scheduleNextPost());
|
|
513
772
|
}
|
|
514
773
|
}, interval);
|
|
515
774
|
}
|
|
516
|
-
createAutomatedPost() {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
775
|
+
async createAutomatedPost() {
|
|
776
|
+
await import_core2.withStandaloneTrajectory(this.runtime, {
|
|
777
|
+
source: "plugin-bluesky:auto-post",
|
|
778
|
+
metadata: {
|
|
779
|
+
platform: "bluesky",
|
|
780
|
+
kind: "public_post_generation",
|
|
781
|
+
automated: true,
|
|
782
|
+
accountId: this.accountId
|
|
783
|
+
}
|
|
784
|
+
}, async () => {
|
|
785
|
+
import_core2.setTrajectoryPurpose("background");
|
|
786
|
+
const payload = {
|
|
787
|
+
runtime: this.runtime,
|
|
788
|
+
source: "bluesky",
|
|
789
|
+
automated: true,
|
|
790
|
+
accountId: this.accountId
|
|
791
|
+
};
|
|
792
|
+
await this.runtime.emitEvent("bluesky.create_post", payload);
|
|
793
|
+
});
|
|
523
794
|
}
|
|
524
795
|
}
|
|
525
796
|
|
|
526
797
|
// services/message.ts
|
|
527
798
|
var import_core3 = require("@elizaos/core");
|
|
528
799
|
|
|
529
|
-
//
|
|
800
|
+
// prompts.ts
|
|
530
801
|
var generateDmTemplate = `Generate a friendly direct message response under 200 characters.`;
|
|
531
802
|
var generatePostTemplate = `Generate an engaging BlueSky post under {{maxLength}} characters.`;
|
|
532
803
|
var truncatePostTemplate = `Shorten to under {{maxLength}} characters: "{{text}}"`;
|
|
533
804
|
|
|
534
805
|
// services/message.ts
|
|
806
|
+
var BLUESKY_CONNECTOR_CONTEXTS = ["social", "connectors"];
|
|
807
|
+
function normalizeBlueSkyQuery(value) {
|
|
808
|
+
return value.trim().replace(/^@/, "").toLowerCase();
|
|
809
|
+
}
|
|
810
|
+
function scoreBlueSkyMatch(query, id, labels) {
|
|
811
|
+
if (!query)
|
|
812
|
+
return 0.45;
|
|
813
|
+
if (id.toLowerCase() === query)
|
|
814
|
+
return 1;
|
|
815
|
+
let bestScore = 0;
|
|
816
|
+
for (const label of labels) {
|
|
817
|
+
const normalized = label?.trim().replace(/^@/, "").toLowerCase();
|
|
818
|
+
if (!normalized)
|
|
819
|
+
continue;
|
|
820
|
+
if (normalized === query) {
|
|
821
|
+
bestScore = Math.max(bestScore, 0.95);
|
|
822
|
+
} else if (normalized.startsWith(query)) {
|
|
823
|
+
bestScore = Math.max(bestScore, 0.85);
|
|
824
|
+
} else if (normalized.includes(query)) {
|
|
825
|
+
bestScore = Math.max(bestScore, 0.7);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
return bestScore;
|
|
829
|
+
}
|
|
830
|
+
|
|
535
831
|
class BlueSkyMessageService {
|
|
536
832
|
client;
|
|
537
833
|
runtime;
|
|
834
|
+
accountId;
|
|
538
835
|
static serviceType = "IMessageService";
|
|
539
|
-
constructor(client, runtime) {
|
|
836
|
+
constructor(client, runtime, accountId = "default") {
|
|
540
837
|
this.client = client;
|
|
541
838
|
this.runtime = runtime;
|
|
839
|
+
this.accountId = accountId;
|
|
840
|
+
}
|
|
841
|
+
getAccountId() {
|
|
842
|
+
return normalizeBlueSkyAccountId(this.accountId);
|
|
542
843
|
}
|
|
543
844
|
async getMessages(convoId, limit = 50) {
|
|
544
845
|
const response = await this.client.getMessages(convoId, limit);
|
|
@@ -552,6 +853,215 @@ class BlueSkyMessageService {
|
|
|
552
853
|
const response = await this.client.getConversations(limit);
|
|
553
854
|
return response.conversations;
|
|
554
855
|
}
|
|
856
|
+
async handleSendMessage(runtime, target, content) {
|
|
857
|
+
const requestedAccountId = normalizeBlueSkyAccountId(target.accountId ?? readBlueSkyAccountId(content, target) ?? this.getAccountId());
|
|
858
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
859
|
+
throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
|
|
860
|
+
}
|
|
861
|
+
const text = typeof content.text === "string" ? content.text.trim() : "";
|
|
862
|
+
if (!text) {
|
|
863
|
+
throw new Error("BlueSky DM connector requires non-empty text content.");
|
|
864
|
+
}
|
|
865
|
+
let convoId = target.channelId ?? target.threadId;
|
|
866
|
+
if (!convoId && target.roomId) {
|
|
867
|
+
const room = await runtime.getRoom(target.roomId);
|
|
868
|
+
convoId = room?.channelId;
|
|
869
|
+
}
|
|
870
|
+
if (!convoId) {
|
|
871
|
+
throw new Error("BlueSky DM connector requires a conversation target.");
|
|
872
|
+
}
|
|
873
|
+
await this.sendMessage(convoId, text);
|
|
874
|
+
}
|
|
875
|
+
async fetchConnectorMessages(context, params = {}) {
|
|
876
|
+
const requestedAccountId = normalizeBlueSkyAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
|
|
877
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
878
|
+
throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
|
|
879
|
+
}
|
|
880
|
+
const target = params.target ?? context.target;
|
|
881
|
+
let convoId = target?.channelId ?? target?.threadId;
|
|
882
|
+
if (!convoId && target?.roomId) {
|
|
883
|
+
const room = await context.runtime.getRoom(target.roomId);
|
|
884
|
+
convoId = room?.channelId;
|
|
885
|
+
}
|
|
886
|
+
if (convoId) {
|
|
887
|
+
const messages = await this.getMessages(convoId, clampLimit(params.limit, 25, 100));
|
|
888
|
+
return messages.map((message) => this.messageToMemory(context.runtime, message, convoId));
|
|
889
|
+
}
|
|
890
|
+
const conversations = await this.getConversations(clampLimit(params.limit, 25, 50));
|
|
891
|
+
const memories = [];
|
|
892
|
+
for (const conversation of conversations) {
|
|
893
|
+
const messages = await this.getMessages(conversation.id, 1);
|
|
894
|
+
memories.push(...messages.map((message) => this.messageToMemory(context.runtime, message, conversation.id)));
|
|
895
|
+
}
|
|
896
|
+
return memories.sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0));
|
|
897
|
+
}
|
|
898
|
+
async resolveConnectorTargets(query, _context) {
|
|
899
|
+
const normalizedQuery = normalizeBlueSkyQuery(query);
|
|
900
|
+
const conversations = await this.getConversations(50);
|
|
901
|
+
return conversations.map((conversation) => {
|
|
902
|
+
const score = scoreBlueSkyMatch(normalizedQuery, conversation.id, [
|
|
903
|
+
...conversation.members.flatMap((member) => [
|
|
904
|
+
member.handle,
|
|
905
|
+
member.displayName,
|
|
906
|
+
member.did
|
|
907
|
+
])
|
|
908
|
+
]);
|
|
909
|
+
return score > 0 ? this.buildConversationTarget(conversation, score) : null;
|
|
910
|
+
}).filter((target) => Boolean(target)).slice(0, 25);
|
|
911
|
+
}
|
|
912
|
+
async listConnectorRooms(_context) {
|
|
913
|
+
const conversations = await this.getConversations(50);
|
|
914
|
+
return conversations.map((conversation) => this.buildConversationTarget(conversation, 0.5));
|
|
915
|
+
}
|
|
916
|
+
async listRecentConnectorTargets(context) {
|
|
917
|
+
const targets = [];
|
|
918
|
+
const room = context.roomId && typeof context.runtime.getRoom === "function" ? await context.runtime.getRoom(context.roomId) : null;
|
|
919
|
+
const convoId = context.target?.channelId ?? room?.channelId;
|
|
920
|
+
if (convoId) {
|
|
921
|
+
targets.push({
|
|
922
|
+
target: {
|
|
923
|
+
source: "bluesky",
|
|
924
|
+
accountId: this.getAccountId(),
|
|
925
|
+
channelId: convoId
|
|
926
|
+
},
|
|
927
|
+
label: `BlueSky conversation ${convoId}`,
|
|
928
|
+
kind: "thread",
|
|
929
|
+
score: 0.95,
|
|
930
|
+
contexts: [...BLUESKY_CONNECTOR_CONTEXTS],
|
|
931
|
+
metadata: { accountId: this.getAccountId(), blueskyConvoId: convoId }
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
targets.push(...await this.listConnectorRooms(context));
|
|
935
|
+
const seen = new Set;
|
|
936
|
+
return targets.filter((target) => {
|
|
937
|
+
const channelId = target.target.channelId;
|
|
938
|
+
if (!channelId || seen.has(channelId))
|
|
939
|
+
return false;
|
|
940
|
+
seen.add(channelId);
|
|
941
|
+
return true;
|
|
942
|
+
}).slice(0, 25);
|
|
943
|
+
}
|
|
944
|
+
async getConnectorChatContext(target, context) {
|
|
945
|
+
let convoId = target.channelId ?? target.threadId;
|
|
946
|
+
if (!convoId && target.roomId) {
|
|
947
|
+
const room = await context.runtime.getRoom(target.roomId);
|
|
948
|
+
convoId = room?.channelId;
|
|
949
|
+
}
|
|
950
|
+
if (!convoId)
|
|
951
|
+
return null;
|
|
952
|
+
const messages = await this.getMessages(convoId, 25);
|
|
953
|
+
return {
|
|
954
|
+
target: {
|
|
955
|
+
source: "bluesky",
|
|
956
|
+
accountId: this.getAccountId(),
|
|
957
|
+
channelId: convoId
|
|
958
|
+
},
|
|
959
|
+
label: `BlueSky conversation ${convoId}`,
|
|
960
|
+
recentMessages: messages.map((message) => ({
|
|
961
|
+
name: message.sender.did,
|
|
962
|
+
text: message.text ?? "",
|
|
963
|
+
timestamp: Date.parse(message.sentAt),
|
|
964
|
+
metadata: {
|
|
965
|
+
accountId: this.getAccountId(),
|
|
966
|
+
blueskyMessageId: message.id,
|
|
967
|
+
blueskySenderDid: message.sender.did
|
|
968
|
+
}
|
|
969
|
+
})),
|
|
970
|
+
metadata: { accountId: this.getAccountId(), blueskyConvoId: convoId }
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
async getConnectorUserContext(entityId, _context) {
|
|
974
|
+
const normalizedEntity = entityId.trim().replace(/^@/, "");
|
|
975
|
+
if (!normalizedEntity)
|
|
976
|
+
return null;
|
|
977
|
+
const conversations = await this.getConversations(50);
|
|
978
|
+
for (const conversation of conversations) {
|
|
979
|
+
const member = conversation.members.find((candidate) => candidate.did === entityId || candidate.handle === normalizedEntity || candidate.displayName === entityId);
|
|
980
|
+
if (!member)
|
|
981
|
+
continue;
|
|
982
|
+
return {
|
|
983
|
+
entityId,
|
|
984
|
+
label: member.displayName || member.handle || member.did,
|
|
985
|
+
aliases: [member.handle, member.displayName, member.did].filter((value) => Boolean(value)),
|
|
986
|
+
handles: {
|
|
987
|
+
bluesky: member.handle ?? member.did
|
|
988
|
+
},
|
|
989
|
+
metadata: {
|
|
990
|
+
accountId: this.getAccountId(),
|
|
991
|
+
blueskyDid: member.did,
|
|
992
|
+
blueskyHandle: member.handle,
|
|
993
|
+
avatar: member.avatar
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
}
|
|
997
|
+
return null;
|
|
998
|
+
}
|
|
999
|
+
buildConversationTarget(conversation, score) {
|
|
1000
|
+
const sessionDid = this.client.getSession()?.did;
|
|
1001
|
+
const otherMembers = conversation.members.filter((member) => member.did !== sessionDid);
|
|
1002
|
+
const label = otherMembers.map((member) => member.displayName || member.handle || member.did).filter(Boolean).join(", ") || `BlueSky conversation ${conversation.id}`;
|
|
1003
|
+
return {
|
|
1004
|
+
target: {
|
|
1005
|
+
source: "bluesky",
|
|
1006
|
+
accountId: this.getAccountId(),
|
|
1007
|
+
channelId: conversation.id
|
|
1008
|
+
},
|
|
1009
|
+
label,
|
|
1010
|
+
kind: "thread",
|
|
1011
|
+
description: "BlueSky direct message conversation",
|
|
1012
|
+
score,
|
|
1013
|
+
contexts: [...BLUESKY_CONNECTOR_CONTEXTS],
|
|
1014
|
+
metadata: {
|
|
1015
|
+
accountId: this.getAccountId(),
|
|
1016
|
+
blueskyConvoId: conversation.id,
|
|
1017
|
+
unreadCount: conversation.unreadCount,
|
|
1018
|
+
muted: conversation.muted,
|
|
1019
|
+
members: conversation.members.map((member) => ({
|
|
1020
|
+
did: member.did,
|
|
1021
|
+
handle: member.handle,
|
|
1022
|
+
displayName: member.displayName
|
|
1023
|
+
}))
|
|
1024
|
+
}
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
messageToMemory(runtime, message, convoId) {
|
|
1028
|
+
const senderDid = message.sender.did || "unknown";
|
|
1029
|
+
const createdAt = Date.parse(message.sentAt) || Date.now();
|
|
1030
|
+
const entityId = senderDid === runtime.agentId ? runtime.agentId : import_core3.createUniqueUuid(runtime, `bluesky:user:${senderDid}`);
|
|
1031
|
+
const roomId = import_core3.createUniqueUuid(runtime, `bluesky:dm:${convoId}`);
|
|
1032
|
+
return {
|
|
1033
|
+
id: import_core3.createUniqueUuid(runtime, `bluesky:dm:${message.id}`),
|
|
1034
|
+
agentId: runtime.agentId,
|
|
1035
|
+
entityId,
|
|
1036
|
+
roomId,
|
|
1037
|
+
createdAt,
|
|
1038
|
+
content: {
|
|
1039
|
+
text: message.text ?? "",
|
|
1040
|
+
source: "bluesky",
|
|
1041
|
+
channelType: import_core3.ChannelType.DM
|
|
1042
|
+
},
|
|
1043
|
+
metadata: {
|
|
1044
|
+
type: "message",
|
|
1045
|
+
source: "bluesky",
|
|
1046
|
+
accountId: this.getAccountId(),
|
|
1047
|
+
provider: "bluesky",
|
|
1048
|
+
timestamp: createdAt,
|
|
1049
|
+
fromBot: entityId === runtime.agentId,
|
|
1050
|
+
messageIdFull: message.id,
|
|
1051
|
+
chatType: import_core3.ChannelType.DM,
|
|
1052
|
+
sender: {
|
|
1053
|
+
id: senderDid
|
|
1054
|
+
},
|
|
1055
|
+
bluesky: {
|
|
1056
|
+
accountId: this.getAccountId(),
|
|
1057
|
+
messageId: message.id,
|
|
1058
|
+
convoId,
|
|
1059
|
+
rev: message.rev,
|
|
1060
|
+
senderDid
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
}
|
|
555
1065
|
async generateReply() {
|
|
556
1066
|
const prompt = import_core3.composePrompt({
|
|
557
1067
|
state: {},
|
|
@@ -564,16 +1074,41 @@ class BlueSkyMessageService {
|
|
|
564
1074
|
return response;
|
|
565
1075
|
}
|
|
566
1076
|
}
|
|
1077
|
+
function clampLimit(value, fallback, max) {
|
|
1078
|
+
if (!Number.isFinite(value))
|
|
1079
|
+
return fallback;
|
|
1080
|
+
return Math.min(Math.max(1, Math.floor(value)), max);
|
|
1081
|
+
}
|
|
567
1082
|
|
|
568
1083
|
// services/post.ts
|
|
569
1084
|
var import_core4 = require("@elizaos/core");
|
|
1085
|
+
function clampLimit2(value, fallback, max) {
|
|
1086
|
+
if (!Number.isFinite(value))
|
|
1087
|
+
return fallback;
|
|
1088
|
+
return Math.min(Math.max(1, Math.floor(value)), max);
|
|
1089
|
+
}
|
|
1090
|
+
function readContentString(content, keys) {
|
|
1091
|
+
const record = content;
|
|
1092
|
+
for (const key of keys) {
|
|
1093
|
+
const value = record[key];
|
|
1094
|
+
if (typeof value === "string" && value.trim())
|
|
1095
|
+
return value.trim();
|
|
1096
|
+
}
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
570
1100
|
class BlueSkyPostService {
|
|
571
1101
|
client;
|
|
572
1102
|
runtime;
|
|
1103
|
+
accountId;
|
|
573
1104
|
static serviceType = "IPostService";
|
|
574
|
-
constructor(client, runtime) {
|
|
1105
|
+
constructor(client, runtime, accountId = "default") {
|
|
575
1106
|
this.client = client;
|
|
576
1107
|
this.runtime = runtime;
|
|
1108
|
+
this.accountId = accountId;
|
|
1109
|
+
}
|
|
1110
|
+
getAccountId() {
|
|
1111
|
+
return normalizeBlueSkyAccountId(this.accountId);
|
|
577
1112
|
}
|
|
578
1113
|
async getPosts(limit = 50, cursor) {
|
|
579
1114
|
const response = await this.client.getTimeline({ limit, cursor });
|
|
@@ -590,9 +1125,91 @@ class BlueSkyPostService {
|
|
|
590
1125
|
};
|
|
591
1126
|
return this.client.sendPost(request);
|
|
592
1127
|
}
|
|
1128
|
+
async handleSendPost(runtime, content) {
|
|
1129
|
+
const requestedAccountId = normalizeBlueSkyAccountId(readBlueSkyAccountId(content) ?? this.getAccountId());
|
|
1130
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
1131
|
+
throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
|
|
1132
|
+
}
|
|
1133
|
+
const replyUri = readContentString(content, ["replyToUri", "replyTo"]);
|
|
1134
|
+
const replyCid = readContentString(content, ["replyToCid"]);
|
|
1135
|
+
const post = await this.createPost(content.text ?? "", replyUri && replyCid ? { uri: replyUri, cid: replyCid } : undefined);
|
|
1136
|
+
return this.postToMemory(runtime, post);
|
|
1137
|
+
}
|
|
1138
|
+
async fetchFeed(context, params = {}) {
|
|
1139
|
+
const requestedAccountId = normalizeBlueSkyAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
|
|
1140
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
1141
|
+
throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
|
|
1142
|
+
}
|
|
1143
|
+
const response = await this.client.getTimeline({
|
|
1144
|
+
limit: clampLimit2(params.limit, 25, 100),
|
|
1145
|
+
cursor: params.cursor
|
|
1146
|
+
});
|
|
1147
|
+
return response.feed.map((item) => this.postToMemory(context.runtime, item.post));
|
|
1148
|
+
}
|
|
1149
|
+
async searchPosts(context, params) {
|
|
1150
|
+
const requestedAccountId = normalizeBlueSkyAccountId(context.accountId ?? context.metadata?.accountId ?? this.getAccountId());
|
|
1151
|
+
if (requestedAccountId !== this.getAccountId()) {
|
|
1152
|
+
throw new Error(`BlueSky account '${requestedAccountId}' is not available in this service instance`);
|
|
1153
|
+
}
|
|
1154
|
+
const query = params.query?.trim();
|
|
1155
|
+
if (!query) {
|
|
1156
|
+
throw new Error("BlueSky searchPosts connector requires a query.");
|
|
1157
|
+
}
|
|
1158
|
+
const response = await this.client.searchPosts({
|
|
1159
|
+
query,
|
|
1160
|
+
limit: clampLimit2(params.limit, 25, 100),
|
|
1161
|
+
cursor: params.cursor
|
|
1162
|
+
});
|
|
1163
|
+
return response.posts.map((post) => this.postToMemory(context.runtime, post));
|
|
1164
|
+
}
|
|
593
1165
|
async deletePost(uri) {
|
|
594
1166
|
await this.client.deletePost(uri);
|
|
595
1167
|
}
|
|
1168
|
+
postToMemory(runtime, post) {
|
|
1169
|
+
const createdAt = Date.parse(post.indexedAt || post.record.createdAt) || Date.now();
|
|
1170
|
+
const authorId = post.author.did || post.author.handle || "unknown";
|
|
1171
|
+
const entityId = authorId === runtime.agentId ? runtime.agentId : import_core4.createUniqueUuid(runtime, `bluesky:user:${authorId}`);
|
|
1172
|
+
const roomId = import_core4.createUniqueUuid(runtime, `bluesky:feed:${authorId}`);
|
|
1173
|
+
return {
|
|
1174
|
+
id: import_core4.createUniqueUuid(runtime, `bluesky:post:${post.uri}`),
|
|
1175
|
+
agentId: runtime.agentId,
|
|
1176
|
+
entityId,
|
|
1177
|
+
roomId,
|
|
1178
|
+
createdAt,
|
|
1179
|
+
content: {
|
|
1180
|
+
text: post.record.text,
|
|
1181
|
+
source: "bluesky",
|
|
1182
|
+
url: `https://bsky.app/profile/${post.author.handle}/post/${post.uri.split("/").pop()}`,
|
|
1183
|
+
channelType: import_core4.ChannelType.FEED
|
|
1184
|
+
},
|
|
1185
|
+
metadata: {
|
|
1186
|
+
type: "message",
|
|
1187
|
+
source: "bluesky",
|
|
1188
|
+
accountId: this.getAccountId(),
|
|
1189
|
+
provider: "bluesky",
|
|
1190
|
+
timestamp: createdAt,
|
|
1191
|
+
fromBot: entityId === runtime.agentId,
|
|
1192
|
+
messageIdFull: post.uri,
|
|
1193
|
+
chatType: import_core4.ChannelType.FEED,
|
|
1194
|
+
sender: {
|
|
1195
|
+
id: authorId,
|
|
1196
|
+
name: post.author.displayName,
|
|
1197
|
+
username: post.author.handle
|
|
1198
|
+
},
|
|
1199
|
+
bluesky: {
|
|
1200
|
+
accountId: this.getAccountId(),
|
|
1201
|
+
uri: post.uri,
|
|
1202
|
+
cid: post.cid,
|
|
1203
|
+
authorDid: post.author.did,
|
|
1204
|
+
authorHandle: post.author.handle,
|
|
1205
|
+
replyCount: post.replyCount,
|
|
1206
|
+
repostCount: post.repostCount,
|
|
1207
|
+
likeCount: post.likeCount,
|
|
1208
|
+
quoteCount: post.quoteCount
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
596
1213
|
async generateContent() {
|
|
597
1214
|
const prompt = import_core4.composePrompt({
|
|
598
1215
|
state: {
|
|
@@ -626,9 +1243,7 @@ class BlueSkyPostService {
|
|
|
626
1243
|
// services/bluesky.ts
|
|
627
1244
|
class BlueSkyService extends import_core5.Service {
|
|
628
1245
|
static instance;
|
|
629
|
-
|
|
630
|
-
messageServices = new Map;
|
|
631
|
-
postServices = new Map;
|
|
1246
|
+
agents = new Map;
|
|
632
1247
|
static serviceType = BLUESKY_SERVICE_NAME;
|
|
633
1248
|
capabilityDescription = "Send and receive messages on BlueSky";
|
|
634
1249
|
static getInstance() {
|
|
@@ -637,48 +1252,230 @@ class BlueSkyService extends import_core5.Service {
|
|
|
637
1252
|
}
|
|
638
1253
|
static async start(runtime) {
|
|
639
1254
|
const service = BlueSkyService.getInstance();
|
|
640
|
-
if (service.
|
|
1255
|
+
if (service.agents.has(runtime.agentId)) {
|
|
641
1256
|
return service;
|
|
642
1257
|
}
|
|
643
1258
|
if (!hasBlueSkyEnabled(runtime)) {
|
|
644
1259
|
return service;
|
|
645
1260
|
}
|
|
646
|
-
const
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
service.
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
1261
|
+
const accountIds = listBlueSkyAccountIds(runtime);
|
|
1262
|
+
const defaultAccountId = normalizeBlueSkyAccountId(resolveDefaultBlueSkyAccountId(runtime));
|
|
1263
|
+
const accounts = {
|
|
1264
|
+
defaultAccountId,
|
|
1265
|
+
managers: new Map,
|
|
1266
|
+
messageServices: new Map,
|
|
1267
|
+
postServices: new Map
|
|
1268
|
+
};
|
|
1269
|
+
service.agents.set(runtime.agentId, accounts);
|
|
1270
|
+
for (const accountId of accountIds) {
|
|
1271
|
+
if (!hasBlueSkyEnabled(runtime, accountId)) {
|
|
1272
|
+
continue;
|
|
1273
|
+
}
|
|
1274
|
+
const config = validateBlueSkyConfig(runtime, accountId);
|
|
1275
|
+
if (!config.handle || !config.password) {
|
|
1276
|
+
import_core5.logger.warn({ agentId: runtime.agentId, accountId }, "Skipping BlueSky account without handle/password");
|
|
1277
|
+
continue;
|
|
1278
|
+
}
|
|
1279
|
+
const client = new BlueSkyClient({
|
|
1280
|
+
service: config.service,
|
|
1281
|
+
handle: config.handle,
|
|
1282
|
+
password: config.password,
|
|
1283
|
+
dryRun: config.dryRun
|
|
1284
|
+
});
|
|
1285
|
+
const manager = new BlueSkyAgentManager(runtime, config, client);
|
|
1286
|
+
accounts.managers.set(accountId, manager);
|
|
1287
|
+
accounts.messageServices.set(accountId, new BlueSkyMessageService(client, runtime, accountId));
|
|
1288
|
+
accounts.postServices.set(accountId, new BlueSkyPostService(client, runtime, accountId));
|
|
1289
|
+
await manager.start();
|
|
1290
|
+
import_core5.logger.success({ agentId: runtime.agentId, accountId }, "BlueSky client started");
|
|
1291
|
+
}
|
|
659
1292
|
return service;
|
|
660
1293
|
}
|
|
661
1294
|
static async stop(runtime) {
|
|
662
1295
|
const service = BlueSkyService.getInstance();
|
|
663
|
-
const
|
|
664
|
-
if (!
|
|
1296
|
+
const accounts = service.agents.get(runtime.agentId);
|
|
1297
|
+
if (!accounts)
|
|
665
1298
|
return;
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
service.
|
|
1299
|
+
for (const manager of accounts.managers.values()) {
|
|
1300
|
+
await manager.stop();
|
|
1301
|
+
}
|
|
1302
|
+
service.agents.delete(runtime.agentId);
|
|
670
1303
|
import_core5.logger.info({ agentId: runtime.agentId }, "BlueSky client stopped");
|
|
671
1304
|
}
|
|
1305
|
+
static registerSendHandlers(runtime, serviceInstance) {
|
|
1306
|
+
const accounts = serviceInstance?.agents.get(runtime.agentId);
|
|
1307
|
+
if (!accounts) {
|
|
1308
|
+
runtime.logger.warn({ src: "plugin:bluesky", agentId: runtime.agentId }, "Cannot register BlueSky connectors; service is not initialized");
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
for (const postService of accounts.postServices.values()) {
|
|
1312
|
+
BlueSkyService.registerPostConnector(runtime, postService);
|
|
1313
|
+
}
|
|
1314
|
+
if (typeof runtime.registerMessageConnector === "function" && accounts.messageServices.size > 0) {
|
|
1315
|
+
for (const messageService2 of accounts.messageServices.values()) {
|
|
1316
|
+
BlueSkyService.registerMessageConnector(runtime, messageService2);
|
|
1317
|
+
}
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
const defaultAccountId = normalizeBlueSkyAccountId(accounts.defaultAccountId);
|
|
1321
|
+
const messageService = accounts.messageServices.get(defaultAccountId);
|
|
1322
|
+
if (!messageService) {
|
|
1323
|
+
runtime.logger.warn({ src: "plugin:bluesky", agentId: runtime.agentId }, "Cannot register legacy BlueSky DM send handler; default message service is not initialized");
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
runtime.registerSendHandler("bluesky", messageService.handleSendMessage.bind(messageService));
|
|
1327
|
+
}
|
|
1328
|
+
static registerMessageConnector(runtime, messageService) {
|
|
1329
|
+
const accountId = messageService.getAccountId();
|
|
1330
|
+
const registration = {
|
|
1331
|
+
source: "bluesky",
|
|
1332
|
+
accountId,
|
|
1333
|
+
label: "BlueSky",
|
|
1334
|
+
description: "BlueSky DM connector for sending private messages to conversations.",
|
|
1335
|
+
capabilities: [
|
|
1336
|
+
"send_message",
|
|
1337
|
+
"fetch_messages",
|
|
1338
|
+
"resolve_targets",
|
|
1339
|
+
"list_rooms",
|
|
1340
|
+
"chat_context",
|
|
1341
|
+
"user_context"
|
|
1342
|
+
],
|
|
1343
|
+
supportedTargetKinds: ["thread", "user"],
|
|
1344
|
+
contexts: ["social", "connectors"],
|
|
1345
|
+
metadata: {
|
|
1346
|
+
accountId,
|
|
1347
|
+
service: BLUESKY_SERVICE_NAME
|
|
1348
|
+
},
|
|
1349
|
+
resolveTargets: messageService.resolveConnectorTargets.bind(messageService),
|
|
1350
|
+
listRecentTargets: messageService.listRecentConnectorTargets.bind(messageService),
|
|
1351
|
+
listRooms: messageService.listConnectorRooms.bind(messageService),
|
|
1352
|
+
getChatContext: messageService.getConnectorChatContext.bind(messageService),
|
|
1353
|
+
getUserContext: messageService.getConnectorUserContext.bind(messageService),
|
|
1354
|
+
fetchMessages: messageService.fetchConnectorMessages.bind(messageService),
|
|
1355
|
+
contentShaping: {
|
|
1356
|
+
systemPromptFragment: "For BlueSky DMs, keep messages direct and conversational. Avoid public-feed conventions like hashtags unless the user asked.",
|
|
1357
|
+
constraints: {
|
|
1358
|
+
supportsMarkdown: false,
|
|
1359
|
+
channelType: import_core5.ChannelType.DM
|
|
1360
|
+
}
|
|
1361
|
+
},
|
|
1362
|
+
sendHandler: messageService.handleSendMessage.bind(messageService)
|
|
1363
|
+
};
|
|
1364
|
+
runtime.registerMessageConnector(registration);
|
|
1365
|
+
runtime.logger.info({ src: "plugin:bluesky", agentId: runtime.agentId, accountId }, "Registered BlueSky DM connector");
|
|
1366
|
+
}
|
|
1367
|
+
static registerPostConnector(runtime, postService) {
|
|
1368
|
+
const withPostConnector = runtime;
|
|
1369
|
+
if (typeof withPostConnector.registerPostConnector !== "function") {
|
|
1370
|
+
return;
|
|
1371
|
+
}
|
|
1372
|
+
const accountId = postService.getAccountId?.() ?? DEFAULT_BLUESKY_ACCOUNT_ID;
|
|
1373
|
+
withPostConnector.registerPostConnector({
|
|
1374
|
+
source: "bluesky",
|
|
1375
|
+
accountId,
|
|
1376
|
+
label: "BlueSky",
|
|
1377
|
+
description: "BlueSky public feed connector for publishing posts, reading the timeline, and searching posts.",
|
|
1378
|
+
capabilities: ["post", "fetch_feed", "search_posts"],
|
|
1379
|
+
contexts: ["social", "social_posting", "connectors"],
|
|
1380
|
+
metadata: {
|
|
1381
|
+
accountId,
|
|
1382
|
+
service: BLUESKY_SERVICE_NAME
|
|
1383
|
+
},
|
|
1384
|
+
postHandler: postService.handleSendPost.bind(postService),
|
|
1385
|
+
fetchFeed: postService.fetchFeed.bind(postService),
|
|
1386
|
+
searchPosts: postService.searchPosts.bind(postService),
|
|
1387
|
+
contentShaping: {
|
|
1388
|
+
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.",
|
|
1389
|
+
constraints: {
|
|
1390
|
+
maxLength: 300,
|
|
1391
|
+
supportsMarkdown: false,
|
|
1392
|
+
channelType: import_core5.ChannelType.FEED
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
});
|
|
1396
|
+
runtime.logger.info({ src: "plugin:bluesky", agentId: runtime.agentId, accountId }, "Registered BlueSky post connector");
|
|
1397
|
+
}
|
|
672
1398
|
async stop() {
|
|
673
|
-
for (const
|
|
674
|
-
|
|
1399
|
+
for (const [agentId, accounts] of this.agents) {
|
|
1400
|
+
for (const manager of accounts.managers.values()) {
|
|
1401
|
+
await manager.stop();
|
|
1402
|
+
}
|
|
1403
|
+
this.agents.delete(agentId);
|
|
675
1404
|
}
|
|
676
1405
|
}
|
|
677
|
-
|
|
678
|
-
|
|
1406
|
+
getMessageServiceForAccount(accountId, agentId) {
|
|
1407
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
1408
|
+
if (!resolvedAgentId)
|
|
1409
|
+
return;
|
|
1410
|
+
const accounts = this.agents.get(resolvedAgentId);
|
|
1411
|
+
if (!accounts)
|
|
1412
|
+
return;
|
|
1413
|
+
const id = accountId ? normalizeBlueSkyAccountId(accountId) : accounts.defaultAccountId;
|
|
1414
|
+
return accounts.messageServices.get(id);
|
|
1415
|
+
}
|
|
1416
|
+
getPostServiceForAccount(accountId, agentId) {
|
|
1417
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
1418
|
+
if (!resolvedAgentId)
|
|
1419
|
+
return;
|
|
1420
|
+
const accounts = this.agents.get(resolvedAgentId);
|
|
1421
|
+
if (!accounts)
|
|
1422
|
+
return;
|
|
1423
|
+
const id = accountId ? normalizeBlueSkyAccountId(accountId) : accounts.defaultAccountId;
|
|
1424
|
+
return accounts.postServices.get(id);
|
|
1425
|
+
}
|
|
1426
|
+
getMessageService(agentId, accountId) {
|
|
1427
|
+
return this.getMessageServiceForAccount(accountId, agentId);
|
|
1428
|
+
}
|
|
1429
|
+
getPostService(agentId, accountId) {
|
|
1430
|
+
return this.getPostServiceForAccount(accountId, agentId);
|
|
1431
|
+
}
|
|
1432
|
+
getDefaultAccountId(agentId) {
|
|
1433
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
1434
|
+
return resolvedAgentId ? this.agents.get(resolvedAgentId)?.defaultAccountId : undefined;
|
|
679
1435
|
}
|
|
680
|
-
|
|
681
|
-
|
|
1436
|
+
listAccountIds(agentId) {
|
|
1437
|
+
const resolvedAgentId = agentId ?? this.firstAgentId();
|
|
1438
|
+
const accounts = resolvedAgentId ? this.agents.get(resolvedAgentId) : undefined;
|
|
1439
|
+
return accounts ? Array.from(accounts.managers.keys()) : [];
|
|
1440
|
+
}
|
|
1441
|
+
firstAgentId() {
|
|
1442
|
+
return this.agents.keys().next().value;
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
// workflow-credential-provider.ts
|
|
1447
|
+
var import_core6 = require("@elizaos/core");
|
|
1448
|
+
var WORKFLOW_CREDENTIAL_PROVIDER_TYPE = "workflow_credential_provider";
|
|
1449
|
+
var SUPPORTED = ["httpHeaderAuth"];
|
|
1450
|
+
|
|
1451
|
+
class BlueskyWorkflowCredentialProvider extends import_core6.Service {
|
|
1452
|
+
static serviceType = WORKFLOW_CREDENTIAL_PROVIDER_TYPE;
|
|
1453
|
+
capabilityDescription = "Supplies Bluesky credentials to the workflow plugin.";
|
|
1454
|
+
static async start(runtime) {
|
|
1455
|
+
return new BlueskyWorkflowCredentialProvider(runtime);
|
|
1456
|
+
}
|
|
1457
|
+
async stop() {}
|
|
1458
|
+
async resolve(_userId, credType) {
|
|
1459
|
+
if (credType !== "httpHeaderAuth")
|
|
1460
|
+
return null;
|
|
1461
|
+
const handle = this.runtime.getSetting("BLUESKY_HANDLE");
|
|
1462
|
+
const password = this.runtime.getSetting("BLUESKY_PASSWORD");
|
|
1463
|
+
if (!handle?.trim() || !password?.trim())
|
|
1464
|
+
return null;
|
|
1465
|
+
return {
|
|
1466
|
+
status: "credential_data",
|
|
1467
|
+
data: {
|
|
1468
|
+
name: "X-Bluesky-Handle",
|
|
1469
|
+
value: handle.trim(),
|
|
1470
|
+
appPassword: password.trim()
|
|
1471
|
+
}
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
checkCredentialTypes(credTypes) {
|
|
1475
|
+
return {
|
|
1476
|
+
supported: credTypes.filter((t) => SUPPORTED.includes(t)),
|
|
1477
|
+
unsupported: credTypes.filter((t) => !SUPPORTED.includes(t))
|
|
1478
|
+
};
|
|
682
1479
|
}
|
|
683
1480
|
}
|
|
684
1481
|
|
|
@@ -695,7 +1492,7 @@ var pluginTests = [
|
|
|
695
1492
|
if (!handle || !password) {
|
|
696
1493
|
throw new Error("BLUESKY_HANDLE and BLUESKY_PASSWORD are not configured");
|
|
697
1494
|
}
|
|
698
|
-
|
|
1495
|
+
import_core7.logger.log("BlueSky credentials are configured");
|
|
699
1496
|
}
|
|
700
1497
|
},
|
|
701
1498
|
{
|
|
@@ -704,14 +1501,14 @@ var pluginTests = [
|
|
|
704
1501
|
const handle = getApiKeyOptional(runtime, "BLUESKY_HANDLE");
|
|
705
1502
|
const password = getApiKeyOptional(runtime, "BLUESKY_PASSWORD");
|
|
706
1503
|
if (!handle || !password) {
|
|
707
|
-
|
|
1504
|
+
import_core7.logger.log("Skipping service initialization test - credentials not configured");
|
|
708
1505
|
return;
|
|
709
1506
|
}
|
|
710
1507
|
const service = await BlueSkyService.start(runtime);
|
|
711
1508
|
if (!service) {
|
|
712
1509
|
throw new Error("Failed to initialize BlueSky service");
|
|
713
1510
|
}
|
|
714
|
-
|
|
1511
|
+
import_core7.logger.log("BlueSky service initialized successfully");
|
|
715
1512
|
}
|
|
716
1513
|
}
|
|
717
1514
|
]
|
|
@@ -734,14 +1531,25 @@ var blueSkyPlugin = {
|
|
|
734
1531
|
BLUESKY_ACTION_INTERVAL: process.env.BLUESKY_ACTION_INTERVAL ?? null,
|
|
735
1532
|
BLUESKY_POST_IMMEDIATELY: process.env.BLUESKY_POST_IMMEDIATELY ?? null,
|
|
736
1533
|
BLUESKY_MAX_ACTIONS_PROCESSING: process.env.BLUESKY_MAX_ACTIONS_PROCESSING ?? null,
|
|
737
|
-
BLUESKY_MAX_POST_LENGTH: process.env.BLUESKY_MAX_POST_LENGTH ?? null
|
|
1534
|
+
BLUESKY_MAX_POST_LENGTH: process.env.BLUESKY_MAX_POST_LENGTH ?? null,
|
|
1535
|
+
BLUESKY_ACCOUNTS: process.env.BLUESKY_ACCOUNTS ?? null,
|
|
1536
|
+
BLUESKY_DEFAULT_ACCOUNT_ID: process.env.BLUESKY_DEFAULT_ACCOUNT_ID ?? null
|
|
738
1537
|
},
|
|
739
|
-
async init(_config,
|
|
740
|
-
|
|
1538
|
+
async init(_config, runtime) {
|
|
1539
|
+
try {
|
|
1540
|
+
const manager = import_core7.getConnectorAccountManager(runtime);
|
|
1541
|
+
manager.registerProvider(createBlueSkyConnectorAccountProvider(runtime));
|
|
1542
|
+
} catch (err) {
|
|
1543
|
+
import_core7.logger.warn({
|
|
1544
|
+
src: "plugin:bluesky",
|
|
1545
|
+
err: err instanceof Error ? err.message : String(err)
|
|
1546
|
+
}, "Failed to register BlueSky provider with ConnectorAccountManager");
|
|
1547
|
+
}
|
|
1548
|
+
import_core7.logger.log("BlueSky plugin initialized");
|
|
741
1549
|
},
|
|
742
|
-
|
|
1550
|
+
actions: [],
|
|
1551
|
+
services: [BlueSkyService, BlueskyWorkflowCredentialProvider],
|
|
743
1552
|
tests: pluginTests
|
|
744
1553
|
};
|
|
745
|
-
var typescript_default = blueSkyPlugin;
|
|
746
1554
|
|
|
747
|
-
//# debugId=
|
|
1555
|
+
//# debugId=3E81B0DAACB595C464756E2164756E21
|