Programando bots para o Mastodon: listando usuários via API

A API do Mastodon é rica, e desenvolver clientes ou robôs para interagir com ela está ao seu alcance em várias linguagens, como veremos em mais um exemplo.

Dando sequência aos exemplos de interação direta com a API do Mastodon que venho publicando, hoje veremos como obter a lista de usuários de uma instância, juntamente com dados sobre a sua atividade e identidade.

O ensejo para escrever este post foi o interesse em gerar esta lista das contas mais falantes da instância brasileira organica.social, que a partir do início da semana passada recebeu uma migração de 700+ brasileiros, graças especialmente a uma ação bem-sucedida da Biana na rede Bluesky.

Para rodar os exemplos abaixo, você precisa ter uma shell Bash ou compatível, com o awk e o gron (para facilitar o consumo das respostas em formato JSON), instalados em um sistema compatível com o Posix (como a maior parte das distribuições Linux, o Mac e Unix em geral).


foto de um brinquedo robô transformer em forma de elefante

Iremos direto ao ponto, e para uma explicação mais detalhada sobre alguns conceitos, sugiro a leitura também do post Programando bots para o Mastodon: obtendo dados via API.

Encaminhando consultas para a API do Mastodon

Já vimos que a interface básica com uma instância do Mastodon é por meio de uma interface (a API) acessada por meio de URLs, e que responde em formato JSON. Há uma série de bibliotecas que intermediam esse acesso em várias linguagens, mas faremos o acesso diretamente, para demonstrar o funcionamento.

Nosso exemplo de hoje é baseado em um endpoint específico da API: o /api/v1/directory, que na minha instância fica visível pela URL https://arram.senta-la.cloud/api/v1/directory - se você acessá-la em seu navegador, verá uma longa linha contendo a lista dos usuários conhecidos pela instância.

Com o acesso básico (da URL acima), a resposta colocará os usuários em ordem de quem postou mais recentemente, e mostrará apenas os primeiros 40 usuários que a instância conhece – ou seja, os usuários que postam em alguma timeline, lista ou tag seguida pelos usuários da própria instância.

A URL também pode incluir parâmetros, como:

  • local=true - ao invés de listar todos os usuários que a instância conhece, lista apenas os usuários da própria instância.
  • limit=60 - ao invés de retornar uma lista com até 40 contas, retornará até 60 (o máximo é 80)
  • offset=120 - ao invés de começar a listar a partir do 1º usuário, começa do 120º usuário.

Embora só possamos receber 80 contas por vez, note que com uma sequência de chamadas usando limit=80 e variando o parâmetro offset, podemos listar quantos quisermos: a primeira será com offset=0 (que é o default), a segunda com offset=80, depois 160, e assim por diante.

Vamos a 2 exemplos de URLs com parâmetros:

  • https://arram.senta-la.cloud/api/v1/directory?local=true - Retorna uma longa linha contendo apenas os usuários locais da minha instância (que é pequena, então será uma lista de meia-dúzia de contas).
  • https://arram.senta-la.cloud/api/v1/directory?limit=80&offset=160 - retorna uma longa linha contendo o equivalente à terceira página (considerando que cada página são 80 contas) da lista de usuários conhecidos pela instância.

Entendendo as respostas da API

Essas longas linhas da resposta são difíceis de compreender visualmente, porque usam o formato JSON, compatível com uma série de sistemas, mas de baixa legibilidade humana direta.

Há várias formas de ampliar a legibilidade do JSON mas, considerando os requisitos deste post, usaremos exemplos com o gron, já mencionado e linkado acima. Se você usar, numa shell compatível, o comando gron 'https://arram.senta-la.cloud/api/v1/directory?local=true' | head, verá uma resposta como a da tela abaixo:


Tela de terminal mostrando o comando gron especificado acima, e com resposta descrita logo abaixo, no texto do post.
Resposta da API decodificada com um campo por linha, usando o utilitário gron.

Note que agora, ao invés de uma linha gigante, temos uma sequência de linhas, cada uma mostrando um campo da resposta da API. Na tela acima, limitada a 10 linhas, todas as linhas se referem a um mesmo usuário e começam com json[0], como nos exemplos:


json[0].acct = "autobrain";
json[0].created_at = "2025-04-23T00:00:00.000Z";
json[0].discoverable = true;
json[0].display_name = "VISITE O DECORADO";

A listagem quanto ao primeiro usuário continua ainda por muitas outras linhas não visíveis na resposta (porque nós a cortamos com o filtro head) e, quando acabar, o próximo usuário começa em seguida, aí com o prefixo json[1], e assim por diante. Veja um exemplo das linhas da transição entre o 3º e o 4º usuários:


