Skip to content

Commit 40c78dc

Browse files
committed
Merge branch 'main' into feature/hypetrain-status
2 parents a1f0973 + 2879dc5 commit 40c78dc

File tree

17 files changed

+549
-16
lines changed

17 files changed

+549
-16
lines changed

docs/exts/commands/core.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ Decorators
5454

5555
.. autofunction:: twitchio.ext.commands.cooldown(*, base: BaseCooldown, rate: int, per: float, key: Callable[[Any], Hashable] | Callable[[Any], Coroutine[Any, Any, Hashable]] | BucketType, **kwargs: ~typing.Any)
5656

57+
.. autofunction:: twitchio.ext.commands.translator
58+
5759

5860
Guards
5961
######
@@ -97,4 +99,11 @@ Converters
9799
:members:
98100

99101
.. autoclass:: twitchio.ext.commands.ColourConverter()
102+
:members:
103+
104+
105+
Translators
106+
###########
107+
108+
.. autoclass:: twitchio.ext.commands.Translator()
100109
:members:

docs/exts/commands/exceptions.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ Exceptions
5757

5858
.. autoexception:: twitchio.ext.commands.NoEntryPointError
5959

60+
.. autoexception:: twitchio.ext.commands.TranslatorError
61+
6062

6163
Exception Hierarchy
6264
~~~~~~~~~~~~~~~~~~~
@@ -80,6 +82,7 @@ Exception Hierarchy
8082
- :exc:`ExpectedClosingQuoteError`
8183
- :exc:`GuardFailure`
8284
- :exc:`CommandOnCooldown`
85+
- :exc:`TranslatorError`
8386
- :exc:`ModuleError`
8487
- :exc:`ModuleLoadFailure`
8588
- :exc:`ModuleAlreadyLoadedError`

docs/getting-started/changelog.rst

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,35 @@ Changelog
1212
- twitchio
1313
- Additions
1414
- Added ``__hash__`` to :class:`twitchio.PartialUser` allowing it to be used as a key.
15+
- Added the ``--create-new`` interactive script to ``__main__`` allowing boiler-plate to be generated for a new Bot.
1516

1617
- Changes
1718
- Adjusted the Starlette logging warning wording.
19+
- Delayed the Starlette logging warning and removed it from ``web/__init__.py``.
1820
- :class:`twitchio.PartialUser`, :class:`twitchio.User` and :class:`twitchio.Chatter` now have ``__hash__`` implementations derived from :class:`~twitchio.PartialUser`, which use the unique ID.
1921

2022
- Bug fixes
2123
- :meth:`twitchio.Clip.fetch_video` now properly returns ``None`` when the :class:`twitchio.Clip` has no ``video_id``.
2224
- :class:`twitchio.ChatterColor` no longer errors whan no valid hex is provided by Twitch.
25+
- Some general typing/spelling errors cleaned up in Documentation and Logging.
26+
- Removed some redundant logging.
27+
- Fixed internal parsing of the payload received in :meth:`twitchio.PartialUser.warn_user` which was resulting in an error.
28+
29+
- twitchio.Client
30+
- Bug fixes
31+
- Fixed tokens not being saved properly when ``load_tokens`` was ``False`` in :meth:`twitchio.Client.login`
32+
33+
- twitchio.AutoClient
34+
- Additions
35+
- Added ``force_subscribe`` keyword argument to :class:`twitchio.AutoClient`, allowing subscriptions passed to be made everytime the client is started.
36+
- Added ``force_scale`` keyword argument to :class:`twitchio.AutoClient`, allowing the associated Conduit to be scaled up/down on startup.
37+
- Added more informative logging in places.
38+
39+
- Changes
40+
- Optimised the cleanup of conduit websockets. This largely only affects applications connected to large amounts of shards.
41+
42+
- twitchio.ext.commands.AutoBot
43+
- Updates are identical to the updates made in the ``twitchio.AutoClient`` changelog above.
2344

