Improves reactions documentation with examples and events
Expands the reactions guide to include practical examples for adding reactions via message objects, demonstrates low-level client methods, and adds comprehensive event handling examples. Shows how to listen for reaction add/remove events with proper type hints and user filtering to ignore bot reactions.
This commit is contained in:
parent
64576203ae
commit
6bcde9c5b0
@ -1,32 +1,62 @@
|
||||
# Handling Reactions
|
||||
|
||||
`disagreement` provides simple helpers for adding and removing message reactions.
|
||||
`disagreement` provides several ways to add, remove, and listen for message reactions.
|
||||
|
||||
## HTTP Methods
|
||||
## Adding & Removing Reactions
|
||||
|
||||
Use the `HTTPClient` methods directly if you need lower level control:
|
||||
The easiest way to add a reaction is to use the helper method on a `Message` object. This is often done within a command context.
|
||||
|
||||
```python
|
||||
await client._http.create_reaction(channel_id, message_id, "👍")
|
||||
await client._http.delete_reaction(channel_id, message_id, "👍")
|
||||
users = await client._http.get_reactions(channel_id, message_id, "👍")
|
||||
# Inside a command function:
|
||||
# ctx is a commands.CommandContext object
|
||||
await ctx.message.add_reaction("👍")
|
||||
```
|
||||
|
||||
You can also use the higher level helpers on :class:`Client`:
|
||||
You can also remove your own reactions.
|
||||
|
||||
```python
|
||||
await ctx.message.remove_reaction("👍", client.user)
|
||||
```
|
||||
|
||||
## Low-Level Control
|
||||
|
||||
For more direct control, you can use methods on the `Client` or `HTTPClient` if you have the channel and message IDs.
|
||||
|
||||
```python
|
||||
# Using the client helper
|
||||
await client.create_reaction(channel_id, message_id, "👍")
|
||||
await client.delete_reaction(channel_id, message_id, "👍")
|
||||
|
||||
# Using the raw HTTP method
|
||||
await client._http.create_reaction(channel_id, message_id, "👍")
|
||||
```
|
||||
|
||||
Similarly, you can delete reactions and get a list of users who reacted.
|
||||
|
||||
```python
|
||||
# Delete a specific user's reaction
|
||||
await client.delete_reaction(channel_id, message_id, "👍", user_id)
|
||||
|
||||
# Get users who reacted with an emoji
|
||||
users = await client.get_reactions(channel_id, message_id, "👍")
|
||||
```
|
||||
|
||||
## Reaction Events
|
||||
|
||||
Register listeners for `MESSAGE_REACTION_ADD` and `MESSAGE_REACTION_REMOVE`.
|
||||
Each listener receives a `Reaction` model instance.
|
||||
Your bot can listen for reaction events by using the `@client.on_event` decorator. The two main events are `MESSAGE_REACTION_ADD` and `MESSAGE_REACTION_REMOVE`.
|
||||
|
||||
The event handlers for these events receive both a `Reaction` object and the `User` or `Member` who triggered the event.
|
||||
|
||||
```python
|
||||
import disagreement
|
||||
from disagreement.models import Reaction, User, Member
|
||||
|
||||
@client.on_event("MESSAGE_REACTION_ADD")
|
||||
async def on_reaction(reaction: disagreement.Reaction):
|
||||
print(f"{reaction.user_id} reacted with {reaction.emoji}")
|
||||
```
|
||||
async def on_reaction_add(reaction: Reaction, user: User | Member):
|
||||
# Ignore reactions from the bot itself
|
||||
if client.user and user.id == client.user.id:
|
||||
return
|
||||
print(f"{user.username} reacted to message {reaction.message_id} with {reaction.emoji}")
|
||||
|
||||
@client.on_event("MESSAGE_REACTION_REMOVE")
|
||||
async def on_reaction_remove(reaction: Reaction, user: User | Member):
|
||||
print(f"{user.username} removed their {reaction.emoji} reaction from message {reaction.message_id}")
|
||||
|
144
examples/reactions.py
Normal file
144
examples/reactions.py
Normal file
@ -0,0 +1,144 @@
|
||||
# examples/reactions.py
|
||||
|
||||
"""
|
||||
An example bot demonstrating reaction handling with the Disagreement library.
|
||||
|
||||
This bot will:
|
||||
1. React to a specific command with a thumbs-up emoji.
|
||||
2. Log when any reaction is added to a message in a server it's in.
|
||||
3. Log when any reaction is removed from a message.
|
||||
|
||||
To run this bot:
|
||||
1. Follow the setup steps in 'basic_bot.py' to set your DISCORD_BOT_TOKEN.
|
||||
2. Ensure you have the GUILD_MESSAGE_REACTIONS intent enabled for your bot in the Discord Developer Portal.
|
||||
3. Run this script: python examples/reactions.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
# Add project root to path for local development
|
||||
if os.path.join(os.getcwd(), "examples") == os.path.dirname(os.path.abspath(__file__)):
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
||||
|
||||
try:
|
||||
import disagreement
|
||||
from disagreement.ext import commands
|
||||
from disagreement.models import Reaction, User, Member
|
||||
except ImportError:
|
||||
print(
|
||||
"Failed to import disagreement. Make sure it's installed or PYTHONPATH is set correctly."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
from dotenv import load_dotenv
|
||||
except ImportError: # pragma: no cover - example helper
|
||||
load_dotenv = None
|
||||
print("python-dotenv is not installed. Environment variables will not be loaded")
|
||||
|
||||
if load_dotenv:
|
||||
load_dotenv()
|
||||
|
||||
# --- Bot Configuration ---
|
||||
BOT_TOKEN = os.environ.get("DISCORD_BOT_TOKEN")
|
||||
|
||||
# --- Intents Configuration ---
|
||||
# We need GUILDS for server context, GUILD_MESSAGES to receive messages,
|
||||
# and GUILD_MESSAGE_REACTIONS to listen for reaction events.
|
||||
intents = (
|
||||
disagreement.GatewayIntent.GUILDS
|
||||
| disagreement.GatewayIntent.GUILD_MESSAGES
|
||||
| disagreement.GatewayIntent.GUILD_MESSAGE_REACTIONS
|
||||
| disagreement.GatewayIntent.MESSAGE_CONTENT # For commands
|
||||
)
|
||||
|
||||
# --- Initialize the Client ---
|
||||
if not BOT_TOKEN:
|
||||
print("Error: The DISCORD_BOT_TOKEN environment variable is not set.")
|
||||
sys.exit(1)
|
||||
|
||||
client = disagreement.Client(token=BOT_TOKEN, intents=intents, command_prefix="!")
|
||||
|
||||
|
||||
# --- Define a Cog for reaction-related commands ---
|
||||
class ReactionCog(commands.Cog):
|
||||
def __init__(self, bot_client):
|
||||
super().__init__(bot_client)
|
||||
|
||||
@commands.command(name="react")
|
||||
async def react_command(self, ctx: commands.CommandContext):
|
||||
"""Reacts to the command message with a thumbs up."""
|
||||
try:
|
||||
# The emoji can be a standard Unicode emoji or a custom one in the format '<:name:id>'
|
||||
await ctx.message.add_reaction("👍")
|
||||
print(f"Reacted to command from {ctx.author.username}")
|
||||
except disagreement.HTTPException as e:
|
||||
print(f"Failed to add reaction: {e}")
|
||||
await ctx.reply("I couldn't add the reaction. I might be missing permissions.")
|
||||
|
||||
|
||||
# --- Event Handlers ---
|
||||
|
||||
@client.event
|
||||
async def on_ready():
|
||||
"""Called when the bot is ready and connected to Discord."""
|
||||
if client.user:
|
||||
print(f"Bot is ready! Logged in as {client.user.username}")
|
||||
else:
|
||||
print("Bot is ready, but client.user is missing!")
|
||||
print("------")
|
||||
print("Reaction example bot is operational.")
|
||||
|
||||
|
||||
@client.on_event("MESSAGE_REACTION_ADD")
|
||||
async def on_reaction_add(reaction: Reaction, user: User | Member):
|
||||
"""Called when a message reaction is added."""
|
||||
# We can ignore reactions from the bot itself
|
||||
if client.user and user.id == client.user.id:
|
||||
return
|
||||
|
||||
print(
|
||||
f"Reaction '{reaction.emoji}' added by {user.username} "
|
||||
f"to message ID {reaction.message_id} in channel ID {reaction.channel_id}"
|
||||
)
|
||||
# You can fetch the message if you need its content, but it's an extra API call.
|
||||
# try:
|
||||
# channel = await client.fetch_channel(reaction.channel_id)
|
||||
# if isinstance(channel, disagreement.TextChannel):
|
||||
# message = await channel.fetch_message(reaction.message_id)
|
||||
# print(f" Message content: '{message.content}'")
|
||||
# except disagreement.errors.NotFound:
|
||||
# print(" Could not fetch message (maybe it was deleted).")
|
||||
|
||||
@client.on_event("MESSAGE_REACTION_REMOVE")
|
||||
async def on_reaction_remove(reaction: Reaction, user: User | Member):
|
||||
"""Called when a message reaction is removed."""
|
||||
print(
|
||||
f"Reaction '{reaction.emoji}' removed by {user.username} "
|
||||
f"from message ID {reaction.message_id} in channel ID {reaction.channel_id}"
|
||||
)
|
||||
|
||||
|
||||
# --- Main Execution ---
|
||||
async def main():
|
||||
print("Starting Reaction Bot...")
|
||||
try:
|
||||
client.add_cog(ReactionCog(client))
|
||||
await client.run()
|
||||
except disagreement.AuthenticationError:
|
||||
print("Authentication failed. Check your bot token.")
|
||||
except Exception as e:
|
||||
print(f"An unexpected error occurred: {e}")
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
if not client.is_closed():
|
||||
await client.close()
|
||||
print("Bot has been shut down.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
Loading…
x
Reference in New Issue
Block a user