@elizaos/plugin-bluesky 2.0.0-beta.1 → 2.0.11-beta.7
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 +88 -160
- package/package.json +14 -7
- package/dist/browser/index.browser.js +0 -72
- package/dist/browser/index.browser.js.map +0 -10
- package/dist/browser/index.d.ts +0 -2
- package/dist/cjs/index.d.ts +0 -2
- package/dist/cjs/index.node.cjs +0 -1555
- package/dist/cjs/index.node.js.map +0 -20
- package/dist/index.d.ts +0 -2
- package/dist/node/index.d.ts +0 -2
- package/dist/node/index.node.js +0 -1510
- package/dist/node/index.node.js.map +0 -20
package/dist/cjs/index.node.cjs
DELETED
|
@@ -1,1555 +0,0 @@
|
|
|
1
|
-
var __create = Object.create;
|
|
2
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
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
|
-
};
|
|
32
|
-
var __toCommonJS = (from) => {
|
|
33
|
-
var entry = (__moduleCache ??= new WeakMap).get(from), desc;
|
|
34
|
-
if (entry)
|
|
35
|
-
return entry;
|
|
36
|
-
entry = __defProp({}, "__esModule", { value: true });
|
|
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
|
-
}
|
|
45
|
-
__moduleCache.set(from, entry);
|
|
46
|
-
return entry;
|
|
47
|
-
};
|
|
48
|
-
var __moduleCache;
|
|
49
|
-
var __returnValue = (v) => v;
|
|
50
|
-
function __exportSetter(name, newValue) {
|
|
51
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
52
|
-
}
|
|
53
|
-
var __export = (target, all) => {
|
|
54
|
-
for (var name in all)
|
|
55
|
-
__defProp(target, name, {
|
|
56
|
-
get: all[name],
|
|
57
|
-
enumerable: true,
|
|
58
|
-
configurable: true,
|
|
59
|
-
set: __exportSetter.bind(all, name)
|
|
60
|
-
});
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// index.node.ts
|
|
64
|
-
var exports_index_node = {};
|
|
65
|
-
__export(exports_index_node, {
|
|
66
|
-
resolveDefaultBlueSkyAccountId: () => resolveDefaultBlueSkyAccountId,
|
|
67
|
-
readBlueSkyAccountId: () => readBlueSkyAccountId,
|
|
68
|
-
normalizeBlueSkyAccountId: () => normalizeBlueSkyAccountId,
|
|
69
|
-
listBlueSkyAccountIds: () => listBlueSkyAccountIds,
|
|
70
|
-
default: () => import__.default,
|
|
71
|
-
createBlueSkyConnectorAccountProvider: () => createBlueSkyConnectorAccountProvider,
|
|
72
|
-
blueSkyPlugin: () => blueSkyPlugin,
|
|
73
|
-
DEFAULT_BLUESKY_ACCOUNT_ID: () => DEFAULT_BLUESKY_ACCOUNT_ID,
|
|
74
|
-
BlueSkyService: () => BlueSkyService,
|
|
75
|
-
BlueSkyClient: () => BlueSkyClient,
|
|
76
|
-
BLUESKY_PROVIDER_ID: () => BLUESKY_PROVIDER_ID
|
|
77
|
-
});
|
|
78
|
-
module.exports = __toCommonJS(exports_index_node);
|
|
79
|
-
|
|
80
|
-
// index.ts
|
|
81
|
-
var import_core7 = require("@elizaos/core");
|
|
82
|
-
|
|
83
|
-
// types/index.ts
|
|
84
|
-
var zod = __toESM(require("zod"));
|
|
85
|
-
var z2 = zod.z ?? zod;
|
|
86
|
-
var BLUESKY_SERVICE_URL = "https://bsky.social";
|
|
87
|
-
var BLUESKY_MAX_POST_LENGTH = 300;
|
|
88
|
-
var BLUESKY_POLL_INTERVAL = 60;
|
|
89
|
-
var BLUESKY_POST_INTERVAL_MIN = 1800;
|
|
90
|
-
var BLUESKY_POST_INTERVAL_MAX = 3600;
|
|
91
|
-
var BLUESKY_ACTION_INTERVAL = 120;
|
|
92
|
-
var BLUESKY_MAX_ACTIONS = 5;
|
|
93
|
-
var BLUESKY_CHAT_SERVICE_DID = "did:web:api.bsky.chat";
|
|
94
|
-
var BLUESKY_SERVICE_NAME = "bluesky";
|
|
95
|
-
var AT_PROTOCOL_HANDLE_REGEX = /^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/;
|
|
96
|
-
var CACHE_TTL = {
|
|
97
|
-
PROFILE: 3600000,
|
|
98
|
-
TIMELINE: 300000,
|
|
99
|
-
POST: 1800000,
|
|
100
|
-
NOTIFICATIONS: 300000,
|
|
101
|
-
CONVERSATIONS: 300000
|
|
102
|
-
};
|
|
103
|
-
var CACHE_SIZE = {
|
|
104
|
-
PROFILE: 1000,
|
|
105
|
-
TIMELINE: 500,
|
|
106
|
-
POST: 1e4,
|
|
107
|
-
NOTIFICATIONS: 1000,
|
|
108
|
-
CONVERSATIONS: 100
|
|
109
|
-
};
|
|
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)
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
class BlueSkyError extends Error {
|
|
127
|
-
code;
|
|
128
|
-
status;
|
|
129
|
-
constructor(message, code, status) {
|
|
130
|
-
super(message);
|
|
131
|
-
this.code = code;
|
|
132
|
-
this.status = status;
|
|
133
|
-
this.name = "BlueSkyError";
|
|
134
|
-
}
|
|
135
|
-
}
|
|
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
|
-
|
|
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");
|
|
381
|
-
function isPostView(item) {
|
|
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";
|
|
383
|
-
}
|
|
384
|
-
function isReplyWithPostViews(reply) {
|
|
385
|
-
return typeof reply === "object" && reply !== null && "root" in reply && "parent" in reply && isPostView(reply.root) && isPostView(reply.parent);
|
|
386
|
-
}
|
|
387
|
-
function adaptPostView(postView) {
|
|
388
|
-
const author = postView.author;
|
|
389
|
-
const record = postView.record;
|
|
390
|
-
return {
|
|
391
|
-
uri: postView.uri,
|
|
392
|
-
cid: postView.cid,
|
|
393
|
-
author: {
|
|
394
|
-
did: author.did,
|
|
395
|
-
handle: author.handle,
|
|
396
|
-
displayName: author.displayName,
|
|
397
|
-
description: author.description,
|
|
398
|
-
avatar: author.avatar,
|
|
399
|
-
banner: author.banner,
|
|
400
|
-
followersCount: author.followersCount,
|
|
401
|
-
followsCount: author.followsCount,
|
|
402
|
-
postsCount: author.postsCount,
|
|
403
|
-
indexedAt: author.indexedAt,
|
|
404
|
-
createdAt: author.createdAt
|
|
405
|
-
},
|
|
406
|
-
record: {
|
|
407
|
-
$type: record.$type ?? "app.bsky.feed.post",
|
|
408
|
-
text: record.text ?? "",
|
|
409
|
-
facets: record.facets,
|
|
410
|
-
embed: record.embed,
|
|
411
|
-
createdAt: record.createdAt ?? ""
|
|
412
|
-
},
|
|
413
|
-
embed: postView.embed,
|
|
414
|
-
replyCount: postView.replyCount,
|
|
415
|
-
repostCount: postView.repostCount,
|
|
416
|
-
likeCount: postView.likeCount,
|
|
417
|
-
quoteCount: postView.quoteCount,
|
|
418
|
-
indexedAt: postView.indexedAt
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
class BlueSkyClient {
|
|
423
|
-
config;
|
|
424
|
-
agent;
|
|
425
|
-
session = null;
|
|
426
|
-
profileCache;
|
|
427
|
-
constructor(config) {
|
|
428
|
-
this.config = config;
|
|
429
|
-
this.agent = new import_api.BskyAgent({ service: config.service });
|
|
430
|
-
this.profileCache = new import_lru_cache.LRUCache({
|
|
431
|
-
max: CACHE_SIZE.PROFILE,
|
|
432
|
-
ttl: CACHE_TTL.PROFILE
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
|
-
async authenticate() {
|
|
436
|
-
const response = await this.agent.login({
|
|
437
|
-
identifier: this.config.handle,
|
|
438
|
-
password: this.config.password
|
|
439
|
-
});
|
|
440
|
-
if (!response.success) {
|
|
441
|
-
throw new BlueSkyError("Authentication failed", "AUTH_FAILED");
|
|
442
|
-
}
|
|
443
|
-
this.session = {
|
|
444
|
-
did: response.data.did,
|
|
445
|
-
handle: response.data.handle,
|
|
446
|
-
email: response.data.email,
|
|
447
|
-
accessJwt: response.data.accessJwt,
|
|
448
|
-
refreshJwt: response.data.refreshJwt
|
|
449
|
-
};
|
|
450
|
-
import_core.logger.info(`Authenticated with BlueSky: ${this.session.handle}`);
|
|
451
|
-
return this.session;
|
|
452
|
-
}
|
|
453
|
-
getSession() {
|
|
454
|
-
return this.session;
|
|
455
|
-
}
|
|
456
|
-
async getProfile(handle) {
|
|
457
|
-
const cached = this.profileCache.get(handle);
|
|
458
|
-
if (cached)
|
|
459
|
-
return cached;
|
|
460
|
-
const response = await this.agent.getProfile({ actor: handle });
|
|
461
|
-
const profile = {
|
|
462
|
-
did: response.data.did,
|
|
463
|
-
handle: response.data.handle,
|
|
464
|
-
displayName: response.data.displayName,
|
|
465
|
-
description: response.data.description,
|
|
466
|
-
avatar: response.data.avatar,
|
|
467
|
-
banner: response.data.banner,
|
|
468
|
-
followersCount: response.data.followersCount,
|
|
469
|
-
followsCount: response.data.followsCount,
|
|
470
|
-
postsCount: response.data.postsCount,
|
|
471
|
-
indexedAt: response.data.indexedAt,
|
|
472
|
-
createdAt: response.data.createdAt
|
|
473
|
-
};
|
|
474
|
-
this.profileCache.set(handle, profile);
|
|
475
|
-
return profile;
|
|
476
|
-
}
|
|
477
|
-
async getTimeline(params = {}) {
|
|
478
|
-
const response = await this.agent.getTimeline({
|
|
479
|
-
algorithm: params.algorithm,
|
|
480
|
-
limit: params.limit ?? 50,
|
|
481
|
-
cursor: params.cursor
|
|
482
|
-
});
|
|
483
|
-
return {
|
|
484
|
-
cursor: response.data.cursor,
|
|
485
|
-
feed: response.data.feed.map((item) => {
|
|
486
|
-
const reply = isReplyWithPostViews(item.reply) ? {
|
|
487
|
-
root: adaptPostView(item.reply.root),
|
|
488
|
-
parent: adaptPostView(item.reply.parent)
|
|
489
|
-
} : undefined;
|
|
490
|
-
return {
|
|
491
|
-
post: adaptPostView(item.post),
|
|
492
|
-
reply,
|
|
493
|
-
reason: item.reason
|
|
494
|
-
};
|
|
495
|
-
})
|
|
496
|
-
};
|
|
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
|
-
}
|
|
510
|
-
async sendPost(request) {
|
|
511
|
-
if (this.config.dryRun) {
|
|
512
|
-
import_core.logger.info(`Dry run: would create post with text: ${request.content.text}`);
|
|
513
|
-
return this.mockPost(request.content.text);
|
|
514
|
-
}
|
|
515
|
-
const rt = new import_api.RichText({ text: request.content.text });
|
|
516
|
-
await rt.detectFacets(this.agent);
|
|
517
|
-
const record = {
|
|
518
|
-
$type: "app.bsky.feed.post",
|
|
519
|
-
text: rt.text,
|
|
520
|
-
facets: rt.facets,
|
|
521
|
-
createdAt: new Date().toISOString()
|
|
522
|
-
};
|
|
523
|
-
if (request.replyTo) {
|
|
524
|
-
record.reply = { root: request.replyTo, parent: request.replyTo };
|
|
525
|
-
}
|
|
526
|
-
if (request.content.embed) {
|
|
527
|
-
record.embed = request.content.embed;
|
|
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");
|
|
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");
|
|
543
|
-
const thread = await this.agent.getPostThread({
|
|
544
|
-
uri: response.uri,
|
|
545
|
-
depth: 0
|
|
546
|
-
});
|
|
547
|
-
if (thread.data.thread.$type !== "app.bsky.feed.defs#threadViewPost") {
|
|
548
|
-
throw new BlueSkyError("Failed to retrieve created post", "POST_CREATE_FAILED");
|
|
549
|
-
}
|
|
550
|
-
const threadViewPost = thread.data.thread;
|
|
551
|
-
return adaptPostView(threadViewPost.post);
|
|
552
|
-
}
|
|
553
|
-
async deletePost(uri) {
|
|
554
|
-
if (this.config.dryRun) {
|
|
555
|
-
import_core.logger.info(`Dry run: would delete post: ${uri}`);
|
|
556
|
-
return;
|
|
557
|
-
}
|
|
558
|
-
await this.agent.deletePost(uri);
|
|
559
|
-
}
|
|
560
|
-
async likePost(uri, cid) {
|
|
561
|
-
if (this.config.dryRun) {
|
|
562
|
-
import_core.logger.info(`Dry run: would like post: ${uri}`);
|
|
563
|
-
return;
|
|
564
|
-
}
|
|
565
|
-
await this.agent.like(uri, cid);
|
|
566
|
-
}
|
|
567
|
-
async repost(uri, cid) {
|
|
568
|
-
if (this.config.dryRun) {
|
|
569
|
-
import_core.logger.info({ uri }, "Dry run: would repost");
|
|
570
|
-
return;
|
|
571
|
-
}
|
|
572
|
-
await this.agent.repost(uri, cid);
|
|
573
|
-
}
|
|
574
|
-
async getNotifications(limit = 50, cursor) {
|
|
575
|
-
const response = await this.agent.listNotifications({ limit, cursor });
|
|
576
|
-
return {
|
|
577
|
-
notifications: response.data.notifications,
|
|
578
|
-
cursor: response.data.cursor
|
|
579
|
-
};
|
|
580
|
-
}
|
|
581
|
-
async updateSeenNotifications() {
|
|
582
|
-
await this.agent.updateSeenNotifications();
|
|
583
|
-
}
|
|
584
|
-
async getConversations(limit = 50, cursor) {
|
|
585
|
-
const response = await this.agent.api.chat.bsky.convo.listConvos({ limit, cursor }, { headers: { "atproto-proxy": BLUESKY_CHAT_SERVICE_DID } });
|
|
586
|
-
return {
|
|
587
|
-
conversations: response.data.convos,
|
|
588
|
-
cursor: response.data.cursor
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
|
-
async getMessages(convoId, limit = 50, cursor) {
|
|
592
|
-
const response = await this.agent.api.chat.bsky.convo.getMessages({ convoId, limit, cursor }, { headers: { "atproto-proxy": BLUESKY_CHAT_SERVICE_DID } });
|
|
593
|
-
return {
|
|
594
|
-
messages: response.data.messages,
|
|
595
|
-
cursor: response.data.cursor
|
|
596
|
-
};
|
|
597
|
-
}
|
|
598
|
-
async sendMessage(request) {
|
|
599
|
-
if (this.config.dryRun) {
|
|
600
|
-
import_core.logger.info({ convoId: request.convoId }, "Dry run: would send message");
|
|
601
|
-
return this.mockMessage(request.message.text ?? "");
|
|
602
|
-
}
|
|
603
|
-
const response = await this.agent.api.chat.bsky.convo.sendMessage({
|
|
604
|
-
convoId: request.convoId,
|
|
605
|
-
message: { text: request.message.text ?? "" }
|
|
606
|
-
}, { headers: { "atproto-proxy": BLUESKY_CHAT_SERVICE_DID } });
|
|
607
|
-
return response.data;
|
|
608
|
-
}
|
|
609
|
-
async cleanup() {
|
|
610
|
-
this.profileCache.clear();
|
|
611
|
-
this.session = null;
|
|
612
|
-
}
|
|
613
|
-
mockPost(text) {
|
|
614
|
-
const now = new Date().toISOString();
|
|
615
|
-
return {
|
|
616
|
-
uri: `mock://post/${Date.now()}`,
|
|
617
|
-
cid: `mock-cid-${Date.now()}`,
|
|
618
|
-
author: {
|
|
619
|
-
did: this.session?.did ?? "did:plc:mock",
|
|
620
|
-
handle: this.session?.handle ?? "mock.handle"
|
|
621
|
-
},
|
|
622
|
-
record: { $type: "app.bsky.feed.post", text, createdAt: now },
|
|
623
|
-
indexedAt: now
|
|
624
|
-
};
|
|
625
|
-
}
|
|
626
|
-
mockMessage(text) {
|
|
627
|
-
return {
|
|
628
|
-
id: `mock-msg-${Date.now()}`,
|
|
629
|
-
rev: "1",
|
|
630
|
-
text,
|
|
631
|
-
sender: { did: this.session?.did ?? "did:plc:mock" },
|
|
632
|
-
sentAt: new Date().toISOString()
|
|
633
|
-
};
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
// managers/agent.ts
|
|
638
|
-
var import_core2 = require("@elizaos/core");
|
|
639
|
-
function cursorCacheKey(agentId, accountId) {
|
|
640
|
-
return `bluesky:cursor:${agentId}:${accountId}`;
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
class BlueSkyAgentManager {
|
|
644
|
-
runtime;
|
|
645
|
-
config;
|
|
646
|
-
client;
|
|
647
|
-
pollTimer = null;
|
|
648
|
-
actionTimer = null;
|
|
649
|
-
postTimer = null;
|
|
650
|
-
running = false;
|
|
651
|
-
lastSeenAt = null;
|
|
652
|
-
accountId;
|
|
653
|
-
constructor(runtime, config, client) {
|
|
654
|
-
this.runtime = runtime;
|
|
655
|
-
this.config = config;
|
|
656
|
-
this.client = client;
|
|
657
|
-
this.accountId = normalizeBlueSkyAccountId(config.accountId ?? DEFAULT_BLUESKY_ACCOUNT_ID);
|
|
658
|
-
}
|
|
659
|
-
getAccountId() {
|
|
660
|
-
return this.accountId;
|
|
661
|
-
}
|
|
662
|
-
async start() {
|
|
663
|
-
if (this.running)
|
|
664
|
-
return;
|
|
665
|
-
await this.client.authenticate();
|
|
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
|
-
}
|
|
671
|
-
this.startNotificationPolling();
|
|
672
|
-
if (this.config.enableActionProcessing) {
|
|
673
|
-
this.startActionProcessing();
|
|
674
|
-
}
|
|
675
|
-
if (isPostingEnabled(this.runtime, this.accountId)) {
|
|
676
|
-
this.startAutomatedPosting();
|
|
677
|
-
}
|
|
678
|
-
import_core2.logger.success({ agentId: this.runtime.agentId, accountId: this.accountId }, "BlueSky agent manager started");
|
|
679
|
-
}
|
|
680
|
-
async stop() {
|
|
681
|
-
this.running = false;
|
|
682
|
-
if (this.pollTimer)
|
|
683
|
-
clearInterval(this.pollTimer);
|
|
684
|
-
if (this.actionTimer)
|
|
685
|
-
clearInterval(this.actionTimer);
|
|
686
|
-
if (this.postTimer)
|
|
687
|
-
clearTimeout(this.postTimer);
|
|
688
|
-
this.pollTimer = null;
|
|
689
|
-
this.actionTimer = null;
|
|
690
|
-
this.postTimer = null;
|
|
691
|
-
await this.client.cleanup();
|
|
692
|
-
import_core2.logger.info({ agentId: this.runtime.agentId, accountId: this.accountId }, "BlueSky agent manager stopped");
|
|
693
|
-
}
|
|
694
|
-
startNotificationPolling() {
|
|
695
|
-
const interval = getPollInterval(this.runtime, this.accountId);
|
|
696
|
-
this.pollNotifications();
|
|
697
|
-
this.pollTimer = setInterval(() => this.pollNotifications(), interval);
|
|
698
|
-
}
|
|
699
|
-
async pollNotifications() {
|
|
700
|
-
if (!this.running)
|
|
701
|
-
return;
|
|
702
|
-
const { notifications } = await this.client.getNotifications(50);
|
|
703
|
-
if (notifications.length === 0)
|
|
704
|
-
return;
|
|
705
|
-
const newNotifications = this.lastSeenAt ? notifications.filter((n) => {
|
|
706
|
-
const lastSeen = this.lastSeenAt;
|
|
707
|
-
return lastSeen !== null && n.indexedAt > lastSeen;
|
|
708
|
-
}) : notifications;
|
|
709
|
-
if (newNotifications.length > 0) {
|
|
710
|
-
this.lastSeenAt = notifications[0].indexedAt;
|
|
711
|
-
await this.runtime.setCache(cursorCacheKey(this.runtime.agentId, this.accountId), this.lastSeenAt);
|
|
712
|
-
for (const notification of newNotifications) {
|
|
713
|
-
this.emitNotificationEvent(notification);
|
|
714
|
-
}
|
|
715
|
-
await this.client.updateSeenNotifications();
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
emitNotificationEvent(notification) {
|
|
719
|
-
const eventMap = {
|
|
720
|
-
mention: "bluesky.mention_received",
|
|
721
|
-
reply: "bluesky.mention_received",
|
|
722
|
-
follow: "bluesky.follow_received",
|
|
723
|
-
like: "bluesky.like_received",
|
|
724
|
-
repost: "bluesky.repost_received",
|
|
725
|
-
quote: "bluesky.quote_received"
|
|
726
|
-
};
|
|
727
|
-
const event = eventMap[notification.reason];
|
|
728
|
-
if (event) {
|
|
729
|
-
const payload = {
|
|
730
|
-
runtime: this.runtime,
|
|
731
|
-
source: "bluesky",
|
|
732
|
-
accountId: this.accountId,
|
|
733
|
-
notification
|
|
734
|
-
};
|
|
735
|
-
this.runtime.emitEvent(event, payload);
|
|
736
|
-
}
|
|
737
|
-
}
|
|
738
|
-
startActionProcessing() {
|
|
739
|
-
const interval = getActionInterval(this.runtime, this.accountId);
|
|
740
|
-
this.processQueuedActions();
|
|
741
|
-
this.actionTimer = setInterval(() => this.processQueuedActions(), interval);
|
|
742
|
-
}
|
|
743
|
-
async processQueuedActions() {
|
|
744
|
-
if (!this.running)
|
|
745
|
-
return;
|
|
746
|
-
const max = getMaxActionsProcessing(this.runtime, this.accountId);
|
|
747
|
-
const { notifications } = await this.client.getNotifications(max);
|
|
748
|
-
for (const notification of notifications) {
|
|
749
|
-
if (notification.reason === "mention" || notification.reason === "reply") {
|
|
750
|
-
const payload = {
|
|
751
|
-
runtime: this.runtime,
|
|
752
|
-
source: "bluesky",
|
|
753
|
-
accountId: this.accountId,
|
|
754
|
-
notification
|
|
755
|
-
};
|
|
756
|
-
this.runtime.emitEvent("bluesky.should_respond", payload);
|
|
757
|
-
}
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
startAutomatedPosting() {
|
|
761
|
-
if (shouldPostImmediately(this.runtime, this.accountId)) {
|
|
762
|
-
this.createAutomatedPost();
|
|
763
|
-
}
|
|
764
|
-
this.scheduleNextPost();
|
|
765
|
-
}
|
|
766
|
-
scheduleNextPost() {
|
|
767
|
-
const { min, max } = getPostIntervalRange(this.runtime, this.accountId);
|
|
768
|
-
const interval = Math.random() * (max - min) + min;
|
|
769
|
-
this.postTimer = setTimeout(() => {
|
|
770
|
-
if (this.running) {
|
|
771
|
-
this.createAutomatedPost().finally(() => this.scheduleNextPost());
|
|
772
|
-
}
|
|
773
|
-
}, interval);
|
|
774
|
-
}
|
|
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
|
-
});
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
// services/message.ts
|
|
798
|
-
var import_core3 = require("@elizaos/core");
|
|
799
|
-
|
|
800
|
-
// prompts.ts
|
|
801
|
-
var generateDmTemplate = `Generate a friendly direct message response under 200 characters.`;
|
|
802
|
-
var generatePostTemplate = `Generate an engaging BlueSky post under {{maxLength}} characters.`;
|
|
803
|
-
var truncatePostTemplate = `Shorten to under {{maxLength}} characters: "{{text}}"`;
|
|
804
|
-
|
|
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
|
-
|
|
831
|
-
class BlueSkyMessageService {
|
|
832
|
-
client;
|
|
833
|
-
runtime;
|
|
834
|
-
accountId;
|
|
835
|
-
static serviceType = "IMessageService";
|
|
836
|
-
constructor(client, runtime, accountId = "default") {
|
|
837
|
-
this.client = client;
|
|
838
|
-
this.runtime = runtime;
|
|
839
|
-
this.accountId = accountId;
|
|
840
|
-
}
|
|
841
|
-
getAccountId() {
|
|
842
|
-
return normalizeBlueSkyAccountId(this.accountId);
|
|
843
|
-
}
|
|
844
|
-
async getMessages(convoId, limit = 50) {
|
|
845
|
-
const response = await this.client.getMessages(convoId, limit);
|
|
846
|
-
return response.messages;
|
|
847
|
-
}
|
|
848
|
-
async sendMessage(convoId, text) {
|
|
849
|
-
const messageText = text.trim() || await this.generateReply();
|
|
850
|
-
return this.client.sendMessage({ convoId, message: { text: messageText } });
|
|
851
|
-
}
|
|
852
|
-
async getConversations(limit = 50) {
|
|
853
|
-
const response = await this.client.getConversations(limit);
|
|
854
|
-
return response.conversations;
|
|
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
|
-
}
|
|
1065
|
-
async generateReply() {
|
|
1066
|
-
const prompt = import_core3.composePrompt({
|
|
1067
|
-
state: {},
|
|
1068
|
-
template: generateDmTemplate
|
|
1069
|
-
});
|
|
1070
|
-
const response = await this.runtime.useModel(import_core3.ModelType.TEXT_SMALL, {
|
|
1071
|
-
prompt,
|
|
1072
|
-
maxTokens: 50
|
|
1073
|
-
});
|
|
1074
|
-
return response;
|
|
1075
|
-
}
|
|
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
|
-
}
|
|
1082
|
-
|
|
1083
|
-
// services/post.ts
|
|
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
|
-
|
|
1100
|
-
class BlueSkyPostService {
|
|
1101
|
-
client;
|
|
1102
|
-
runtime;
|
|
1103
|
-
accountId;
|
|
1104
|
-
static serviceType = "IPostService";
|
|
1105
|
-
constructor(client, runtime, accountId = "default") {
|
|
1106
|
-
this.client = client;
|
|
1107
|
-
this.runtime = runtime;
|
|
1108
|
-
this.accountId = accountId;
|
|
1109
|
-
}
|
|
1110
|
-
getAccountId() {
|
|
1111
|
-
return normalizeBlueSkyAccountId(this.accountId);
|
|
1112
|
-
}
|
|
1113
|
-
async getPosts(limit = 50, cursor) {
|
|
1114
|
-
const response = await this.client.getTimeline({ limit, cursor });
|
|
1115
|
-
return response.feed.map((item) => item.post);
|
|
1116
|
-
}
|
|
1117
|
-
async createPost(text, replyTo) {
|
|
1118
|
-
let postText = text.trim() || await this.generateContent();
|
|
1119
|
-
if (postText.length > BLUESKY_MAX_POST_LENGTH) {
|
|
1120
|
-
postText = await this.truncate(postText);
|
|
1121
|
-
}
|
|
1122
|
-
const request = {
|
|
1123
|
-
content: { text: postText },
|
|
1124
|
-
replyTo
|
|
1125
|
-
};
|
|
1126
|
-
return this.client.sendPost(request);
|
|
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
|
-
}
|
|
1165
|
-
async deletePost(uri) {
|
|
1166
|
-
await this.client.deletePost(uri);
|
|
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
|
-
}
|
|
1213
|
-
async generateContent() {
|
|
1214
|
-
const prompt = import_core4.composePrompt({
|
|
1215
|
-
state: {
|
|
1216
|
-
maxLength: String(BLUESKY_MAX_POST_LENGTH)
|
|
1217
|
-
},
|
|
1218
|
-
template: generatePostTemplate
|
|
1219
|
-
});
|
|
1220
|
-
const response = await this.runtime.useModel(import_core4.ModelType.TEXT_SMALL, {
|
|
1221
|
-
prompt,
|
|
1222
|
-
maxTokens: 100
|
|
1223
|
-
});
|
|
1224
|
-
return response;
|
|
1225
|
-
}
|
|
1226
|
-
async truncate(text) {
|
|
1227
|
-
const prompt = import_core4.composePrompt({
|
|
1228
|
-
state: {
|
|
1229
|
-
maxLength: String(BLUESKY_MAX_POST_LENGTH),
|
|
1230
|
-
text
|
|
1231
|
-
},
|
|
1232
|
-
template: truncatePostTemplate
|
|
1233
|
-
});
|
|
1234
|
-
const response = await this.runtime.useModel(import_core4.ModelType.TEXT_SMALL, {
|
|
1235
|
-
prompt,
|
|
1236
|
-
maxTokens: 100
|
|
1237
|
-
});
|
|
1238
|
-
const truncated = response;
|
|
1239
|
-
return truncated.length > BLUESKY_MAX_POST_LENGTH ? `${truncated.substring(0, BLUESKY_MAX_POST_LENGTH - 3)}...` : truncated;
|
|
1240
|
-
}
|
|
1241
|
-
}
|
|
1242
|
-
|
|
1243
|
-
// services/bluesky.ts
|
|
1244
|
-
class BlueSkyService extends import_core5.Service {
|
|
1245
|
-
static instance;
|
|
1246
|
-
agents = new Map;
|
|
1247
|
-
static serviceType = BLUESKY_SERVICE_NAME;
|
|
1248
|
-
capabilityDescription = "Send and receive messages on BlueSky";
|
|
1249
|
-
static getInstance() {
|
|
1250
|
-
BlueSkyService.instance ??= new BlueSkyService;
|
|
1251
|
-
return BlueSkyService.instance;
|
|
1252
|
-
}
|
|
1253
|
-
static async start(runtime) {
|
|
1254
|
-
const service = BlueSkyService.getInstance();
|
|
1255
|
-
if (service.agents.has(runtime.agentId)) {
|
|
1256
|
-
return service;
|
|
1257
|
-
}
|
|
1258
|
-
if (!hasBlueSkyEnabled(runtime)) {
|
|
1259
|
-
return service;
|
|
1260
|
-
}
|
|
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
|
-
}
|
|
1292
|
-
return service;
|
|
1293
|
-
}
|
|
1294
|
-
static async stop(runtime) {
|
|
1295
|
-
const service = BlueSkyService.getInstance();
|
|
1296
|
-
const accounts = service.agents.get(runtime.agentId);
|
|
1297
|
-
if (!accounts)
|
|
1298
|
-
return;
|
|
1299
|
-
for (const manager of accounts.managers.values()) {
|
|
1300
|
-
await manager.stop();
|
|
1301
|
-
}
|
|
1302
|
-
service.agents.delete(runtime.agentId);
|
|
1303
|
-
import_core5.logger.info({ agentId: runtime.agentId }, "BlueSky client stopped");
|
|
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
|
-
}
|
|
1398
|
-
async stop() {
|
|
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);
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
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;
|
|
1435
|
-
}
|
|
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
|
-
};
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
// index.ts
|
|
1483
|
-
var pluginTests = [
|
|
1484
|
-
{
|
|
1485
|
-
name: "bluesky_plugin_tests",
|
|
1486
|
-
tests: [
|
|
1487
|
-
{
|
|
1488
|
-
name: "bluesky_test_credentials_validation",
|
|
1489
|
-
fn: async (runtime) => {
|
|
1490
|
-
const handle = getApiKeyOptional(runtime, "BLUESKY_HANDLE");
|
|
1491
|
-
const password = getApiKeyOptional(runtime, "BLUESKY_PASSWORD");
|
|
1492
|
-
if (!handle || !password) {
|
|
1493
|
-
throw new Error("BLUESKY_HANDLE and BLUESKY_PASSWORD are not configured");
|
|
1494
|
-
}
|
|
1495
|
-
import_core7.logger.log("BlueSky credentials are configured");
|
|
1496
|
-
}
|
|
1497
|
-
},
|
|
1498
|
-
{
|
|
1499
|
-
name: "bluesky_test_service_initialization",
|
|
1500
|
-
fn: async (runtime) => {
|
|
1501
|
-
const handle = getApiKeyOptional(runtime, "BLUESKY_HANDLE");
|
|
1502
|
-
const password = getApiKeyOptional(runtime, "BLUESKY_PASSWORD");
|
|
1503
|
-
if (!handle || !password) {
|
|
1504
|
-
import_core7.logger.log("Skipping service initialization test - credentials not configured");
|
|
1505
|
-
return;
|
|
1506
|
-
}
|
|
1507
|
-
const service = await BlueSkyService.start(runtime);
|
|
1508
|
-
if (!service) {
|
|
1509
|
-
throw new Error("Failed to initialize BlueSky service");
|
|
1510
|
-
}
|
|
1511
|
-
import_core7.logger.log("BlueSky service initialized successfully");
|
|
1512
|
-
}
|
|
1513
|
-
}
|
|
1514
|
-
]
|
|
1515
|
-
}
|
|
1516
|
-
];
|
|
1517
|
-
var blueSkyPlugin = {
|
|
1518
|
-
name: "bluesky",
|
|
1519
|
-
description: "BlueSky client plugin using AT Protocol for social interactions",
|
|
1520
|
-
config: {
|
|
1521
|
-
BLUESKY_HANDLE: process.env.BLUESKY_HANDLE ?? null,
|
|
1522
|
-
BLUESKY_PASSWORD: process.env.BLUESKY_PASSWORD ?? null,
|
|
1523
|
-
BLUESKY_SERVICE: process.env.BLUESKY_SERVICE ?? null,
|
|
1524
|
-
BLUESKY_DRY_RUN: process.env.BLUESKY_DRY_RUN ?? null,
|
|
1525
|
-
BLUESKY_POLL_INTERVAL: process.env.BLUESKY_POLL_INTERVAL ?? null,
|
|
1526
|
-
BLUESKY_ENABLE_POSTING: process.env.BLUESKY_ENABLE_POSTING ?? null,
|
|
1527
|
-
BLUESKY_ENABLE_DMS: process.env.BLUESKY_ENABLE_DMS ?? null,
|
|
1528
|
-
BLUESKY_POST_INTERVAL_MIN: process.env.BLUESKY_POST_INTERVAL_MIN ?? null,
|
|
1529
|
-
BLUESKY_POST_INTERVAL_MAX: process.env.BLUESKY_POST_INTERVAL_MAX ?? null,
|
|
1530
|
-
BLUESKY_ENABLE_ACTION_PROCESSING: process.env.BLUESKY_ENABLE_ACTION_PROCESSING ?? null,
|
|
1531
|
-
BLUESKY_ACTION_INTERVAL: process.env.BLUESKY_ACTION_INTERVAL ?? null,
|
|
1532
|
-
BLUESKY_POST_IMMEDIATELY: process.env.BLUESKY_POST_IMMEDIATELY ?? null,
|
|
1533
|
-
BLUESKY_MAX_ACTIONS_PROCESSING: process.env.BLUESKY_MAX_ACTIONS_PROCESSING ?? 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
|
|
1537
|
-
},
|
|
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");
|
|
1549
|
-
},
|
|
1550
|
-
actions: [],
|
|
1551
|
-
services: [BlueSkyService, BlueskyWorkflowCredentialProvider],
|
|
1552
|
-
tests: pluginTests
|
|
1553
|
-
};
|
|
1554
|
-
|
|
1555
|
-
//# debugId=3E81B0DAACB595C464756E2164756E21
|