Compare commits
2
Commits
f43ce2d8f1
...
v1.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e97d7f1ff | ||
|
|
c76f5286d9 |
No files matched your search
@@ -8,7 +8,7 @@ each time (wasteful, rate-limited, and it leaks toward the cap).
|
|||||||
## Install
|
## Install
|
||||||
|
|
||||||
```
|
```
|
||||||
dpy_webhooks @ git+ssh://git@git.rethinkstudios.io/rethink-public/dpy_webhooks.git@v1.0.0
|
dpy_webhooks @ git+ssh://git@git.rethinkstudios.io/rethink-public/dpy_webhooks.git@v1.1.0
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|||||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "dpy_webhooks"
|
name = "dpy_webhooks"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
description = "Per-channel Discord webhook management for discord.py — get-or-create, persist, enforce the cap, send. Config-free, injectable, installable."
|
description = "Per-channel Discord webhook management for discord.py — get-or-create, persist, enforce the cap, send. Config-free, injectable, installable."
|
||||||
requires-python = ">=3.10"
|
requires-python = ">=3.10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ through :meth:`clear` as usual.
|
|||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import io
|
import io
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
@@ -71,7 +72,6 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
MAX_WEBHOOKS_PER_CHANNEL = 10
|
MAX_WEBHOOKS_PER_CHANNEL = 10
|
||||||
|
|
||||||
_DEAD_WEBHOOK_STATUSES = (401, 404)
|
|
||||||
_UNKNOWN_WEBHOOK_CODE = 10015
|
_UNKNOWN_WEBHOOK_CODE = 10015
|
||||||
_INVALID_TOKEN_CODE = 50027
|
_INVALID_TOKEN_CODE = 50027
|
||||||
|
|
||||||
@@ -132,11 +132,8 @@ class InMemoryWebhookStore:
|
|||||||
|
|
||||||
def _is_dead_webhook(exc: discord.HTTPException) -> bool:
|
def _is_dead_webhook(exc: discord.HTTPException) -> bool:
|
||||||
"""true when an HTTPException signals a webhook that no longer exists or has an invalid
|
"""true when an HTTPException signals a webhook that no longer exists or has an invalid
|
||||||
token (the recover-and-recreate trigger)"""
|
token (the recover-and-recreate trigger); keyed on discord's error code only - a bare
|
||||||
if isinstance(exc, discord.NotFound):
|
404/401 can be another resource (e.g. 10003 Unknown Channel for a deleted thread=)"""
|
||||||
return True
|
|
||||||
if exc.status in _DEAD_WEBHOOK_STATUSES:
|
|
||||||
return True
|
|
||||||
return exc.code in (_UNKNOWN_WEBHOOK_CODE, _INVALID_TOKEN_CODE)
|
return exc.code in (_UNKNOWN_WEBHOOK_CODE, _INVALID_TOKEN_CODE)
|
||||||
|
|
||||||
|
|
||||||
@@ -203,6 +200,7 @@ class DPYWebhooks:
|
|||||||
self.default_name = default_name
|
self.default_name = default_name
|
||||||
self._cache: dict[int, discord.Webhook] = {}
|
self._cache: dict[int, discord.Webhook] = {}
|
||||||
self._rr: dict[int, dict] = {}
|
self._rr: dict[int, dict] = {}
|
||||||
|
self._create_lock = asyncio.Lock()
|
||||||
|
|
||||||
async def get_or_create(
|
async def get_or_create(
|
||||||
self,
|
self,
|
||||||
@@ -212,15 +210,18 @@ class DPYWebhooks:
|
|||||||
) -> discord.Webhook:
|
) -> discord.Webhook:
|
||||||
"""return the channel's managed webhook, restoring from store or creating if absent;
|
"""return the channel's managed webhook, restoring from store or creating if absent;
|
||||||
raises WebhookCapacityError at the cap unless evict_oldest, and propagates Forbidden
|
raises WebhookCapacityError at the cap unless evict_oldest, and propagates Forbidden
|
||||||
when Manage Webhooks is missing"""
|
when Manage Webhooks is missing; concurrent cold calls share one create"""
|
||||||
cached = self._cache.get(channel.id)
|
cached = self._cache.get(channel.id)
|
||||||
if cached is not None:
|
if cached is not None:
|
||||||
return cached
|
return cached
|
||||||
|
|
||||||
|
async with self._create_lock:
|
||||||
|
cached = self._cache.get(channel.id)
|
||||||
|
if cached is not None:
|
||||||
|
return cached
|
||||||
restored = await self._restore_from_store(channel)
|
restored = await self._restore_from_store(channel)
|
||||||
if restored is not None:
|
if restored is not None:
|
||||||
return restored
|
return restored
|
||||||
|
|
||||||
return await self._create(channel, evict_oldest=evict_oldest)
|
return await self._create(channel, evict_oldest=evict_oldest)
|
||||||
|
|
||||||
async def get(self, channel: discord.TextChannel) -> "Optional[discord.Webhook]":
|
async def get(self, channel: discord.TextChannel) -> "Optional[discord.Webhook]":
|
||||||
|
|||||||
Reference in new issue
Block a user