From e7c6c30ca04241285894d1a0f212d903885487d2 Mon Sep 17 00:00:00 2001 From: Patrick ZAJDA Date: Fri, 28 Nov 2025 16:19:47 +0100 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9C=A8=20feat(bot):=20allow=20configurin?= =?UTF-8?q?g=20TeamTalk=20SDK=20license?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add optional license_name/license_key parameters on TeamTalkBot - apply SDK license during initialization with validation - document license usage in README and Sphinx quick start --- README.md | 12 +++++++++++- docs/index.rst | 10 +++++++++- pytalk/bot.py | 29 ++++++++++++++++++++++++++++- 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b0f8ff8..2bbfd26 100644 --- a/README.md +++ b/README.md @@ -37,10 +37,20 @@ hatch run sdk-download ### Usage +If you have a TeamTalk SDK license, you can pass it when instantiating the bot so +the SDK is initialized with your credentials. Both `license_name` and +`license_key` must be provided together; otherwise the library raises a +`ValueError`. + ```python import pytalk -bot = pytalk.TeamTalkBot() +bot = pytalk.TeamTalkBot( + client_name="PyTalk", + # Optional: apply your TeamTalk SDK license if you have one + license_name="Your Company", + license_key="XXXXX-XXXXX-XXXXX-XXXXX", +) @bot.event async def on_ready(): diff --git a/docs/index.rst b/docs/index.rst index b9f512d..df4252f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -26,13 +26,21 @@ Alternatively, you can download the source code from the GitHub repository and r Quick Start ----------- +If you own a TeamTalk SDK license, you can supply it at initialization so the +SDK is unlocked with your credentials. Both ``license_name`` and +``license_key`` must be provided together. Otherwise, you can omit them. + To quickly get started with Pytalk, you can use the following code snippet: .. code-block:: import pytalk -bot = pytalk.TeamTalkBot() +bot = pytalk.TeamTalkBot( + # Optional: apply your TeamTalk SDK license details if you have them + license_name="Your Company", + license_key="XXXXX-XXXXX-XXXXX-XXXXX", +) @bot.event async def on_ready(): diff --git a/pytalk/bot.py b/pytalk/bot.py index 64920f8..ba429aa 100644 --- a/pytalk/bot.py +++ b/pytalk/bot.py @@ -16,6 +16,7 @@ import uvloop from .enums import TeamTalkServerInfo +from .implementation.TeamTalkPy import TeamTalk5 as sdk from .instance import TeamTalkInstance T = TypeVar("T") @@ -38,12 +39,23 @@ def __getattr__(self, attr: str) -> None: class TeamTalkBot: """A class that represents a TeamTalk bot.""" - def __init__(self, client_name: str | None = "PyTalk") -> None: + def __init__( + self, + client_name: str | None = "PyTalk", + *, + license_name: str | None = None, + license_key: str | None = None, + ) -> None: """Initialize a TeamTalkBot object. Args: client_name (Optional[str]): The name of the client. Defaults to "Teamtalk.py". + license_name (Optional[str]): Name/company issued with the TeamTalk SDK + license. Must be provided together with ``license_key`` to take + effect. + license_key (Optional[str]): TeamTalk SDK license key. Must be provided + together with ``license_name`` to take effect. """ self.client_name = client_name @@ -53,6 +65,21 @@ def __init__(self, client_name: str | None = "PyTalk") -> None: str, list[tuple[asyncio.Future[Any], Callable[..., bool]]] ] = {} + if (license_name is None) ^ (license_key is None): + msg = "Both license_name and license_key must be provided together." + raise ValueError(msg) + + if license_name and license_key: + applied = sdk.setLicense( + sdk.ttstr(license_name), # type: ignore[arg-type] + sdk.ttstr(license_key), # type: ignore[arg-type] + ) + if not applied: + _log.warning( + "TeamTalk SDK license information was not accepted; proceeding " + "without applying a license.", + ) + async def add_server( self, server: TeamTalkServerInfo | dict[str, Any], From 87cee185574e7bbe1d1767ba3f4a2eed1555ce7a Mon Sep 17 00:00:00 2001 From: Patrick ZAJDA Date: Fri, 28 Nov 2025 16:38:27 +0100 Subject: [PATCH 2/2] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor(bot):=20harde?= =?UTF-8?q?n=20TeamTalk=20license=20setup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - treat SDK license as process-wide and enforce reuse across bot instances - reject empty license values and keep validation symmetric - document process-wide license limitation in README and docs --- README.md | 3 ++- docs/index.rst | 4 +++- pytalk/bot.py | 37 ++++++++++++++++++++++++++++--------- 3 files changed, 33 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 2bbfd26..b9b9109 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,8 @@ hatch run sdk-download If you have a TeamTalk SDK license, you can pass it when instantiating the bot so the SDK is initialized with your credentials. Both `license_name` and `license_key` must be provided together; otherwise the library raises a -`ValueError`. +`ValueError`. The TeamTalk SDK license is process-wide, so all bot instances in +the same process must reuse the same credentials. ```python import pytalk diff --git a/docs/index.rst b/docs/index.rst index df4252f..86a74ed 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -28,7 +28,9 @@ Quick Start If you own a TeamTalk SDK license, you can supply it at initialization so the SDK is unlocked with your credentials. Both ``license_name`` and -``license_key`` must be provided together. Otherwise, you can omit them. +``license_key`` must be provided together, and the license is process-wide, so +all bot instances must share the same credentials. Otherwise, you can omit +them. To quickly get started with Pytalk, you can use the following code snippet: diff --git a/pytalk/bot.py b/pytalk/bot.py index ba429aa..cdd2b52 100644 --- a/pytalk/bot.py +++ b/pytalk/bot.py @@ -39,6 +39,9 @@ def __getattr__(self, attr: str) -> None: class TeamTalkBot: """A class that represents a TeamTalk bot.""" + _license_applied: bool = False + _license_value: tuple[str, str] | None = None + def __init__( self, client_name: str | None = "PyTalk", @@ -65,20 +68,36 @@ def __init__( str, list[tuple[asyncio.Future[Any], Callable[..., bool]]] ] = {} - if (license_name is None) ^ (license_key is None): + if (license_name or license_key) and not (license_name and license_key): msg = "Both license_name and license_key must be provided together." raise ValueError(msg) if license_name and license_key: - applied = sdk.setLicense( - sdk.ttstr(license_name), # type: ignore[arg-type] - sdk.ttstr(license_key), # type: ignore[arg-type] - ) - if not applied: - _log.warning( - "TeamTalk SDK license information was not accepted; proceeding " - "without applying a license.", + if not license_name.strip() or not license_key.strip(): + msg = "license_name and license_key cannot be empty strings." + raise ValueError(msg) + + license_tuple = (license_name, license_key) + if TeamTalkBot._license_applied: + if license_tuple != TeamTalkBot._license_value: + msg = ( + "TeamTalk SDK license is already configured for this process; " + "provide the same credentials or restart the process." + ) + raise ValueError(msg) + else: + applied = sdk.setLicense( + sdk.ttstr(license_name), # type: ignore[arg-type] + sdk.ttstr(license_key), # type: ignore[arg-type] ) + if not applied: + _log.warning( + "TeamTalk SDK license information was not accepted; proceeding " + "without applying a license.", + ) + else: + TeamTalkBot._license_applied = True + TeamTalkBot._license_value = license_tuple async def add_server( self,