Como criei um bot do Tabnews para o Bluesky
Olá pessoal, na semana retrasada, fiquei motivado no desafio de criar um bot do Tabnews para o Bluesky, graças a ideia de nosso colega fausto
e pelo fato da experiência de desenvolvimento no Bluesky ter sido muito elogiada em alguns artigos que vi. Como tinha falado no texto anterior, irei compartilhar com vocês como foi o processo de criação do bot e explicando como funciona o processo de desenvolvimento de um bot no Bluesky.
Para isso, é necessário ter em mãos os itens:
- Uma conta no Bluesky criada - Basta apenas criar a conta. Não existe um tempo mínimo para a conta ficar ativa para ativar a geração de senhas de App.
- Algum local para rodar a Cronjob. Para o meu bot, usei o servidor PHP simples do meu site pessoal na Hostinger
Como eu criei para rodar em um servidor PHP, irei manifestar a maioria das instruções em PHP, mas procurarei também explicar como funciona em JavaScript.
O Bluesky suporta envio de códigos de verificação por e-mail. Eu recomendo ativar isso nas configurações, enquanto que o Bluesky não suporta outros métodos de 2FA.
Criando a senha de App
Para que o seu bot possa fazer as postagens, é necessário você criar um App Password
(Senha de Aplicativo), para que você possa informar no ato do login pela biblioteca de conexão ao Bluesky. É importante reiterar que a senha que você usa para entrar no site ou aplicativo NÃO funcionará para o bot.
Para criar, acesse o site do Bluesky, faça o login no site, e acesse a seção "Settings". Em seguida, no painel central, procure por "App Passwords" e clique nele:
Em seguida, caso não tenha criado um senha de aplicativo, você deve ver apenas o botão "Add App Password". Clique neste botão. No print a seguir, temos um criado, que é usado pelo bot para fazer a alimentação dos posts todos os dias às 12h.
O Bluesky perguntará qual é o nome que você quer dar para identificar a senha de aplicativo. Este nome aparecerá na lista das senhas de aplicativo para você identificar quando você desejar excluir a senha do app. Escreva o nome e clique em "Create App Password".
Após isso, o Bluesky mostrará pela única vez a senha do aplicativo criada. Copie e salve a senha em um lugar seguro. Quando você se certificar que você copiou e salvou a senha, clique em "Done".
Com a senha salva, podemos avançar para a próxima etapa.
Configurando a Biblioteca de conexão ao Bluesky e fazendo a primeira postagem
Com a senha de aplicativo salva em um lugar seguro, é necessário instalar em seu projeto a biblioteca responsável por fazer a ponte entre seu código e o Bluesky. Para NodeJS, é necessário instalar a biblioteca oficial @atproto/api
. Já para o PHP (linguagem a qual programei o Bot) temos o pacote mantido por Clark Rasmussen cjrasmussen/bluesky-api
. Para instalar, use o comando:
composer require cjrasmussen/bluesky-api
Com o pacote instalado, você pode fazer a conexão do seu bot ao Bluesky. Para NodeJS, você pode seguir este script baseado no guia oficial do Bluesky:
import { BskyAgent } from '@atproto/api';
import { CronJob } from 'cron';
import * as process from 'process';
const handle = "tabnewsbot.bsky.social"
const app_password = "ISTO NÃO DEVE SER PASSADO NO CÓDIGO"
// Create a Bluesky Agent
const agent = new BskyAgent({
service: 'https://bsky.social',
})
async function login() {
await agent.login({ identifier: handle, password: password})
console.log("Connected!")
}
main();
Para realizar a autenticação usando o pacote PHP, há algumas diferenças. A conexão seria desta forma:
use cjrasmussen\BlueskyApi\BlueskyApi;
$bluesky = new BlueskyApi();
$handle = "tabnewsbot.bsky.social"
$app_password = "ISTO NÃO DEVE SER PASSADO NO CÓDIGO"
try {
$bluesky->auth($handle, $app_password);
} catch (Exception $e) {
// TODO: Aqui é o tratamento de exceções
}
Para realizar a postagem, é algo simples. Segue abaixo o trecho de código Typescript para realizar esta tarefa:
async function main() {
await login();
// este é o trecho necessário
await agent.post({
text: "🙂"
});
console.log("Just posted!")
}
main();
Já para a biblioteca PHP, a função é mais complexa, mas o entendimento é fácil. Com a variável $bluesky
com a conexão, basta indicar no atributo text
o texto a ser postado e em langs
, o array com as linguagens da postagem. No caso do nosso bot, alteramos o array para ['pt-br', 'en']
, para sinalizar ao Bluesky que o post tem como línguas Português e Inglês.
Se o atributo
langs
não for informado, o Bluesky tentará detectar a linguagem com base no texto fornecido.
A seguir, apresentamos como ficaria o código para fazer a postagem em PHP:
$args = [
'collection' => 'app.bsky.feed.post',
'repo' => $bluesky->getAccountDid(),
'record' => [
'text' => 'Testing #TestingInProduction',
'langs' => ['en'],
'createdAt' => date('c'),
'$type' => 'app.bsky.feed.post',
],
];
$data = $bluesky->request('POST', 'com.atproto.repo.createRecord', $args);
A partir deste momento, seu script já deve estar postando no Bluesky. Em relação aos requisitos do nosso Bot do Tabnews, estamos quase lá. Na próxima seção mostraremos como funciona a postagem de links.
Adicionando links a postagem
Quando estava fazendo o Bot do Tabnews, primeiro testei apenas com o texto normal. Com as postagens funcionando, decidi colocar no campo texto o link para ver o que acontecia. O texto era postado com o link, porém quando via a postagem no Bluesky, o link aparecia como texto puro, não sendo clicável.
Descobri então, que para postar links usando a API do Bluesky, é necessário declarar explicitamente o link para a API, marcando os bytes de início e final do link. O Bluesky chama este recurso de Rich Text Facets, que é a forma que o Bluesky encontrou para delimitar quando começa e quando termina no texto um link.
Neste sistema, você determina os bytes de início e fim da string onde serão marcados como o link. Este texto a ser demarcado não precisa ser a URL explícita, podendo ser qualquer texto.
No projeto do Bot, como o link será a URL do post, criei uma função, onde dado um texto com a URL e a URL a ser colocada no texto, ele procura o byte inicial da URL a ser encontrada usando a função strpos
e depois, através de um strlen
na string contendo a URL a ser encontrada e somando o valor retornado pelo strlen
pela soma do byte inicial, encontramos as posições inicial e final a serem demarcadas:
function discoverByteStartAndEnd($merged, $find){
// Find the start byte position of $firstPositionUrl
$startBytePos = strpos($merged, $find);
// Find the length of $firstPositionUrl (in bytes)
$byteLength = strlen($find);
// Find the end byte position (start position + length)
$endBytePos = $startBytePos + $byteLength ;
return [$startBytePos, $endBytePos];
}
Com o texto a ser enviado na variável $text
, a URL para ser incorporada como link $url
e as posições inicial e final do texto a ser considerado como link nas variáveis $startBytePos
e $endBytePos
, podemos enviar o texto com o Rich Text Facet da seguinte maneira (note o novo atributo facets
para declarar a posição inicial e final do texto a ser considerado como link e a declaração da URL):
$args = [
'collection' => 'app.bsky.feed.post',
'repo' => $bluesky->getAccountDid(),
'record' => [
'text' => $text,
'langs' => ['pt-br', 'en'],
'createdAt' => date('c'),
'$type' => 'app.bsky.feed.post',
'facets' => [
[
'index' => [
'byteStart' => $startBytePos,
'byteEnd' => $endBytePos,
],
'features' => [
[
'$type' => 'app.bsky.richtext.facet#link',
'uri' => $url,
]
]
]
]
],
];
$data = $bluesky->request('POST', 'com.atproto.repo.createRecord', $args);
E com isso, temos o nosso post com o link incorporado:
Para entender mais detalhes de como funciona este recurso, consulte a documentação oficial do Rich Text Facets do Bluesky. Através deste recurso, você pode marcar também usuários e hashtags.
E depois?
O bot está funcionando initerruptamente há 11 dias seguidos, levando os links mais relevantes do Tabnews aos usuários do Bluesky. Porém, em relação ao bot original que está rodando na Rede X - o bot do Bluesky falta apenas um recurso: Postar os links como se fossem cards, para facilitar o usuário a visualizar e clicar na postagem.
Para isso, preciso usar um outro recurso avançado do Bluesky, que é o app.bsky.embed.external
. Procurarei implementar isso, e irei trazer mais informações de como implementar em um novo artigo que publicarei aqui no Tabnews.
Se você tem conta no Bluesky e quer seguir os posts mais relevantes daqui, basta só seguir. Nosso handle é tabnewsbot.bsky.social
e você pode acessar aqui.
Para encerrar, permita me sugerir uma leitura adicional. Recomendo ler o artigo oficial do Bluesky que se trata a respeito dos rate limits que os bots devem seguir. Neste momento que escrevo este artigo, o Bluesky classifica as operações por pontos e limita as contas em relação ao número de pontos acumulados no período. Ao contrário do seu concorrente Rede X, o Bluesky oferece neste momento um limite generoso, sendo permitido criar 1666 posts por hora e 11666 posts por dia. Mas recomendamos sempre que você use esta ferramenta com moderação.
Espero que este artigo seja de grande valia para você!