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: Bot
bot = new new Bot({ service, langs, emitEvents, emitChatEvents, rateLimitOptions, cacheOptions, eventEmitterOptions, chatEmitterOptions, }?: BotOptions): Bot
Create a new bot.Bot();
await const bot: Bot
bot.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: Conversation
conversation = await const bot: Bot
bot.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 | undefined
cursor, const conversations: Conversation[]
conversations } = await const bot: Bot
bot.Bot.listConversations(options?: BotListConversationsOptions): Promise<{
cursor: string | undefined;
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: Bot
bot.Bot.listConversations(options?: BotListConversationsOptions): Promise<{
cursor: string | undefined;
conversations: Array<Conversation>;
}>
Fetch all conversations the bot is a member of.listConversations({ BotListConversationsOptions.cursor?: string | undefined
The 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: Conversation
conversation = await const bot: Bot
bot.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 | undefined
cursor, const messages: (ChatMessage | DeletedChatMessage)[]
messages } = await const conversation: Conversation
conversation.Conversation.getMessages(cursor?: string): Promise<{
cursor: string | undefined;
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: Conversation
conversation.Conversation.getMessages(cursor?: string): Promise<{
cursor: string | undefined;
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 | undefined
cursor);
You can send messages to the user by calling the Conversation#sendMessage method.
await const conversation: Conversation
conversation.Conversation.sendMessage(payload: Omit<ChatMessagePayload, "conversationId">, options?: BotSendMessageOptions): Promise<ChatMessage>
Send a message in the conversation.sendMessage({ text: string | RichtextBuilder
The 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 RichText
Builder for constructing Bluesky rich textsRichText } from "@skyware/bot";
await const conversation: Conversation
conversation.Conversation.sendMessage(payload: Omit<ChatMessagePayload, "conversationId">, options?: BotSendMessageOptions): Promise<ChatMessage>
Send a message in the conversation.sendMessage({
text: string | RichText
The message text. Can be a string or a RichText instance containing facets.text: new new RichText(): RichText
Builder for constructing Bluesky rich textsRichText().RichtextBuilder.addText(substr: string): RichText
Add plain text to the rich textaddText("I love ").RichtextBuilder.addLink(substr: string, uri: string): RichText
Add link to the rich textaddLink("Skyware", "https://skyware.js.org").RichtextBuilder.addText(substr: string): RichText
Add 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: Profile
profile.Profile.sendMessage(payload: Omit<ChatMessagePayload, "conversationId">): Promise<ChatMessage>
Send the user a direct message.sendMessage({ text: string | RichtextBuilder
The message text. Can be a string or a RichText instance containing facets.text: "Hello, world!" });
const { const messages: (ChatMessage | DeletedChatMessage)[]
messages } = await const profile: Profile
profile.Profile.getMessages(cursor?: string | undefined): Promise<{
cursor: string | undefined;
messages: (DeletedChatMessage | ChatMessage)[];
}>
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: Bot
bot = new new Bot({ service, langs, emitEvents, emitChatEvents, rateLimitOptions, cacheOptions, eventEmitterOptions, chatEmitterOptions, }?: BotOptions): Bot
Create a new bot.Bot({ BotOptions.emitChatEvents?: boolean | undefined
Whether 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: Bot
bot.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: ChatMessage
message: class ChatMessage
Represents a message in a chat conversation.ChatMessage) => {
var console: Console
The 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 err
console.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: ChatMessage
message.ChatMessage.text: string
The 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: Bot
bot.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: ChatMessage
message: class ChatMessage
Represents a message in a chat conversation.ChatMessage) => {
const const sender: Profile
sender = await message: ChatMessage
message.BaseChatMessage.getSender(): Promise<Profile>
Fetch the profile of the user who sent this message.getSender();
var console: Console
The 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 err
console.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: Profile
sender.Profile.handle: string
The user's handle.handle}: ${message: ChatMessage
message.ChatMessage.text: string
The message's text.text}`);
const const conversation: Conversation | null
conversation = await message: ChatMessage
message.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 | null
conversation) {
// It may not always be possible to resolve the conversation the message was sent in!
await const conversation: Conversation
conversation.Conversation.sendMessage(payload: Omit<ChatMessagePayload, "conversationId">, options?: BotSendMessageOptions): Promise<ChatMessage>
Send a message in the conversation.sendMessage({ text: string | RichtextBuilder
The message text. Can be a string or a RichText instance containing facets.text: "Hey there, " + const sender: Profile
sender.Profile.displayName?: string | undefined
The 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.