Executando verificação de segurança...
8

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:

Como encontrar o App Passwords

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.

Criando App Password

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".

Criando 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".

Exibir App Password

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.

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:

Post com link postado

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ê!

Carregando publicação patrocinada...
1