2445
- twitchio.eventsub
2546
- Additions
@@ -82,19 +103,51 @@ Changelog
82103
- Added :meth:`twitchio.ShoutoutReceive.respond`
83104
- Added :meth:`twitchio.StreamOnline.respond`
84105
- Added :meth:`twitchio.StreamOffline.respond`
106+
107+
- Bug fixes
108+
- Remove the unnecessary ``token_for`` parameter from :meth:`twitchio.ChannelPointsReward.fetch_reward`. `#510 <https://github.com/PythonistaGuild/TwitchIO/pull/510>`_
109+
110+
- twitchio.web.AiohttpAdapter
111+
- Bug fixes
112+
- Fixed the redirect URL not allowing HOST/PORT when a custom domain was passed.
113+
- The redirect URL is now determined based on where the request came from.
114+
- Now correctly changes the protocol to ``https`` when SSL is used directly on the adapter.
115+
116+
- twitchio.web.StarletteAdapter
117+
- Additions
118+
- Added the ``timeout_graceful_shutdown`` keyword parameter which allows controlling how long ``Starlette/Uvicorn`` will wait to gracefully close.
119+
- Added the ``timeout_keep_alive`` keyword parameter which allows controlling how long ``Uvicorn`` will wait until closing Keep-Alive connections after not receiving any data.
120+
121+
- Bug fixes
122+
- Fixed the redirect URL not allowing HOST/PORT when a custom domain was passed.
123+
- The redirect URL is now determined based on where the request came from.
124+
- Fixed Uvicorn hanging the process when attempting to close the :class:`asyncio.Loop` on **Windows**.
125+
- After a default of ``3 seconds`` Uvicorn will be forced closed if it cannot gracefully close in this time. This time can be changed with the ``timeout_graceful_shutdown`` parameter.
126+
- Now correctly changes the protocol to ``https`` when SSL is used directly on the adapter.
85127

86128
- ext.commands
87129
- Additions
130+
- Added :class:`~twitchio.ext.commands.Translator`
131+
- Added :func:`~twitchio.ext.commands.translator`
132+
- Added :attr:`twitchio.ext.commands.Command.translator`
133+
- Added :meth:`twitchio.ext.commands.Context.send_translated`
134+
- Added :meth:`twitchio.ext.commands.Context.reply_translated`
135+
- Added :attr:`twitchio.ext.commands.Context.translator`
88136
- Added :class:`~twitchio.ext.commands.Converter`
89137
- Added :class:`~twitchio.ext.commands.UserConverter`
90138
- Added :class:`~twitchio.ext.commands.ColourConverter`
91139
- Added :class:`~twitchio.ext.commands.ColorConverter` alias.
140+
- Added :attr:`twitchio.ext.commands.Command.signature` which is a POSIX-like signature for the command.
141+
- Added :attr:`twitchio.ext.commands.Command.parameters` which is a mapping of parameter name to :class:`inspect.Parameter` associated with the command callback.
92142
- Added :attr:`twitchio.ext.commands.Command.help` which is the docstring of the command callback.
93143
- Added ``__doc__`` to :class:`~twitchio.ext.commands.Command` which takes from the callback ``__doc__``.
94144
- Added :meth:`twitchio.ext.commands.Command.run_guards`
95145
- Added :meth:`twitchio.ext.commands.Context.fetch_command`
96146
- :class:`~twitchio.ext.commands.Context` is now ``Generic`` and accepts a generic argument bound to :class:`~twitchio.ext.commands.Bot` or :class:`~twitchio.ext.commands.AutoBot`.
97147

148+
- Bug fixes
149+
- Prevent multiple :class:`~twitchio.ext.commands.Component`'s of the same name being added to a bot resulting in one overriding the other.
150+
98151

99152
3.0.0
100153
======

docs/getting-started/quickstart.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Since TwitchIO 3 is fully asynchronous we will be using `asqlite` as our library
2929
pip install -U git+https://github.com/Rapptz/asqlite.git
3030
3131
32-
Before running the code below, there just a couple more steps we need to take.
32+
Before running the code below, there are just a couple more steps we need to take. **You only have to do this sequence of steps once. Or if you change the scopes used.**
3333

3434
#. Create a new Twitch account. This will be the dedicated bot account.
3535
#. Enter your CLIENT_ID, CLIENT_SECRET, BOT_ID and OWNER_ID into the placeholders in the below example. See :ref:`faqs` on how to retrieve the ``BOT_ID`` and ``OWNER_ID``.
@@ -41,8 +41,6 @@ Before running the code below, there just a couple more steps we need to take.
4141
.. note::
4242
If you are unsure how to get the user IDs for BOT_ID and OWNER_ID, please check :ref:`bot-id-owner-id`
4343