json[3].url = "https://arram.senta-la.cloud/@medidor";
json[3].username = "medidor";
json[4] = {};
json[4].acct = "TrendsBR";
json[4].bot = true;

Na versão atual da API, geralmente cada usuário é descrito por mais de 20 campos – ou seja, mais de 20 linhas geradas pelo gron –, e a último delas é referente ao campo username.

Um script básico para listar usuários

Este post de hoje nasceu de um script criado para apoiar a elaboração de uma lista dos usuários mais ativos da instância brasileira organica.social, que em dezembro de 2025 recebeu um grande influxo de usuários alcançados por uma bem-sucedida ação da Biana na rede Bluesky.

Tudo que precisamos para fazer uma lista dessas pode ser obtido pelo endpoint /api/v1/directory, que conhecemos acima. Como a organica.social recebeu centenas de novos usuários, será necessário buscar nela várias páginas de resposta (cada página listando 80 contas, como vimos acima).

Entre os diversos atributos da conta, que a resposta da API descreve, os que nos interessam especialmente são o login, o nome, a data de criação da conta (pois para a nossa listagem queremos só os recém-chegados) e a contagem de posts.

Adicionaremos também o campo referente à quantidade de contas que a pessoa segue, para assim ser mais fácil identificar uma parte das pessoas que criou conta apenas para "reservar o login". Os campos são os seguintes, retirados diretamente da resposta do gron sobre a minha conta pessoal:


json[0].display_name = "VISITE O DECORADO";
json[0].following_count = 1194;
json[0].indexable = true;
json[0].statuses_count = 9579;
json[0].username = "autobrain";

Há uma infinidade de maneiras de extrair esses dados usando linguagens e utilitários, e o programa a seguir, em shell e awk (com apoio do gron) é uma delas.

Ele baixa as 6 primeiras páginas de resultados, o que corresponde a até 480 contas - ou, no caso, as 480 contas locais da instância, que tiverem postado mais recentemente. Em seguida, ele faz um processamento básico de formatação das linhas (extraindo a identificação do campo e o ";" ao final da linha) e produz a saída em formato CSV básico, ou seja, com os campos separados por vírgulas, as linhas separadas por Return, e as strings entre aspas.

Segue a listagem completa:


#!/usr/bin/env bash
#
# masto_userdir.sh - exemplo de uso da API 'directory' do Mastodon
#
# Copyright (c) 2025,  Augusto Campos (https://augustocampos.net/).
# Licensed under the Apache License, Version 2.0.
#

echo "DISPLAY_NAME,FOLLOWING_COUNT,INDEXABLE,STATUSES_COUNT,USERNAME"
delta=0
inst="organica.social"
url="https://$inst/api/v1/directory?local=true&limit=80&offset=$delta"

# loop de 6 leituras
for i in {1..6}; do
  gron "$url" | awk '
  # filtra só as linhas desses campos mencionados:
  /\.(display_name|following_count|indexable|statuses_count|username) = / {

    # separador será uma vírgula, exceto no último campo ("username"), 
    # quando será um Return:
    SEP=","
    if ($1 ~ /\.username/) SEP="
"

    # remove a identificação do campo ("json[x].xxx"), o "=" e o ";"
    $1=$2=""
    sub(/;$/,"")

    # gera a saída com o conteúdo do campo e o separador
    printf("%s"SEP, substr($0,3))
  }'

  # antes de repetir o loop, aumenta o valor que será usado no "offset"
  delta=$((delta + 80))
done

Explicações sobre a linguagem estão fora do escopo deste post, mas incluí comentários nos trechos do código, para orientar quem preferir implementar com estrutura similar, em sua tecnologia favorita.

A saída produzida será algo similar a essa, que é o resultado de rodar o mesmo programa, mas direcionado à minha instância:


DISPLAY_NAME,FOLLOWING_COUNT,INDEXABLE,STATUSES_COUNT,USERNAME
"Locutora de feeds",1,true,1168,"locutora"
"VISITE O DECORADO",1194,true,9581,"autobrain"
"Gugu",0,true,176,"gugu"
"Medidor de ranço",11,true,63,"medidor"
"Trends Brasil 🚀",1,true,827,"TrendsBR"
"Madame Sandra Rosa Madalena",22,true,31,"MadameSandra"
"Tags Temáticas BR",2,true,366,"TagsBR"
"Bebê Taz Ininteligível",24,true,433,"aridiculaideia"
"Sextou, galera!",1,true,6,"sextou"

