From d32b340b498fb6070b66db1b4b747b3471c82c76 Mon Sep 17 00:00:00 2001 From: CommandLine <71168720+Def-Try@users.noreply.github.com> Date: Mon, 8 Jan 2024 21:48:03 +0700 Subject: [PATCH 01/15] Fix async webhook assuming that _thread_id can not be None Signed-off-by: CommandLine <71168720+Def-Try@users.noreply.github.com> --- discord/webhook/async_.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/webhook/async_.py b/discord/webhook/async_.py index da001408ac..f1cde8c0bb 100644 --- a/discord/webhook/async_.py +++ b/discord/webhook/async_.py @@ -894,7 +894,7 @@ async def edit( There was no token associated with this webhook. """ thread = MISSING - if hasattr(self, "_thread_id"): + if hasattr(self, "_thread_id") and self._thread_id is not None: thread = Object(self._thread_id) elif isinstance(self.channel, Thread): thread = Object(self.channel.id) @@ -942,7 +942,7 @@ async def delete(self, *, delay: float | None = None) -> None: Deleting the message failed. """ thread_id: int | None = None - if hasattr(self, "_thread_id"): + if hasattr(self, "_thread_id") and self._thread_id is not None: thread_id = self._thread_id elif isinstance(self.channel, Thread): thread_id = self.channel.id From d106101a54760822b7ecb6fab8a3d222dead1cf0 Mon Sep 17 00:00:00 2001 From: CommandLine <71168720+Def-Try@users.noreply.github.com> Date: Mon, 8 Jan 2024 21:51:58 +0700 Subject: [PATCH 02/15] Fix sync webhook assuming that _thread_id can not be None Signed-off-by: CommandLine <71168720+Def-Try@users.noreply.github.com> --- discord/webhook/sync.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/discord/webhook/sync.py b/discord/webhook/sync.py index 9e812b5709..cf8f2fb9a1 100644 --- a/discord/webhook/sync.py +++ b/discord/webhook/sync.py @@ -515,7 +515,7 @@ def edit( There was no token associated with this webhook. """ thread = MISSING - if hasattr(self, "_thread_id"): + if hasattr(self, "_thread_id") and self._thread_id is not None: thread = Object(self._thread_id) elif isinstance(self.channel, Thread): thread = Object(self.channel.id) @@ -555,7 +555,7 @@ def delete(self, *, delay: float | None = None) -> None: """ thread_id: int | None = None - if hasattr(self, "_thread_id"): + if hasattr(self, "_thread_id") and self._thread_id is not None: thread_id = self._thread_id elif isinstance(self.channel, Thread): thread_id = self.channel.id From 1a37a0d85ecd1fb87b9d31363533d25de15e0c97 Mon Sep 17 00:00:00 2001 From: CommandLine <71168720+Def-Try@users.noreply.github.com> Date: Mon, 8 Jan 2024 22:02:34 +0700 Subject: [PATCH 03/15] Update CHANGELOG.md Signed-off-by: CommandLine <71168720+Def-Try@users.noreply.github.com> --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fea137c75a..a3110a0576 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -207,6 +207,8 @@ These changes are available on the `master` branch, but have not yet been releas ([#2301](https://github.com/Pycord-Development/pycord/pull/2301)) - Fixed `AttributeError` caused by `command.cog` being `MISSING`. ([#2303](https://github.com/Pycord-Development/pycord/issues/2303)) +- Fixed `TypeError` caused by `WebhookMessage._thread_id` being `None`. + ([#2314](https://github.com/Pycord-Development/pycord/pull/2314)) ## [2.4.1] - 2023-03-20 From 0773b805ec730f229051a083b5022f092b0a6467 Mon Sep 17 00:00:00 2001 From: CommandLine <71168720+Def-Try@users.noreply.github.com> Date: Mon, 15 Jan 2024 22:05:24 +0700 Subject: [PATCH 04/15] remove code repetition #1 Signed-off-by: CommandLine <71168720+Def-Try@users.noreply.github.com> --- discord/webhook/sync.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/discord/webhook/sync.py b/discord/webhook/sync.py index cf8f2fb9a1..6535141e8d 100644 --- a/discord/webhook/sync.py +++ b/discord/webhook/sync.py @@ -463,6 +463,7 @@ class SyncWebhookMessage(Message): """ _state: _WebhookState + _thread_id: int | None = None def edit( self, @@ -515,7 +516,7 @@ def edit( There was no token associated with this webhook. """ thread = MISSING - if hasattr(self, "_thread_id") and self._thread_id is not None: + if self._thread_id is not None: thread = Object(self._thread_id) elif isinstance(self.channel, Thread): thread = Object(self.channel.id) @@ -555,7 +556,7 @@ def delete(self, *, delay: float | None = None) -> None: """ thread_id: int | None = None - if hasattr(self, "_thread_id") and self._thread_id is not None: + if self._thread_id is not None: thread_id = self._thread_id elif isinstance(self.channel, Thread): thread_id = self.channel.id From 71d279133520334fbc3de73ed502ac4f3309d85d Mon Sep 17 00:00:00 2001 From: CommandLine <71168720+Def-Try@users.noreply.github.com> Date: Mon, 15 Jan 2024 22:06:38 +0700 Subject: [PATCH 05/15] Update async_.py Signed-off-by: CommandLine <71168720+Def-Try@users.noreply.github.com> --- discord/webhook/async_.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/discord/webhook/async_.py b/discord/webhook/async_.py index f1cde8c0bb..21c8706683 100644 --- a/discord/webhook/async_.py +++ b/discord/webhook/async_.py @@ -819,6 +819,7 @@ class WebhookMessage(Message): """ _state: _WebhookState + _thread_id: int | None = None async def edit( self, @@ -894,7 +895,7 @@ async def edit( There was no token associated with this webhook. """ thread = MISSING - if hasattr(self, "_thread_id") and self._thread_id is not None: + if self._thread_id is not None: thread = Object(self._thread_id) elif isinstance(self.channel, Thread): thread = Object(self.channel.id) @@ -942,7 +943,7 @@ async def delete(self, *, delay: float | None = None) -> None: Deleting the message failed. """ thread_id: int | None = None - if hasattr(self, "_thread_id") and self._thread_id is not None: + if self._thread_id is not None: thread_id = self._thread_id elif isinstance(self.channel, Thread): thread_id = self.channel.id From f510930ec312c39011a9c5aaeeb43350ae273dd2 Mon Sep 17 00:00:00 2001 From: CommandLine <71168720+Def-Try@users.noreply.github.com> Date: Mon, 15 Jan 2024 22:25:09 +0700 Subject: [PATCH 06/15] remove code repetition Signed-off-by: CommandLine <71168720+Def-Try@users.noreply.github.com> --- discord/webhook/async_.py | 40 +++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/discord/webhook/async_.py b/discord/webhook/async_.py index 21c8706683..1f98400c31 100644 --- a/discord/webhook/async_.py +++ b/discord/webhook/async_.py @@ -819,7 +819,25 @@ class WebhookMessage(Message): """ _state: _WebhookState - _thread_id: int | None = None + _thread: Object | None = MISSING + + def __init__( + self, + *, + state: ConnectionState, + channel: MessageableChannel, + data: MessagePayload, + thread_id: int | None = None + ): + super().__init__(state=state, channel=channel, data=data) + + if thread_id is not None: + self._thread = Object(thread_id) + elif isinstance(self.channel, Thread): + self._thread = Object(self.channel.id) + elif isinstance(self.channel, ForumChannel): + self._thread = Object(self.id) + async def edit( self, @@ -894,13 +912,6 @@ async def edit( InvalidArgument There was no token associated with this webhook. """ - thread = MISSING - if self._thread_id is not None: - thread = Object(self._thread_id) - elif isinstance(self.channel, Thread): - thread = Object(self.channel.id) - elif isinstance(self.channel, ForumChannel): - thread = Object(self.id) if attachments is MISSING: attachments = self.attachments or MISSING @@ -918,7 +929,7 @@ async def edit( attachments=attachments, view=view, allowed_mentions=allowed_mentions, - thread=thread, + thread=self.thread, suppress=suppress, ) @@ -942,11 +953,6 @@ async def delete(self, *, delay: float | None = None) -> None: HTTPException Deleting the message failed. """ - thread_id: int | None = None - if self._thread_id is not None: - thread_id = self._thread_id - elif isinstance(self.channel, Thread): - thread_id = self.channel.id if delay is not None: @@ -954,14 +960,14 @@ async def inner_call(delay: float = delay): await asyncio.sleep(delay) try: await self._state._webhook.delete_message( - self.id, thread_id=thread_id + self.id, thread_id=self._thread.id ) except HTTPException: pass asyncio.create_task(inner_call()) else: - await self._state._webhook.delete_message(self.id, thread_id=thread_id) + await self._state._webhook.delete_message(self.id, thread_id=self._thread.id) class BaseWebhook(Hashable): @@ -1840,8 +1846,6 @@ async def fetch_message( thread_id=thread_id, ) msg = self._create_message(data) - if isinstance(msg.channel, PartialMessageable): - msg._thread_id = thread_id return msg From ee6ca95a5b0c88086d45f077e713e387a3b0ec0f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 15:25:31 +0000 Subject: [PATCH 07/15] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/webhook/async_.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/discord/webhook/async_.py b/discord/webhook/async_.py index 1f98400c31..b229752765 100644 --- a/discord/webhook/async_.py +++ b/discord/webhook/async_.py @@ -827,7 +827,7 @@ def __init__( state: ConnectionState, channel: MessageableChannel, data: MessagePayload, - thread_id: int | None = None + thread_id: int | None = None, ): super().__init__(state=state, channel=channel, data=data) @@ -838,7 +838,6 @@ def __init__( elif isinstance(self.channel, ForumChannel): self._thread = Object(self.id) - async def edit( self, content: str | None = MISSING, @@ -967,7 +966,9 @@ async def inner_call(delay: float = delay): asyncio.create_task(inner_call()) else: - await self._state._webhook.delete_message(self.id, thread_id=self._thread.id) + await self._state._webhook.delete_message( + self.id, thread_id=self._thread.id + ) class BaseWebhook(Hashable): From 72c151f8e16f440b30bb59e825e21b0a58158d88 Mon Sep 17 00:00:00 2001 From: CommandLine <71168720+Def-Try@users.noreply.github.com> Date: Mon, 15 Jan 2024 22:26:24 +0700 Subject: [PATCH 08/15] remove code repetition Signed-off-by: CommandLine <71168720+Def-Try@users.noreply.github.com> --- discord/webhook/sync.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/discord/webhook/sync.py b/discord/webhook/sync.py index 6535141e8d..c878727222 100644 --- a/discord/webhook/sync.py +++ b/discord/webhook/sync.py @@ -463,7 +463,24 @@ class SyncWebhookMessage(Message): """ _state: _WebhookState - _thread_id: int | None = None + _thread: Object | None = MISSING + + def __init__( + self, + *, + state: ConnectionState, + channel: MessageableChannel, + data: MessagePayload, + thread_id: int | None = None + ): + super().__init__(state=state, channel=channel, data=data) + + if thread_id is not None: + self._thread = Object(thread_id) + elif isinstance(self.channel, Thread): + self._thread = Object(self.channel.id) + elif isinstance(self.channel, ForumChannel): + self._thread = Object(self.id) def edit( self, @@ -1148,8 +1165,6 @@ def fetch_message( thread_id=thread_id, ) msg = self._create_message(data) - if isinstance(msg.channel, PartialMessageable): - msg._thread_id = thread_id return msg From 6e9aef0e21890eb0d733fca3a5218c1621c70dff Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 15:27:47 +0000 Subject: [PATCH 09/15] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/webhook/sync.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/discord/webhook/sync.py b/discord/webhook/sync.py index c878727222..d75d3e56eb 100644 --- a/discord/webhook/sync.py +++ b/discord/webhook/sync.py @@ -471,7 +471,7 @@ def __init__( state: ConnectionState, channel: MessageableChannel, data: MessagePayload, - thread_id: int | None = None + thread_id: int | None = None, ): super().__init__(state=state, channel=channel, data=data) From 92a1c561ae2739a473bd7c10d0dfe6ef37d7679f Mon Sep 17 00:00:00 2001 From: CommandLine <71168720+Def-Try@users.noreply.github.com> Date: Mon, 15 Jan 2024 22:33:00 +0700 Subject: [PATCH 10/15] forgot to import Message's __init__ args types Signed-off-by: CommandLine <71168720+Def-Try@users.noreply.github.com> --- discord/webhook/sync.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/discord/webhook/sync.py b/discord/webhook/sync.py index d75d3e56eb..ce6b982b22 100644 --- a/discord/webhook/sync.py +++ b/discord/webhook/sync.py @@ -54,6 +54,9 @@ from ..object import Object from ..threads import Thread from .async_ import BaseWebhook, _WebhookState, handle_message_parameters +from ..types.message import Message as MessagePayload +from ..state import ConnectionState +from ..abc import MessageableChannel __all__ = ( "SyncWebhook", From 087ef736ae3e7fbfdce3f633a893b4fdbd8d04aa Mon Sep 17 00:00:00 2001 From: CommandLine <71168720+Def-Try@users.noreply.github.com> Date: Mon, 15 Jan 2024 22:34:15 +0700 Subject: [PATCH 11/15] forgot to import Message's __init__ args types Signed-off-by: CommandLine <71168720+Def-Try@users.noreply.github.com> --- discord/webhook/async_.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/discord/webhook/async_.py b/discord/webhook/async_.py index b229752765..b8758bdd99 100644 --- a/discord/webhook/async_.py +++ b/discord/webhook/async_.py @@ -54,6 +54,9 @@ from ..object import Object from ..threads import Thread from ..user import BaseUser, User +from ..types.message import Message as MessagePayload +from ..state import ConnectionState +from ..abc import MessageableChannel __all__ = ( "Webhook", From 3427d5c352ed347f2fcc3a273ce189b71db50a68 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 15:37:29 +0000 Subject: [PATCH 12/15] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/webhook/async_.py | 6 +++--- discord/webhook/sync.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/discord/webhook/async_.py b/discord/webhook/async_.py index b8758bdd99..47f264d8c9 100644 --- a/discord/webhook/async_.py +++ b/discord/webhook/async_.py @@ -37,6 +37,7 @@ import aiohttp from .. import utils +from ..abc import MessageableChannel from ..asset import Asset from ..channel import ForumChannel, PartialMessageable from ..enums import WebhookType, try_enum @@ -52,11 +53,10 @@ from ..message import Attachment, Message from ..mixins import Hashable from ..object import Object +from ..state import ConnectionState from ..threads import Thread -from ..user import BaseUser, User from ..types.message import Message as MessagePayload -from ..state import ConnectionState -from ..abc import MessageableChannel +from ..user import BaseUser, User __all__ = ( "Webhook", diff --git a/discord/webhook/sync.py b/discord/webhook/sync.py index ce6b982b22..fb622b9f3f 100644 --- a/discord/webhook/sync.py +++ b/discord/webhook/sync.py @@ -41,6 +41,7 @@ from urllib.parse import quote as urlquote from .. import utils +from ..abc import MessageableChannel from ..channel import PartialMessageable from ..errors import ( DiscordServerError, @@ -52,11 +53,10 @@ from ..http import Route from ..message import Message from ..object import Object +from ..state import ConnectionState from ..threads import Thread -from .async_ import BaseWebhook, _WebhookState, handle_message_parameters from ..types.message import Message as MessagePayload -from ..state import ConnectionState -from ..abc import MessageableChannel +from .async_ import BaseWebhook, _WebhookState, handle_message_parameters __all__ = ( "SyncWebhook", From 72fa1c37c574f69b4f0236c09bdc04056a22885d Mon Sep 17 00:00:00 2001 From: CommandLine <71168720+Def-Try@users.noreply.github.com> Date: Mon, 15 Jan 2024 22:42:54 +0700 Subject: [PATCH 13/15] SyncWebhookMessage does not need ForumChannel thread apparently?? Signed-off-by: CommandLine <71168720+Def-Try@users.noreply.github.com> --- discord/webhook/sync.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/discord/webhook/sync.py b/discord/webhook/sync.py index fb622b9f3f..dc11b3a34f 100644 --- a/discord/webhook/sync.py +++ b/discord/webhook/sync.py @@ -482,8 +482,6 @@ def __init__( self._thread = Object(thread_id) elif isinstance(self.channel, Thread): self._thread = Object(self.channel.id) - elif isinstance(self.channel, ForumChannel): - self._thread = Object(self.id) def edit( self, From 5d1efd9b0ea6d58769e757ebdca24979612d7ec5 Mon Sep 17 00:00:00 2001 From: Dorukyum <53639936+Dorukyum@users.noreply.github.com> Date: Thu, 25 Jan 2024 10:31:49 +0300 Subject: [PATCH 14/15] feat: add thread property --- CHANGELOG.md | 2 ++ discord/webhook/async_.py | 31 +++++++++--------------------- discord/webhook/sync.py | 40 +++++++++------------------------------ 3 files changed, 20 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3110a0576..54f2c0a02a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,8 @@ These changes are available on the `master` branch, but have not yet been releas - Added `default_reaction_emoji` parameter to `Guild.create_forum_channel()` and `ForumChannel.edit()` methods. ([#2178](https://github.com/Pycord-Development/pycord/pull/2178)) +- Added properties `WebhookMessage.thread` and `SyncWebhookMessage.thread`. + ([#2314](https://github.com/Pycord-Development/pycord/pull/2314)) ### Changed diff --git a/discord/webhook/async_.py b/discord/webhook/async_.py index 47f264d8c9..f1ace957ef 100644 --- a/discord/webhook/async_.py +++ b/discord/webhook/async_.py @@ -37,7 +37,6 @@ import aiohttp from .. import utils -from ..abc import MessageableChannel from ..asset import Asset from ..channel import ForumChannel, PartialMessageable from ..enums import WebhookType, try_enum @@ -53,10 +52,9 @@ from ..message import Attachment, Message from ..mixins import Hashable from ..object import Object -from ..state import ConnectionState from ..threads import Thread -from ..types.message import Message as MessagePayload from ..user import BaseUser, User +from ..utils import cached_property __all__ = ( "Webhook", @@ -822,24 +820,13 @@ class WebhookMessage(Message): """ _state: _WebhookState - _thread: Object | None = MISSING - def __init__( - self, - *, - state: ConnectionState, - channel: MessageableChannel, - data: MessagePayload, - thread_id: int | None = None, - ): - super().__init__(state=state, channel=channel, data=data) - - if thread_id is not None: - self._thread = Object(thread_id) - elif isinstance(self.channel, Thread): - self._thread = Object(self.channel.id) - elif isinstance(self.channel, ForumChannel): - self._thread = Object(self.id) + @cached_property + def thread(self) -> Thread | Object | None: + if isinstance(self.channel, Thread): + return self.channel + if self._thread_id: + return Object(self._thread_id) async def edit( self, @@ -962,7 +949,7 @@ async def inner_call(delay: float = delay): await asyncio.sleep(delay) try: await self._state._webhook.delete_message( - self.id, thread_id=self._thread.id + self.id, thread_id=self.thread.id ) except HTTPException: pass @@ -970,7 +957,7 @@ async def inner_call(delay: float = delay): asyncio.create_task(inner_call()) else: await self._state._webhook.delete_message( - self.id, thread_id=self._thread.id + self.id, thread_id=self.thread.id ) diff --git a/discord/webhook/sync.py b/discord/webhook/sync.py index dc11b3a34f..d095fc2a81 100644 --- a/discord/webhook/sync.py +++ b/discord/webhook/sync.py @@ -41,7 +41,6 @@ from urllib.parse import quote as urlquote from .. import utils -from ..abc import MessageableChannel from ..channel import PartialMessageable from ..errors import ( DiscordServerError, @@ -53,9 +52,8 @@ from ..http import Route from ..message import Message from ..object import Object -from ..state import ConnectionState from ..threads import Thread -from ..types.message import Message as MessagePayload +from ..utils import cached_property from .async_ import BaseWebhook, _WebhookState, handle_message_parameters __all__ = ( @@ -466,22 +464,13 @@ class SyncWebhookMessage(Message): """ _state: _WebhookState - _thread: Object | None = MISSING - def __init__( - self, - *, - state: ConnectionState, - channel: MessageableChannel, - data: MessagePayload, - thread_id: int | None = None, - ): - super().__init__(state=state, channel=channel, data=data) - - if thread_id is not None: - self._thread = Object(thread_id) - elif isinstance(self.channel, Thread): - self._thread = Object(self.channel.id) + @cached_property + def thread(self) -> Thread | Object | None: + if isinstance(self.channel, Thread): + return self.channel + if self._thread_id: + return Object(self._thread_id) def edit( self, @@ -533,11 +522,6 @@ def edit( InvalidArgument There was no token associated with this webhook. """ - thread = MISSING - if self._thread_id is not None: - thread = Object(self._thread_id) - elif isinstance(self.channel, Thread): - thread = Object(self.channel.id) if suppress is MISSING: suppress = self.flags.suppress_embeds @@ -550,8 +534,8 @@ def edit( file=file, files=files, allowed_mentions=allowed_mentions, - thread=thread, suppress=suppress, + thread=self.thread, ) def delete(self, *, delay: float | None = None) -> None: @@ -573,16 +557,10 @@ def delete(self, *, delay: float | None = None) -> None: Deleting the message failed. """ - thread_id: int | None = None - if self._thread_id is not None: - thread_id = self._thread_id - elif isinstance(self.channel, Thread): - thread_id = self.channel.id - if delay is not None: time.sleep(delay) - self._state._webhook.delete_message(self.id, thread_id=thread_id) + self._state._webhook.delete_message(self.id, thread_id=self.thread.id) class SyncWebhook(BaseWebhook): From c4a924c374d5c02f8a5f5fb17ecc57d0d5a9bcd3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 25 Jan 2024 07:33:14 +0000 Subject: [PATCH 15/15] style(pre-commit): auto fixes from pre-commit.com hooks --- discord/webhook/async_.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/discord/webhook/async_.py b/discord/webhook/async_.py index e1c40b50bb..964e47e891 100644 --- a/discord/webhook/async_.py +++ b/discord/webhook/async_.py @@ -960,9 +960,7 @@ async def inner_call(delay: float = delay): asyncio.create_task(inner_call()) else: - await self._state._webhook.delete_message( - self.id, thread_id=self.thread.id - ) + await self._state._webhook.delete_message(self.id, thread_id=self.thread.id) class BaseWebhook(Hashable):