44-
**You only have to do this sequence of steps once. Or if the scopes need to change.**
45-
4644
.. code:: python3
4745
4846
"""An example of connecting to a conduit and subscribing to EventSub when a User Authorizes the application.
@@ -90,6 +88,7 @@ Before running the code below, there just a couple more steps we need to take.
9088
owner_id=OWNER_ID,
9189
prefix="!",
9290
subscriptions=subs,
91+
force_subscribe=True,
9392
)
9493
9594
async def setup_hook(self) -> None:
@@ -236,6 +235,10 @@ Before running the code below, there just a couple more steps we need to take.
236235
237236
for row in rows:
238237
tokens.append((row["token"], row["refresh"]))
238+
239+
if row["user_id"] == BOT_ID:
240+
continue
241+
239242
subs.extend([eventsub.ChatMessageSubscription(broadcaster_user_id=row["user_id"], user_id=BOT_ID)])
240243
241244
return tokens, subs

examples/basic_conduits/main.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ def __init__(self, subs: list[eventsub.SubscriptionPayload]) -> None:
4545
owner_id=OWNER_ID,
4646
prefix="!",
4747
subscriptions=subs,
48+
force_subscribe=True,
4849
)
4950

5051
async def event_ready(self) -> None:
@@ -85,6 +86,9 @@ def main() -> None:
8586
with open(".tio.tokens.json", "rb") as fp:
8687
tokens = json.load(fp)
8788
for user_id in tokens:
89+
if user_id == BOT_ID:
90+
continue
91+
8892
subs.extend(
8993
[
9094
eventsub.ChatMessageSubscription(broadcaster_user_id=user_id, user_id=BOT_ID),

twitchio/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
__author__ = "PythonistaGuild"
2727
__license__ = "MIT"
2828
__copyright__ = "Copyright 2017-Present (c) TwitchIO, PythonistaGuild"
29-
__version__ = "3.1.0b"
29+
__version__ = "3.2.0b"
3030

3131
from . import ( # noqa: F401
3232
authentication as authentication,

twitchio/__main__.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,14 +257,12 @@ def get_version() -> str:
257257

258258

259259
def version_info() -> None:
260-
python_info = "\n".join(sys.version.split("\n"))
261-
262260
info: str = f"""
263261
twitchio : {get_version()}
264262
aiohttp : {aiohttp.__version__}
265263
266264
Python:
267-
- {python_info}
265+
- {sys.version}
268266
System:
269267
- {platform.platform()}
270268
Extras:

