Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-22.04
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

steps:
- name: Setup dependencies
Expand Down
1,575 changes: 956 additions & 619 deletions poetry.lock

Large diffs are not rendered by default.

28 changes: 25 additions & 3 deletions pybotx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
BotAPIUnverifiedRequestResponse,
build_unverified_request_response,
)
from pybotx.auth import BotXAuthVersion
from pybotx.bot.bot import Bot
from pybotx.bot.callbacks.callback_repo_proto import CallbackRepoProto
from pybotx.bot.exceptions import (
Expand All @@ -38,6 +39,8 @@
CantUpdatePersonalChatError,
ChatCreationError,
ChatCreationProhibitedError,
ChatLinkCreationError,
ChatLinkCreationProhibitedError,
InvalidUsersListError,
ThreadAlreadyExistsError,
ThreadCreationError,
Expand Down Expand Up @@ -88,9 +91,16 @@
from pybotx.models.bot_account import BotAccount, BotAccountWithSecret
from pybotx.models.bot_catalog import BotsListItem
from pybotx.models.bot_sender import BotSender
from pybotx.models.chats import Chat, ChatInfo, ChatInfoMember, ChatListItem
from pybotx.models.chats import (
Chat,
ChatInfo,
ChatInfoMember,
ChatLink,
ChatListItem,
)
from pybotx.models.enums import (
AttachmentTypes,
ChatLinkTypes,
ChatTypes,
ClientPlatforms,
ConferenceLinkTypes,
Expand Down Expand Up @@ -127,10 +137,14 @@
from pybotx.models.message.outgoing_message import OutgoingMessage
from pybotx.models.message.reply import Reply
from pybotx.models.message.reply_message import ReplyMessage
from pybotx.models.method_callbacks import BotAPIMethodFailedCallback
from pybotx.models.method_callbacks import (
BotAPIMethodFailedCallback,
BotAPIMethodSuccessfulCallback,
BotXMethodCallback,
)
from pybotx.models.smartapps import SmartApp
from pybotx.models.status import BotMenu, StatusRecipient
from pybotx.models.stickers import Sticker, StickerPack
from pybotx.models.stickers import Sticker, StickerPack, StickerPackFromList
from pybotx.models.sync_smartapp_event import (
BotAPISyncSmartAppEventErrorResponse,
BotAPISyncSmartAppEventResponse,
Expand Down Expand Up @@ -166,19 +180,22 @@
"BotAPIBotDisabledErrorData",
"BotAPIBotDisabledResponse",
"BotAPIMethodFailedCallback",
"BotAPIMethodSuccessfulCallback",
"BotAPISyncSmartAppEventErrorResponse",
"BotAPISyncSmartAppEventResponse",
"BotAPISyncSmartAppEventResultResponse",
"BotAPIUnverifiedRequestErrorData",
"BotAPIUnverifiedRequestResponse",
"BotAccount",
"BotAccountWithSecret",
"BotXAuthVersion",
"BotIsNotChatMemberError",
"BotMenu",
"BotSender",
"BotShuttingDownError",
"BotXMethodCallbackNotFoundError",
"BotXMethodFailedCallbackReceivedError",
"BotXMethodCallback",
"BotsListItem",
"BubbleMarkup",
"Button",
Expand All @@ -197,8 +214,12 @@
"ChatDeletedByUserEvent",
"ChatInfo",
"ChatInfoMember",
"ChatLink",
"ChatLinkCreationError",
"ChatLinkCreationProhibitedError",
"ChatListItem",
"ChatNotFoundError",
"ChatLinkTypes",
"ChatTypes",
"ClientPlatforms",
"ConferenceChangedEvent",
Expand Down Expand Up @@ -261,6 +282,7 @@
"StealthModeDisabledError",
"Sticker",
"StickerPack",
"StickerPackFromList",
"StickerPackOrStickerNotFoundError",
"SyncSmartAppEventHandlerFunc",
"SyncSmartAppEventHandlerNotFoundError",
Expand Down
8 changes: 2 additions & 6 deletions pybotx/async_buffer.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import abc
import os
from typing import Optional

try:
from typing import Protocol
except ImportError:
from typing_extensions import Protocol # type: ignore
from typing import Protocol


class AsyncBufferBase(Protocol):
Expand All @@ -27,7 +23,7 @@ class AsyncBufferReadable(AsyncBufferBase):
@abc.abstractmethod
async def read(
self,
bytes_to_read: Optional[int] = None,
bytes_to_read: int | None = None,
) -> bytes: ... # pragma: no cover


Expand Down
35 changes: 35 additions & 0 deletions pybotx/auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import secrets
import time
from enum import Enum
from uuid import UUID

import jwt


class BotXAuthVersion(str, Enum):
V1 = "v1"
V2 = "v2"


def build_botx_jwt_v2(
*,
bot_id: UUID,
bot_host: str,
secret_key: str,
issued_at: int | None = None,
token_id: str | None = None,
) -> str:
iat = int(time.time()) if issued_at is None else issued_at
jti = token_id or secrets.token_hex(12)

payload = {
"iss": str(bot_id),
"aud": bot_host,
"exp": iat + 60,
"nbf": iat,
"jti": jti,
"iat": iat,
"version": 2,
}

return jwt.encode(payload=payload, key=secret_key, algorithm="HS256")
10 changes: 5 additions & 5 deletions pybotx/bot/api/responses/bot_disabled.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from dataclasses import asdict, dataclass, field
from typing import Any, Dict, List, Literal
from typing import Any, Literal


@dataclass
@dataclass(slots=True)
class BotAPIBotDisabledErrorData:
status_message: str


@dataclass
@dataclass(slots=True)
class BotAPIBotDisabledResponse:
"""Disabled bot response model.

Expand All @@ -16,11 +16,11 @@ class BotAPIBotDisabledResponse:
"""

error_data: BotAPIBotDisabledErrorData
errors: List[str] = field(default_factory=list)
errors: list[str] = field(default_factory=list)
reason: Literal["bot_disabled"] = "bot_disabled"


def build_bot_disabled_response(status_message: str) -> Dict[str, Any]:
def build_bot_disabled_response(status_message: str) -> dict[str, Any]:
"""Build bot disabled response for BotX.

It should be sent if the bot can't process the command.
Expand Down
4 changes: 2 additions & 2 deletions pybotx/bot/api/responses/command_accepted.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any, Dict
from typing import Any


def build_command_accepted_response() -> Dict[str, Any]:
def build_command_accepted_response() -> dict[str, Any]:
"""Build accepted response for BotX.

It should be sent if the bot started processing a command.
Expand Down
10 changes: 5 additions & 5 deletions pybotx/bot/api/responses/unverified_request.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from dataclasses import asdict, dataclass, field
from typing import Any, Dict, List, Literal
from typing import Any, Literal


@dataclass
@dataclass(slots=True)
class BotAPIUnverifiedRequestErrorData:
status_message: str


@dataclass
@dataclass(slots=True)
class BotAPIUnverifiedRequestResponse:
"""`Unverified request` response model.

Expand All @@ -16,11 +16,11 @@ class BotAPIUnverifiedRequestResponse:
"""

error_data: BotAPIUnverifiedRequestErrorData
errors: List[str] = field(default_factory=list)
errors: list[str] = field(default_factory=list)
reason: Literal["unverified_request"] = "unverified_request"


def build_unverified_request_response(status_message: str) -> Dict[str, Any]:
def build_unverified_request_response(status_message: str) -> dict[str, Any]:
"""Build `unverified request` response for BotX.

It should be sent if the header with the authorization token is missing or
Expand Down
Loading
Loading