Browse Source
* Base read states * Base read states * Models and helpers * Increase mention counter * Rest of the ack events * Finishing touches and doccs * Fix RawUserFeatureAckEvent docstring * Add top-level mention count property * Add ack shortfall note * Expose and document ReadStateType * Add Client.read_states getter * Update scheduled event read state badge count * Update READMEpull/10109/head
committed by
GitHub
19 changed files with 1140 additions and 33 deletions
@ -0,0 +1,185 @@ |
|||||
|
""" |
||||
|
The MIT License (MIT) |
||||
|
|
||||
|
Copyright (c) 2021-present Dolfies |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||
|
copy of this software and associated documentation files (the "Software"), |
||||
|
to deal in the Software without restriction, including without limitation |
||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||
|
Software is furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in |
||||
|
all copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||
|
DEALINGS IN THE SOFTWARE. |
||||
|
""" |
||||
|
|
||||
|
from __future__ import annotations |
||||
|
|
||||
|
from typing import TYPE_CHECKING, Optional, Union |
||||
|
|
||||
|
from .enums import ReadStateType, try_enum |
||||
|
from .utils import parse_time |
||||
|
|
||||
|
if TYPE_CHECKING: |
||||
|
from datetime import datetime |
||||
|
|
||||
|
from typing_extensions import Self |
||||
|
|
||||
|
from .abc import MessageableChannel |
||||
|
from .guild import Guild |
||||
|
from .state import ConnectionState |
||||
|
from .user import ClientUser |
||||
|
from .types.read_state import ReadState as ReadStatePayload |
||||
|
|
||||
|
# fmt: off |
||||
|
__all__ = ( |
||||
|
'ReadState', |
||||
|
) |
||||
|
# fmt: on |
||||
|
|
||||
|
|
||||
|
class ReadState: |
||||
|
"""Represents the read state of a resource. |
||||
|
|
||||
|
This is a purposefuly very low-level object. |
||||
|
|
||||
|
.. container:: operations |
||||
|
|
||||
|
.. describe:: x == y |
||||
|
|
||||
|
Checks if two read states are equal. |
||||
|
|
||||
|
.. describe:: x != y |
||||
|
|
||||
|
Checks if two read states are not equal. |
||||
|
|
||||
|
.. describe:: hash(x) |
||||
|
|
||||
|
Returns the read state's hash. |
||||
|
|
||||
|
.. versionadded:: 2.1 |
||||
|
|
||||
|
Attributes |
||||
|
----------- |
||||
|
id: :class:`int` |
||||
|
The ID of the resource. |
||||
|
type: :class:`ReadStateType` |
||||
|
The type of the read state. |
||||
|
last_acked_id: :class:`int` |
||||
|
The ID of the last acknowledged resource (e.g. message) in the read state. |
||||
|
It may *not* point to an existing or valid resource. |
||||
|
acked_pin_timestamp: Optional[:class:`datetime.datetime`] |
||||
|
When the channel's pins were last acknowledged. |
||||
|
badge_count: :class:`int` |
||||
|
The number of badges in the read state (e.g. mentions). |
||||
|
""" |
||||
|
|
||||
|
__slots__ = ( |
||||
|
'id', |
||||
|
'type', |
||||
|
'last_acked_id', |
||||
|
'acked_pin_timestamp', |
||||
|
'badge_count', |
||||
|
'last_viewed', |
||||
|
'_flags', |
||||
|
'_last_entity_id', |
||||
|
'_state', |
||||
|
) |
||||
|
|
||||
|
def __init__(self, *, state: ConnectionState, data: ReadStatePayload): |
||||
|
self._state = state |
||||
|
|
||||
|
self.id: int = int(data['id']) |
||||
|
self.type: ReadStateType = try_enum(ReadStateType, data.get('read_state_type', 0)) |
||||
|
self._last_entity_id: Optional[int] = None |
||||
|
self._update(data) |
||||
|
|
||||
|
def _update(self, data: ReadStatePayload): |
||||
|
self.last_acked_id: int = int(data.get('last_acked_id', data.get('last_message_id', 0))) |
||||
|
self.acked_pin_timestamp: Optional[datetime] = parse_time(data.get('last_pin_timestamp')) |
||||
|
self.badge_count: int = int(data.get('badge_count', data.get('mention_count', 0))) |
||||
|
self.last_viewed: Optional[datetime] = parse_time(data.get('last_viewed')) |
||||
|
self._flags: int = data.get('flags') or 0 |
||||
|
|
||||
|
def __eq__(self, other: object) -> bool: |
||||
|
if isinstance(other, ReadState): |
||||
|
return other.id == self.id and other.type == self.type |
||||
|
return False |
||||
|
|
||||
|
def __ne__(self, other: object) -> bool: |
||||
|
if isinstance(other, ReadState): |
||||
|
return other.id != self.id or other.type != self.type |
||||
|
return True |
||||
|
|
||||
|
def __hash__(self) -> int: |
||||
|
return (self.id * self.type.value) >> 22 |
||||
|
|
||||
|
@classmethod |
||||
|
def default(cls, id: int, type: ReadStateType, *, state: ConnectionState) -> Self: |
||||
|
self = cls.__new__(cls) |
||||
|
self._state = state |
||||
|
self.id = id |
||||
|
self.type = type |
||||
|
self._last_entity_id = None |
||||
|
self.last_acked_id = 0 |
||||
|
self.acked_pin_timestamp = None |
||||
|
self.badge_count = 0 |
||||
|
return self |
||||
|
|
||||
|
@property |
||||
|
def resource(self) -> Optional[Union[ClientUser, Guild, MessageableChannel]]: |
||||
|
"""Optional[Union[:class:`ClientUser`, :class:`Guild`, :class:`TextChannel`, :class:`StageChannel`, :class:`VoiceChannel`, :class:`Thread`, :class:`DMChannel`, :class:`GroupChannel`, :class:`PartialMessageable`]]: The entity associated with the read state.""" |
||||
|
state = self._state |
||||
|
|
||||
|
if self.type == ReadStateType.channel: |
||||
|
return state._get_or_create_partial_messageable(self.id) # type: ignore |
||||
|
elif self.type in (ReadStateType.scheduled_events, ReadStateType.guild_home, ReadStateType.onboarding): |
||||
|
return state._get_or_create_unavailable_guild(self.id) |
||||
|
elif self.type == ReadStateType.notification_center and self.id == state.self_id: |
||||
|
return state.user |
||||
|
|
||||
|
@property |
||||
|
def last_entity_id(self) -> int: |
||||
|
""":class:`int`: The ID of the last resource (e.g. message) in the read state. |
||||
|
It may *not* point to an existing or valid resource. |
||||
|
""" |
||||
|
if self._last_entity_id is not None: |
||||
|
return self._last_entity_id |
||||
|
resource = self.resource |
||||
|
if not resource: |
||||
|
return 0 |
||||
|
|
||||
|
if self.type == ReadStateType.channel: |
||||
|
return resource.last_message_id or 0 # type: ignore |
||||
|
elif self.type == ReadStateType.scheduled_events: |
||||
|
return max(resource.scheduled_events, key=lambda e: e.id).id # type: ignore |
||||
|
return 0 |
||||
|
|
||||
|
@property |
||||
|
def last_pin_timestamp(self) -> Optional[datetime]: |
||||
|
"""Optional[:class:`datetime.datetime`]: When the last pinned message was pinned in the channel.""" |
||||
|
if self.resource and hasattr(self.resource, 'last_pin_timestamp'): |
||||
|
return self.resource.last_pin_timestamp # type: ignore |
||||
|
|
||||
|
async def delete(self): |
||||
|
"""|coro| |
||||
|
|
||||
|
Deletes the read state. |
||||
|
|
||||
|
Raises |
||||
|
------- |
||||
|
HTTPException |
||||
|
Deleting the read state failed. |
||||
|
""" |
||||
|
state = self._state |
||||
|
await state.http.delete_read_state(self.id, self.type.value) |
||||
|
state.remove_read_state(self) |
@ -0,0 +1,54 @@ |
|||||
|
""" |
||||
|
The MIT License (MIT) |
||||
|
|
||||
|
Copyright (c) 2021-present Dolfies |
||||
|
|
||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||
|
copy of this software and associated documentation files (the "Software"), |
||||
|
to deal in the Software without restriction, including without limitation |
||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||
|
Software is furnished to do so, subject to the following conditions: |
||||
|
|
||||
|
The above copyright notice and this permission notice shall be included in |
||||
|
all copies or substantial portions of the Software. |
||||
|
|
||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||
|
DEALINGS IN THE SOFTWARE. |
||||
|
""" |
||||
|
|
||||
|
from __future__ import annotations |
||||
|
|
||||
|
from typing import Literal, Optional, TypedDict |
||||
|
from typing_extensions import NotRequired |
||||
|
|
||||
|
from .snowflake import Snowflake |
||||
|
|
||||
|
ReadStateType = Literal[0, 1, 2, 3, 4] |
||||
|
|
||||
|
|
||||
|
class ReadState(TypedDict): |
||||
|
id: Snowflake |
||||
|
read_state_type: NotRequired[ReadStateType] |
||||
|
last_message_id: NotRequired[Snowflake] |
||||
|
last_acked_id: NotRequired[Snowflake] |
||||
|
last_pin_timestamp: NotRequired[str] |
||||
|
mention_count: NotRequired[int] |
||||
|
badge_count: NotRequired[int] |
||||
|
flags: NotRequired[int] |
||||
|
# last_viewed: NotRequired[Optional[str]] |
||||
|
|
||||
|
|
||||
|
class BulkReadState(TypedDict): |
||||
|
channel_id: Snowflake |
||||
|
message_id: Snowflake |
||||
|
read_state_type: ReadStateType |
||||
|
|
||||
|
|
||||
|
class AcknowledgementToken(TypedDict): |
||||
|
token: Optional[str] |
Loading…
Reference in new issue