twitchio/client.py

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3200,12 +3200,19 @@ async def main() -> None:
32003200
simply: ``len(subscriptons) / max_per_shard`` or ``2`` whichever is greater. Note this parameter has no effect when
32013201
``shard_ids`` is explicitly passed.
32023202
subscriptions: list[twitchio.eventsub.SubscriptionPayload]
3203-
A list of any combination of EventSub subscriptions (all of which inherit from
3203+
An optional list of any combination of EventSub subscriptions (all of which inherit from
32043204
:class:`~twitchio.eventsub.SubscriptionPayload`) the Client should attempt to subscribe to when required. The
32053205
:class:`~twitchio.AutoClient` will only attempt to subscribe to these subscriptions when it creates a new Conduit. If
32063206
your Client connects to an existing Conduit either by passing ``conduit_id`` or automatically, this parameter has no
32073207
effect. In cases where you need to update an existing Conduit with new subscriptions see:
3208-
:meth:`~twitchio.AutoClient.multi_subscribe`.
3208+
:meth:`~twitchio.AutoClient.multi_subscribe` or the parameter ``force_subscribe``.
3209+
force_subscribe: bool
3210+
An optional :class:`bool` which when ``True`` will force attempt to subscribe to the subscriptions provided in the
3211+
``subscriptions`` parameter, regardless of whether a new conduit was created or not. Defaults to ``False``.
3212+
force_scale: bool
3213+
An optional :class:`bool` which when ``True`` will force the :class:`~twitchio.Conduit` associated with the
3214+
AutoClient/Bot to scale up/down to the provided amount of shards in the ``shard_ids`` parameter if provided. If the
3215+
``shard_ids`` parameter is not passed, this parameter has no effect. Defaults to ``False``.
32093216
"""
32103217

32113218
# NOTE:
@@ -3224,7 +3231,11 @@ def __init__(
32243231
**kwargs: Unpack[AutoClientOptions],
32253232
) -> None:
32263233
self._shard_ids: list[int] = kwargs.pop("shard_ids", [])
3234+
self._original_shards = self._shard_ids
32273235
self._conduit_id: str | bool | None = kwargs.pop("conduit_id", MISSING)
3236+
self._force_sub: bool = kwargs.pop("force_subscribe", False)
3237+
self._force_scale: bool = kwargs.pop("force_scale", False)
3238+
self._subbed: bool = False
32283239

32293240
if self._conduit_id is MISSING or self._conduit_id is None:
32303241
logger.warning(
@@ -3352,7 +3363,14 @@ async def _setup(self) -> None:
33523363
# TODO: Maybe log currernt conduit info?
33533364
raise MissingConduit("No conduit could be found with the provided ID or a new one can not be created.")
33543365

3366+
if self._force_scale and self._original_shards:
3367+
logger.info("Scaling %r to %d shards.", len(self._original_shards))
3368+
await self._conduit_info.update_shard_count(len(self._original_shards), assign_transports=False)
3369+
33553370
await self._associate_shards(self._shard_ids)
3371+
if self._force_sub and not self._subbed:
3372+
await self.multi_subscribe(self._initial_subs)
3373+
33563374
await self.setup_hook()
33573375

33583376
self._setup_called = True
@@ -3429,7 +3447,12 @@ async def _process_batched(self, batched: list[Websocket]) -> None:
34293447
await self._conduit_info._update_shards(payloads)
34303448
self._conduit_info._sockets.update({str(socket._shard_id): socket for socket in batched})
34313449

3432-
logger.info("Associated shards with %r successfully.", self._conduit_info)
3450+
logger.info(
3451+
"Associated shards with %r successfully. Shards: %d / %d (connected / Conduit total).",
3452+
self._conduit_info,
3453+
len(self._conduit_info.websockets),
3454+
self._conduit_info.shard_count,
3455+
)
34333456

34343457
async def _associate_shards(self, shard_ids: list[int]) -> None:
34353458
await self._associate_lock.acquire()
@@ -3484,6 +3507,7 @@ async def _generate_new_conduit(self) -> Conduit:
34843507
if self._initial_subs:
34853508
logger.info("Attempting to do an initial subscription on new conduit: %r.", self._conduit_info)
34863509
await self._multi_sub(self._initial_subs, stop_on_error=False)
3510+
self._subbed = True
34873511

34883512
return new
34893513

@@ -3616,14 +3640,25 @@ async def multi_subscribe(
36163640
)
36173641
return task
36183642

3643+
async def _close_sockets(self) -> None:
3644+
socks = self._conduit_info._sockets.values()
3645+
logger.info("Attempting to close %d associated Conduit Websockets.", len(socks))
3646+
3647+
tasks: list[asyncio.Task[None]] = [asyncio.create_task(s.close()) for s in socks]
3648+
await asyncio.wait(tasks)
3649+
3650+
logger.info("Successfully closed %d Conduit Websockets on %r.", len(socks), self)
3651+
36193652
async def close(self, **options: Any) -> None:
36203653
if self._closing:
36213654
return
36223655

36233656
self._closing = True
36243657

3625-
for socket in self._conduit_info.websockets.values():
3626-
await socket.close()
3658+
try:
3659+
await self._close_sockets()
3660+
except Exception as e:
3661+
logger.warning("An error occurred during the cleanup of Conduit Websockets: %s", e)
36273662

36283663
self._conduit_info._sockets.clear()
36293664

twitchio/ext/commands/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@
2929
from .cooldowns import *
3030
from .core import *
3131
from .exceptions import *
32+
from .translators import *

twitchio/ext/commands/bot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -812,7 +812,7 @@ def modules(self) -> Mapping[str, types.ModuleType]:
812812
class AutoBot(Bot, AutoClient):
813813
"""The TwitchIO :class:`~twitchio.ext.commands.AutoBot` class used to easily manage Twitch Conduits and Shards.
814814
815-
This class beahves idential to :class:`~twitchio.ext.commands.Bot` with the addition of inheriting from
815+
This class behaves identically to :class:`~twitchio.ext.commands.Bot` with the addition of inheriting from
816816
:class:`~twitchio.AutoClient`. See: :class:`~twitchio.AutoClient` for more details on how this class differs from
817817
the :class:`~twitchio.ext.commands.Bot` which inherits from :class:`~twitchio.Client`.
818818
"""

0 commit comments

Comments
 (0)