Desenvolvedor Front-End
April 26, 2024 - 5 min read
Estava tranquilo em casa quando ouvi falar dessa BlueSky e fui dar uma olhada, olhei e é praticamente o twitter, mas uma coisa que me chamou muito a atenção foi, você pode fazer tudo no site com codigo, fazer bot gratuitamente sem pagar api, postar, editar a foto, trocar o banner, dar like e etc, quando olhei isso pensei: "E se eu fizesse um loading na minha foto, que quando chegasse a 100 seguidores ele completasse a minha foto com o loading", e foi isso que eu fiz e vim mostrar para vocês!
Nunca mexi muito com o nodejs puro, então ai já começou um desafio legal, mas a documentação e a api do BlueSky é bem intuitiva e completa. Começei instalando o @atproto/api que a documentação pede para a gente se conectar ao BlueSky, depois disso a gente Chama o BskyAgent depois literalmente é só fazer login na sua conta:
const agent = new BskyAgent({
service: 'https://bsky.social'
})
await agent.login({
identifier: 'User',
password: 'Password'
})
Agora a gente vai pegar a sua foto de perfil e continua sendo muito simples, coloque o username da sua conta para pode pegar o url da foto de perfil que está lá e sua quantidade de seguidores:
const { data } = await agent.getProfile({ actor: username })
const followers = data.followersCount
const avatar = data.avatar
agora chegamos aonde eu acho que é a parte mais complicada de explicar, fiz um svg aonde ele fica certinho com a imagem que vem do blueSky e precisamos fazer uns calculos para ficar carregando em cima da sua foto de perfil.
Primeiro Fazemos o calculo:
const QuantosSeguidoresQueQuerChegar = 100
const QuantosSeguidoresTemNoMomento = data.followersCount
const QuantoOLoadingVaiCarregando = ( 3018 / QuantosSeguidoresQueQuerChegar ) * QuantosSeguidoresTemNoMomento - 3018
O 3018 é um numero aonde o circulo fica fechado certinho, Pegamos o 3018 e dividimos pelo QuantosSeguidoresQueQuerChegar que dar: 30.18, multiplicamos por QuantosSeguidoresTemNoMomento: 30.18 * 7 = 211,26 e subritairmos pelo 3018 que fica: 2.806,74, para o circulo fechar a gente vai dimunuindo o 3018!
Agora fazemos o SVG da seguinte forma:
const svg = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
<circle cx="500" cy="500" r="480" fill="none" stroke="#e11d48" stroke-width="40" stroke-dasharray="3018" stroke-dashoffset="${QuantoOLoadingVaiCarregando}" transform="rotate(-90 500 500)" />
</svg>
`;
Com o getProfile a gente pode pegar o link da foto de perfil simplesmente fazendo: data.avatar, agora a gente tem o link mas precisamos do arquivo então fiz do seguinte modo, com o Axios:
const input = (await axios({ url: data.avatar, responseType: "arraybuffer" })).data as Buffer
pronto com isso podemos usar a foto de perfil no Sharp e fica desse modo:
// Colocamos a foto no sharp para ele reconhecer a imagem.
sharp(input)
// Agora colocamos o SVG, e sempre tem q ser um buffer.
.composite([{ input: Buffer.from(svg) }])
// Ele salva a imagem, caso der um error ele vai para o if e mostra no console.
.toFile('imagem_combinada.jpg', async (err) => {
if (err) {
console.error('Erro ao combinar a imagem e o SVG:', err);
return;
}
// Vamos carregar essa imagem lá do lado do BlueSky, Não tem como enviar a foto direto pro upsertProfile
const { data } = await agent.uploadBlob(readFileSync('imagem_combinada.jpg'), { encoding: "image/png" });
// Agora vamos atualizar o perfil
await agent.upsertProfile(async existingProfile => {
// Verificando se o perfil existe
const existing = existingProfile ?? {};
// Vamos atualizar a imagem
existing.avatar = data.blob;
return existing;
});
});
Para verificar coloquei dentro de um while que verifica a quantidade de followers quando alterar ele atualiza a foto de perfil desse modo:
let verificarQuantidadeDeFollowrs = 0
while (true){
verificarQuantidadeDeFollowrs = QuantosSeguidoresTemNoMomento
}
No final o codigo ficou desta forma:
import { BskyAgent } from '@atproto/api';
import { readFileSync } from 'fs';
import sharp from 'sharp';
import axios from 'axios';
async function VerifyAndChangeImage(){
const username = 'User'
const password = 'Password'
const agent = new BskyAgent({
service: 'https://bsky.social'
});
try {
await agent.login({
identifier: username,
password: password
});
let verificarQuantidadeDeFollowrs = 0
while (true) {
const { data } = await agent.getProfile({ actor: username });
if (data.followersCount === undefined) {
console.log('Erro: Não foi possível obter o número de seguidores.');
return;
}
if(verificarQuantidadeDeFollowrs !== data.followersCount){
const QuantosSeguidoresQueQuerChegar = 100
const QuantosSeguidoresTemNoMomento = data.followersCount
const QuantoOLoadingVaiCarregando = ( 3018 / QuantosSeguidoresQueQuerChegar ) * QuantosSeguidoresTemNoMomento - 3018
verificarQuantidadeDeFollowrs = QuantosSeguidoresTemNoMomento
const svg = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
<circle cx="500" cy="500" r="480" fill="none" stroke="#e11d48" stroke-width="40" stroke-dasharray="3018" stroke-dashoffset="${QuantoOLoadingVaiCarregando}" transform="rotate(-90 500 500)" />
</svg>
`;
const input = (await axios({ url: data.avatar, responseType: "arraybuffer" })).data as Buffer;
sharp(input)
.composite([{ input: Buffer.from(svg) }])
.toFile('imagem_combinada.jpg', async (err) => {
if (err) {
console.error('Erro ao combinar a imagem e o SVG:', err);
return;
}
const { data } = await agent.uploadBlob(readFileSync('imagem_combinada.jpg'), { encoding: "image/png" });
await agent.upsertProfile(async existingProfile => {
const existing = existingProfile ?? {};
existing.avatar = data.blob;
return existing;
});
});
}
await new Promise(resolve => setTimeout(resolve, 15000));
}
} catch (error) {
console.error(error);
}
}
VerifyAndChangeImage()