Note que esse formato de saída com linhas, aspas e separação por vírgulas, conhecido como CSV (ou RFC 4180), serve para ser importado em planilhas, bancos de dados e outros sistemas, e aí usá-los para reordenar, classificar, aplicar condições, gerar gráficos e o que mais você quiser fazer com essas informações – no meu caso, serviu para gerar uma thread no Mastodon.

O programa acima é bem cru, e tem muitas oportunidades de melhoria para você se exercitar, inclusive pela adição de tratamento de erro e de ser capaz de perceber que ele está tentando ler mais usuários do que a instância tem - na versão acima, o loop de 6 leituras é feito até mesmo para instâncias com menos de 80 usuários, e aí repete várias vezes a resposta da primeira leitura.

Um detalhe importante é que, embora o protocolo do Mastodon preveja que esse endpoint da API seja acessado sem precisar de autenticação, algumas instâncias configuram (com intenções variadas e eficácia baixa) bloqueios a esse acesso não autenticado, então se você experimentar em uma instância e não receber resposta, essa é uma das razões possíveis.

Referências e documentação

Venho preferindo exemplos com operações simples, envolvendo apenas um endpoint de API por vez, e com acesso a dados públicos, porque desenvolver interfaces ou clientes para o Mastodon tem uma escala de complexidade a ser vencida, como em tudo na vida, e não há razão para começar já tentando operações difíceis, autenticadas e envolvendo múltiplas instâncias e entidades.

Lembre-se que a API do Mastodon é bem documentada, e desde já recomendo os capítulos "Getting Started with the API", "Playing with public data" e "Directory API methods" para entendimento aprofundado do que apresentei acima com bem menos detalhes.

Leia também o post anterior: t Programando bots para o Mastodon: obtendo dados via API.

O valor da Duolingo e o mercado ~sedento por IA

Às vezes o mesmo fator que barateia custos também corrói valor, e essa é a história contada pelas ações do Duolingo em 2025.

A gente ouve o tempo todo que o mercado está sedento por IA. É verdade, dependendo do contexto, mas já não está mais como foi até recentemente, e esse gráfico das ações do Duolingo nos EUA ilustra isso muito bem.

Em abril o valor subiu muito, após anunciarem que iam demitir a galera dos idiomas e trocar por IA. Era verdade, e eles produziram bem mais conteúdo desse jeito. Mas também é verdade que isso produziu uma dose de má-vontade com a marca, por parte de uma parcela do mercado, especialmente quando surgiram relatos sobre a qualidade variável dos conteúdos produzidos a partir de então.


Gráfico do valor da ação do Duolingo na NASDAQ ao longo dos últimos 365 dias, descrito em maior detalhe no parágrafo imediatamente seguinte, no texto do post.
Gráfico do valor da ação do Duolingo na NASDAQ ao longo dos últimos 365 dias.

O gráfico acima mostra uma escalada súbita na virada de abril para maio (logo após o CEO anunciar que ia trocar por IA a mão de obra contratada que lidava com idiomas), e uma queda também súbita na virada de outubro para novembro, logo após a divulgação da previsão de desempenho para o próximo ano.

O mesmo gráfico também permite verificar que a ação vale hoje 38% a menos do que há 365 dias, e que entre a subidona e a descidona tivemos um longo período de queda menos acentuada – a realidade não sustentou as previsões do final de abril.

Por que o valor despencou em novembro?

Tem toda uma questão da dinâmica (e aprendizado) do mercado aí envolvida, mas também dá pra notar um ponto importante relacionado à estratégia: em abril/maio o que o mercado valorizou foi um aspecto de processos internos da empresa: ia ficar mais barato produzir o serviço que os clientes da empresa topam comprar.

Já em novembro, o que aconteceu é sobre algo mais relevante: a percepção (que veio quando a empresa publicou o relatório obrigatório sobre expectativas para o ano seguinte) de que aquele ganho de abril está ao alcance de todos os concorrentes, e o diferencial da proposta de valor do Duolingo já era.

Ou seja: oferecer grande volume de material sobre idiomas produzido por IA é mais barato para o Duolingo, mas está ao alcance de diversos outros concorrentes também, e o produto de todos tende a ficar parecido – e a percepção de liderança dessa marca foi abalada.

Pode mudar? Pode, mercado de ações é dinâmico. Inclusive, quem sabe alguém contrata aquelas pessoas que em abril ficaram disponíveis, e recupera o diferencial?

Cuidado com o imobilismo bem intencionado

