Chatting with Users
Not all conversations are meant to be public. You can create a chat conversation to interact with users in private.
Note: Your bot should never interact with users without obtaining their consent. Be extra careful to avoid potentially spammy behaviour in private conversations!
Accepting Messages
By default, your bot account will only receive messages from users it follows. To receive messages from any (or no) user, you can use the Bot#setChatPreference method.
Valid options for this method are:
- IncomingChatPreference.None - Your bot will not receive any new messages. Existing conversations will be unaffected.
- IncomingChatPreference.Following - Your bot will only receive messages from users it follows. This is the default setting.
- IncomingChatPreference.All - Your bot will receive messages from any user.
const const bot: Botbot = new new Bot({ service, langs, emitEvents, emitChatEvents, rateLimitOptions, cacheOptions, eventEmitterOptions, chatEmitterOptions, }?: BotOptions): BotCreate a new bot.Bot();
await const bot: Botbot.Bot.setChatPreference(preference: IncomingChatPreference): Promise<void>Set the bot's preference for who can initiate a new chat conversation. This does not affect existing conversations.setChatPreference(const IncomingChatPreference: {
readonly All: "all";
readonly None: "none";
readonly Following: "following";
}A user's preference for who can initiate a chat conversation.IncomingChatPreference.type All: "all"Receive messages from everyone.All);
This only needs to be set once for your bot account. You can change it at any time.
Starting a Conversation
To chat with a user, you need a Conversation object.
If you know the DID(s) of the user(s) you want to chat with, you can use the Bot#getConversationForMembers method.
const const conversation: Conversationconversation = await const bot: Botbot.Bot.getConversationForMembers(members: Array<string>, options?: BaseBotGetMethodOptions): Promise<Conversation>Fetch a conversation containing 1-10 members. If a conversation doesn't exist, it will be created.getConversationForMembers(["did:plc:ith6w2xyj2qy3rcvmlsem6fz"]);
This method will fetch an existing conversation if one exists, or create a new one if it doesn’t.
You can obtain a list of all conversations the bot is a part of by calling the Bot#listConversations method.
const { const cursor: string | undefinedcursor, const conversations: Conversation[]conversations } = await const bot: Botbot.Bot.listConversations(options?: BotListConversationsOptions): Promise<{
cursor?: string;
conversations: Array<Conversation>;
}>Fetch all conversations the bot is a member of.listConversations();
// The method only returns 100 conversations at a time — use the cursor to fetch more conversations
const { conversations: Conversation[]conversations: const moreConversations: Conversation[]moreConversations } = await const bot: Botbot.Bot.listConversations(options?: BotListConversationsOptions): Promise<{
cursor?: string;
conversations: Array<Conversation>;
}>Fetch all conversations the bot is a member of.listConversations({ BotListConversationsOptions.cursor?: string | undefinedThe offset at which to start fetching conversations.cursor });
Sending and Reading Messages
Start by obtaining the Conversation object for the user you want to chat with. You can then send messages to the user and read their responses.
const const conversation: Conversationconversation = await const bot: Botbot.Bot.getConversationForMembers(members: Array<string>, options?: BaseBotGetMethodOptions): Promise<Conversation>Fetch a conversation containing 1-10 members. If a conversation doesn't exist, it will be created.getConversationForMembers(["skyware.js.org"]);
You can read chat history by calling the Conversation#getMessages method. This will return an array of ChatMessage and DeletedChatMessage objects.
const { const cursor: string | undefinedcursor, const messages: (ChatMessage | DeletedChatMessage)[]messages } = await const conversation: Conversationconversation.Conversation.getMessages(cursor?: string): Promise<{
cursor?: string;
messages: Array<ChatMessage | DeletedChatMessage>;
}>Fetch a list of messages in this conversation.
This method returns 100 messages at a time, beginning from the latest message, alongside a cursor to fetch the next 100.getMessages();
// You'll only receive up to 100 messages at a time — you can use the cursor to fetch messages further into the past!
const { messages: (ChatMessage | DeletedChatMessage)[]messages: const moreMessages: (ChatMessage | DeletedChatMessage)[]moreMessages } = await const conversation: Conversationconversation.Conversation.getMessages(cursor?: string): Promise<{
cursor?: string;
messages: Array<ChatMessage | DeletedChatMessage>;
}>Fetch a list of messages in this conversation.
This method returns 100 messages at a time, beginning from the latest message, alongside a cursor to fetch the next 100.getMessages(const cursor: string | undefinedcursor);
You can send messages to the user by calling the Conversation#sendMessage method.
await const conversation: Conversationconversation.Conversation.sendMessage(payload: Omit<ChatMessagePayload, "conversationId">, options?: BotSendMessageOptions): Promise<ChatMessage>Send a message in the conversation.sendMessage({ text: string | RichtextBuilderThe message text. Can be a string or a RichText instance containing facets.text: "Hello, world!" });
Like posts, you can use a RichText instance to send rich text messages containing links and mentions. More on that in Rich Text.
import { class RichTextBuilder for constructing Bluesky rich textsRichText } from "@skyware/bot";
await const conversation: Conversationconversation.Conversation.sendMessage(payload: Omit<ChatMessagePayload, "conversationId">, options?: BotSendMessageOptions): Promise<ChatMessage>Send a message in the conversation.sendMessage({
text: string | RichTextThe message text. Can be a string or a RichText instance containing facets.text: new new RichText(): RichTextBuilder for constructing Bluesky rich textsRichText().RichtextBuilder.addText(substr: string): RichTextAdd plain text to the rich textaddText("I love ").RichtextBuilder.addLink(substr: string, uri: GenericUri): RichTextAdd link to the rich textaddLink("Skyware", "https://skyware.js.org").RichtextBuilder.addText(substr: string): RichTextAdd plain text to the rich textaddText("!")
});
If you already have a Profile object, you can skip the Conversation and send and read messages directly.
await const profile: Profileprofile.Profile.sendMessage(payload: Omit<ChatMessagePayload, "conversationId">): Promise<ChatMessage>Send the user a direct message.sendMessage({ text: string | RichtextBuilderThe message text. Can be a string or a RichText instance containing facets.text: "Hello, world!" });
const { const messages: (ChatMessage | DeletedChatMessage)[]messages } = await const profile: Profileprofile.Profile.getMessages(cursor?: string | undefined): Promise<{
cursor?: string;
messages: Array<ChatMessage | DeletedChatMessage>;
}>Fetch the message history between the bot and this user.getMessages();
Receiving Message Events
Of course, you won’t usually know in advance who you want to message. You may want your bot to respond to private messages from any user.
To start, you’ll want to initialize your bot with emitChatEvents set to true.
const const bot: Botbot = new new Bot({ service, langs, emitEvents, emitChatEvents, rateLimitOptions, cacheOptions, eventEmitterOptions, chatEmitterOptions, }?: BotOptions): BotCreate a new bot.Bot({ BotOptions.emitChatEvents?: boolean | undefinedWhether to emit chatMessage events (this is independent of
{@link
emitEvents
}
).emitChatEvents: true });
Your bot will now emit an event whenever a message is received, which you can handle as follows:
const bot: Botbot.Bot.on(event: "message", listener: (message: ChatMessage) => void): Bot (+9 overloads)Emitted when the bot receives a message in a DM conversation.on("message", async (message: ChatMessagemessage: class ChatMessageRepresents a message in a chat conversation.ChatMessage) => {
var console: ConsoleThe console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
* A Console class with methods such as console.log(), console.error() andconsole.warn() that can be used to write to any Node.js stream.
* A global console instance configured to write to process.stdout and process.stderr. The global console can be used without callingrequire('console').
_**Warning**_: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to errconsole.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to `printf(3)` (the arguments are all passed to util.format()).
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
See util.format() for more information.log(`Received message: ${message: ChatMessagemessage.ChatMessage.text: stringThe message's text.text}`);
});
You can access a reference to the sender’s profile and the conversation the message was sent in by calling the getSender and getConversation methods.
const bot: Botbot.Bot.on(event: "message", listener: (message: ChatMessage) => void): Bot (+9 overloads)Emitted when the bot receives a message in a DM conversation.on("message", async (message: ChatMessagemessage: class ChatMessageRepresents a message in a chat conversation.ChatMessage) => {
const const sender: Profilesender = await message: ChatMessagemessage.BaseChatMessage.getSender(): Promise<Profile>Fetch the profile of the user who sent this message.getSender();
var console: ConsoleThe console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
* A Console class with methods such as console.log(), console.error() andconsole.warn() that can be used to write to any Node.js stream.
* A global console instance configured to write to process.stdout and process.stderr. The global console can be used without callingrequire('console').
_**Warning**_: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to errconsole.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to `printf(3)` (the arguments are all passed to util.format()).
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
See util.format() for more information.log(`Received message from @${const sender: Profilesender.Profile.handle: stringThe user's handle.handle}: ${message: ChatMessagemessage.ChatMessage.text: stringThe message's text.text}`);
const const conversation: Conversation | nullconversation = await message: ChatMessagemessage.BaseChatMessage.getConversation(): Promise<Conversation | null>Fetch the Conversation instance this message belongs to.
Returns null if the conversation could not be found.getConversation();
if (const conversation: Conversation | nullconversation) {
// It may not always be possible to resolve the conversation the message was sent in!
await const conversation: Conversationconversation.Conversation.sendMessage(payload: Omit<ChatMessagePayload, "conversationId">, options?: BotSendMessageOptions): Promise<ChatMessage>Send a message in the conversation.sendMessage({ text: string | RichtextBuilderThe message text. Can be a string or a RichText instance containing facets.text: "Hey there, " + const sender: Profilesender.Profile.displayName?: string | undefinedThe user's display name.displayName + "!" });
}
});
With Great Power…
…comes great responsibility. People don’t like unsolicited messages. It’s your responsibility to ensure your bot is well-behaved and makes Bluesky a better place for everyone.