Rodando um bot¶

bot.run()¶
bot.run() é o programa inteiro para a maioria dos bots. Ele configura o logging em INFO no stderr, a menos que o logger raiz já tenha handlers, inicia um event loop e roda o bot até ele parar. Ctrl+C e, na thread principal, SIGTERM o encerram de forma limpa.
O token vem da variável de ambiente BOSTIE_TOKEN, ou de bot.run(token). Um Bot no início é removido, e um token ausente levanta ValueError. run também aceita loop_factory, uma função que cria o event loop, repassada para asyncio.run.
O ciclo de vida¶
bot.start() faz o trabalho por trás de run():
- Ele envia o evento
Startinge faz await de cada um dos listeners dele, um de cada vez. O REST funciona aqui, o gateway não; um listener que levanta uma exceção interrompe a inicialização. - Ele sincroniza os comandos, a menos que
auto_sync=False. - Ele conecta os shards do gateway. Quando todos os shards estão prontos e os servidores deles chegaram, ele envia
Starteduma vez, inicia as tarefas em segundo plano e deixawait_until_ready()retornar. - Ele trata eventos até
close(), ou até o Discord fechar o gateway de vez, o que levanta uma exceção, por exemploInvalidTokenouPrivilegedIntentsRequired.
import logging
import bostie
from bostie.events import Started, Starting, Stopped, Stopping
log = logging.getLogger("my-bot")
bot = bostie.Bot()
@bot.listen
async def starting(event: Starting) -> None:
application = await bot.rest.application.fetch()
log.info("starting %s", application.name)
@bot.listen
async def started(event: Started) -> None:
log.info("ready in %d guilds, latency %s s", len(bot.cache.guilds()), bot.latency)
@bot.listen
async def stopping(event: Stopping) -> None:
log.info("saying goodbye while the gateway is still open")
@bot.listen
async def stopped(event: Stopped) -> None:
log.info("the gateway and REST are closed")
bot.run()
await bot.close() encerra nesta ordem:
- Se
Startedfoi enviado, ele enviaStoppinge dá aos listeners dele até 5 segundos enquanto o gateway ainda está aberto. - Ele sai de todos os canais de voz e depois fecha cada shard, o que encerra a sessão dele com o código de fechamento 1000, ou a guarda para uma retomada.
- Ele cancela as tarefas em segundo plano. Os handlers ainda em execução recebem
shutdown_gracesegundos, 0 por padrão, antes de serem cancelados também. - Ele fecha o cliente HTTP e envia
Stopped, de cujos listeners faz await.bot.http,bot.restebot.gatewaydeixam de existir.
close() pode ser chamado de qualquer lugar, mais de uma vez, e enquanto start() roda; o bot pode ser iniciado de novo depois. async with bot: o fecha quando o bloco termina.
Dentro de outro programa¶
Quando o seu programa já roda um event loop, inicie o bot como uma tarefa:
import asyncio
import logging
import bostie
from bostie import StateError
log = logging.getLogger("my-bot")
bot = bostie.Bot(shutdown_grace=10.0)
async def main() -> None:
logging.basicConfig(level=logging.INFO)
running = asyncio.create_task(bot.start())
try:
await bot.wait_until_ready()
except StateError:
await running
raise
log.info("%s is in %d guilds", bot.user, len(bot.cache.guilds()))
try:
await running
finally:
await bot.close()
if __name__ == "__main__":
asyncio.run(main())
await bot.wait_until_ready() retorna assim que Started foi enviado, na hora se já tiver sido, e levanta StateError se o bot parar antes; fazer await da tarefa de start() então levanta o motivo. bot.is_ready diz o mesmo sem esperar. O exemplo de app web roda um bot dentro do lifespan de um servidor web e publica mensagens a partir de uma rota web.
Um bot que só responde a interações por HTTP não precisa de gateway: veja Interações por HTTP. await bot.open() o inicia desse jeito e retorna: ele abre o cliente HTTP, busca o aplicativo e o usuário do bot, envia Starting, sincroniza os comandos, inicia as tarefas em segundo plano e retorna o aplicativo. ASGIApp o chama, e close() o encerra. Um script que só chama a API REST usa um RESTClient.
Enquanto ele roda¶
| Atributo | Valor |
|---|---|
bot.user |
o CurrentUser do bot, depois de conectado |
bot.application_id |
o ID do app, depois de iniciado |
bot.latency |
a média do tempo de ida e volta do heartbeat dos shards em segundos, ou None |
bot.is_running |
se start() ou open() está rodando o bot, até close() encerrá-lo |
bot.is_ready |
se Started foi enviado desde o último start() |
bot.rest, bot.http |
a API REST, depois de iniciado |
bot.gateway |
os shards do gateway, depois de conectado |
bot.cache |
o cache |
bot.voice |
as conexões de voz |
bot.auto_defer |
o atraso padrão do adiamento automático; pode ser mudado a qualquer momento |
bot.rest, bot.http e bot.gateway levantam StateError antes de start().
Logging¶
O bostie escreve logs no módulo padrão logging com estes nomes:
| Logger | O que ele informa |
|---|---|
bostie.bot |
handlers que falharam ou não responderam, intents de que um listener precisa, interações não tratadas |
bostie.gateway |
conexões, desconexões, retomadas, contrapressão, requisições inválidas |
bostie.rest |
rate limits e novas tentativas, vindos do cliente HTTP em Rust |
bostie.sync |
o que cada sincronização de comandos mudou |
bostie.context |
respostas que não puderam ser aplicadas, como uma flag efêmera em uma edição |
bostie.events |
payloads que não couberam no seu evento, uma vez por nome de evento |
bostie.voice |
conexões de voz, DAVE, sinks, receptores e players |
bostie.bridge |
a ligação entre o Python e o núcleo em Rust |
O núcleo em Rust escreve logs pelos mesmos loggers e lê os níveis deles quando o bot inicia: defina logging.getLogger("bostie.gateway").setLevel(logging.DEBUG) antes de run() para ver o gateway em detalhe.
Apontando o bostie para outro lugar¶
Bot(api_url=...) envia as requisições REST para outra URL base, como um proxy de rate limit compartilhado por vários processos ou um Discord falso em testes. Se for omitido, o bostie lê BOSTIE_API_URL quando o bot inicia, e usa https://discord.com/api/v10 quando ela também não está definida. Atrás de um proxy de rate limit, passe http_settings=HTTPSettings(rate_limiter=False), para que o bostie deixe a espera por conta do proxy. O guia de REST cobre as outras configurações de HTTP.
Vários bots em um processo¶
Cada Bot tem a sua própria conexão com o núcleo em Rust, o seu próprio cache e o seu próprio cliente HTTP, então um processo pode rodar vários bots, cada um iniciado e fechado por conta própria, em um único event loop.
Guardando estado¶
Um bot é um objeto comum: crie uma subclasse de Bot para dar atributos a ele, como o exemplo de ciclo de vida faz com uma conexão de banco de dados aberta em um listener de Starting e fechada em um de Stopped. Todas as opções de Bot estão listadas na referência de opções do Bot.