📝 Add examples

This commit is contained in:
2025-03-08 20:42:14 +01:00
parent bfcb7cc33d
commit 7dde4fd16f
5 changed files with 217 additions and 6 deletions

View File

@@ -14,7 +14,7 @@ This project is built on:
## Installation
```bash
pip install pycord-rest-bot
pip install pycord-reactive-bot
```
## Quick Start
@@ -40,6 +40,25 @@ if __name__ == "__main__":
)
```
For more examples, check out the [examples directory](/examples) which includes:
- Basic slash command setup
- Button interactions
- Modal forms
- Production deployment configurations
## How It Works
Under the hood, Pycord REST creates an HTTP server using FastAPI and Uvicorn that:
1. Listens for incoming Discord interaction requests on your specified endpoint
2. Verifies the request signature using your application's public key
3. Routes the interaction to the appropriate command handler
4. Returns the response back to Discord
Unlike traditional Discord bots that maintain a persistent WebSocket connection to Discord's gateway, HTTP-based bots:
- Only wake up when an interaction is received
- Don't receive real-time events from Discord
## Usage
### Setting up your bot on Discord
@@ -94,15 +113,25 @@ app.run(
token="YOUR_BOT_TOKEN",
public_key="YOUR_PUBLIC_KEY",
uvicorn_options={
"host": "0.0.0.0",
"port": 8000,
"log_level": "info",
"host": "0.0.0.0", # Listen on all network interfaces
"port": 8000, # Port to listen on
"log_level": "info", # Uvicorn logging level
# Any valid uvicorn server options
},
health=True # Enable /health endpoint
)
```
### Server Configuration
For Discord to reach your bot, you need a publicly accessible HTTPS URL. Options include:
- Using a VPS with a domain and SSL certificate
- Deploying to a cloud service like Heroku, Railway, or Fly.io
### Health Check
By default, Pycord REST includes a `/health` endpoint that returns a 200 status code. This endpoint is useful for monitoring services like UptimeRobot or health checks.
## Advanced Usage
### Adding Custom FastAPI Routes
@@ -115,6 +144,24 @@ async def custom_endpoint(request: Request):
return {"message": "This is a custom endpoint"}
```
## Development Workflow
For faster development and testing, you can use tunneling tools to expose your local development server:
- **ngrok** - Creates a secure tunnel to your localhost
```bash
# Install ngrok
npm install -g ngrok
# Expose your local server
ngrok http 8000
```
- **Cloudflare Tunnel** - Provides a secure connection to your local server
- **localtunnel** - Simple tunnel service for exposing local endpoints
These tools provide temporary URLs that you can use in the Discord Developer Portal during development, allowing you to test changes quickly without deploying to production.
## Contributing
Contributions are welcome! This project is in early development, so there might be bugs or unexpected behaviors.

46
examples/basic_bot.py Normal file
View File

@@ -0,0 +1,46 @@
"""Basic Discord bot example using Pycord REST.
This is a minimal example showing how to create slash commands.
"""
import os
from pydoc import describe
import discord
from dotenv import load_dotenv
from pycord_rest import App
# Load environment variables from .env file
load_dotenv()
app = App()
# Simple ping command
@app.slash_command(name="ping", description="Responds with pong!")
async def ping(ctx: discord.ApplicationContext) -> None:
await ctx.respond("Pong!")
# Command with parameters
@app.slash_command(name="greet", description="Greets a user")
@discord.option("name", input_type=str, description="The name of the user to greet", required=False)
async def greet(ctx: discord.ApplicationContext, name: str | None = None) -> None:
if name:
await ctx.respond(f"Hello, {name}!")
else:
await ctx.respond(f"Hello, {ctx.author.display_name}!")
# Run the app
if __name__ == "__main__":
app.run(
token=os.environ["DISCORD_TOKEN"],
public_key=os.environ["DISCORD_PUBLIC_KEY"],
uvicorn_options={
"host": "0.0.0.0", # noqa: S104
"port": 8000,
"log_level": "info",
},
)

View File

@@ -0,0 +1,52 @@
"""Example demonstrating how to use buttons with Pycord REST."""
import os
from typing import Any
import discord
from dotenv import load_dotenv
from pycord_rest import App
# Load environment variables from .env file
load_dotenv()
app = App()
class MyView(discord.ui.View):
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(*args, **kwargs)
self.add_item(
discord.ui.Button(
style=discord.ButtonStyle.link, label="GitHub", url="https://github.com/Paillat-dev/pycord-rest"
)
)
@discord.ui.button(label="Green", style=discord.ButtonStyle.success)
async def green_button(self, button: "discord.ui.Button[MyView]", interaction: discord.Interaction) -> None:
await interaction.respond("You clicked the green button!", ephemeral=True)
@discord.ui.button(label="Red", style=discord.ButtonStyle.danger)
async def red_button(self, button: "discord.ui.Button[MyView]", interaction: discord.Interaction) -> None:
await interaction.respond("You clicked the red button!", ephemeral=True)
# Create a slash command that shows buttons
@app.slash_command(name="buttons", description="Shows interactive buttons")
async def buttons(ctx: discord.ApplicationContext) -> None:
# Create a view with buttons
view = MyView()
await ctx.respond("Choose a button:", view=view)
if __name__ == "__main__":
app.run(
token=os.environ["DISCORD_TOKEN"],
public_key=os.environ["DISCORD_PUBLIC_KEY"],
uvicorn_options={
"host": "0.0.0.0", # noqa: S104
"port": 8000,
"log_level": "info",
},
)

60
examples/modal_example.py Normal file
View File

@@ -0,0 +1,60 @@
"""Example showing how to work with modals in Pycord REST."""
import asyncio
import os
from typing import Any
import discord
from dotenv import load_dotenv
from pycord_rest import App
# Load environment variables from .env file
load_dotenv()
app = App()
class MyModal(discord.ui.Modal):
def __init__(self, *args: Any, **kwargs: Any) -> None:
super().__init__(
discord.ui.InputText(
label="Name", placeholder="Enter your name", style=discord.InputTextStyle.short, custom_id="name_input"
),
discord.ui.InputText(
label="Feedback",
placeholder="Please provide your feedback here...",
style=discord.InputTextStyle.paragraph,
custom_id="feedback_input",
),
*args,
**kwargs,
)
async def callback(self, interaction: discord.Interaction) -> None:
name = self.children[0].value
await interaction.respond(
f"Thank you for your feedback, {name}! Your submission has been received.", ephemeral=True
)
# Command that shows a form modal
@app.slash_command(name="feedback", description="Submit feedback through a form")
async def feedback(ctx: discord.ApplicationContext) -> None:
# Create a modal
modal = MyModal(title="Feedback Form")
await ctx.send_modal(modal)
await ctx.respond("Opening feedback form...", ephemeral=True)
if __name__ == "__main__":
app.run(
token=os.environ["DISCORD_TOKEN"],
public_key=os.environ["DISCORD_PUBLIC_KEY"],
uvicorn_options={
"host": "0.0.0.0", # noqa: S104
"port": 8000,
"log_level": "info",
},
)

View File

@@ -13,7 +13,6 @@ classifiers = [
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12"
]
keywords = ["discord", "bot", "rest", "pycord"]
@@ -50,6 +49,7 @@ source_archive = "https://github.com/Paillat-dev/pycord-rest/archive/{commit_has
name = "pycord-rest-bot"
[tool.hatch.build]
packages = ["src/pycord_rest"]
exclude = [
".copywrite.hcl",
".github",
@@ -62,8 +62,13 @@ include = [
[tool.pyright]
pythonVersion = "3.12"
typeCheckingMode = "all"
reportUnusedCallResult = false
reportAny = false
executionEnvironments = [{ root = "src/pycord_rest/_version.py", reportDeprecated = false }]
executionEnvironments = [
{ root = "src/pycord_rest/_version.py", reportDeprecated = false },
{ root = "examples", reportExplicitAny = false, reportUnknownMemberType = false, reportUnusedParameter = false, reportImplicitOverride = false }
]
[tool.ruff]
target-version = "py312"
@@ -83,6 +88,7 @@ exclude = [
[tool.ruff.lint]
select = ["ALL"]
per-file-ignores = { "examples/**/*" = ["INP001", "ARG002"] }
extend-ignore = [
"N999",
"D104",