Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wiseplat
GitHub Repository: wiseplat/python-code
Path: blob/master/ invest-robot-contest_TinkoffBotTwitch-main/venv/lib/python3.8/site-packages/twitchio/client.py
7796 views
1
"""
2
The MIT License (MIT)
3
4
Copyright (c) 2017-2021 TwitchIO
5
6
Permission is hereby granted, free of charge, to any person obtaining a
7
copy of this software and associated documentation files (the "Software"),
8
to deal in the Software without restriction, including without limitation
9
the rights to use, copy, modify, merge, publish, distribute, sublicense,
10
and/or sell copies of the Software, and to permit persons to whom the
11
Software is furnished to do so, subject to the following conditions:
12
13
The above copyright notice and this permission notice shall be included in
14
all copies or substantial portions of the Software.
15
16
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22
DEALINGS IN THE SOFTWARE.
23
"""
24
25
import asyncio
26
import inspect
27
import warnings
28
import logging
29
import traceback
30
import sys
31
from typing import Union, Callable, List, Optional, Tuple, Any, Coroutine
32
33
from twitchio.errors import HTTPException
34
from . import models
35
from .websocket import WSConnection
36
from .http import TwitchHTTP
37
from .channel import Channel
38
from .message import Message
39
from .user import User, PartialUser, SearchUser
40
from .cache import user_cache, id_cache
41
42
__all__ = ("Client",)
43
44
logger = logging.getLogger("twitchio.client")
45
46
47
class Client:
48
"""TwitchIO Client object that is used to interact with the Twitch API and connect to Twitch IRC over websocket.
49
50
Parameters
51
------------
52
token: :class:`str`
53
An OAuth Access Token to login with on IRC and interact with the API.
54
client_secret: Optional[:class:`str`]
55
An optional application Client Secret used to generate Access Tokens automatically.
56
initial_channels: Optional[Union[:class:`list`, :class:`tuple`, Callable]]
57
An optional list, tuple or callable which contains channel names to connect to on startup.
58
If this is a callable, it must return a list or tuple.
59
loop: Optional[:class:`asyncio.AbstractEventLoop`]
60
The event loop the client will use to run.
61
heartbeat: Optional[float]
62
An optional float in seconds to send a PING message to the server. Defaults to 30.0.
63
64
Attributes
65
------------
66
loop: :class:`asyncio.AbstractEventLoop`
67
The event loop the Client uses.
68
"""
69
70
def __init__(
71
self,
72
token: str,
73
*,
74
client_secret: str = None,
75
initial_channels: Union[list, tuple, Callable] = None,
76
loop: asyncio.AbstractEventLoop = None,
77
heartbeat: Optional[float] = 30.0,
78
):
79
80
self.loop: asyncio.AbstractEventLoop = loop or asyncio.get_event_loop()
81
self._heartbeat = heartbeat
82
83
token = token.replace("oauth:", "")
84
85
self._http = TwitchHTTP(self, api_token=token, client_secret=client_secret)
86
self._connection = WSConnection(
87
client=self,
88
token=token,
89
loop=self.loop,
90
initial_channels=initial_channels,
91
heartbeat=heartbeat,
92
)
93
94
self._events = {}
95
self._waiting: List[Tuple[str, Callable[[...], bool], asyncio.Future]] = []
96
97
@classmethod
98
def from_client_credentials(
99
cls,
100
client_id: str,
101
client_secret: str,
102
*,
103
loop: asyncio.AbstractEventLoop = None,
104
heartbeat: Optional[float] = 30.0,
105
) -> "Client":
106
"""
107
creates a client application token from your client credentials.
108
109
.. warning:
110
111
this is not suitable for logging in to IRC.
112
113
.. note:
114
115
This classmethod skips :meth:`~.__init__`
116
117
Parameters
118
------------
119
client_id: :class:`str`
120
121
client_secret: :class:`str`
122
An application Client Secret used to generate Access Tokens automatically.
123
loop: Optional[:class:`asyncio.AbstractEventLoop`]
124
The event loop the client will use to run.
125
126
Returns
127
--------
128
A new :class:`Client` instance
129
"""
130
self = cls.__new__(cls)
131
self.loop = loop or asyncio.get_event_loop()
132
self._http = TwitchHTTP(self, client_id=client_id, client_secret=client_secret)
133
self._heartbeat = heartbeat
134
self._connection = WSConnection(
135
client=self,
136
loop=self.loop,
137
initial_channels=None,
138
heartbeat=self._heartbeat,
139
) # The only reason we're even creating this is to avoid attribute errors
140
self._events = {}
141
self._waiting = []
142
return self
143
144
def run(self):
145
"""
146
A blocking function that starts the asyncio event loop,
147
connects to the twitch IRC server, and cleans up when done.
148
"""
149
try:
150
self.loop.create_task(self.connect())
151
self.loop.run_forever()
152
except KeyboardInterrupt:
153
pass
154
finally:
155
self.loop.run_until_complete(self.close())
156
self.loop.close()
157
158
async def start(self):
159
"""|coro|
160
161
Connects to the twitch IRC server, and cleanly disconnects when done.
162
"""
163
if self.loop is not asyncio.get_running_loop():
164
raise RuntimeError(
165
f"Attempted to start a {self.__class__.__name__} instance on a different loop "
166
f"than the one it was initialized with."
167
)
168
try:
169
await self.connect()
170
finally:
171
await self.close()
172
173
async def connect(self):
174
"""|coro|
175
176
Connects to the twitch IRC server
177
"""
178
await self._connection._connect()
179
180
async def close(self):
181
"""|coro|
182
183
Cleanly disconnects from the twitch IRC server
184
"""
185
await self._connection._close()
186
187
def run_event(self, event_name, *args):
188
name = f"event_{event_name}"
189
logger.debug(f"dispatching event {event_name}")
190
191
async def wrapped(func):
192
try:
193
await func(*args)
194
except Exception as e:
195
if name == "event_error":
196
# don't enter a dispatch loop!
197
raise
198
199
self.run_event("error", e)
200
201
inner_cb = getattr(self, name, None)
202
if inner_cb is not None:
203
if inspect.iscoroutinefunction(inner_cb):
204
self.loop.create_task(wrapped(inner_cb))
205
else:
206
warnings.warn(
207
f"event '{name}' callback is not a coroutine",
208
category=RuntimeWarning,
209
)
210
211
if name in self._events:
212
for event in self._events[name]:
213
self.loop.create_task(wrapped(event))
214
215
for e, check, future in self._waiting:
216
if e == event_name:
217
if check(*args):
218
future.set_result(args)
219
if future.done():
220
self._waiting.remove((e, check, future))
221
222
def add_event(self, callback: Callable, name: str = None) -> None:
223
try:
224
func = callback.func
225
except AttributeError:
226
func = callback
227
228
if not inspect.iscoroutine(func) and not inspect.iscoroutinefunction(func):
229
raise ValueError("Event callback must be a coroutine")
230
231
event_name = name or callback.__name__
232
callback._event = event_name # used to remove the event
233
234
if event_name in self._events:
235
self._events[event_name].append(callback)
236
237
else:
238
self._events[event_name] = [callback]
239
240
def remove_event(self, callback: Callable) -> bool:
241
if not hasattr(callback, "_event"):
242
raise ValueError("Event callback is not a registered event")
243
244
if callback in self._events[callback._event]:
245
self._events[callback._event].remove(callback)
246
return True
247
248
return False
249
250
def event(self, name: str = None) -> Callable:
251
def decorator(func: Callable) -> Callable:
252
self.add_event(func, name)
253
return func
254
255
return decorator
256
257
async def wait_for(
258
self,
259
event: str,
260
predicate: Callable[[], bool] = lambda *a: True,
261
*,
262
timeout=60.0,
263
) -> Tuple[Any]:
264
"""|coro|
265
266
267
Waits for an event to be dispatched, then returns the events data
268
269
Parameters
270
-----------
271
event: :class:`str`
272
The event to wait for. Do not include the `event_` prefix
273
predicate: Callable[[...], bool]
274
A check that is fired when the desired event is dispatched. if the check returns false,
275
the waiting will continue until the timeout.
276
timeout: :class:`int`
277
How long to wait before timing out and raising an error.
278
279
Returns
280
--------
281
The arguments passed to the event.
282
"""
283
fut = self.loop.create_future()
284
tup = (event, predicate, fut)
285
self._waiting.append(tup)
286
values = await asyncio.wait_for(fut, timeout)
287
return values
288
289
def wait_for_ready(self) -> Coroutine[Any, Any, bool]:
290
"""|coro|
291
292
Waits for the underlying connection to finish startup
293
294
Returns
295
--------
296
:class:`bool` The state of the underlying flag. This will always be ``True``
297
"""
298
return self._connection.is_ready.wait()
299
300
@id_cache()
301
def get_channel(self, name: str) -> Optional[Channel]:
302
"""Retrieve a channel from the cache.
303
304
Parameters
305
-----------
306
name: str
307
The channel name to retrieve from cache. Returns None if no channel was found.
308
309
Returns
310
--------
311
:class:`.Channel`
312
"""
313
name = name.lower()
314
315
if name in self._connection._cache:
316
# Basically the cache doesn't store channels naturally, instead it stores a channel key
317
# With the associated users as a set.
318
# We create a Channel here and return it only if the cache has that channel key.
319
320
return Channel(name=name, websocket=self._connection)
321
322
async def join_channels(self, channels: Union[List[str], Tuple[str]]):
323
"""|coro|
324
325
326
Join the specified channels.
327
328
Parameters
329
------------
330
channels: Union[List[str], Tuple[str]]
331
The channels in either a list or tuple form to join.
332
"""
333
await self._connection.join_channels(*channels)
334
335
@property
336
def connected_channels(self) -> List[Channel]:
337
"""A list of currently connected :class:`.Channel`"""
338
return [self.get_channel(x) for x in self._connection._cache.keys()]
339
340
@property
341
def events(self):
342
"""A mapping of events name to coroutine."""
343
return self._events
344
345
@property
346
def nick(self):
347
"""The IRC bots nick."""
348
return self._http.nick or self._connection.nick
349
350
@property
351
def user_id(self):
352
"""The IRC bot user id."""
353
return self._http.user_id or self._connection.user_id
354
355
def create_user(self, user_id: int, user_name: str) -> PartialUser:
356
"""
357
A helper method to create a :class:`twitchio.PartialUser` from a user id and user name.
358
359
Parameters
360
-----------
361
user_id: :class:`int`
362
The id of the user
363
user_name: :class:`str`
364
The name of the user
365
366
Returns
367
--------
368
:class:`twitchio.PartialUser`
369
"""
370
return PartialUser(self._http, user_id, user_name)
371
372
@user_cache()
373
async def fetch_users(
374
self,
375
names: List[str] = None,
376
ids: List[int] = None,
377
token: str = None,
378
force=False,
379
) -> List[User]:
380
"""|coro|
381
382
Fetches users from the helix API
383
384
Parameters
385
-----------
386
names: Optional[List[:class:`str`]]
387
usernames of people to fetch
388
ids: Optional[List[:class:`str`]]
389
ids of people to fetch
390
token: Optional[:class:`str`]
391
An optional OAuth token to use instead of the bot OAuth token
392
force: :class:`bool`
393
whether to force a fetch from the api, or check the cache first. Defaults to False
394
395
Returns
396
--------
397
List[:class:`twitchio.User`]
398
"""
399
# the forced argument doesnt actually get used here, it gets used by the cache wrapper.
400
# But we'll include it in the args here so that sphinx catches it
401
assert names or ids
402
data = await self._http.get_users(ids, names, token=token)
403
return [User(self._http, x) for x in data]
404
405
async def fetch_clips(self, ids: List[str]):
406
"""|coro|
407
408
Fetches clips by clip id.
409
To fetch clips by user id, use :meth:`twitchio.PartialUser.fetch_clips`
410
411
Parameters
412
-----------
413
ids: List[:class:`str`]
414
A list of clip ids
415
416
Returns
417
--------
418
List[:class:`twitchio.Clip`]
419
"""
420
data = await self._http.get_clips(ids=ids)
421
return [models.Clip(self._http, d) for d in data]
422
423
async def fetch_channel(self, broadcaster: str):
424
"""Retrieve channel information from the API.
425
426
Parameters
427
-----------
428
broadcaster: str
429
The channel name or ID to request from API. Returns empty dict if no channel was found.
430
431
Returns
432
--------
433
:class:`twitchio.ChannelInfo`
434
"""
435
436
if not broadcaster.isdigit():
437
get_id = await self.fetch_users(names=[broadcaster.lower()])
438
if not get_id:
439
raise IndexError("Invalid channel name.")
440
broadcaster = get_id[0].id
441
try:
442
data = await self._http.get_channels(broadcaster)
443
444
from .models import ChannelInfo
445
446
return ChannelInfo(self._http, data=data[0])
447
448
except HTTPException:
449
raise HTTPException("Incorrect channel ID.")
450
451
async def fetch_videos(
452
self,
453
ids: List[int] = None,
454
game_id: int = None,
455
user_id: int = None,
456
period=None,
457
sort=None,
458
type=None,
459
language=None,
460
):
461
"""|coro|
462
463
Fetches videos by id, game id, or user id
464
465
Parameters
466
-----------
467
ids: Optional[List[:class:`int`]]
468
A list of video ids
469
game_id: Optional[:class:`int`]
470
A game to fetch videos from
471
user_id: Optional[:class:`int`]
472
A user to fetch videos from. See :meth:`twitchio.PartialUser.fetch_videos`
473
period: Optional[:class:`str`]
474
The period for which to fetch videos. Valid values are `all`, `day`, `week`, `month`. Defaults to `all`.
475
Cannot be used when video id(s) are passed
476
sort: :class:`str`
477
Sort orders of the videos. Valid values are `time`, `trending`, `views`, Defaults to `time`.
478
Cannot be used when video id(s) are passed
479
type: Optional[:class:`str`]
480
Type of the videos to fetch. Valid values are `upload`, `archive`, `highlight`. Defaults to `all`.
481
Cannot be used when video id(s) are passed
482
language: Optional[:class:`str`]
483
Language of the videos to fetch. Must be an `ISO-639-1 <https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes>`_ two letter code.
484
Cannot be used when video id(s) are passed
485
486
Returns
487
--------
488
List[:class:`twitchio.Video`]
489
"""
490
from .models import Video
491
492
data = await self._http.get_videos(
493
ids,
494
user_id=user_id,
495
game_id=game_id,
496
period=period,
497
sort=sort,
498
type=type,
499
language=language,
500
)
501
return [Video(self._http, x) for x in data]
502
503
async def fetch_cheermotes(self, user_id: int = None):
504
"""|coro|
505
506
507
Fetches cheermotes from the twitch API
508
509
Parameters
510
-----------
511
user_id: Optional[:class:`int`]
512
The channel id to fetch from.
513
514
Returns
515
--------
516
List[:class:`twitchio.CheerEmote`]
517
"""
518
data = await self._http.get_cheermotes(str(user_id) if user_id else None)
519
return [models.CheerEmote(self._http, x) for x in data]
520
521
async def fetch_top_games(self) -> List[models.Game]:
522
"""|coro|
523
524
Fetches the top games from the api
525
526
Returns
527
--------
528
List[:class:`twitchio.Game`]
529
"""
530
data = await self._http.get_top_games()
531
return [models.Game(d) for d in data]
532
533
async def fetch_games(self, ids: List[int] = None, names: List[str] = None) -> List[models.Game]:
534
"""|coro|
535
536
Fetches games by id or name.
537
At least one id or name must be provided
538
539
Parameters
540
-----------
541
ids: Optional[List[:class:`int`]]
542
An optional list of game ids
543
names: Optional[List[:class:`str`]]
544
An optional list of game names
545
546
Returns
547
--------
548
List[:class:`twitchio.Game`]
549
"""
550
data = await self._http.get_games(ids, names)
551
return [models.Game(d) for d in data]
552
553
async def fetch_tags(self, ids: List[str] = None):
554
"""|coro|
555
556
Fetches stream tags.
557
558
Parameters
559
-----------
560
ids: Optional[List[:class:`str`]]
561
The ids of the tags to fetch
562
563
Returns
564
--------
565
List[:class:`twitchio.Tag`]
566
"""
567
data = await self._http.get_stream_tags(ids)
568
return [models.Tag(x) for x in data]
569
570
async def fetch_streams(
571
self,
572
user_ids: List[int] = None,
573
game_ids: List[int] = None,
574
user_logins: List[str] = None,
575
languages: List[str] = None,
576
token: str = None,
577
):
578
"""|coro|
579
580
Fetches live streams from the helix API
581
582
Parameters
583
-----------
584
user_ids: Optional[List[:class:`int`]]
585
user ids of people whose streams to fetch
586
game_ids: Optional[List[:class:`int`]]
587
game ids of streams to fetch
588
user_logins: Optional[List[:class:`str`]]
589
user login names of people whose streams to fetch
590
languages: Optional[List[:class:`str`]]
591
language for the stream(s). ISO 639-1 or two letter code for supported stream language
592
token: Optional[:class:`str`]
593
An optional OAuth token to use instead of the bot OAuth token
594
595
Returns
596
--------
597
List[:class:`twitchio.Stream`]
598
"""
599
from .models import Stream
600
601
assert user_ids or game_ids or user_logins
602
data = await self._http.get_streams(
603
game_ids=game_ids,
604
user_ids=user_ids,
605
user_logins=user_logins,
606
languages=languages,
607
token=token,
608
)
609
return [Stream(self._http, x) for x in data]
610
611
async def fetch_teams(
612
self,
613
team_name: str = None,
614
team_id: str = None,
615
):
616
"""|coro|
617
618
Fetches information for a specific Twitch Team.
619
620
Parameters
621
-----------
622
name: Optional[:class:`str`]
623
Team name to fetch
624
id: Optional[:class:`str`]
625
Team id to fetch
626
627
Returns
628
--------
629
List[:class:`twitchio.Team`]
630
"""
631
from .models import Team
632
633
assert team_name or team_id
634
data = await self._http.get_teams(
635
team_name=team_name,
636
team_id=team_id,
637
)
638
639
return Team(self._http, data[0])
640
641
async def search_categories(self, query: str):
642
"""|coro|
643
644
Searches twitches categories
645
646
Parameters
647
-----------
648
query: :class:`str`
649
The query to search for
650
651
Returns
652
--------
653
List[:class:`twitchio.Game`]
654
"""
655
data = await self._http.get_search_categories(query)
656
return [models.Game(x) for x in data]
657
658
async def search_channels(self, query: str, *, live_only=False):
659
"""|coro|
660
661
Searches channels for the given query
662
663
Parameters
664
-----------
665
query: :class:`str`
666
The query to search for
667
live_only: :class:`bool`
668
Only search live channels. Defaults to False
669
670
Returns
671
--------
672
List[:class:`twitchio.SearchUser`]
673
"""
674
data = await self._http.get_search_channels(query, live=live_only)
675
return [SearchUser(self._http, x) for x in data]
676
677
async def delete_videos(self, token: str, ids: List[int]) -> List[int]:
678
"""|coro|
679
680
Delete videos from the api. Returns the video ids that were successfully deleted.
681
682
Parameters
683
-----------
684
token: :class:`str`
685
An oauth token with the channel:manage:videos scope
686
ids: List[:class:`int`]
687
A list of video ids from the channel of the oauth token to delete
688
689
Returns
690
--------
691
List[:class:`int`]
692
"""
693
resp = []
694
for chunk in [ids[x : x + 3] for x in range(0, len(ids), 3)]:
695
resp.append(await self._http.delete_videos(token, chunk))
696
697
return resp
698
699
async def get_webhook_subscriptions(self):
700
"""|coro|
701
702
Fetches your current webhook subscriptions. Requires your bot to be logged in with an app access token.
703
704
Returns
705
--------
706
List[:class:`twitchio.WebhookSubscription`]
707
"""
708
data = await self._http.get_webhook_subs()
709
return [models.WebhookSubscription(x) for x in data]
710
711
async def event_token_expired(self):
712
"""|coro|
713
714
715
A special event called when the oauth token expires. This is a hook into the http system, it will call this
716
when a call to the api fails due to a token expiry. This function should return either a new token, or `None`.
717
Returning `None` will cause the client to attempt an automatic token generation.
718
719
.. note::
720
This event is a callback hook. It is not a dispatched event. Any errors raised will be passed to the
721
:func:`~event_error` event.
722
"""
723
return None
724
725
async def event_mode(self, channel: Channel, user: User, status: str):
726
"""|coro|
727
728
729
Event called when a MODE is received from Twitch.
730
731
Parameters
732
------------
733
channel: :class:`.Channel`
734
Channel object relevant to the MODE event.
735
user: :class:`.User`
736
User object containing relevant information to the MODE.
737
status: str
738
The JTV status received by Twitch. Could be either o+ or o-.
739
Indicates a moderation promotion/demotion to the :class:`.User`
740
"""
741
pass
742
743
async def event_userstate(self, user: User):
744
"""|coro|
745
746
747
Event called when a USERSTATE is received from Twitch.
748
749
Parameters
750
------------
751
user: :class:`.User`
752
User object containing relevant information to the USERSTATE.
753
"""
754
pass
755
756
async def event_raw_usernotice(self, channel: Channel, tags: dict):
757
"""|coro|
758
759
760
Event called when a USERNOTICE is received from Twitch.
761
Since USERNOTICE's can be fairly complex and vary, the following sub-events are available:
762
763
:meth:`event_usernotice_subscription` :
764
Called when a USERNOTICE Subscription or Re-subscription event is received.
765
766
.. tip::
767
768
For more information on how to handle USERNOTICE's visit:
769
https://dev.twitch.tv/docs/irc/tags/#usernotice-twitch-tags
770
771
772
Parameters
773
------------
774
channel: :class:`.Channel`
775
Channel object relevant to the USERNOTICE event.
776
tags : dict
777
A dictionary with the relevant information associated with the USERNOTICE.
778
This could vary depending on the event.
779
"""
780
pass
781
782
async def event_usernotice_subscription(self, metadata):
783
"""|coro|
784
785
786
Event called when a USERNOTICE subscription or re-subscription event is received from Twitch.
787
788
Parameters
789
------------
790
metadata: :class:`NoticeSubscription`
791
The object containing various metadata about the subscription event.
792
For ease of use, this contains a :class:`User` and :class:`Channel`.
793
794
"""
795
pass
796
797
async def event_part(self, user: User):
798
"""|coro|
799
800
801
Event called when a PART is received from Twitch.
802
803
Parameters
804
------------
805
user: :class:`.User`
806
User object containing relevant information to the PART.
807
"""
808
pass
809
810
async def event_join(self, channel: Channel, user: User):
811
"""|coro|
812
813
814
Event called when a JOIN is received from Twitch.
815
816
Parameters
817
------------
818
channel: :class:`.Channel`
819
The channel associated with the JOIN.
820
user: :class:`.User`
821
User object containing relevant information to the JOIN.
822
"""
823
pass
824
825
async def event_message(self, message: Message):
826
"""|coro|
827
828
829
Event called when a PRIVMSG is received from Twitch.
830
831
Parameters
832
------------
833
message: :class:`.Message`
834
Message object containing relevant information.
835
"""
836
pass
837
838
async def event_error(self, error: Exception, data: str = None):
839
"""|coro|
840
841
842
Event called when an error occurs while processing data.
843
844
Parameters
845
------------
846
error: Exception
847
The exception raised.
848
data: str
849
The raw data received from Twitch. Depending on how this is called, this could be None.
850
851
Example
852
---------
853
.. code:: py
854
855
@bot.event()
856
async def event_error(error, data):
857
traceback.print_exception(type(error), error, error.__traceback__, file=sys.stderr)
858
"""
859
traceback.print_exception(type(error), error, error.__traceback__, file=sys.stderr)
860
861
async def event_ready(self):
862
"""|coro|
863
864
865
Event called when the Bot has logged in and is ready.
866
867
Example
868
---------
869
.. code:: py
870
871
@bot.event()
872
async def event_ready():
873
print(f'Logged into Twitch | {bot.nick}')
874
"""
875
pass
876
877
async def event_raw_data(self, data: str):
878
"""|coro|
879
880
881
Event called with the raw data received by Twitch.
882
883
Parameters
884
------------
885
data: str
886
The raw data received from Twitch.
887
888
Example
889
---------
890
.. code:: py
891
892
@bot.event()
893
async def event_raw_data(data):
894
print(data)
895
"""
896
pass
897
898