import type { AppBskyActorDefs } from "@atcute/bluesky"; import { type AtpSessionData } from "@atcute/client"; import type { InferOutput } from "@atcute/lexicons/validations"; import type { ToolsOzoneModerationDefs } from "@atcute/ozone"; import { EventEmitter } from "node:events"; import type QuickLRU from "quick-lru"; import { ChatMessage, type ChatMessagePayload } from "../struct/chat/ChatMessage.js"; import { Conversation } from "../struct/chat/Conversation.js"; import { DeletedChatMessage } from "../struct/chat/DeletedChatMessage.js"; import { FeedGenerator } from "../struct/FeedGenerator.js"; import { Labeler } from "../struct/Labeler.js"; import { List } from "../struct/List.js"; import { Post } from "../struct/post/Post.js"; import type { PostPayload } from "../struct/post/PostPayload.js"; import { PostReference } from "../struct/post/PostReference.js"; import { type IncomingChatPreference, Profile } from "../struct/Profile.js"; import { StarterPack } from "../struct/StarterPack.js"; import { type lexicons } from "../util/lexicon.js"; import { type BotChatEmitterOptions } from "./BotChatEmitter.js"; import { type BotEventEmitterOptions } from "./BotEventEmitter.js"; import { type CacheOptions } from "./cache.js"; import { RateLimitedAgent } from "./RateLimitedAgent.js"; /** * Options for the Bot constructor. */ export interface BotOptions { /** * The PDS to connect to. * @default "https://bsky.social" */ service?: string; /** * The default list of languages to attach to posts. * @default ["en"] */ langs?: Array; /** * Whether to emit events. * @default true */ emitEvents?: boolean; /** * Whether to emit chatMessage events (this is independent of {@link emitEvents}). * @default false */ emitChatEvents?: boolean; /** Options for the built-in rate limiter. */ rateLimitOptions?: RateLimitOptions; /** Options for the request cache. */ cacheOptions?: CacheOptions; /** Options for the event emitter. */ eventEmitterOptions?: BotEventEmitterOptions; /** Options for the chat emitter. If this isn't set, the bot will use {@link eventEmitterOptions}. */ chatEmitterOptions?: BotChatEmitterOptions; } /** * A bot that can interact with a Bluesky PDS. */ export declare class Bot extends EventEmitter { /** The agent used to communicate with a Bluesky PDS. */ readonly agent: RateLimitedAgent; /** The credential manager used to authenticate with a Bluesky PDS. */ private readonly handler; /** A cache to store API responses. */ private readonly cache; /** Receives and emits events. */ private readonly eventEmitter?; /** Receives and emits chat events. */ private readonly chatEventEmitter?; /** The proxy agent for chat-related requests. */ chatProxy?: RateLimitedAgent; /** The default list of languages to attach to posts. */ langs: Array; /** The bot account's Bluesky profile. */ profile: Profile; /** * Create a new bot. * @param options Configuration options. */ constructor({ service, langs, emitEvents, emitChatEvents, rateLimitOptions, cacheOptions, eventEmitterOptions, chatEmitterOptions, }?: BotOptions); /** Whether the bot has an active session. */ get hasSession(): boolean; /** * Log in with an identifier and password. * @param options The bot account's identifier and password. * @returns Session data. */ login({ identifier, password }: BotLoginOptions): Promise; /** * Resume an existing session. * @param session Session data. * @returns Updated session data. */ resumeSession(session: AtpSessionData): Promise; /** * Shut down any event emitters & listeners associated with the bot. */ shutdown(): void; /** * Fetch a post by its AT URI. * @param uri The post's AT URI. * @param options Optional configuration. */ getPost(uri: string, options?: BotGetPostOptions): Promise; /** * Fetch up to 25 posts by their AT URIs. * @param uris The URIs of the posts to fetch. * @param options Optional configuration. */ getPosts(uris: Array, options?: BaseBotGetMethodOptions): Promise>; /** * Fetch up to 100 (default 100) posts by a user's DID. * @param did The user's DID. * @param options Optional configuration. * @returns The user's posts and, if there are more posts to fetch, a cursor. */ getUserPosts(did: string, options?: BotGetUserPostsOptions): Promise<{ cursor?: string; posts: Array; }>; /** * Iterate over posts from a user. * @param did The user's DID. * @param options Optional configuration. */ iterateUserPosts(did: string, options?: BotGetUserPostsOptions): AsyncIterableIterator; /** * Fetch up to 100 (default 100) posts liked by a user. * @param did The user's DID. * @param options Optional configuration. */ getUserLikes(did: string, options?: BotGetUserLikesOptions): Promise<{ cursor?: string; posts: Array; }>; /** * Iterate over the posts liked by a user. * @param did The user's DID. * @param options Optional configuration. */ iterateUserLikes(did: string, options?: BotGetUserLikesOptions): AsyncIterableIterator; /** * Fetch a profile by DID or handle. * @param didOrHandle The user's DID or handle. * @param options Optional configuration. */ getProfile(didOrHandle: string, options?: BaseBotGetMethodOptions): Promise; /** * Fetch up to 25 (default 25) profiles by their DIDs or handles. * @param identifiers The identifiers of the profiles to fetch. * @param options Optional configuration. */ getProfiles(identifiers: Array, options?: BaseBotGetMethodOptions): Promise>; /** * Fetch a list by its AT URI. * @param uri The list's AT URI. * @param options Optional configuration. */ getList(uri: string, options?: BaseBotGetMethodOptions): Promise; /** * Fetch all (up to 100, default 100) lists created by a user. * @param did The user's DID. * @param options Optional configuration. */ getUserLists(did: string, options: BotGetUserListsOptions): Promise<{ cursor?: string; lists: Array; }>; /** * Iterate over lists created by a user. * @param did The user's DID. * @param options Optional configuration. */ iterateUserLists(did: string, options?: BotGetUserListsOptions): AsyncIterableIterator; /** * Fetch a feed generator by its AT URI. * @param uri The feed generator's AT URI. * @param options Optional configuration. */ getFeedGenerator(uri: string, options?: BaseBotGetMethodOptions): Promise; /** * Fetch a list of feed generators by their AT URIs. * @param uris The URIs of the feed generators to fetch. * @param options Optional configuration. */ getFeedGenerators(uris: Array, options?: BaseBotGetMethodOptions): Promise>; /** * Get the bot's home timeline. * @param options Optional configuration. */ getTimeline(options?: BotGetTimelineOptions): Promise>; /** * Fetch a labeler by its account DID. * @param did The DID of the labeler to fetch. * @param options Optional configuration. */ getLabeler(did: string, options?: BaseBotGetMethodOptions): Promise; /** * Fetch a list of labelers by their account DIDs. * @param dids The DIDs of the labelers to fetch. * @param options Optional configuration. */ getLabelers(dids: Array, options?: BaseBotGetMethodOptions): Promise>; /** * Fetch a starter pack by its AT URI. * @param uri The starter pack's AT URI. * @param options Optional configuration. */ getStarterPack(uri: string, options?: BaseBotGetMethodOptions): Promise; /** * Fetch a list of starter packs by their AT URIs. * @param uris The URIs of the starter packs to fetch. * @param options Optional configuration. */ getStarterPacks(uris: Array, options?: BaseBotGetMethodOptions): Promise>; /** * Fetch a list of starter packs by their creator's DID. * @param did The creator's DID. * @param options Optional configuration. */ getUserStarterPacks(did: string, options?: BotGetUserStarterPacksOptions): Promise>; /** * Fetch a conversation containing 1-10 members. If a conversation doesn't exist, it will be created. * @param members The DIDs of the conversation members. * @param options Optional configuration. */ getConversationForMembers(members: Array, options?: BaseBotGetMethodOptions): Promise; /** * Fetch a conversation by its ID. * @param id The conversation's ID. * @param options Optional configuration. */ getConversation(id: string, options?: BaseBotGetMethodOptions): Promise; /** * Fetch all conversations the bot is a member of. * @param options Optional configuration. */ listConversations(options?: BotListConversationsOptions): Promise<{ cursor?: string; conversations: Array; }>; /** * Iterate over all conversations the bot is a member of. * @param options Optional configuration. */ iterateConversations(options?: BotListConversationsOptions): AsyncIterableIterator; /** * Fetch the message history for a conversation. * @param conversationId The ID of the conversation to fetch messages for. * @param options Optional configuration. * @returns An array of messages and a cursor for pagination. */ getConversationMessages(conversationId: string, options?: BotGetConversationMessagesOptions): Promise<{ cursor?: string; messages: Array; }>; /** * Iterate over the messages in a conversation. * @param conversationId The ID of the conversation to fetch messages for. * @param options Optional configuration. */ iterateConversationMessages(conversationId: string, options?: BotGetConversationMessagesOptions): AsyncIterableIterator; /** * Create a post. * @param payload The post payload. * @param options Optional configuration. * @returns A reference to the created post. */ post(payload: PostPayload, options?: BotPostOptions): Promise; /** * Delete a post. * @param uri The post's AT URI. */ deletePost(uri: string): Promise; /** * Like a post or feed generator. * @param reference The post or feed generator to like. * @returns The like record's AT URI and CID. */ like({ uri, cid }: StrongRef): Promise; /** * Delete a like. * @param uri The liked record's AT URI or the like record's AT URI. */ unlike(uri: string): Promise; /** * Repost a post. * @param reference The post to repost. * @returns The repost record's AT URI and CID. */ repost({ uri, cid }: StrongRef): Promise; /** * Delete a repost. * @param uri The post's AT URI or the repost record's AT URI. */ deleteRepost(uri: string): Promise; /** * Follow a user. * @param did The user's DID. * @returns The follow record's AT URI and CID. */ follow(did: string): Promise; /** * Delete a follow. * @param didOrUri The user's DID or the follow record's AT URI. */ unfollow(didOrUri: string): Promise; /** * Mute a user. * @param did The user's DID. */ mute(did: string): Promise; /** * Delete a mute. * @param did The user's DID. */ unmute(did: string): Promise; /** * Block a user. * @param did The user's DID. * @returns The block record's AT URI and CID. */ block(did: string): Promise; /** * Delete a block. * @param didOrUri The user's DID or the block record's AT URI. */ unblock(didOrUri: string): Promise; /** * Send a message in a DM conversation. * @param payload The message payload. * @param options Optional configuration. * @returns The sent message. */ sendMessage(payload: ChatMessagePayload, options?: BotSendMessageOptions): Promise; /** * Send up to 100 private messages at once. * @param payload The messages payload. * @param options Optional configuration. * @returns The sent messages. */ sendMessages(payload: Array, options?: BotSendMessageOptions): Promise>; /** * Leave a DM conversation. * @param id The conversation's ID. */ leaveConversation(id: string): Promise; /** * Label a user or record. Note that you need a running labeler server on this DID to publish labels! * @param options Information on the label to apply. * @see [@skyware/labeler | Getting Started](https://skyware.js.org/guides/labeler/introduction/getting-started/) to run a minimal labeler server. * @see [Self-hosting Ozone](https://github.com/bluesky-social/ozone/blob/main/HOSTING.md) for a full web UI and report handling. */ label({ reference, labels, blobCids, comment }: BotLabelRecordOptions): Promise; /** * Negate labels previously applied to a record by the bot. * @param options Information on the record to negate labels on. */ negateLabels({ reference, labels, blobCids, comment }: BotLabelRecordOptions): Promise; private emitLabelEvent; /** * Subscribe to a labeler while this Bot instance exists. * @param did The labeler's DID. */ addLabeler(did: string): void; /** * Unsubscribe the current Bot instance from a labeler. * @param did The labeler's DID. */ removeLabeler(did: string): void; /** * Resolve a handle to a DID. * @param handle The handle to resolve. * @returns The user's DID. */ resolveHandle(handle: string): Promise; /** * Update the bot's handle. * @param handle The new handle. */ updateHandle(handle: string): Promise; /** * Set the bot's preference for who can initiate a new chat conversation. This does not affect existing conversations. * @param preference The new preference. */ setChatPreference(preference: IncomingChatPreference): Promise; /** * Create a record. * @param nsid The collection's NSID. * @param record The record to create. * @param rkey The rkey to use. * @returns The record's AT URI and CID. */ createRecord(nsid: NSID, record: Omit, "$type" | "createdAt">, rkey?: string): Promise; /** * Put a record in place of an existing record. * @param nsid The collection's NSID. * @param record The record to put. * @param rkey The rkey to use. * @returns The record's AT URI and CID. */ putRecord(nsid: string, record: object, rkey: string): Promise; /** * Delete a record. * @param uri The record's AT URI. */ deleteRecord(uri: string): Promise; /** * Update private user preferences for the bot account. * @param callback A callback function that receives the current preferences and returns the updated preferences. * @returns The updated preferences. */ updatePreferences(callback: (preferences: AppBskyActorDefs.Preferences) => AppBskyActorDefs.Preferences): Promise; /** Emitted when the bot begins listening for events. */ on(event: "open", listener: () => void): this; /** Emitted when an error occurs while listening for events. */ on(event: "error", listener: (error: unknown) => void): this; /** Emitted when the bot stops listening for events. */ on(event: "close", listener: () => void): this; /** Emitted when the bot receives a reply. */ on(event: "reply", listener: (post: Post) => void): this; /** Emitted when the bot receives a quote post. */ on(event: "quote", listener: (post: Post) => void): this; /** Emitted when the bot is mentioned. */ on(event: "mention", listener: (post: Post) => void): this; /** * Emitted when one of the bot's posts is reposted. * @param listener A callback function that receives the post that was reposted, the user who reposted it, and the repost's AT URI. */ on(event: "repost", listener: (event: { post: Post; user: Profile; uri: string; }) => void): this; /** * Emitted when one of the bot's posts is liked. * @param listener A callback function that receives the subject that was liked, the user who liked it, and the like's AT URI. */ on(event: "like", listener: (event: { subject: Post | FeedGenerator | Labeler; user: Profile; uri: string; }) => void): this; /** * Emitted when the bot is followed. * @param listener A callback function that receives the user who followed the bot and the follow's AT URI. */ on(event: "follow", listener: (event: { user: Profile; uri: string; }) => void): this; /** * Emitted when the bot receives a message in a DM conversation. * @param listener A callback function that receives the message. */ on(event: "message", listener: (message: ChatMessage) => void): this; addListener(event: "open", listener: () => void): this; addListener(event: "error", listener: (error: unknown) => void): this; addListener(event: "close", listener: () => void): this; addListener(event: "reply", listener: (post: Post) => void): this; addListener(event: "quote", listener: (post: Post) => void): this; addListener(event: "mention", listener: (post: Post) => void): this; addListener(event: "repost", listener: (event: { post: Post; user: Profile; uri: string; }) => void): this; addListener(event: "like", listener: (event: { post: Post; user: Profile; uri: string; }) => void): this; addListener(event: "follow", listener: (event: { user: Profile; uri: string; }) => void): this; addListener(event: "message", listener: (message: ChatMessage) => void): this; /** * Remove an event listener. * @param event The event to remove the listener for. * @param listener The listener callback to remove. */ off(event: string, listener: (...args: any[]) => void): this; /** Alias for {@link Bot#off}. */ removeListener(event: string, listener: (...args: any[]) => void): this; /** * Remove all event listeners, or those of the specified event. * @param event The event to remove listeners for. */ removeAllListeners(event?: string): this; } /** * The bot's cache. */ export interface BotCache { profiles: QuickLRU; posts: QuickLRU; lists: QuickLRU; feeds: QuickLRU; labelers: QuickLRU; starterPacks: QuickLRU; conversations: QuickLRU; } /** * Options for the built-in rate limiter. */ export interface RateLimitOptions { /** * The maximum number of requests that can be made to a Bluesky PDS in a given interval. * Don't set this unless you know what you're doing. * @default 3000 * @see https://www.docs.bsky.app/docs/advanced-guides/rate-limits */ rateLimit?: number; /** * The interval after which the rate limit will reset, in seconds * @default 300 * @see https://www.docs.bsky.app/docs/advanced-guides/rate-limits */ rateLimitInterval?: number; } /** * Options for the {@link Bot#login} method. */ export interface BotLoginOptions { /** The bot account's email, handle, or DID. */ identifier: string; /** The bot account's password. */ password: string; } /** * A reference to a user repository. */ export interface RepoRef { /** The user's DID. */ did: string; } /** * A reference to a record. */ export interface StrongRef { /** The record's AT URI. */ uri: string; /** The record's CID. */ cid: string; } /** * Base options for any Bot method that fetches data. */ export interface BaseBotGetMethodOptions { /** * Whether to skip checking the cache. * @default false */ skipCache?: boolean; /** * Whether to skip caching the response. * @default false */ noCacheResponse?: boolean; } /** * Options for the {@link Bot#getPost} method. */ export interface BotGetPostOptions extends BaseBotGetMethodOptions { /** * How many levels of parent posts to fetch. * @default 1 */ parentHeight?: number; /** * How many levels of child posts to fetch. * @default 1 */ depth?: number; } /** * Types of posts to be included in the response to {@link Bot#getUserPosts}. * @enum */ export declare const GetUserPostsFilter: { /** All posts. */ readonly PostsWithReplies: "posts_with_replies"; /** Top-level posts only. */ readonly PostsNoReplies: "posts_no_replies"; /** Posts with media. */ readonly PostsWithMedia: "posts_with_media"; /** Top-level posts and threads where the only author is the user. */ readonly PostsAndAuthorThreads: "posts_and_author_threads"; }; export type GetUserPostsFilter = typeof GetUserPostsFilter[keyof typeof GetUserPostsFilter]; /** * Options for the {@link Bot#getUserPosts} method. */ export interface BotGetUserPostsOptions extends Omit { /** * The maximum number of posts to fetch (up to 100, inclusive). * @default 100 */ limit?: number; /** * The offset at which to start fetching posts. */ cursor?: string; /** * Post type to include in the response. * @default GetUserPostsFilter.PostsWithReplies */ filter?: GetUserPostsFilter; } /** * Options for the {@link Bot#getUserLikes} method. */ export interface BotGetUserLikesOptions extends Omit { /** * The maximum number of posts to fetch (up to 100, inclusive). * @default 100 */ limit?: number; /** * The offset at which to start fetching posts. */ cursor?: string; } /** * Options for the {@link Bot#getUserLists} method. */ export interface BotGetUserListsOptions extends Omit { /** * The maximum number of lists to fetch (up to 100, inclusive). * @default 100 */ limit?: number; /** * The offset at which to start fetching lists. */ cursor?: string; } /** * Options for the {@link Bot#getTimeline} method. */ export interface BotGetTimelineOptions extends BaseBotGetMethodOptions { /** * The maximum number of posts to fetch (up to 100, inclusive). * @default 100 */ limit?: number; /** * The offset at which to start fetching posts. */ cursor?: string; } /** * Options for the {@link Bot#getUserStarterPacks} method. */ export interface BotGetUserStarterPacksOptions extends Omit { /** * The maximum number of starter packs to fetch (up to 100, inclusive). * @default 100 */ limit?: number; /** * The offset at which to start fetching starter packs. */ cursor?: string; } /** * Options for the {@link Bot#listConversations} method. */ export interface BotListConversationsOptions extends BaseBotGetMethodOptions { /** * The maximum number of conversations to fetch (up to 100, inclusive). * @default 100 */ limit?: number; /** * The offset at which to start fetching conversations. */ cursor?: string; } /** * Options for the {@link Bot#getConversationMessages} method. */ export interface BotGetConversationMessagesOptions { /** * The maximum number of messages to fetch (up to 100, inclusive). * @default 100 */ limit?: number; /** * The offset at which to start fetching messages. */ cursor?: string; } /** * Options for the {@link Bot#post} method. */ export interface BotPostOptions { /** * Whether to automatically resolve facets in the post's text. * * This will be ignored if the provided post data already has facets attached. * @default true */ resolveFacets?: boolean; /** * Whether to split the post into multiple posts if it exceeds the character limit. * @default false */ splitLongPost?: boolean; /** * Whether to shorten inline links. * @default false */ shortenLinks?: boolean; } /** * Options for the {@link Bot#sendMessage} method. */ export interface BotSendMessageOptions { /** * Whether to automatically resolve facets in the message's text. * * This will be ignored if the provided message data already has facets attached. * @default true */ resolveFacets?: boolean; } export interface BotLabelRecordOptions { /** * A reference to the record to label. */ reference: RepoRef | StrongRef; /** * The labels to apply. */ labels: Array; /** * The CIDs of specific blobs within the record that the labels apply to, if any. */ blobCids?: Array | undefined; /** * An optional comment. */ comment?: string | undefined; }