mirror of
https://github.com/Paillat-dev/pycord-rest.git
synced 2026-01-02 17:14:56 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 124ce383bb | |||
| 5827a2e98a | |||
| 5e84515c03 | |||
| 190dce6f5d | |||
| 7a98827a23 | |||
| fb9e506d15 | |||
| ad41014c94 | |||
| cd444d51d1 | |||
| a94ffb6729 | |||
| 353ae04dac | |||
| 4fe7cb47a7 |
41
README.md
41
README.md
@@ -1,18 +1,28 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
<h1>Pycord REST</h1>
|
<h1>Pycord REST</h1>
|
||||||
|
|
||||||

|
<!-- badges -->
|
||||||

|
|
||||||

|
[](https://pypi.org/project/pycord-rest-bot/)
|
||||||

|
[](https://pypi.org/project/pycord-rest-bot/)
|
||||||

|
[](https://pypi.org/project/pycord-rest-bot/)
|
||||||
|
[](https://pypi.org/project/pycord-rest-bot/)
|
||||||
|
[](https://github.com/Paillat-dev/pycord-rest/actions/workflows/CI.yaml)
|
||||||
[](https://results.pre-commit.ci/latest/github/Paillat-dev/pycord-rest/main)
|
[](https://results.pre-commit.ci/latest/github/Paillat-dev/pycord-rest/main)
|
||||||
|
|
||||||
|
<!-- end badges -->
|
||||||
|
|
||||||
|
<!-- short description -->
|
||||||
|
|
||||||
A lightweight wrapper for Discord's HTTP interactions and webhook events using py-cord
|
A lightweight wrapper for Discord's HTTP interactions and webhook events using py-cord
|
||||||
and FastAPI.
|
and FastAPI.
|
||||||
|
|
||||||
|
<!-- end short description -->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- toc -->
|
||||||
|
|
||||||
## Table of Contents
|
## Table of Contents
|
||||||
|
|
||||||
- [Overview](#overview)
|
- [Overview](#overview)
|
||||||
@@ -31,6 +41,7 @@ and FastAPI.
|
|||||||
- [Custom Routes](#custom-routes)
|
- [Custom Routes](#custom-routes)
|
||||||
- [Configuration](#configuration)
|
- [Configuration](#configuration)
|
||||||
- [Limitations](#limitations)
|
- [Limitations](#limitations)
|
||||||
|
- [Getting Help](#getting-help)
|
||||||
- [Development](#development)
|
- [Development](#development)
|
||||||
- [Local Testing](#local-testing)
|
- [Local Testing](#local-testing)
|
||||||
- [Contributing](#contributing)
|
- [Contributing](#contributing)
|
||||||
@@ -59,6 +70,8 @@ pip install pycord-rest-bot --prerelease=allow
|
|||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> The package is currently in pre-release.
|
> The package is currently in pre-release.
|
||||||
|
|
||||||
|
<!-- quick-start -->
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -104,7 +117,7 @@ Unlike traditional WebSocket-based Discord bots, HTTP-based applications:
|
|||||||
1. Create an application on the
|
1. Create an application on the
|
||||||
[Discord Developer Portal](https://discord.com/developers/applications)
|
[Discord Developer Portal](https://discord.com/developers/applications)
|
||||||
2. Copy your public key to verify signatures
|
2. Copy your public key to verify signatures
|
||||||
3. Run your FastAPI server
|
3. Run the Pycord REST app
|
||||||
4. Configure the endpoints:
|
4. Configure the endpoints:
|
||||||
|
|
||||||
- **Interactions Endpoint URL** - For slash commands and component interactions
|
- **Interactions Endpoint URL** - For slash commands and component interactions
|
||||||
@@ -232,6 +245,22 @@ Since Pycord REST doesn't use Discord's WebSocket gateway:
|
|||||||
- Member tracking
|
- Member tracking
|
||||||
- **Limited Events** - Only interaction-based and webhook events work
|
- **Limited Events** - Only interaction-based and webhook events work
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
If you encounter issues or have questions about pycord-rest:
|
||||||
|
|
||||||
|
- **GitHub Issues**:
|
||||||
|
[Submit a bug report or feature request](https://github.com/Paillat-dev/pycord-rest/issues)
|
||||||
|
- **Discord Support**:
|
||||||
|
- For py-cord related questions: Join the
|
||||||
|
[Pycord Official Server](https://discord.gg/pycord)
|
||||||
|
- For pycord-rest specific help: Join the
|
||||||
|
[Pycord Official Server](https://discord.gg/pycord) and mention `@paillat`
|
||||||
|
|
||||||
|
<!-- prettier-ignore -->
|
||||||
|
> [!TIP]
|
||||||
|
> Before asking for help, check if your question is already answered in the [examples directory](/examples) or existing GitHub issues.
|
||||||
|
|
||||||
## Development
|
## Development
|
||||||
|
|
||||||
### Local Testing
|
### Local Testing
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
[build-system]
|
[build-system]
|
||||||
requires = ["hatchling", "hatch-vcs"]
|
requires = ["hatchling", "hatch-vcs", "hatch-fancy-pypi-readme"]
|
||||||
build-backend = "hatchling.build"
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "pycord-rest-bot"
|
name = "pycord-rest-bot"
|
||||||
dynamic = ["version", "urls"]
|
dynamic = ["version", "urls", "readme"]
|
||||||
description = "A discord rest-bot wrapper for pycord"
|
description = "A lightweight wrapper for Discord's HTTP interactions and webhook events using py-cord and FastAPI"
|
||||||
readme = "README.md"
|
|
||||||
authors = [
|
authors = [
|
||||||
{ name = "Paillat-dev", email = "me@paillat.dev" }
|
{ name = "Paillat-dev", email = "me@paillat.dev" }
|
||||||
]
|
]
|
||||||
@@ -47,6 +46,34 @@ version-file = "src/pycord_rest/_version.py"
|
|||||||
Homepage = "https://github.com/Paillat-dev/pycord-rest"
|
Homepage = "https://github.com/Paillat-dev/pycord-rest"
|
||||||
source_archive = "https://github.com/Paillat-dev/pycord-rest/archive/{commit_hash}.zip"
|
source_archive = "https://github.com/Paillat-dev/pycord-rest/archive/{commit_hash}.zip"
|
||||||
|
|
||||||
|
[tool.hatch.metadata.hooks.fancy-pypi-readme]
|
||||||
|
content-type = "text/markdown"
|
||||||
|
|
||||||
|
[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]]
|
||||||
|
path = "README.md"
|
||||||
|
start-after = "<!-- badges -->\n"
|
||||||
|
end-before = "\n<!-- end badges -->"
|
||||||
|
|
||||||
|
[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]]
|
||||||
|
text = "\n\n---\n"
|
||||||
|
|
||||||
|
[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]]
|
||||||
|
path = "README.md"
|
||||||
|
start-after = "## Overview\n"
|
||||||
|
end-before = "\n## Installation"
|
||||||
|
|
||||||
|
[[tool.hatch.metadata.hooks.fancy-pypi-readme.fragments]]
|
||||||
|
path = "README.md"
|
||||||
|
start-after = "<!-- quick-start -->"
|
||||||
|
|
||||||
|
[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
|
||||||
|
pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)'
|
||||||
|
replacement = '[\1](https://github.com/Paillat-dev/pycord-rest/tree/main\g<2>)'
|
||||||
|
|
||||||
|
[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
|
||||||
|
pattern = '\[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]'
|
||||||
|
replacement = '**\1**:'
|
||||||
|
|
||||||
[tool.hatchling]
|
[tool.hatchling]
|
||||||
name = "pycord-rest-bot"
|
name = "pycord-rest-bot"
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
# Copyright (c) Paillat-dev
|
# Copyright (c) Paillat-dev
|
||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import functools
|
||||||
import logging
|
import logging
|
||||||
|
import warnings
|
||||||
from collections.abc import Callable, Coroutine
|
from collections.abc import Callable, Coroutine
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from typing import Any, Never, override
|
from typing import Any, Never, override
|
||||||
@@ -14,6 +16,7 @@ from fastapi import APIRouter, Depends, FastAPI, HTTPException, Request, Respons
|
|||||||
from nacl.exceptions import BadSignatureError
|
from nacl.exceptions import BadSignatureError
|
||||||
from nacl.signing import VerifyKey
|
from nacl.signing import VerifyKey
|
||||||
|
|
||||||
|
from .errors import InvalidCredentialsError
|
||||||
from .models import EventType, WebhookEventPayload, WebhookType
|
from .models import EventType, WebhookEventPayload, WebhookType
|
||||||
|
|
||||||
logger = logging.getLogger("pycord.rest")
|
logger = logging.getLogger("pycord.rest")
|
||||||
@@ -34,21 +37,38 @@ class ApplicationAuthorizedEvent:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PycordRestError(discord.DiscordException):
|
def not_supported[T, U](func: Callable[[T], U]) -> Callable[[T], U]:
|
||||||
pass
|
@functools.wraps(func)
|
||||||
|
def inner(*args: T, **kwargs: T) -> U:
|
||||||
|
logger.warning(f"{func.__qualname__} is not supported by REST apps.")
|
||||||
|
warnings.warn(
|
||||||
|
f"{func.__qualname__} is not supported by REST apps.",
|
||||||
|
SyntaxWarning,
|
||||||
|
stacklevel=2,
|
||||||
|
)
|
||||||
|
return func(*args, **kwargs)
|
||||||
|
|
||||||
|
return inner
|
||||||
class InvalidCredentialsError(PycordRestError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class App(discord.Bot):
|
class App(discord.Bot):
|
||||||
def __init__(self, *args: Any, **options: Any) -> None: # pyright: ignore [reportExplicitAny]
|
_UvicornConfig: type[uvicorn.Config] = uvicorn.Config
|
||||||
|
_UvicornServer: type[uvicorn.Server] = uvicorn.Server
|
||||||
|
_FastAPI: type[FastAPI] = FastAPI
|
||||||
|
_APIRouter: type[APIRouter] = APIRouter
|
||||||
|
|
||||||
|
def __init__(self, *args: Any, path_prefix: str = "", **options: Any) -> None: # pyright: ignore [reportExplicitAny]
|
||||||
super().__init__(*args, **options) # pyright: ignore [reportUnknownMemberType]
|
super().__init__(*args, **options) # pyright: ignore [reportUnknownMemberType]
|
||||||
self._app: FastAPI = FastAPI(openapi_url=None, docs_url=None, redoc_url=None)
|
self._app: FastAPI = self._FastAPI(openapi_url=None, docs_url=None, redoc_url=None)
|
||||||
self.router: APIRouter = APIRouter()
|
self.router: APIRouter = self._APIRouter(prefix=path_prefix)
|
||||||
self._public_key: str | None = None
|
self._public_key: str | None = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
@override
|
||||||
|
@not_supported
|
||||||
|
def latency(self) -> float:
|
||||||
|
return 0.0
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def _verify_key(self) -> VerifyKey:
|
def _verify_key(self) -> VerifyKey:
|
||||||
if self._public_key is None:
|
if self._public_key is None:
|
||||||
@@ -247,8 +267,9 @@ class App(discord.Bot):
|
|||||||
self._app.include_router(self.router)
|
self._app.include_router(self.router)
|
||||||
uvicorn_options = uvicorn_options or {}
|
uvicorn_options = uvicorn_options or {}
|
||||||
uvicorn_options["log_level"] = uvicorn_options.get("log_level", logging.root.level)
|
uvicorn_options["log_level"] = uvicorn_options.get("log_level", logging.root.level)
|
||||||
config = uvicorn.Config(self._app, **uvicorn_options)
|
uvicorn_options["server_header"] = uvicorn_options.get("server_header", False)
|
||||||
server = uvicorn.Server(config)
|
config = self._UvicornConfig(self._app, **uvicorn_options)
|
||||||
|
server = self._UvicornServer(config)
|
||||||
try:
|
try:
|
||||||
self.dispatch("connect")
|
self.dispatch("connect")
|
||||||
await server.serve()
|
await server.serve()
|
||||||
@@ -258,7 +279,10 @@ class App(discord.Bot):
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
async def close(self) -> None:
|
async def close(self) -> None:
|
||||||
pass
|
self._closed: bool = True
|
||||||
|
|
||||||
|
await self.http.close()
|
||||||
|
self._ready.clear()
|
||||||
|
|
||||||
@override
|
@override
|
||||||
async def start( # pyright: ignore [reportIncompatibleMethodOverride]
|
async def start( # pyright: ignore [reportIncompatibleMethodOverride]
|
||||||
|
|||||||
12
src/pycord_rest/errors.py
Normal file
12
src/pycord_rest/errors.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Copyright (c) Paillat-dev
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
import discord
|
||||||
|
|
||||||
|
|
||||||
|
class PycordRestError(discord.DiscordException):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class InvalidCredentialsError(PycordRestError):
|
||||||
|
pass
|
||||||
Reference in New Issue
Block a user