mirror of
https://github.com/Paillat-dev/pycord-rest.git
synced 2026-01-02 00:56:19 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 08bf3e9521 | |||
| f6e7567d27 | |||
| 06885ace8c | |||
| b840984780 | |||
| 7874951a18 | |||
| 1ae66d05ee |
23
README.md
23
README.md
@@ -1,8 +1,18 @@
|
||||
# Pycord REST
|
||||
<div align="center">
|
||||
<h1>Pycord REST</h1>
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
[](https://results.pre-commit.ci/latest/github/Paillat-dev/pycord-rest/main)
|
||||
|
||||
A lightweight wrapper for Discord's HTTP interactions and webhook events using py-cord
|
||||
and FastAPI.
|
||||
|
||||
</div>
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
@@ -14,6 +24,7 @@ and FastAPI.
|
||||
- [Features](#features)
|
||||
- [Interaction Handling](#interaction-handling)
|
||||
- [Webhook Events](#webhook-events)
|
||||
- [Type Safety](#type-safety)
|
||||
- [Usage Examples](#usage-examples)
|
||||
- [Basic Commands](#basic-commands)
|
||||
- [Event Handling](#event-handling)
|
||||
@@ -124,6 +135,15 @@ Handle Discord webhook events such as:
|
||||
user
|
||||
- **Entitlement creation** - When a user subscribes to your app's premium features
|
||||
|
||||
### Type Safety
|
||||
|
||||
Pycord REST is fully type-annotated and type-safe. It uses `basedpyright` for type
|
||||
checking.
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
> [!NOTE]
|
||||
> While Pycord REST itself is fully typed, the underlying py-cord library has limited type annotations, which may affect type checking in some areas.
|
||||
|
||||
## Usage Examples
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
@@ -243,6 +263,7 @@ These tools provide temporary URLs for testing without deploying to production.
|
||||
|
||||
**Development Tools**:
|
||||
|
||||
- **uv**: For dependency management
|
||||
- **Ruff**: For linting and formatting
|
||||
- **HashiCorp Copywrite**: For managing license headers
|
||||
- **basedpyright**: For type checking
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
# Copyright (c) Paillat-dev
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
"""Basic Discord bot example using Pycord REST.
|
||||
"""Example showing how to work with webhook events in Pycord REST."""
|
||||
|
||||
This is a minimal example showing how to create slash commands.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from dotenv import load_dotenv
|
||||
@@ -16,10 +12,6 @@ from pycord_rest import App, ApplicationAuthorizedEvent
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
|
||||
app = App()
|
||||
|
||||
|
||||
@@ -31,7 +23,7 @@ async def on_application_authorized(event: ApplicationAuthorizedEvent) -> None:
|
||||
|
||||
else:
|
||||
print(
|
||||
f"Bot {event.user.display_name} ({event.user.id}) installed the application" # noqa: ISC003
|
||||
f"Bot {event.user.display_name} ({event.user.id}) installed the application"
|
||||
+ f" to guild {event.guild.name} ({event.guild.id})."
|
||||
)
|
||||
|
||||
|
||||
@@ -17,7 +17,9 @@ classifiers = [
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Python :: 3 :: Only",
|
||||
"Programming Language :: Python :: 3.12"
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Typing :: Typed",
|
||||
"Operating System :: OS Independent",
|
||||
]
|
||||
keywords = ["discord", "bot", "rest", "pycord"]
|
||||
dependencies = [
|
||||
@@ -116,5 +118,6 @@ extend-ignore = [
|
||||
"FBT002",
|
||||
"PLR2004",
|
||||
"PLR0913",
|
||||
"C901"
|
||||
"C901",
|
||||
"ISC003" # conflicts with basedpyright reportImplicitStringConcatenation
|
||||
]
|
||||
|
||||
@@ -11,7 +11,6 @@ import discord
|
||||
import uvicorn
|
||||
from discord import Entitlement, Interaction, InteractionType
|
||||
from fastapi import APIRouter, Depends, FastAPI, HTTPException, Request, Response
|
||||
from fastapi.exceptions import FastAPIError
|
||||
from nacl.exceptions import BadSignatureError
|
||||
from nacl.signing import VerifyKey
|
||||
|
||||
@@ -35,18 +34,26 @@ class ApplicationAuthorizedEvent:
|
||||
)
|
||||
|
||||
|
||||
class PycordRestError(discord.DiscordException):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidCredentialsError(PycordRestError):
|
||||
pass
|
||||
|
||||
|
||||
class App(discord.Bot):
|
||||
def __init__(self, *args: Any, **options: Any) -> None: # pyright: ignore [reportExplicitAny]
|
||||
super().__init__(*args, **options) # pyright: ignore [reportUnknownMemberType]
|
||||
self.app: FastAPI = FastAPI(openapi_url=None, docs_url=None, redoc_url=None)
|
||||
self._app: FastAPI = FastAPI(openapi_url=None, docs_url=None, redoc_url=None)
|
||||
self.router: APIRouter = APIRouter()
|
||||
self.public_key: str | None = None
|
||||
self._public_key: str | None = None
|
||||
|
||||
@cached_property
|
||||
def _verify_key(self) -> VerifyKey:
|
||||
if self.public_key is None:
|
||||
raise FastAPIError("No public key provided")
|
||||
return VerifyKey(bytes.fromhex(self.public_key))
|
||||
if self._public_key is None:
|
||||
raise InvalidCredentialsError("No public key provided")
|
||||
return VerifyKey(bytes.fromhex(self._public_key))
|
||||
|
||||
async def _dispatch_view(self, component_type: int, custom_id: str, interaction: Interaction) -> None:
|
||||
# Code taken from ViewStore.dispatch
|
||||
@@ -110,6 +117,7 @@ class App(discord.Bot):
|
||||
async def process_application_commands( # noqa: PLR0912
|
||||
self, interaction: Interaction, auto_sync: bool | None = None
|
||||
) -> None:
|
||||
# Code taken from super().process_application_commands
|
||||
if auto_sync is None:
|
||||
auto_sync = self._bot.auto_sync_commands # pyright: ignore [reportUnknownVariableType, reportUnknownMemberType]
|
||||
# TODO: find out why the isinstance check below doesn't stop the type errors below # noqa: FIX002, TD002, TD003
|
||||
@@ -231,15 +239,15 @@ class App(discord.Bot):
|
||||
uvicorn_options: dict[str, Any] | None = None, # pyright: ignore [reportExplicitAny]
|
||||
health: bool = True,
|
||||
) -> None:
|
||||
self.public_key = public_key
|
||||
self._public_key = public_key
|
||||
_ = self._process_interaction_factory()
|
||||
_ = self._webhook_event_factory()
|
||||
if health:
|
||||
_ = self._health_factory()
|
||||
self.app.include_router(self.router)
|
||||
self._app.include_router(self.router)
|
||||
uvicorn_options = uvicorn_options or {}
|
||||
uvicorn_options["log_level"] = uvicorn_options.get("log_level", logging.root.level)
|
||||
config = uvicorn.Config(self.app, **uvicorn_options)
|
||||
config = uvicorn.Config(self._app, **uvicorn_options)
|
||||
server = uvicorn.Server(config)
|
||||
try:
|
||||
self.dispatch("connect")
|
||||
@@ -260,6 +268,10 @@ class App(discord.Bot):
|
||||
uvicorn_options: dict[str, Any] | None = None, # pyright: ignore [reportExplicitAny]
|
||||
health: bool = True,
|
||||
) -> None:
|
||||
if not token:
|
||||
raise InvalidCredentialsError("No token provided")
|
||||
if not public_key:
|
||||
raise InvalidCredentialsError("No public key provided")
|
||||
await self.login(token)
|
||||
await self.connect(
|
||||
token=token,
|
||||
|
||||
Reference in New Issue
Block a user