Automated Labeling
This guide assumes you’ve followed the steps in Getting Started and have a labeler set up. If you haven’t, do that now!
Now that you’re running a labeler server, you might want to start labeling content based on user interaction; for instance, liking a post or sending a DM. This guide will walk you through building a bot that will allow users to select labels by liking posts.
For this guide, we’ve set up an example labeler with four labels for a user to choose from: fire
, water
, air
, and earth
. These labels have been set to “inform” because they’re intended to be used for informational purposes, “warn” severity so that they appear on user profiles and posts, and no blur because these are not moderation labels.
Here’s what the profile we’ll be working with looks like, all set up, though it’s not ready to start applying labels quite yet.
Setting up
Start by installing @skyware/labeler
and @skyware/bot
.
npm install @skyware/labeler @skyware/bot
You’ll also need to create a .env
file in the root of your project. You can use the dotenv
package to load these credentials into your project.
LABELER_DID=did:...
LABELER_PASSWORD=...
SIGNING_KEY=...
LABELER_DID
is the DID of the labeler account you created in the previous guide. You can look this up at internect.info.
LABELER_PASSWORD
is the password you set when you created the labeler account.
SIGNING_KEY
is the signing key you received when setting up the labeler, if you used the CLI for setup.
Creating Posts
The first step is to create posts that users can like to receive labels. Using @skyware/bot
, you can create posts with the Bot#post method. Create a file called post.ts
with the following code:
import { class Bot
A bot that can interact with a Bluesky PDS.Bot } from "@skyware/bot";
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.login({ identifier, password }: BotLoginOptions): Promise<AtpSessionData>
Log in with an identifier and password.login({
BotLoginOptions.identifier: string
The bot account's email, handle, or DID.identifier: var process: NodeJS.Process
process.NodeJS.Process.env: NodeJS.ProcessEnv
The process.env
property returns an object containing the user environment.
See `environ(7)`.
An example of this object looks like:
{
TERM: 'xterm-256color',
SHELL: '/usr/local/bin/bash',
USER: 'maciej',
PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
PWD: '/Users/maciej',
EDITOR: 'vim',
SHLVL: '1',
HOME: '/Users/maciej',
LOGNAME: 'maciej',
_: '/usr/local/bin/node'
}
It is possible to modify this object, but such modifications will not be
reflected outside the Node.js process, or (unless explicitly requested)
to other Worker
threads.
In other words, the following example would not work:
$ node -e 'process.env.foo = "bar"' && echo $foo
While the following will:
import { env } from 'process';
env.foo = 'bar';
console.log(env.foo);
Assigning a property on process.env
will implicitly convert the value
to a string. **This behavior is deprecated.** Future versions of Node.js may
throw an error when the value is not a string, number, or boolean.
import { env } from 'process';
env.test = null;
console.log(env.test);
// => 'null'
env.test = undefined;
console.log(env.test);
// => 'undefined'
Use delete
to delete a property from process.env
.
import { env } from 'process';
env.TEST = 1;
delete env.TEST;
console.log(env.TEST);
// => undefined
On Windows operating systems, environment variables are case-insensitive.
import { env } from 'process';
env.TEST = 1;
console.log(env.test);
// => 1
Unless explicitly specified when creating a Worker
instance,
each Worker
thread has its own copy of process.env
, based on its
parent thread’s process.env
, or whatever was specified as the env
option
to the Worker
constructor. Changes to process.env
will not be visible
across Worker
threads, and only the main thread can make changes that
are visible to the operating system or to native add-ons.env.string
LABELER_DID,
BotLoginOptions.password: string
The bot account's password.password: var process: NodeJS.Process
process.NodeJS.Process.env: NodeJS.ProcessEnv
The process.env
property returns an object containing the user environment.
See `environ(7)`.
An example of this object looks like:
{
TERM: 'xterm-256color',
SHELL: '/usr/local/bin/bash',
USER: 'maciej',
PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
PWD: '/Users/maciej',
EDITOR: 'vim',
SHLVL: '1',
HOME: '/Users/maciej',
LOGNAME: 'maciej',
_: '/usr/local/bin/node'
}
It is possible to modify this object, but such modifications will not be
reflected outside the Node.js process, or (unless explicitly requested)
to other Worker
threads.
In other words, the following example would not work:
$ node -e 'process.env.foo = "bar"' && echo $foo
While the following will:
import { env } from 'process';
env.foo = 'bar';
console.log(env.foo);
Assigning a property on process.env
will implicitly convert the value
to a string. **This behavior is deprecated.** Future versions of Node.js may
throw an error when the value is not a string, number, or boolean.
import { env } from 'process';
env.test = null;
console.log(env.test);
// => 'null'
env.test = undefined;
console.log(env.test);
// => 'undefined'
Use delete
to delete a property from process.env
.
import { env } from 'process';
env.TEST = 1;
delete env.TEST;
console.log(env.TEST);
// => undefined
On Windows operating systems, environment variables are case-insensitive.
import { env } from 'process';
env.TEST = 1;
console.log(env.test);
// => 1
Unless explicitly specified when creating a Worker
instance,
each Worker
thread has its own copy of process.env
, based on its
parent thread’s process.env
, or whatever was specified as the env
option
to the Worker
constructor. Changes to process.env
will not be visible
across Worker
threads, and only the main thread can make changes that
are visible to the operating system or to native add-ons.env.string
LABELER_PASSWORD,
});
const const post: PostReference
post = await const bot: Bot
bot.Bot.post(payload: PostPayload, options?: BotPostOptions): Promise<PostReference>
Create a post.post({ PostPayload.text: string | RichtextBuilder
The post text. Can be a string or a RichText instance containing facets.text: "Like the replies to this post to receive labels.", PostPayload.threadgate?: {
allowMentioned?: boolean;
allowFollowing?: boolean;
allowLists?: Array<string> | Array<List>;
} | undefined
An optional threadgate to be applied to the post.threadgate: { allowLists?: string[] | List[] | undefined
Lists or AT URIs pointing to lists whose members are allowed to reply.allowLists: [] } });
const const firePost: PostReference
firePost = await const post: PostReference
post.PostReference.reply(payload: PostPayload, options?: BotPostOptions): Promise<PostReference>
Reply to the post.reply({ PostPayload.text: string | RichtextBuilder
The post text. Can be a string or a RichText instance containing facets.text: "Fire!" });
const const waterPost: PostReference
waterPost = await const post: PostReference
post.PostReference.reply(payload: PostPayload, options?: BotPostOptions): Promise<PostReference>
Reply to the post.reply({ PostPayload.text: string | RichtextBuilder
The post text. Can be a string or a RichText instance containing facets.text: "Water!"});
const const airPost: PostReference
airPost = await const post: PostReference
post.PostReference.reply(payload: PostPayload, options?: BotPostOptions): Promise<PostReference>
Reply to the post.reply({ PostPayload.text: string | RichtextBuilder
The post text. Can be a string or a RichText instance containing facets.text: "Air!" });
const const earthPost: PostReference
earthPost = await const post: PostReference
post.PostReference.reply(payload: PostPayload, options?: BotPostOptions): Promise<PostReference>
Reply to the post.reply({ PostPayload.text: string | RichtextBuilder
The post text. Can be a string or a RichText instance containing facets.text: "Earth!" });
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(
`Fire: ${const firePost: PostReference
firePost.PostReference.uri: string
The post's AT URI.uri}\n`,
`Water: ${const waterPost: PostReference
waterPost.PostReference.uri: string
The post's AT URI.uri}\n`,
`Air: ${const airPost: PostReference
airPost.PostReference.uri: string
The post's AT URI.uri}\n`,
`Earth: ${const earthPost: PostReference
earthPost.PostReference.uri: string
The post's AT URI.uri}\n`,
);
// Fire: at://did:.../app.bsky.feed.post/...
// Water: at://did:.../app.bsky.feed.post/...
// Air: at://did:.../app.bsky.feed.post/...
// Earth: at://did:.../app.bsky.feed.post/...
The post
method returns a PostReference object, which you can use to reply to the post. The PostReference#reply method will return a reference to the reply. We’re using the threadgate property to prevent anyone else from replying to the post, by setting the allowed lists of users to an empty array.
Transpile this code if necessary and run it. You should see the AT URI of each post printed to the console. Take note of these, as you’ll need them later to process likes.
Running the Labeler
Now that you’ve got the posts you need, create a new file called labeler.ts
. This is where you’ll run your labeler server and listen for likes.
import { class LabelerServer
LabelerServer } from "@skyware/labeler";
import { class Bot
A bot that can interact with a Bluesky PDS.Bot } from "@skyware/bot";
const const server: LabelerServer
server = new new LabelerServer(options: LabelerOptions): LabelerServer
Create a labeler server.LabelerServer({
LabelerOptions.did: string
The DID of the labeler account.did: var process: NodeJS.Process
process.NodeJS.Process.env: NodeJS.ProcessEnv
The process.env
property returns an object containing the user environment.
See `environ(7)`.
An example of this object looks like:
{
TERM: 'xterm-256color',
SHELL: '/usr/local/bin/bash',
USER: 'maciej',
PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
PWD: '/Users/maciej',
EDITOR: 'vim',
SHLVL: '1',
HOME: '/Users/maciej',
LOGNAME: 'maciej',
_: '/usr/local/bin/node'
}
It is possible to modify this object, but such modifications will not be
reflected outside the Node.js process, or (unless explicitly requested)
to other Worker
threads.
In other words, the following example would not work:
$ node -e 'process.env.foo = "bar"' && echo $foo
While the following will:
import { env } from 'process';
env.foo = 'bar';
console.log(env.foo);
Assigning a property on process.env
will implicitly convert the value
to a string. **This behavior is deprecated.** Future versions of Node.js may
throw an error when the value is not a string, number, or boolean.
import { env } from 'process';
env.test = null;
console.log(env.test);
// => 'null'
env.test = undefined;
console.log(env.test);
// => 'undefined'
Use delete
to delete a property from process.env
.
import { env } from 'process';
env.TEST = 1;
delete env.TEST;
console.log(env.TEST);
// => undefined
On Windows operating systems, environment variables are case-insensitive.
import { env } from 'process';
env.TEST = 1;
console.log(env.test);
// => 1
Unless explicitly specified when creating a Worker
instance,
each Worker
thread has its own copy of process.env
, based on its
parent thread’s process.env
, or whatever was specified as the env
option
to the Worker
constructor. Changes to process.env
will not be visible
across Worker
threads, and only the main thread can make changes that
are visible to the operating system or to native add-ons.env.string
LABELER_DID,
LabelerOptions.signingKey: string
The private signing key used for the labeler.
If you don't have a key, generate and set one using
{@link
plcSetupLabeler
}
.signingKey: var process: NodeJS.Process
process.NodeJS.Process.env: NodeJS.ProcessEnv
The process.env
property returns an object containing the user environment.
See `environ(7)`.
An example of this object looks like:
{
TERM: 'xterm-256color',
SHELL: '/usr/local/bin/bash',
USER: 'maciej',
PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
PWD: '/Users/maciej',
EDITOR: 'vim',
SHLVL: '1',
HOME: '/Users/maciej',
LOGNAME: 'maciej',
_: '/usr/local/bin/node'
}
It is possible to modify this object, but such modifications will not be
reflected outside the Node.js process, or (unless explicitly requested)
to other Worker
threads.
In other words, the following example would not work:
$ node -e 'process.env.foo = "bar"' && echo $foo
While the following will:
import { env } from 'process';
env.foo = 'bar';
console.log(env.foo);
Assigning a property on process.env
will implicitly convert the value
to a string. **This behavior is deprecated.** Future versions of Node.js may
throw an error when the value is not a string, number, or boolean.
import { env } from 'process';
env.test = null;
console.log(env.test);
// => 'null'
env.test = undefined;
console.log(env.test);
// => 'undefined'
Use delete
to delete a property from process.env
.
import { env } from 'process';
env.TEST = 1;
delete env.TEST;
console.log(env.TEST);
// => undefined
On Windows operating systems, environment variables are case-insensitive.
import { env } from 'process';
env.TEST = 1;
console.log(env.test);
// => 1
Unless explicitly specified when creating a Worker
instance,
each Worker
thread has its own copy of process.env
, based on its
parent thread’s process.env
, or whatever was specified as the env
option
to the Worker
constructor. Changes to process.env
will not be visible
across Worker
threads, and only the main thread can make changes that
are visible to the operating system or to native add-ons.env.string
SIGNING_KEY,
});
const server: LabelerServer
server.LabelerServer.start(port: number, callback: (error: Error | null, address: string) => void): void (+1 overload)
Start the server.start(14831, (error: Error | null
error) => {
if (error: Error | null
error) {
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.error(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stderr
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 code = 5;
console.error('error #%d', code);
// Prints: error #5, to stderr
console.error('error', code);
// Prints: error 5, to stderr
If formatting elements (e.g. %d
) are not found in the first string then util.inspect()
is called on each argument and the resulting string
values are concatenated. See util.format()
for more information.error("Failed to start: ", error: Error
error);
} else {
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("Listening on port 14831");
}
});
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.login({ identifier, password }: BotLoginOptions): Promise<AtpSessionData>
Log in with an identifier and password.login({
BotLoginOptions.identifier: string
The bot account's email, handle, or DID.identifier: var process: NodeJS.Process
process.NodeJS.Process.env: NodeJS.ProcessEnv
The process.env
property returns an object containing the user environment.
See `environ(7)`.
An example of this object looks like:
{
TERM: 'xterm-256color',
SHELL: '/usr/local/bin/bash',
USER: 'maciej',
PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
PWD: '/Users/maciej',
EDITOR: 'vim',
SHLVL: '1',
HOME: '/Users/maciej',
LOGNAME: 'maciej',
_: '/usr/local/bin/node'
}
It is possible to modify this object, but such modifications will not be
reflected outside the Node.js process, or (unless explicitly requested)
to other Worker
threads.
In other words, the following example would not work:
$ node -e 'process.env.foo = "bar"' && echo $foo
While the following will:
import { env } from 'process';
env.foo = 'bar';
console.log(env.foo);
Assigning a property on process.env
will implicitly convert the value
to a string. **This behavior is deprecated.** Future versions of Node.js may
throw an error when the value is not a string, number, or boolean.
import { env } from 'process';
env.test = null;
console.log(env.test);
// => 'null'
env.test = undefined;
console.log(env.test);
// => 'undefined'
Use delete
to delete a property from process.env
.
import { env } from 'process';
env.TEST = 1;
delete env.TEST;
console.log(env.TEST);
// => undefined
On Windows operating systems, environment variables are case-insensitive.
import { env } from 'process';
env.TEST = 1;
console.log(env.test);
// => 1
Unless explicitly specified when creating a Worker
instance,
each Worker
thread has its own copy of process.env
, based on its
parent thread’s process.env
, or whatever was specified as the env
option
to the Worker
constructor. Changes to process.env
will not be visible
across Worker
threads, and only the main thread can make changes that
are visible to the operating system or to native add-ons.env.string
LABELER_DID,
BotLoginOptions.password: string
The bot account's password.password: var process: NodeJS.Process
process.NodeJS.Process.env: NodeJS.ProcessEnv
The process.env
property returns an object containing the user environment.
See `environ(7)`.
An example of this object looks like:
{
TERM: 'xterm-256color',
SHELL: '/usr/local/bin/bash',
USER: 'maciej',
PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
PWD: '/Users/maciej',
EDITOR: 'vim',
SHLVL: '1',
HOME: '/Users/maciej',
LOGNAME: 'maciej',
_: '/usr/local/bin/node'
}
It is possible to modify this object, but such modifications will not be
reflected outside the Node.js process, or (unless explicitly requested)
to other Worker
threads.
In other words, the following example would not work:
$ node -e 'process.env.foo = "bar"' && echo $foo
While the following will:
import { env } from 'process';
env.foo = 'bar';
console.log(env.foo);
Assigning a property on process.env
will implicitly convert the value
to a string. **This behavior is deprecated.** Future versions of Node.js may
throw an error when the value is not a string, number, or boolean.
import { env } from 'process';
env.test = null;
console.log(env.test);
// => 'null'
env.test = undefined;
console.log(env.test);
// => 'undefined'
Use delete
to delete a property from process.env
.
import { env } from 'process';
env.TEST = 1;
delete env.TEST;
console.log(env.TEST);
// => undefined
On Windows operating systems, environment variables are case-insensitive.
import { env } from 'process';
env.TEST = 1;
console.log(env.test);
// => 1
Unless explicitly specified when creating a Worker
instance,
each Worker
thread has its own copy of process.env
, based on its
parent thread’s process.env
, or whatever was specified as the env
option
to the Worker
constructor. Changes to process.env
will not be visible
across Worker
threads, and only the main thread can make changes that
are visible to the operating system or to native add-ons.env.string
LABELER_PASSWORD,
});
A reminder that you’ll need to set up reverse proxying so that the domain you’re using for the labeler points to the server running this code, on the port you choose for server.start
. You can use a tool like Caddy to do this.
If no errors are printed to the console, you’re ready to start labeling!
Labeling
Next, we’ll build on this to listen for likes and apply labels to users. To start, we can use the Bot#on method to listen for likes. This method will be called whenever a user likes a post, feed generator, or the labeler profile. Add the following code to the end of labeler.ts
:
// Modify your import to include the Post class
import { class Bot
A bot that can interact with a Bluesky PDS.Bot, class Post
Represents a post on Bluesky.Post } from "@skyware/bot";
// ...
const bot: Bot
bot.Bot.on(event: "like", listener: (event: {
subject: Post | FeedGenerator | Labeler;
user: Profile;
uri: string;
}) => void): Bot (+9 overloads)
Emitted when one of the bot's posts is liked.on("like", async ({ subject: Post | FeedGenerator | Labeler
subject, user: Profile
user }) => {
if (subject: Post | FeedGenerator | Labeler
subject instanceof class Post
Represents a post on Bluesky.Post) {
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(subject: Post
subject.PostReference.uri: string
The post's AT URI.uri);
}
});
Now, whenever the bot receives a like, it will first ensure that the like is for a post (feed generators and the labeler profile can also receive likes!) and then log the AT URI of the post to the console.
Of course, that’s not very useful. Let’s start applying labels based on which post the user liked. We’re going to create an object mapping the AT URIs of the posts we created earlier to the labels we want to apply, then use that object to label the user’s account.
const const postsToLabels: Record<string, string>
postsToLabels: type Record<K extends keyof any, T> = { [P in K]: T; }
Construct a type with a set of properties K of type TRecord<string, string> = {
"at://did:.../app.bsky.feed.post/f1234": "fire",
"at://did:.../app.bsky.feed.post/w3456": "water",
"at://did:.../app.bsky.feed.post/a5678": "air",
"at://did:.../app.bsky.feed.post/e7890": "earth",
}
const bot: Bot
bot.Bot.on(event: "like", listener: (event: {
subject: Post | FeedGenerator | Labeler;
user: Profile;
uri: string;
}) => void): Bot (+9 overloads)
Emitted when one of the bot's posts is liked.on("like", async ({ subject: Post | FeedGenerator | Labeler
subject, user: Profile
user }) => {
if (subject: Post | FeedGenerator | Labeler
subject instanceof class Post
Represents a post on Bluesky.Post) {
const const label: string
label = const postsToLabels: Record<string, string>
postsToLabels[subject: Post
subject.PostReference.uri: string
The post's AT URI.uri];
if (const label: string
label) {
await user: Profile
user.Profile.labelAccount(labels: Array<string>, comment?: string): Promise<ToolsOzoneModerationDefs.ModEventView>
Apply labels to the user's account. Note that this will label the user's profile and all posts they create!
If you only want to label their profile, use the
{@link
labelProfile
}
method.labelAccount([const label: string
label]);
}
}
});
Make sure to substitute in the right AT URIs from earlier. We’re using the Profile#labelAccount method to label the user’s account, so that the label will appear on both their profile and posts.
Your final code should look something like this:
import { class LabelerServer
LabelerServer } from "@skyware/labeler";
import { class Bot
A bot that can interact with a Bluesky PDS.Bot, class Post
Represents a post on Bluesky.Post } from "@skyware/bot";
const const server: LabelerServer
server = new new LabelerServer(options: LabelerOptions): LabelerServer
Create a labeler server.LabelerServer({
LabelerOptions.did: string
The DID of the labeler account.did: var process: NodeJS.Process
process.NodeJS.Process.env: NodeJS.ProcessEnv
The process.env
property returns an object containing the user environment.
See `environ(7)`.
An example of this object looks like:
{
TERM: 'xterm-256color',
SHELL: '/usr/local/bin/bash',
USER: 'maciej',
PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
PWD: '/Users/maciej',
EDITOR: 'vim',
SHLVL: '1',
HOME: '/Users/maciej',
LOGNAME: 'maciej',
_: '/usr/local/bin/node'
}
It is possible to modify this object, but such modifications will not be
reflected outside the Node.js process, or (unless explicitly requested)
to other Worker
threads.
In other words, the following example would not work:
$ node -e 'process.env.foo = "bar"' && echo $foo
While the following will:
import { env } from 'process';
env.foo = 'bar';
console.log(env.foo);
Assigning a property on process.env
will implicitly convert the value
to a string. **This behavior is deprecated.** Future versions of Node.js may
throw an error when the value is not a string, number, or boolean.
import { env } from 'process';
env.test = null;
console.log(env.test);
// => 'null'
env.test = undefined;
console.log(env.test);
// => 'undefined'
Use delete
to delete a property from process.env
.
import { env } from 'process';
env.TEST = 1;
delete env.TEST;
console.log(env.TEST);
// => undefined
On Windows operating systems, environment variables are case-insensitive.
import { env } from 'process';
env.TEST = 1;
console.log(env.test);
// => 1
Unless explicitly specified when creating a Worker
instance,
each Worker
thread has its own copy of process.env
, based on its
parent thread’s process.env
, or whatever was specified as the env
option
to the Worker
constructor. Changes to process.env
will not be visible
across Worker
threads, and only the main thread can make changes that
are visible to the operating system or to native add-ons.env.string
LABELER_DID,
LabelerOptions.signingKey: string
The private signing key used for the labeler.
If you don't have a key, generate and set one using
{@link
plcSetupLabeler
}
.signingKey: var process: NodeJS.Process
process.NodeJS.Process.env: NodeJS.ProcessEnv
The process.env
property returns an object containing the user environment.
See `environ(7)`.
An example of this object looks like:
{
TERM: 'xterm-256color',
SHELL: '/usr/local/bin/bash',
USER: 'maciej',
PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
PWD: '/Users/maciej',
EDITOR: 'vim',
SHLVL: '1',
HOME: '/Users/maciej',
LOGNAME: 'maciej',
_: '/usr/local/bin/node'
}
It is possible to modify this object, but such modifications will not be
reflected outside the Node.js process, or (unless explicitly requested)
to other Worker
threads.
In other words, the following example would not work:
$ node -e 'process.env.foo = "bar"' && echo $foo
While the following will:
import { env } from 'process';
env.foo = 'bar';
console.log(env.foo);
Assigning a property on process.env
will implicitly convert the value
to a string. **This behavior is deprecated.** Future versions of Node.js may
throw an error when the value is not a string, number, or boolean.
import { env } from 'process';
env.test = null;
console.log(env.test);
// => 'null'
env.test = undefined;
console.log(env.test);
// => 'undefined'
Use delete
to delete a property from process.env
.
import { env } from 'process';
env.TEST = 1;
delete env.TEST;
console.log(env.TEST);
// => undefined
On Windows operating systems, environment variables are case-insensitive.
import { env } from 'process';
env.TEST = 1;
console.log(env.test);
// => 1
Unless explicitly specified when creating a Worker
instance,
each Worker
thread has its own copy of process.env
, based on its
parent thread’s process.env
, or whatever was specified as the env
option
to the Worker
constructor. Changes to process.env
will not be visible
across Worker
threads, and only the main thread can make changes that
are visible to the operating system or to native add-ons.env.string
SIGNING_KEY,
});
const server: LabelerServer
server.LabelerServer.start(port: number, callback: (error: Error | null, address: string) => void): void (+1 overload)
Start the server.start(14831, (error: Error | null
error) => {
if (error: Error | null
error) {
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.error(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stderr
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 code = 5;
console.error('error #%d', code);
// Prints: error #5, to stderr
console.error('error', code);
// Prints: error 5, to stderr
If formatting elements (e.g. %d
) are not found in the first string then util.inspect()
is called on each argument and the resulting string
values are concatenated. See util.format()
for more information.error("Failed to start: ", error: Error
error);
} else {
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("Listening on port 14831");
}
});
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.login({ identifier, password }: BotLoginOptions): Promise<AtpSessionData>
Log in with an identifier and password.login({
BotLoginOptions.identifier: string
The bot account's email, handle, or DID.identifier: var process: NodeJS.Process
process.NodeJS.Process.env: NodeJS.ProcessEnv
The process.env
property returns an object containing the user environment.
See `environ(7)`.
An example of this object looks like:
{
TERM: 'xterm-256color',
SHELL: '/usr/local/bin/bash',
USER: 'maciej',
PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
PWD: '/Users/maciej',
EDITOR: 'vim',
SHLVL: '1',
HOME: '/Users/maciej',
LOGNAME: 'maciej',
_: '/usr/local/bin/node'
}
It is possible to modify this object, but such modifications will not be
reflected outside the Node.js process, or (unless explicitly requested)
to other Worker
threads.
In other words, the following example would not work:
$ node -e 'process.env.foo = "bar"' && echo $foo
While the following will:
import { env } from 'process';
env.foo = 'bar';
console.log(env.foo);
Assigning a property on process.env
will implicitly convert the value
to a string. **This behavior is deprecated.** Future versions of Node.js may
throw an error when the value is not a string, number, or boolean.
import { env } from 'process';
env.test = null;
console.log(env.test);
// => 'null'
env.test = undefined;
console.log(env.test);
// => 'undefined'
Use delete
to delete a property from process.env
.
import { env } from 'process';
env.TEST = 1;
delete env.TEST;
console.log(env.TEST);
// => undefined
On Windows operating systems, environment variables are case-insensitive.
import { env } from 'process';
env.TEST = 1;
console.log(env.test);
// => 1
Unless explicitly specified when creating a Worker
instance,
each Worker
thread has its own copy of process.env
, based on its
parent thread’s process.env
, or whatever was specified as the env
option
to the Worker
constructor. Changes to process.env
will not be visible
across Worker
threads, and only the main thread can make changes that
are visible to the operating system or to native add-ons.env.string
LABELER_DID,
BotLoginOptions.password: string
The bot account's password.password: var process: NodeJS.Process
process.NodeJS.Process.env: NodeJS.ProcessEnv
The process.env
property returns an object containing the user environment.
See `environ(7)`.
An example of this object looks like:
{
TERM: 'xterm-256color',
SHELL: '/usr/local/bin/bash',
USER: 'maciej',
PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin',
PWD: '/Users/maciej',
EDITOR: 'vim',
SHLVL: '1',
HOME: '/Users/maciej',
LOGNAME: 'maciej',
_: '/usr/local/bin/node'
}
It is possible to modify this object, but such modifications will not be
reflected outside the Node.js process, or (unless explicitly requested)
to other Worker
threads.
In other words, the following example would not work:
$ node -e 'process.env.foo = "bar"' && echo $foo
While the following will:
import { env } from 'process';
env.foo = 'bar';
console.log(env.foo);
Assigning a property on process.env
will implicitly convert the value
to a string. **This behavior is deprecated.** Future versions of Node.js may
throw an error when the value is not a string, number, or boolean.
import { env } from 'process';
env.test = null;
console.log(env.test);
// => 'null'
env.test = undefined;
console.log(env.test);
// => 'undefined'
Use delete
to delete a property from process.env
.
import { env } from 'process';
env.TEST = 1;
delete env.TEST;
console.log(env.TEST);
// => undefined
On Windows operating systems, environment variables are case-insensitive.
import { env } from 'process';
env.TEST = 1;
console.log(env.test);
// => 1
Unless explicitly specified when creating a Worker
instance,
each Worker
thread has its own copy of process.env
, based on its
parent thread’s process.env
, or whatever was specified as the env
option
to the Worker
constructor. Changes to process.env
will not be visible
across Worker
threads, and only the main thread can make changes that
are visible to the operating system or to native add-ons.env.string
LABELER_PASSWORD,
});
const const postsToLabels: Record<string, string>
postsToLabels: type Record<K extends keyof any, T> = { [P in K]: T; }
Construct a type with a set of properties K of type TRecord<string, string> = {
"at://did:.../app.bsky.feed.post/f1234": "fire",
"at://did:.../app.bsky.feed.post/w3456": "water",
"at://did:.../app.bsky.feed.post/a5678": "air",
"at://did:.../app.bsky.feed.post/e7890": "earth",
}
const bot: Bot
bot.Bot.on(event: "like", listener: (event: {
subject: Post | FeedGenerator | Labeler;
user: Profile;
uri: string;
}) => void): Bot (+9 overloads)
Emitted when one of the bot's posts is liked.on("like", async ({ subject: Post | FeedGenerator | Labeler
subject, user: Profile
user }) => {
if (subject: Post | FeedGenerator | Labeler
subject instanceof class Post
Represents a post on Bluesky.Post) {
const const label: string
label = const postsToLabels: Record<string, string>
postsToLabels[subject: Post
subject.PostReference.uri: string
The post's AT URI.uri];
if (const label: string
label) {
await user: Profile
user.Profile.labelAccount(labels: Array<string>, comment?: string): Promise<ToolsOzoneModerationDefs.ModEventView>
Apply labels to the user's account. Note that this will label the user's profile and all posts they create!
If you only want to label their profile, use the
{@link
labelProfile
}
method.labelAccount([const label: string
label]);
}
}
});
All that’s left is to log into another account, like a post or multiple, and see the labels appear on your profile! (Make sure you’ve subscribed to the labeler and set the labels you want to see to “Show badge”!)