Webhooks¶
A webhook posts messages into one channel under a name and avatar of its own. Its URL holds an id and a token, and the token is all it takes to post, so a deploy script or a monitoring job can send messages without a bot.
import asyncio
import os
import bostie
from bostie import Embed, color, ui
async def announce(url: str, version: str) -> None:
async with bostie.WebhookClient(url) as webhook:
message = await webhook.execute(
embeds=[Embed(title=f"Deploying {version}", color=color.YELLOW)],
username="Deploy bot",
avatar_url="https://example.com/deploy.png",
)
await webhook.edit_message(
message.id,
embeds=[Embed(title=f"Deployed {version}", color=color.GREEN)],
)
await webhook.execute(
components=[ui.text(f"Release notes for {version} are out.")],
thread_name=f"Release {version}",
with_components=True,
)
def announce_from_a_script(url: str) -> None:
webhook = bostie.SyncWebhookClient(url)
message = webhook.execute("The nightly backup is running.")
webhook.edit_message(message.id, content="The nightly backup finished.")
if __name__ == "__main__":
asyncio.run(announce(os.environ["WEBHOOK_URL"], "2.0"))
WebhookClient(url) parses the id and token from a webhook URL, and raises ValueError for anything else. Use it inside async with, which opens and closes its HTTP client. SyncWebhookClient(url) has the same methods for code without an event loop; each call runs its own.
| Method | Does |
|---|---|
execute(content, **parts) |
posts a message and returns it once Discord saved it |
edit_message(message_id, **parts) |
edits a message the webhook sent; a part left out stays, None clears it |
fetch_message(message_id), delete_message(message_id) |
fetch or delete one |
fetch(), edit(name=..., avatar=...), delete() |
the webhook itself |
execute_slack(payload), execute_github(payload) |
post a payload in the formats Discord's Slack-compatible and GitHub-compatible endpoints accept |
execute takes the message parts and also:
usernameandavatar_url, shown instead of the webhook's own for this message;thread_id, to post in a thread of the channel; the message methods take it too;thread_name, to start a thread in a forum or media channel, withapplied_tags;with_components=True, needed for components on a webhook that no app owns.
The clients take api_url, http_settings and allowed_mentions like the bot; by default a webhook can ping users and roles but not @everyone.
When Discord answers 404 Unknown Webhook, the webhook was deleted. bostie then answers every later request for it with the same 404 without sending it, so a dead webhook does not count against the invalid request limit.
Managing webhooks with a bot¶
bot.rest.webhooks creates and lists a channel's or guild's webhooks, which needs the Manage Webhooks permission, and has the token methods too:
create(channel_id, name=..., avatar=File(...))returns aWebhookwhoseurlyou can hand to aWebhookClient.list(channel_id),list_guild(guild_id),fetch(webhook_id),edit(webhook_id, ...)anddelete(webhook_id)work with the bot's token.execute(webhook_id, token, ...)and the other_with_tokenand message methods are whatWebhookClientcalls.
The WebhooksUpdate event says that a channel's webhooks changed. A message a webhook sent arrives as a WebhookMessage, whose author is a PartialUser and whose webhook_id is set.