### Basic Bot Setup and Message Handling Source: https://github.com/tav-gg/taverns.py/blob/main/README.md Set up a basic bot client and handle incoming messages. This example shows how to respond to a '!ping' command. ```python import os from taverns import Client, Intents client = Client(intents=Intents.MESSAGES | Intents.MESSAGE_CONTENT) @client.event async def on_ready(): print(f"Logged in as {client.user.display_name}") @client.event async def on_message_create(message): if message.sender_id == client.user.id: return if message.content == "!ping": await client.send_message(message.tavern_id, message.channel_id, content="Pong!") client.run(os.environ["BOT_TOKEN"]) ``` -------------------------------- ### Install taverns.py Source: https://github.com/tav-gg/taverns.py/blob/main/README.md Install the taverns.py library using pip. You can install the latest version from PyPI or from a local source checkout. ```bash pip install taverns.py # or from source: pip install -e . ``` -------------------------------- ### Access REST API Source: https://github.com/tav-gg/taverns.py/blob/main/README.md Use the REST client for direct API access after the bot has logged in. Examples include fetching channels, members, and messages. ```python # Available after login channels = await client.rest.get_channels(tavern_id) members = await client.rest.get_members(tavern_id) messages = await client.rest.get_messages(tavern_id, channel_id, limit=50) ``` -------------------------------- ### Initialize a Tavern Bot Client Source: https://context7.com/tav-gg/taverns.py/llms.txt Demonstrates setting up a basic bot client with specific gateway intents and event handlers for readiness and message processing. ```python import os from taverns import Client, Intents, Message # Create client with specific intents client = Client( intents=Intents.MESSAGES | Intents.MESSAGE_CONTENT, ) @client.event async def on_ready(): print(f"Logged in as {client.user.display_name}") print(f"Connected to {len(client.taverns)} tavern(s)") @client.event async def on_message_create(message: Message): # Don't respond to ourselves if message.sender_id == client.user.id: return if message.content == "!ping": await client.send_message( message.tavern_id, message.channel_id, content="Pong!", ) if __name__ == "__main__": token = os.environ.get("BOT_TOKEN") if not token: print("Set BOT_TOKEN environment variable") raise SystemExit(1) client.run(token) ``` -------------------------------- ### Register and Handle Slash Commands Source: https://context7.com/tav-gg/taverns.py/llms.txt Illustrates how to register slash commands during the ready event and process interactions using the interaction create event. ```python import os from taverns import Client, Intents, Interaction from taverns.commands import command, option from taverns.types import CommandOptionType client = Client(intents=Intents.MESSAGES | Intents.INTERACTIONS) @client.event async def on_ready(): print(f"Logged in as {client.user.display_name}") # Register commands on startup await client.register_commands([ command("ping", "Check bot latency"), command("greet", "Greet a user", options=[ option("name", "Who to greet", CommandOptionType.STRING, required=True), option("loud", "Shout the greeting", CommandOptionType.BOOLEAN), ]), command("ban", "Ban a user from the tavern", options=[ option("user", "User to ban", CommandOptionType.USER, required=True), option("reason", "Ban reason", CommandOptionType.STRING), ]), ]) print("Commands registered") @client.event async def on_interaction_create(interaction: Interaction): if interaction.command_name == "ping": await interaction.reply("Pong!") elif interaction.command_name == "greet": name = interaction.get_option("name", "World") loud = interaction.get_option("loud", False) greeting = f"Hello, {name}!" if loud: greeting = greeting.upper() await interaction.reply(greeting) if __name__ == "__main__": client.run(os.environ["BOT_TOKEN"]) ``` -------------------------------- ### Configure Gateway Intents Source: https://context7.com/tav-gg/taverns.py/llms.txt Shows various methods for configuring gateway intents, including bitwise OR combinations and list-based initialization. ```python from taverns import Client, Intents # Specific intents using bitwise OR client = Client(intents=Intents.MESSAGES | Intents.MESSAGE_CONTENT | Intents.INTERACTIONS) # All non-privileged intents client = Client(intents=Intents.DEFAULT) # All intents including privileged client = Client(intents=Intents.ALL) # Build intents from names intents = Intents.from_list("MESSAGES", "INTERACTIONS", "REACTIONS") client = Client(intents=intents) # Available intents: # MESSAGES - Message events (create, update, delete) # MESSAGE_CONTENT - Access message content (privileged) # MEMBERS - Member join/leave events (privileged) # MODERATION - Moderation events # CHANNELS - Channel events # THREADS - Thread events # FORUMS - Forum events # ROLES - Role events # SETTINGS - Settings events # EVENTS - Tavern events # VOICE - Voice state events # BROADCASTS - Broadcast events # TYPING - Typing indicators # REACTIONS - Reaction events # PINS - Pin events # INTERACTIONS - Slash command interactions ``` -------------------------------- ### Handle Slash Command Interactions Source: https://context7.com/tav-gg/taverns.py/llms.txt Demonstrates various interaction response types including immediate, ephemeral, deferred, and rich embed replies. ```python from taverns import Client, Intents, Interaction from taverns.embed import EmbedBuilder client = Client(intents=Intents.INTERACTIONS) @client.event async def on_interaction_create(interaction: Interaction): if interaction.command_name == "ping": # Immediate reply await interaction.reply("Pong!") elif interaction.command_name == "secret": # Ephemeral reply - only the user sees this await interaction.reply("This is a secret message!", ephemeral=True) elif interaction.command_name == "slow": # Deferred reply for long-running operations await interaction.defer_reply() # Shows "thinking..." to the user # Do slow work here... result = await do_slow_work() # Follow up within 15 minutes await interaction.follow_up(f"Done! Result: {result}") elif interaction.command_name == "card": # Reply with rich embed embed = ( EmbedBuilder() .set_title("Daily Card") .set_description("You received a **Rare** card!") .set_color("#FFD700") .add_field("ATK", "5", inline=True) .add_field("DEF", "3", inline=True) .set_footer("Taverns TCG") .build() ) await interaction.reply("Here's your card:", embeds=[embed]) elif interaction.command_name == "announce": # Send a regular channel message instead of interaction reply await interaction.send_message("Announcement from the bot!") # Access interaction properties # interaction.id - Unique interaction ID # interaction.command_name - Name of the invoked command # interaction.tavern_id - Tavern where interaction occurred # interaction.channel_id - Channel where interaction occurred # interaction.user_id - User who invoked the command # interaction.options - Dict of option values # interaction.get_option("name", default) - Get option value with default ``` -------------------------------- ### Registering and Handling Slash Commands Source: https://github.com/tav-gg/taverns.py/blob/main/README.md Register slash commands like 'ping' and 'greet' with options, and handle incoming interaction events. Ensure you have the Intents.INTERACTIONS intent enabled. ```python from taverns import Client, Intents, Interaction from taverns.commands import command, option from taverns.types import CommandOptionType client = Client(intents=Intents.MESSAGES | Intents.INTERACTIONS) @client.event async def on_ready(): await client.register_commands([ command("ping", "Check latency"), command("greet", "Greet someone", options=[ option("name", "Who to greet", CommandOptionType.STRING, required=True), ]), ]) @client.event async def on_interaction_create(interaction: Interaction): if interaction.command_name == "ping": await interaction.reply("Pong!") elif interaction.command_name == "greet": name = interaction.get_option("name", "World") await interaction.reply(f"Hello, {name}!") client.run(os.environ["BOT_TOKEN"]) ``` -------------------------------- ### Configure Bot Intents Source: https://github.com/tav-gg/taverns.py/blob/main/README.md Control the events your bot receives by specifying intents during client initialization. Use `Intents.DEFAULT` for non-privileged intents or `Intents.ALL` for all intents. ```python from taverns import Intents # Specific intents client = Client(intents=Intents.MESSAGES | Intents.INTERACTIONS) # All non-privileged intents client = Client(intents=Intents.DEFAULT) # All intents (including privileged) client = Client(intents=Intents.ALL) ``` -------------------------------- ### Handle Specific Exceptions in Taverns.py Source: https://context7.com/tav-gg/taverns.py/llms.txt Demonstrates how to catch and handle specific exception types provided by the taverns SDK, such as AuthenticationError, RateLimitError, and general TavernError. This is crucial for building robust bots that can gracefully recover from API issues. ```python from taverns import ( Client, Intents, TavernError, AuthenticationError, HTTPError, RateLimitError, GatewayError, InteractionError, ) client = Client(intents=Intents.MESSAGES) @client.event async def on_message_create(message): if message.content == "!test": try: # Attempt some API operation await client.rest.send_message( message.tavern_id, "invalid-channel-id", content="Test", ) except AuthenticationError: print("Invalid bot token") except RateLimitError as e: print(f"Rate limited, retry after {e.retry_after} seconds") except HTTPError as e: print(f"HTTP error {e.status}: {e}") if e.response_body: print(f"Response: {e.response_body}") except GatewayError as e: print(f"Gateway error: {e}") except InteractionError as e: print(f"Interaction error: {e}") except TavernError as e: print(f"General Tavern error: {e}") ``` -------------------------------- ### Create Rich Embeds Source: https://context7.com/tav-gg/taverns.py/llms.txt Shows how to construct message embeds using the fluent EmbedBuilder API or by instantiating the Embed dataclass directly. ```python from taverns import Client, Intents from taverns.embed import Embed, EmbedBuilder, EmbedField, EmbedFooter, EmbedImage client = Client(intents=Intents.MESSAGES) @client.event async def on_message_create(message): if message.content == "!card": # Using EmbedBuilder (fluent API) embed = ( EmbedBuilder() .set_title("Legendary Dragon") .set_description("A powerful creature from ancient times.") .set_color("#FF5733") .add_field("Attack", "95", inline=True) .add_field("Defense", "80", inline=True) .add_field("Special Ability", "Breathes fire dealing 50 damage") .set_thumbnail("https://example.com/dragon-thumb.png") .set_image("https://example.com/dragon.png") .set_footer("Card #1234", icon_url="https://example.com/logo.png") .build() ) await client.send_message( message.tavern_id, message.channel_id, content="Here's your card!", embeds=[embed], ) elif message.content == "!stats": # Using Embed dataclass directly embed = Embed( title="Server Stats", description="Current server statistics", color="#00FF00", fields=[ EmbedField(name="Members", value="1,234", inline=True), EmbedField(name="Online", value="456", inline=True), EmbedField(name="Messages Today", value="7,890", inline=True), ], footer=EmbedFooter(text="Updated every 5 minutes"), thumbnail=EmbedImage(url="https://example.com/stats-icon.png"), ) await client.send_message( message.tavern_id, message.channel_id, content="", embeds=[embed], ) ``` -------------------------------- ### Interact with Taverns API via RESTClient Source: https://context7.com/tav-gg/taverns.py/llms.txt Demonstrates using the RESTClient to fetch tavern data, search messages, and manage interactions like pins and reactions. ```python from taverns import Client, Intents from taverns.embed import EmbedBuilder client = Client(intents=Intents.MESSAGES) @client.event async def on_ready(): # Get all taverns the bot is in for tavern_id, tavern in client.taverns.items(): print(f"Tavern: {tavern.name} ({tavern.member_count} members)") # Get channels in the tavern channels = await client.rest.get_channels(tavern_id) for channel in channels: print(f" Channel: #{channel.name} ({channel.type})") # Get members members = await client.rest.get_members(tavern_id) print(f" Fetched {len(members)} members") @client.event async def on_message_create(message): if message.content == "!history": # Get recent messages from channel messages = await client.rest.get_messages( message.tavern_id, message.channel_id, limit=10, ) summary = "\n".join([f"- {m.content[:50]}" for m in messages]) await client.send_message( message.tavern_id, message.channel_id, content=f"Last 10 messages:\n{summary}", ) elif message.content == "!search hello": # Search messages in tavern results = await client.rest.search_messages( message.tavern_id, query="hello", limit=5, ) await client.send_message( message.tavern_id, message.channel_id, content=f"Found {len(results)} messages containing 'hello'", ) elif message.content == "!pin": # Pin a message await client.rest.pin_message(message.tavern_id, message.id) elif message.content == "!react": # Add a reaction await client.rest.add_reaction(message.tavern_id, message.id, "👍") elif message.content == "!roles": # Get tavern roles roles = await client.rest.get_roles(message.tavern_id) role_names = [r.get("name", "Unknown") for r in roles] await client.send_message( message.tavern_id, message.channel_id, content=f"Roles: {', '.join(role_names)}", ) ``` -------------------------------- ### Register Gateway Event Handlers in Taverns.py Source: https://context7.com/tav-gg/taverns.py/llms.txt Shows how to register event handlers for various gateway events using the `@client.event` decorator or the `@client.on()` method. This allows your bot to respond to real-time updates from the platform. ```python from taverns import Client, Intents from taverns.types import Message, Member, Channel client = Client(intents=Intents.ALL) @client.event async def on_ready(): print("Bot is ready!") @client.event async def on_message_create(message: Message): print(f"New message: {message.content}") @client.event async def on_message_update(message: Message): print(f"Message edited: {message.content}") @client.event async def on_message_delete(data: dict): print(f"Message deleted: {data.get('id')}") @client.event async def on_member_join(member: Member): print(f"Member joined: {member.user_id}") @client.event async def on_member_leave(member: Member): print(f"Member left: {member.user_id}") @client.event async def on_channel_create(channel: Channel): print(f"Channel created: {channel.name}") @client.event async def on_channel_update(channel: Channel): print(f"Channel updated: {channel.name}") @client.event async def on_channel_delete(data: dict): print(f"Channel deleted: {data.get('id')}") @client.event async def on_typing(data: dict): print(f"User typing in {data.get('channelId')}") @client.event async def on_reaction_update(data: dict): print(f"Reaction on message {data.get('messageId')}") @client.event async def on_bot_installed(data: dict): print(f"Bot installed in tavern {data.get('tavernId')}") @client.event async def on_bot_removed(data: dict): print(f"Bot removed from tavern {data.get('tavernId')}") # Alternative: register by event name @client.on("voice_state_update") async def handle_voice(data: dict): print(f"Voice state update: {data}") ``` -------------------------------- ### Taverns.py SDK Data Types Overview Source: https://context7.com/tav-gg/taverns.py/llms.txt Provides an overview of the data types available in the taverns.py SDK for representing platform objects like users, taverns, channels, members, and messages. These dataclasses simplify data access and manipulation. ```python from taverns.types import ( User, Tavern, Channel, Member, Message, BotCommand, BotSelf, CommandOption, CommandOptionType, ) # User - represents a user on the platform # user.id - Unique user ID # user.display_name - User's display name # user.avatar_url - Avatar URL (optional) # user.banner_url - Banner URL (optional) # user.is_bot - Whether user is a bot # Tavern - represents a server/community # tavern.id - Unique tavern ID # tavern.name - Tavern name # tavern.slug - URL slug # tavern.icon_url - Icon URL (optional) # tavern.member_count - Number of members # tavern.granted_permissions - Bot's permission bitfield # Channel - represents a channel in a tavern # channel.id - Unique channel ID # channel.name - Channel name # channel.type - Channel type (TEXT, VOICE, etc.) # channel.tavern_id - Parent tavern ID # channel.position - Display position ``` -------------------------------- ### Handling Slow Interactions with Deferred Responses Source: https://github.com/tav-gg/taverns.py/blob/main/README.md Defer interaction replies to show a 'thinking...' state to the user while performing slow operations. Use `follow_up` to send the final result. ```python @client.event async def on_interaction_create(interaction: Interaction): if interaction.command_name == "slow": await interaction.defer_reply() # Shows "thinking..." to the user result = await do_slow_work() await interaction.follow_up(f"Done: {result}") ``` -------------------------------- ### Manage Messages with Taverns Client Source: https://context7.com/tav-gg/taverns.py/llms.txt Covers sending, replying, editing, and deleting messages, as well as triggering typing indicators. ```python from taverns import Client, Intents from taverns.embed import EmbedBuilder client = Client(intents=Intents.MESSAGES | Intents.MESSAGE_CONTENT) @client.event async def on_message_create(message): if message.sender_id == client.user.id: return if message.content == "!hello": # Send a simple message sent = await client.send_message( message.tavern_id, message.channel_id, content="Hello, world!", ) print(f"Sent message ID: {sent.id}") elif message.content == "!reply": # Reply to the message await client.send_message( message.tavern_id, message.channel_id, content="This is a reply!", reply_to_id=message.id, ) elif message.content == "!edit": # Send and then edit a message sent = await client.send_message( message.tavern_id, message.channel_id, content="Original message", ) # Edit the message await client.rest.edit_message( message.tavern_id, sent.id, content="Edited message!", ) elif message.content == "!delete": # Delete a message await client.rest.delete_message( message.tavern_id, message.id, ) elif message.content == "!typing": # Send typing indicator await client.start_typing(message.tavern_id, message.channel_id) # Message properties: # message.id - Unique message ID # message.channel_id - Channel ID # message.tavern_id - Tavern ID # message.content - Message text # message.sender_id - Sender's user ID # message.sender_is_bot - Whether sender is a bot # message.reply_to_id - ID of message being replied to (if any) # message.metadata - Additional message metadata ``` -------------------------------- ### Manage Tavern Permissions Source: https://context7.com/tav-gg/taverns.py/llms.txt Checks granted permissions using the Permissions bitfield class. Permissions are evaluated against the tavern's granted_permissions field. ```python from taverns import Client, Intents, Permissions from taverns.permissions import ( SEND_MESSAGES, MANAGE_MESSAGES, KICK_MEMBERS, BAN_MEMBERS, ADMINISTRATOR, ) client = Client(intents=Intents.MESSAGES) @client.event async def on_message_create(message): if message.content == "!perms": # Get tavern info with granted permissions tavern = client.taverns.get(message.tavern_id) if tavern: perms = Permissions(tavern.granted_permissions) # Check specific permissions can_send = perms.has(SEND_MESSAGES) can_manage = perms.has(MANAGE_MESSAGES) can_kick = perms.has(KICK_MEMBERS) is_admin = perms.has(ADMINISTRATOR) # Get list of all permissions perm_list = perms.to_list() await client.send_message( message.tavern_id, message.channel_id, content=f"Bot permissions:\n" f"- Send Messages: {can_send}\n" f"- Manage Messages: {can_manage}\n" f"- Kick Members: {can_kick}\n" f"- Administrator: {is_admin}\n" f"- All: {', '.join(perm_list)}", ) # Available permissions: # VIEW_CHANNELS, MANAGE_CHANNELS, MANAGE_ROLES, MANAGE_TAVERN, # CREATE_INVITE, KICK_MEMBERS, BAN_MEMBERS, MANAGE_NICKNAMES, # MANAGE_PERMISSIONS, PIN_MESSAGES, SEND_MESSAGES, MANAGE_MESSAGES, # MANAGE_STICKERS, ATTACH_FILES, ADD_REACTIONS, MENTION_EVERYONE, # EMBED_LINKS, READ_MESSAGE_HISTORY, USE_EXTERNAL_EMOJI, # USE_EXTERNAL_STICKERS, MUTE_MEMBERS, BYPASS_SLOWMODE, # CONNECT_VOICE, SPEAK, MANAGE_VOICE, VIDEO, CREATE_EVENTS, # MANAGE_EVENTS, CREATE_POLLS, VOTE_IN_POLLS, ADMINISTRATOR, # CREATE_PUBLIC_THREADS, CREATE_PRIVATE_THREADS, MANAGE_THREADS, # SEND_MESSAGES_IN_THREADS, MANAGE_BOTS ``` -------------------------------- ### Verify Webhook Signature Source: https://github.com/tav-gg/taverns.py/blob/main/README.md Verify the signature of incoming webhook requests to ensure they are from the Tavern platform. Requires the raw request body, signature header, secret, and timestamp. ```python from taverns import verify_webhook_signature is_valid = verify_webhook_signature( raw_body=request.data, signature=request.headers["X-Tavern-Signature"], secret=WEBHOOK_SECRET, timestamp=request.headers.get("X-Tavern-Timestamp"), ) ``` -------------------------------- ### Tavern Data Retrieval Source: https://context7.com/tav-gg/taverns.py/llms.txt Endpoints for retrieving information about taverns, including channels, members, and roles. ```APIDOC ## GET /taverns/{tavern_id}/channels ### Description Retrieves a list of channels within a specific tavern. ### Method GET ### Endpoint /taverns/{tavern_id}/channels ### Parameters #### Path Parameters - **tavern_id** (string) - Required - The unique identifier of the tavern. ## GET /taverns/{tavern_id}/members ### Description Retrieves a list of members currently in the tavern. ### Method GET ### Endpoint /taverns/{tavern_id}/members ### Parameters #### Path Parameters - **tavern_id** (string) - Required - The unique identifier of the tavern. ## GET /taverns/{tavern_id}/roles ### Description Retrieves the roles defined within a tavern. ### Method GET ### Endpoint /taverns/{tavern_id}/roles ### Parameters #### Path Parameters - **tavern_id** (string) - Required - The unique identifier of the tavern. ``` -------------------------------- ### Sending Ephemeral Responses Source: https://github.com/tav-gg/taverns.py/blob/main/README.md Send a reply that is only visible to the user who invoked the command by setting `ephemeral=True`. ```python await interaction.reply("Only you can see this", ephemeral=True) ``` -------------------------------- ### Verify Webhook Requests Source: https://context7.com/tav-gg/taverns.py/llms.txt Uses HMAC-SHA256 to validate incoming HTTP webhook requests. Requires the WEBHOOK_SECRET environment variable to be set. ```python import json import os from flask import Flask, request from taverns import verify_webhook_signature app = Flask(__name__) WEBHOOK_SECRET = os.environ.get("WEBHOOK_SECRET", "") @app.post("/webhook") def handle_webhook(): # Verify signature is_valid = verify_webhook_signature( raw_body=request.data, signature=request.headers.get("X-Tavern-Signature"), secret=WEBHOOK_SECRET, timestamp=request.headers.get("X-Tavern-Timestamp"), max_age=300, # 5 minutes (default) ) if not is_valid: return "Invalid signature", 401 # Parse event payload = json.loads(request.data) event = payload.get("event", "unknown") data = payload.get("data", {}) delivery_id = request.headers.get("X-Tavern-Delivery-Id", "?") print(f"[{delivery_id}] Received {event}") # Handle events if event == "tavern_message": content = data.get("content", "") sender = data.get("senderId", "?") print(f" Message from {sender}: {content}") elif event == "interaction_create": command_name = data.get("commandName", "") user_id = data.get("userId", "") print(f" Interaction: /{command_name} by {user_id}") elif event == "tavern_member_joined": user_id = data.get("userId", "") tavern_id = data.get("tavernId", "") print(f" Member {user_id} joined {tavern_id}") return "OK", 200 if __name__ == "__main__": if not WEBHOOK_SECRET: print("Set WEBHOOK_SECRET environment variable") raise SystemExit(1) app.run(port=8080) # Run: WEBHOOK_SECRET=your_secret flask --app webhook_server run --port 8080 ``` -------------------------------- ### Message Operations Source: https://context7.com/tav-gg/taverns.py/llms.txt Endpoints for managing messages, including retrieval, searching, editing, deleting, and interaction. ```APIDOC ## GET /taverns/{tavern_id}/channels/{channel_id}/messages ### Description Retrieves recent messages from a specific channel. ### Method GET ### Endpoint /taverns/{tavern_id}/channels/{channel_id}/messages ### Parameters #### Query Parameters - **limit** (integer) - Optional - The maximum number of messages to return. ## POST /taverns/{tavern_id}/messages/{message_id}/pin ### Description Pins a specific message in a tavern. ### Method POST ### Endpoint /taverns/{tavern_id}/messages/{message_id}/pin ## POST /taverns/{tavern_id}/messages/{message_id}/reactions ### Description Adds a reaction to a message. ### Method POST ### Endpoint /taverns/{tavern_id}/messages/{message_id}/reactions ## PATCH /taverns/{tavern_id}/messages/{message_id} ### Description Edits the content of an existing message. ### Method PATCH ### Endpoint /taverns/{tavern_id}/messages/{message_id} ### Request Body - **content** (string) - Required - The new message content. ## DELETE /taverns/{tavern_id}/messages/{message_id} ### Description Deletes a message from a tavern. ### Method DELETE ### Endpoint /taverns/{tavern_id}/messages/{message_id} ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.