Não pergunte se você é uma boa pessoa, e sim se você está fazendo coisas boas.

Quantas iniciativas coletivas eu já vi naufragarem porque, ao invés de prevalecer o impulso de quem queria produzir avanços com atos concretos, prevalecia a atitude paralisante de quem defende evitar a qualquer custo fazer qualquer coisa imperfeita.

Isso é um mal frequente em organizações voluntárias, e em grupos motivados por um ideal de evolução – mas pode se manifestar até na vida individual, quando a pessoa se pergunta muito mais “será que eu sou uma boa pessoa?” do que “será que o que estou fazendo é bom?”

E há uma pergunta ainda melhor pra impulsionar a mudar o mundo (e a própria realidade individual) para melhor: “Qual coisa boa eu posso fazer, aqui e agora?”


Foto de um ponbo que comeu o miolo de uma fatia de pão e acabou ficando com a casca dela pendurada em seu pescoço, como um colar.

Tentar ser uma boa pessoa, embora seja uma atitude virtuosa, pode ser sufocante e paralisante. É mais frutífero o caminho de quem procura, ao invés disso, fazer coisas boas (ou “fazer o bem”, dependendo do contexto).

Não se trata de defender ações perversas, desonestas ou irresponsáveis: elas são incompatíveis com o conceito de fazer coisas boas.

O critério da ação voluntária e orientada a ideais não pode ser aquele modelo inodoro e insípido do "zero defeito" ou da busca por zerar riscos - os processos precisam ser orientados a produzir o resultado, por meios bons.

Uma coisa de cada vez

Quando eu escrevo (ou crio em qualquer mídia), uma coisa que funciona muito bem pra mim é não misturar a hora da criação com a hora da edição.

Na hora da criação, tem que dar vazão às ideias. Na edição a gente seleciona, reordena, edita, rejeita frases, parágrafos e capítulos, reescreve, etc.

Tentar fazer as duas coisas ao mesmo tempo parece eficiente, mas sufoca a criação.

O guarda-roupa monocromático ajuda até nisso

Me faltam as habilidades de costura, mas na linha da atitude punk de combater a obsolescência programada, as minhas roupas começaram a ter uma sobrevida grande depois que eu passei a lembrar de tingi-las (deixo na lavanderia e eles fazem isso pra mim) quando elas começam a perder a cor original.

O meu guarda-roupa é bem monocromático e isso ajuda, claro.

Frustração das grandes, e repetitiva

A minha vida deu uma virada no início de 2021, quando eu troquei minha alimentação baseada em frituras e iFood por preparar minha própria comida (e saudável).

A partir dali, fui progredindo: passei a beber água (e hj bebo 2 litros por dia), parei de ser sedentário, larguei o costume de beber álcool, investi na higiene do sono etc.

Me dá uma raiva tão grande, mas TÃO GRANDE, quando eu conto isso e a primeira pergunta de alguém é "e quantos quilos você perdeu?"

Minha vida ficou mais fácil

depois que coloquei os dígitos finais do meu CEP, que nunca consigo memorizar, na tela de travamento do meu celular. Aí quando me pedem o CEP, é só olhar pra tela, sem nem destravar.

Invertendo a manipulação

Uma técnica de convencimento comum dessa gente que faz treinamento de vendas disfarçado de neuroqualquercoisa é te fazer uma pergunta sobre algum assunto, esperar tu responder e aí direcionar pro interesse dela perguntando “mas você não acha que _________?”

Isso me irrita muito, não só por ser manipulação – mas também porque a minha resposta é totalmente descartada. A pergunta inicial era só um engodo pra me fazer engajar, e o teor da minha resposta não será aproveitado nesse script.


Quatro exemplares da carta de reversão, do jogo Uno
Invertendo o fluxo!

Responder me custa! Quando alguém vem com essa, tenho adotando meu próprio script e respondendo: “eu acabei de te dizer o que eu acho”.

Não é só pra cortar o script: também é porque eu não tenho o menor interesse em dar continuidade a uma conversa que foi planejada pra me atrair com uma isca e depois conduzir em outra direção.

500 CARACTERES, Edição 008 – planilhas incas, pombos-correio e maquetes

Na newsletter 500 CARACTERES eu reúno curiosidades e histórias que possam ter escapado à atenção coletiva: links que nem são tão recentes, mas sobreviveram à passagem do tempo, selecionados periodicamente a partir do meu acervo.

Nesta edição:

🌐🔷 Por que os links da web são azuis?

📼📺 A feminista que arquivou décadas de telejornais

🔢⛰️ Planilhas, como faziam os antigos incas

