@elizaos/plugin-bluesky 2.0.0-alpha.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.
@@ -0,0 +1,709 @@
1
+ // index.ts
2
+ import { logger as logger4 } from "@elizaos/core";
3
+
4
+ // services/bluesky.ts
5
+ import { logger as logger3, Service } from "@elizaos/core";
6
+
7
+ // client.ts
8
+ import { BskyAgent, RichText } from "@atproto/api";
9
+ import { logger } from "@elizaos/core";
10
+ import { LRUCache } from "lru-cache";
11
+
12
+ // types/index.ts
13
+ import { z } from "zod";
14
+ var BLUESKY_SERVICE_URL = "https://bsky.social";
15
+ var BLUESKY_MAX_POST_LENGTH = 300;
16
+ var BLUESKY_POLL_INTERVAL = 60;
17
+ var BLUESKY_POST_INTERVAL_MIN = 1800;
18
+ var BLUESKY_POST_INTERVAL_MAX = 3600;
19
+ var BLUESKY_ACTION_INTERVAL = 120;
20
+ var BLUESKY_MAX_ACTIONS = 5;
21
+ var BLUESKY_CHAT_SERVICE_DID = "did:web:api.bsky.chat";
22
+ var BLUESKY_SERVICE_NAME = "bluesky";
23
+ 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])?$/;
24
+ var CACHE_TTL = {
25
+ PROFILE: 3600000,
26
+ TIMELINE: 300000,
27
+ POST: 1800000,
28
+ NOTIFICATIONS: 300000,
29
+ CONVERSATIONS: 300000
30
+ };
31
+ var CACHE_SIZE = {
32
+ PROFILE: 1000,
33
+ TIMELINE: 500,
34
+ POST: 1e4,
35
+ NOTIFICATIONS: 1000,
36
+ CONVERSATIONS: 100
37
+ };
38
+ var BlueSkyConfigSchema = z.object({
39
+ handle: z.string().regex(AT_PROTOCOL_HANDLE_REGEX, "Invalid handle format"),
40
+ password: z.string().min(1),
41
+ service: z.string().url().default(BLUESKY_SERVICE_URL),
42
+ dryRun: z.boolean().default(false),
43
+ pollInterval: z.number().positive().default(BLUESKY_POLL_INTERVAL),
44
+ enablePost: z.boolean().default(true),
45
+ postIntervalMin: z.number().positive().default(BLUESKY_POST_INTERVAL_MIN),
46
+ postIntervalMax: z.number().positive().default(BLUESKY_POST_INTERVAL_MAX),
47
+ enableActionProcessing: z.boolean().default(true),
48
+ actionInterval: z.number().positive().default(BLUESKY_ACTION_INTERVAL),
49
+ postImmediately: z.boolean().default(false),
50
+ maxActionsProcessing: z.number().positive().default(BLUESKY_MAX_ACTIONS),
51
+ enableDMs: z.boolean().default(true)
52
+ });
53
+
54
+ class BlueSkyError extends Error {
55
+ code;
56
+ status;
57
+ constructor(message, code, status) {
58
+ super(message);
59
+ this.code = code;
60
+ this.status = status;
61
+ this.name = "BlueSkyError";
62
+ }
63
+ }
64
+
65
+ // client.ts
66
+ function isPostView(item) {
67
+ return typeof item === "object" && item !== null && "uri" in item && "cid" in item && "author" in item && "record" in item && "indexedAt" in item && typeof item.uri === "string" && typeof item.cid === "string";
68
+ }
69
+ function adaptPostView(postView) {
70
+ const author = postView.author;
71
+ const record = postView.record;
72
+ return {
73
+ uri: postView.uri,
74
+ cid: postView.cid,
75
+ author: {
76
+ did: author.did,
77
+ handle: author.handle,
78
+ displayName: author.displayName,
79
+ description: author.description,
80
+ avatar: author.avatar,
81
+ banner: author.banner,
82
+ followersCount: author.followersCount,
83
+ followsCount: author.followsCount,
84
+ postsCount: author.postsCount,
85
+ indexedAt: author.indexedAt,
86
+ createdAt: author.createdAt
87
+ },
88
+ record: {
89
+ $type: record.$type ?? "app.bsky.feed.post",
90
+ text: record.text ?? "",
91
+ facets: record.facets,
92
+ embed: record.embed,
93
+ createdAt: record.createdAt ?? ""
94
+ },
95
+ embed: postView.embed,
96
+ replyCount: postView.replyCount,
97
+ repostCount: postView.repostCount,
98
+ likeCount: postView.likeCount,
99
+ quoteCount: postView.quoteCount,
100
+ indexedAt: postView.indexedAt
101
+ };
102
+ }
103
+
104
+ class BlueSkyClient {
105
+ config;
106
+ agent;
107
+ session = null;
108
+ profileCache;
109
+ constructor(config) {
110
+ this.config = config;
111
+ this.agent = new BskyAgent({ service: config.service });
112
+ this.profileCache = new LRUCache({
113
+ max: CACHE_SIZE.PROFILE,
114
+ ttl: CACHE_TTL.PROFILE
115
+ });
116
+ }
117
+ async authenticate() {
118
+ const response = await this.agent.login({
119
+ identifier: this.config.handle,
120
+ password: this.config.password
121
+ });
122
+ if (!response.success) {
123
+ throw new BlueSkyError("Authentication failed", "AUTH_FAILED");
124
+ }
125
+ this.session = {
126
+ did: response.data.did,
127
+ handle: response.data.handle,
128
+ email: response.data.email,
129
+ accessJwt: response.data.accessJwt,
130
+ refreshJwt: response.data.refreshJwt
131
+ };
132
+ logger.info(`Authenticated with BlueSky: ${this.session.handle}`);
133
+ return this.session;
134
+ }
135
+ getSession() {
136
+ return this.session;
137
+ }
138
+ async getProfile(handle) {
139
+ const cached = this.profileCache.get(handle);
140
+ if (cached)
141
+ return cached;
142
+ const response = await this.agent.getProfile({ actor: handle });
143
+ const profile = {
144
+ did: response.data.did,
145
+ handle: response.data.handle,
146
+ displayName: response.data.displayName,
147
+ description: response.data.description,
148
+ avatar: response.data.avatar,
149
+ banner: response.data.banner,
150
+ followersCount: response.data.followersCount,
151
+ followsCount: response.data.followsCount,
152
+ postsCount: response.data.postsCount,
153
+ indexedAt: response.data.indexedAt,
154
+ createdAt: response.data.createdAt
155
+ };
156
+ this.profileCache.set(handle, profile);
157
+ return profile;
158
+ }
159
+ async getTimeline(params = {}) {
160
+ const response = await this.agent.getTimeline({
161
+ algorithm: params.algorithm,
162
+ limit: params.limit ?? 50,
163
+ cursor: params.cursor
164
+ });
165
+ return {
166
+ cursor: response.data.cursor,
167
+ feed: response.data.feed.map((item) => ({
168
+ post: adaptPostView(item.post),
169
+ reply: item.reply && isPostView(item.reply.root) && isPostView(item.reply.parent) ? {
170
+ root: adaptPostView(item.reply.root),
171
+ parent: adaptPostView(item.reply.parent)
172
+ } : undefined,
173
+ reason: item.reason
174
+ }))
175
+ };
176
+ }
177
+ async sendPost(request) {
178
+ if (this.config.dryRun) {
179
+ logger.info(`Dry run: would create post with text: ${request.content.text}`);
180
+ return this.mockPost(request.content.text);
181
+ }
182
+ const rt = new RichText({ text: request.content.text });
183
+ await rt.detectFacets(this.agent);
184
+ const record = {
185
+ $type: "app.bsky.feed.post",
186
+ text: rt.text,
187
+ facets: rt.facets,
188
+ createdAt: new Date().toISOString()
189
+ };
190
+ if (request.replyTo) {
191
+ record.reply = { root: request.replyTo, parent: request.replyTo };
192
+ }
193
+ if (request.content.embed) {
194
+ record.embed = request.content.embed;
195
+ }
196
+ const response = await this.agent.post(record);
197
+ const thread = await this.agent.getPostThread({
198
+ uri: response.uri,
199
+ depth: 0
200
+ });
201
+ if (thread.data.thread.$type !== "app.bsky.feed.defs#threadViewPost") {
202
+ throw new BlueSkyError("Failed to retrieve created post", "POST_CREATE_FAILED");
203
+ }
204
+ const threadViewPost = thread.data.thread;
205
+ return adaptPostView(threadViewPost.post);
206
+ }
207
+ async deletePost(uri) {
208
+ if (this.config.dryRun) {
209
+ logger.info(`Dry run: would delete post: ${uri}`);
210
+ return;
211
+ }
212
+ await this.agent.deletePost(uri);
213
+ }
214
+ async likePost(uri, cid) {
215
+ if (this.config.dryRun) {
216
+ logger.info(`Dry run: would like post: ${uri}`);
217
+ return;
218
+ }
219
+ await this.agent.like(uri, cid);
220
+ }
221
+ async repost(uri, cid) {
222
+ if (this.config.dryRun) {
223
+ logger.info({ uri }, "Dry run: would repost");
224
+ return;
225
+ }
226
+ await this.agent.repost(uri, cid);
227
+ }
228
+ async getNotifications(limit = 50, cursor) {
229
+ const response = await this.agent.listNotifications({ limit, cursor });
230
+ return {
231
+ notifications: response.data.notifications,
232
+ cursor: response.data.cursor
233
+ };
234
+ }
235
+ async updateSeenNotifications() {
236
+ await this.agent.updateSeenNotifications();
237
+ }
238
+ async getConversations(limit = 50, cursor) {
239
+ const response = await this.agent.api.chat.bsky.convo.listConvos({ limit, cursor }, { headers: { "atproto-proxy": BLUESKY_CHAT_SERVICE_DID } });
240
+ return {
241
+ conversations: response.data.convos,
242
+ cursor: response.data.cursor
243
+ };
244
+ }
245
+ async getMessages(convoId, limit = 50, cursor) {
246
+ const response = await this.agent.api.chat.bsky.convo.getMessages({ convoId, limit, cursor }, { headers: { "atproto-proxy": BLUESKY_CHAT_SERVICE_DID } });
247
+ return {
248
+ messages: response.data.messages,
249
+ cursor: response.data.cursor
250
+ };
251
+ }
252
+ async sendMessage(request) {
253
+ if (this.config.dryRun) {
254
+ logger.info({ convoId: request.convoId }, "Dry run: would send message");
255
+ return this.mockMessage(request.message.text ?? "");
256
+ }
257
+ const response = await this.agent.api.chat.bsky.convo.sendMessage({
258
+ convoId: request.convoId,
259
+ message: { text: request.message.text ?? "" }
260
+ }, { headers: { "atproto-proxy": BLUESKY_CHAT_SERVICE_DID } });
261
+ return response.data;
262
+ }
263
+ async cleanup() {
264
+ this.profileCache.clear();
265
+ this.session = null;
266
+ }
267
+ mockPost(text) {
268
+ const now = new Date().toISOString();
269
+ return {
270
+ uri: `mock://post/${Date.now()}`,
271
+ cid: `mock-cid-${Date.now()}`,
272
+ author: {
273
+ did: this.session?.did ?? "did:plc:mock",
274
+ handle: this.session?.handle ?? "mock.handle"
275
+ },
276
+ record: { $type: "app.bsky.feed.post", text, createdAt: now },
277
+ indexedAt: now
278
+ };
279
+ }
280
+ mockMessage(text) {
281
+ return {
282
+ id: `mock-msg-${Date.now()}`,
283
+ rev: "1",
284
+ text,
285
+ sender: { did: this.session?.did ?? "did:plc:mock" },
286
+ sentAt: new Date().toISOString()
287
+ };
288
+ }
289
+ }
290
+
291
+ // managers/agent.ts
292
+ import { logger as logger2 } from "@elizaos/core";
293
+
294
+ // utils/config.ts
295
+ function getApiKeyOptional(runtime, key) {
296
+ const value = runtime.getSetting(key);
297
+ return typeof value === "string" ? value : undefined;
298
+ }
299
+ function hasBlueSkyEnabled(runtime) {
300
+ const enabled = runtime.getSetting("BLUESKY_ENABLED");
301
+ if (enabled)
302
+ return String(enabled).toLowerCase() === "true";
303
+ return Boolean(runtime.getSetting("BLUESKY_HANDLE") && runtime.getSetting("BLUESKY_PASSWORD"));
304
+ }
305
+ function validateBlueSkyConfig(runtime) {
306
+ const result = BlueSkyConfigSchema.safeParse({
307
+ handle: String(runtime.getSetting("BLUESKY_HANDLE") ?? ""),
308
+ password: String(runtime.getSetting("BLUESKY_PASSWORD") ?? ""),
309
+ service: String(runtime.getSetting("BLUESKY_SERVICE") ?? BLUESKY_SERVICE_URL),
310
+ dryRun: runtime.getSetting("BLUESKY_DRY_RUN") === "true",
311
+ pollInterval: parseInt(String(runtime.getSetting("BLUESKY_POLL_INTERVAL") ?? ""), 10) || BLUESKY_POLL_INTERVAL,
312
+ enablePost: runtime.getSetting("BLUESKY_ENABLE_POSTING") !== "false",
313
+ postIntervalMin: parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MIN") ?? ""), 10) || BLUESKY_POST_INTERVAL_MIN,
314
+ postIntervalMax: parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MAX") ?? ""), 10) || BLUESKY_POST_INTERVAL_MAX,
315
+ enableActionProcessing: runtime.getSetting("BLUESKY_ENABLE_ACTION_PROCESSING") !== "false",
316
+ actionInterval: parseInt(String(runtime.getSetting("BLUESKY_ACTION_INTERVAL") ?? ""), 10) || BLUESKY_ACTION_INTERVAL,
317
+ postImmediately: runtime.getSetting("BLUESKY_POST_IMMEDIATELY") === "true",
318
+ maxActionsProcessing: parseInt(String(runtime.getSetting("BLUESKY_MAX_ACTIONS_PROCESSING") ?? ""), 10) || BLUESKY_MAX_ACTIONS,
319
+ enableDMs: runtime.getSetting("BLUESKY_ENABLE_DMS") !== "false"
320
+ });
321
+ if (!result.success) {
322
+ const errors = result.error.errors?.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ") || result.error.toString();
323
+ throw new Error(`Invalid BlueSky configuration: ${errors}`);
324
+ }
325
+ return result.data;
326
+ }
327
+ function getPollInterval(runtime) {
328
+ const seconds = parseInt(String(runtime.getSetting("BLUESKY_POLL_INTERVAL") ?? ""), 10) || BLUESKY_POLL_INTERVAL;
329
+ return seconds * 1000;
330
+ }
331
+ function getActionInterval(runtime) {
332
+ const seconds = parseInt(String(runtime.getSetting("BLUESKY_ACTION_INTERVAL") ?? ""), 10) || BLUESKY_ACTION_INTERVAL;
333
+ return seconds * 1000;
334
+ }
335
+ function getMaxActionsProcessing(runtime) {
336
+ return parseInt(String(runtime.getSetting("BLUESKY_MAX_ACTIONS_PROCESSING") ?? ""), 10) || BLUESKY_MAX_ACTIONS;
337
+ }
338
+ function isPostingEnabled(runtime) {
339
+ return runtime.getSetting("BLUESKY_ENABLE_POSTING") !== "false";
340
+ }
341
+ function shouldPostImmediately(runtime) {
342
+ return runtime.getSetting("BLUESKY_POST_IMMEDIATELY") === "true";
343
+ }
344
+ function getPostIntervalRange(runtime) {
345
+ const min = parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MIN") ?? ""), 10) || BLUESKY_POST_INTERVAL_MIN;
346
+ const max = parseInt(String(runtime.getSetting("BLUESKY_POST_INTERVAL_MAX") ?? ""), 10) || BLUESKY_POST_INTERVAL_MAX;
347
+ return { min: min * 1000, max: max * 1000 };
348
+ }
349
+
350
+ // managers/agent.ts
351
+ class BlueSkyAgentManager {
352
+ runtime;
353
+ config;
354
+ client;
355
+ pollTimer = null;
356
+ actionTimer = null;
357
+ postTimer = null;
358
+ running = false;
359
+ lastSeenAt = null;
360
+ constructor(runtime, config, client) {
361
+ this.runtime = runtime;
362
+ this.config = config;
363
+ this.client = client;
364
+ }
365
+ async start() {
366
+ if (this.running)
367
+ return;
368
+ await this.client.authenticate();
369
+ this.running = true;
370
+ this.startNotificationPolling();
371
+ if (this.config.enableActionProcessing) {
372
+ this.startActionProcessing();
373
+ }
374
+ if (isPostingEnabled(this.runtime)) {
375
+ this.startAutomatedPosting();
376
+ }
377
+ logger2.success({ agentId: this.runtime.agentId }, "BlueSky agent manager started");
378
+ }
379
+ async stop() {
380
+ this.running = false;
381
+ if (this.pollTimer)
382
+ clearInterval(this.pollTimer);
383
+ if (this.actionTimer)
384
+ clearInterval(this.actionTimer);
385
+ if (this.postTimer)
386
+ clearTimeout(this.postTimer);
387
+ this.pollTimer = null;
388
+ this.actionTimer = null;
389
+ this.postTimer = null;
390
+ await this.client.cleanup();
391
+ logger2.info({ agentId: this.runtime.agentId }, "BlueSky agent manager stopped");
392
+ }
393
+ startNotificationPolling() {
394
+ const interval = getPollInterval(this.runtime);
395
+ this.pollNotifications();
396
+ this.pollTimer = setInterval(() => this.pollNotifications(), interval);
397
+ }
398
+ async pollNotifications() {
399
+ if (!this.running)
400
+ return;
401
+ const { notifications } = await this.client.getNotifications(50);
402
+ if (notifications.length === 0)
403
+ return;
404
+ const newNotifications = this.lastSeenAt ? notifications.filter((n) => {
405
+ const lastSeen = this.lastSeenAt;
406
+ return lastSeen !== null && n.indexedAt > lastSeen;
407
+ }) : notifications;
408
+ if (newNotifications.length > 0) {
409
+ this.lastSeenAt = notifications[0].indexedAt;
410
+ for (const notification of newNotifications) {
411
+ this.emitNotificationEvent(notification);
412
+ }
413
+ await this.client.updateSeenNotifications();
414
+ }
415
+ }
416
+ emitNotificationEvent(notification) {
417
+ const eventMap = {
418
+ mention: "bluesky.mention_received",
419
+ reply: "bluesky.mention_received",
420
+ follow: "bluesky.follow_received",
421
+ like: "bluesky.like_received",
422
+ repost: "bluesky.repost_received",
423
+ quote: "bluesky.quote_received"
424
+ };
425
+ const event = eventMap[notification.reason];
426
+ if (event) {
427
+ const payload = {
428
+ runtime: this.runtime,
429
+ source: "bluesky",
430
+ notification
431
+ };
432
+ this.runtime.emitEvent(event, payload);
433
+ }
434
+ }
435
+ startActionProcessing() {
436
+ const interval = getActionInterval(this.runtime);
437
+ this.processActions();
438
+ this.actionTimer = setInterval(() => this.processActions(), interval);
439
+ }
440
+ async processActions() {
441
+ if (!this.running)
442
+ return;
443
+ const max = getMaxActionsProcessing(this.runtime);
444
+ const { notifications } = await this.client.getNotifications(max);
445
+ for (const notification of notifications) {
446
+ if (notification.reason === "mention" || notification.reason === "reply") {
447
+ const payload = {
448
+ runtime: this.runtime,
449
+ source: "bluesky",
450
+ notification
451
+ };
452
+ this.runtime.emitEvent("bluesky.should_respond", payload);
453
+ }
454
+ }
455
+ }
456
+ startAutomatedPosting() {
457
+ if (shouldPostImmediately(this.runtime)) {
458
+ this.createAutomatedPost();
459
+ }
460
+ this.scheduleNextPost();
461
+ }
462
+ scheduleNextPost() {
463
+ const { min, max } = getPostIntervalRange(this.runtime);
464
+ const interval = Math.random() * (max - min) + min;
465
+ this.postTimer = setTimeout(() => {
466
+ if (this.running) {
467
+ this.createAutomatedPost();
468
+ this.scheduleNextPost();
469
+ }
470
+ }, interval);
471
+ }
472
+ createAutomatedPost() {
473
+ const payload = {
474
+ runtime: this.runtime,
475
+ source: "bluesky",
476
+ automated: true
477
+ };
478
+ this.runtime.emitEvent("bluesky.create_post", payload);
479
+ }
480
+ }
481
+
482
+ // services/message.ts
483
+ import { composePrompt, ModelType } from "@elizaos/core";
484
+
485
+ // generated/prompts/typescript/prompts.ts
486
+ var generateDmTemplate = `Generate a friendly direct message response under 200 characters.`;
487
+ var generatePostTemplate = `Generate an engaging BlueSky post under {{maxLength}} characters.`;
488
+ var truncatePostTemplate = `Shorten to under {{maxLength}} characters: "{{text}}"`;
489
+
490
+ // services/message.ts
491
+ class BlueSkyMessageService {
492
+ client;
493
+ runtime;
494
+ static serviceType = "IMessageService";
495
+ constructor(client, runtime) {
496
+ this.client = client;
497
+ this.runtime = runtime;
498
+ }
499
+ async getMessages(convoId, limit = 50) {
500
+ const response = await this.client.getMessages(convoId, limit);
501
+ return response.messages;
502
+ }
503
+ async sendMessage(convoId, text) {
504
+ const messageText = text.trim() || await this.generateReply();
505
+ return this.client.sendMessage({ convoId, message: { text: messageText } });
506
+ }
507
+ async getConversations(limit = 50) {
508
+ const response = await this.client.getConversations(limit);
509
+ return response.conversations;
510
+ }
511
+ async generateReply() {
512
+ const prompt = composePrompt({
513
+ state: {},
514
+ template: generateDmTemplate
515
+ });
516
+ const response = await this.runtime.useModel(ModelType.TEXT_SMALL, {
517
+ prompt,
518
+ maxTokens: 50
519
+ });
520
+ return response;
521
+ }
522
+ }
523
+
524
+ // services/post.ts
525
+ import { composePrompt as composePrompt2, ModelType as ModelType2 } from "@elizaos/core";
526
+ class BlueSkyPostService {
527
+ client;
528
+ runtime;
529
+ static serviceType = "IPostService";
530
+ constructor(client, runtime) {
531
+ this.client = client;
532
+ this.runtime = runtime;
533
+ }
534
+ async getPosts(limit = 50, cursor) {
535
+ const response = await this.client.getTimeline({ limit, cursor });
536
+ return response.feed.map((item) => item.post);
537
+ }
538
+ async createPost(text, replyTo) {
539
+ let postText = text.trim() || await this.generateContent();
540
+ if (postText.length > BLUESKY_MAX_POST_LENGTH) {
541
+ postText = await this.truncate(postText);
542
+ }
543
+ const request = {
544
+ content: { text: postText },
545
+ replyTo
546
+ };
547
+ return this.client.sendPost(request);
548
+ }
549
+ async deletePost(uri) {
550
+ await this.client.deletePost(uri);
551
+ }
552
+ async generateContent() {
553
+ const prompt = composePrompt2({
554
+ state: {
555
+ maxLength: String(BLUESKY_MAX_POST_LENGTH)
556
+ },
557
+ template: generatePostTemplate
558
+ });
559
+ const response = await this.runtime.useModel(ModelType2.TEXT_SMALL, {
560
+ prompt,
561
+ maxTokens: 100
562
+ });
563
+ return response;
564
+ }
565
+ async truncate(text) {
566
+ const prompt = composePrompt2({
567
+ state: {
568
+ maxLength: String(BLUESKY_MAX_POST_LENGTH),
569
+ text
570
+ },
571
+ template: truncatePostTemplate
572
+ });
573
+ const response = await this.runtime.useModel(ModelType2.TEXT_SMALL, {
574
+ prompt,
575
+ maxTokens: 100
576
+ });
577
+ const truncated = response;
578
+ return truncated.length > BLUESKY_MAX_POST_LENGTH ? `${truncated.substring(0, BLUESKY_MAX_POST_LENGTH - 3)}...` : truncated;
579
+ }
580
+ }
581
+
582
+ // services/bluesky.ts
583
+ class BlueSkyService extends Service {
584
+ static instance;
585
+ managers = new Map;
586
+ messageServices = new Map;
587
+ postServices = new Map;
588
+ static serviceType = BLUESKY_SERVICE_NAME;
589
+ capabilityDescription = "Send and receive messages on BlueSky";
590
+ static getInstance() {
591
+ BlueSkyService.instance ??= new BlueSkyService;
592
+ return BlueSkyService.instance;
593
+ }
594
+ static async start(runtime) {
595
+ const service = BlueSkyService.getInstance();
596
+ if (service.managers.has(runtime.agentId)) {
597
+ return service;
598
+ }
599
+ if (!hasBlueSkyEnabled(runtime)) {
600
+ return service;
601
+ }
602
+ const config = validateBlueSkyConfig(runtime);
603
+ const client = new BlueSkyClient({
604
+ service: config.service,
605
+ handle: config.handle,
606
+ password: config.password,
607
+ dryRun: config.dryRun
608
+ });
609
+ const manager = new BlueSkyAgentManager(runtime, config, client);
610
+ service.managers.set(runtime.agentId, manager);
611
+ service.messageServices.set(runtime.agentId, new BlueSkyMessageService(client, runtime));
612
+ service.postServices.set(runtime.agentId, new BlueSkyPostService(client, runtime));
613
+ await manager.start();
614
+ logger3.success({ agentId: runtime.agentId }, "BlueSky client started");
615
+ return service;
616
+ }
617
+ static async stop(runtime) {
618
+ const service = BlueSkyService.getInstance();
619
+ const manager = service.managers.get(runtime.agentId);
620
+ if (!manager)
621
+ return;
622
+ await manager.stop();
623
+ service.managers.delete(runtime.agentId);
624
+ service.messageServices.delete(runtime.agentId);
625
+ service.postServices.delete(runtime.agentId);
626
+ logger3.info({ agentId: runtime.agentId }, "BlueSky client stopped");
627
+ }
628
+ async stop() {
629
+ for (const manager of this.managers.values()) {
630
+ await BlueSkyService.stop(manager.runtime);
631
+ }
632
+ }
633
+ getMessageService(agentId) {
634
+ return this.messageServices.get(agentId);
635
+ }
636
+ getPostService(agentId) {
637
+ return this.postServices.get(agentId);
638
+ }
639
+ }
640
+
641
+ // index.ts
642
+ var pluginTests = [
643
+ {
644
+ name: "bluesky_plugin_tests",
645
+ tests: [
646
+ {
647
+ name: "bluesky_test_credentials_validation",
648
+ fn: async (runtime) => {
649
+ const handle = getApiKeyOptional(runtime, "BLUESKY_HANDLE");
650
+ const password = getApiKeyOptional(runtime, "BLUESKY_PASSWORD");
651
+ if (!handle || !password) {
652
+ throw new Error("BLUESKY_HANDLE and BLUESKY_PASSWORD are not configured");
653
+ }
654
+ logger4.log("BlueSky credentials are configured");
655
+ }
656
+ },
657
+ {
658
+ name: "bluesky_test_service_initialization",
659
+ fn: async (runtime) => {
660
+ const handle = getApiKeyOptional(runtime, "BLUESKY_HANDLE");
661
+ const password = getApiKeyOptional(runtime, "BLUESKY_PASSWORD");
662
+ if (!handle || !password) {
663
+ logger4.log("Skipping service initialization test - credentials not configured");
664
+ return;
665
+ }
666
+ const service = await BlueSkyService.start(runtime);
667
+ if (!service) {
668
+ throw new Error("Failed to initialize BlueSky service");
669
+ }
670
+ logger4.log("BlueSky service initialized successfully");
671
+ }
672
+ }
673
+ ]
674
+ }
675
+ ];
676
+ var blueSkyPlugin = {
677
+ name: "bluesky",
678
+ description: "BlueSky client plugin using AT Protocol for social interactions",
679
+ config: {
680
+ BLUESKY_HANDLE: process.env.BLUESKY_HANDLE ?? null,
681
+ BLUESKY_PASSWORD: process.env.BLUESKY_PASSWORD ?? null,
682
+ BLUESKY_SERVICE: process.env.BLUESKY_SERVICE ?? null,
683
+ BLUESKY_DRY_RUN: process.env.BLUESKY_DRY_RUN ?? null,
684
+ BLUESKY_POLL_INTERVAL: process.env.BLUESKY_POLL_INTERVAL ?? null,
685
+ BLUESKY_ENABLE_POSTING: process.env.BLUESKY_ENABLE_POSTING ?? null,
686
+ BLUESKY_ENABLE_DMS: process.env.BLUESKY_ENABLE_DMS ?? null,
687
+ BLUESKY_POST_INTERVAL_MIN: process.env.BLUESKY_POST_INTERVAL_MIN ?? null,
688
+ BLUESKY_POST_INTERVAL_MAX: process.env.BLUESKY_POST_INTERVAL_MAX ?? null,
689
+ BLUESKY_ENABLE_ACTION_PROCESSING: process.env.BLUESKY_ENABLE_ACTION_PROCESSING ?? null,
690
+ BLUESKY_ACTION_INTERVAL: process.env.BLUESKY_ACTION_INTERVAL ?? null,
691
+ BLUESKY_POST_IMMEDIATELY: process.env.BLUESKY_POST_IMMEDIATELY ?? null,
692
+ BLUESKY_MAX_ACTIONS_PROCESSING: process.env.BLUESKY_MAX_ACTIONS_PROCESSING ?? null,
693
+ BLUESKY_MAX_POST_LENGTH: process.env.BLUESKY_MAX_POST_LENGTH ?? null
694
+ },
695
+ async init(_config, _runtime) {
696
+ logger4.log("BlueSky plugin initialized");
697
+ },
698
+ services: [BlueSkyService],
699
+ tests: pluginTests
700
+ };
701
+ var typescript_default = blueSkyPlugin;
702
+ export {
703
+ typescript_default as default,
704
+ blueSkyPlugin,
705
+ BlueSkyService,
706
+ BlueSkyClient
707
+ };
708
+
709
+ //# debugId=408D40F56498941D64756E2164756E21