🕊️📨 Os pombos-correio ainda eram atuais no século XX!

🏙️🤏🏻 Arte urbana, mas em forma de maquetes

Qual você curtiu mais? Quero saber!

 

Planilhas, como faziam os antigos incas


Ilustração do século XVI mostra um inca segurando um quipu, e apresenta um exemplo de decodificação dos nós.
Ilustração do século XVI mostra um inca segurando um quipu, e apresenta um exemplo de decodificação dos nós.

A civilização inca se caracterizava por modernidades, incluindo mais de 30.000km de estradas, arquitetura avançada, comunicação à distância, e um sistema de registro contábil nos chamados quipus.

Os quipus eram coleções de cordões com nós, em que cada coleção era como se fosse uma planilha, e cada cordão era uma célula contendo um número (inteiro ou fração) ou operação. Cerca de 1500 quipus sobreviveram à invasão europeia, e são atentamente estudados.

📎 rethinkq.adp.com

 

Por que os links da web são azuis?

Por que os links da web são azuis? Nem sempre foi assim, e Elise Blanchard - hoje uma repórter no Afeganistão, mas na época blogueira na Mozilla - foi atrás da origem dessa ideia.

Pesquisadores da universidade de Maryland em 1985(!) comprovaram que azul era a cor mais visível entre as que não pioram a legibilidade, e isso foi publicado e implementado no ano seguinte.

A web surgiu na mesma década, mas só adotou essa cor em 1993, com o Mosaic 0.13.

📎 blog.mozilla.org

 

Os pombos-correio ainda eram atuais no século XX!


Por uma pequena abertura coberta por uma janela removível, uma mão acessa o exterior de um tanque Mark V, segurando um pombo-correio para soltá-lo.
Por uma pequena abertura coberta por uma janela removível, uma mão acessa o exterior de um tanque Mark V, segurando um pombo-correio para soltá-lo.

Essa foto é poderosa porque ela comunica algo difícil de captar neste século hiperconectado: faz bem pouco tempo que temos acesso instantâneo a (quase) qualquer lugar, e até 30 anos atrás, era comum ter que esperar horas, dias ou semanas para conseguir mandar uma mensagem a algum parente ou parceiro de negócios e receber sua resposta.

A foto mostra uma abertura específica para enviar pombos-correio, num tanque Mark V durante a Batalha de Amiens (1918), na Primeira Guerra Mundial.

 

Arte urbana, mas em forma de maquetes


Foto da mão de Joshua segurando a sua reprodução da fachada de uma banquinha de jornal que também é lotérica, bomboniére e tabacaria.
Foto da mão de Joshua segurando a sua reprodução da fachada de uma banquinha de jornal que também é lotérica, bomboniére e tabacaria.

Eu sou fã de arte urbana, incluindo as que reproduzem as formas das nossas cidades (aliás, você já segue o @ivanjeronimo? Ele faz desenhos urbanos lindos!) – e isso às vezes inclui coisas diferenciadas.

Tipo um artista australiano que desde 2015 faz esculturas realistas reproduzindo bancas, lojinhas, becos, cabines de fotografia e outros detalhes despercebidos, frequentemente sujos e grafitados, de cidades de todo o mundo. Massa, né?

Mais: joshua_smith_street_artist no Instagram

 

A feminista que arquivou décadas de telejornais

Marion Stokes foi uma feminista, ativista pelos direitos civis nos EUA, e fez algo que ninguém mais fez: gravou cópias de telejornais ao longo de décadas.

Ela começou nessa missão em 1977, e comprava fitas VHS em dúzias. Quando nos deixou, em 2012, sua coleção tinha 140.000 fitas de 6h, armazenadas em vários imóveis.

Esse acervo vem sendo digitalizado pelo Internet Archive, e contém momentos históricos cuja transmissão não tem nenhum outro registro.

📎 archive.org

 


🗞️ A minha newsletter se chama 500 CARACTERES porque os textos originais dela são publicados primeiro no Mastodon, onde cada pauta individual obedece ao limite de 500 caracteres. Nela eu acumulo histórias interessantes, que depois agrupo na forma de um índice como este, para facilitar o acesso.

🐘 Veja o original desta edição, que foi publicado primeiro no Mastodon.

Ontem eu completei 52 anos e estava fazendo umas revisões.

Uma coisa que precisará ser melhor explorada pelos biógrafos é que eu saí de casa aos 19 anos, mas aos 28 anos fugi da minha própria casa quando as dinâmicas familiares chegaram a ela. E jamais voltei.