Skip to content

Commit 18037c9

Browse files
committed
Add conduit example
1 parent 06a1921 commit 18037c9

File tree

1 file changed

+106
-0
lines changed

1 file changed

+106
-0
lines changed

examples/simple_conduit/main.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"""An example of connecting to a conduit and subscribing to EventSub when a User Authorizes the application.
2+
3+
This bot can be restarted as many times without needing to subscribe or worry about tokens:
4+
- Tokens are stored in '.tio.tokens.json' by default
5+
- Subscriptions last 72 hours after the bot is disconnected and refresh when the bot starts.
6+
7+
1. Fill out the constants below; CLIENT_ID etc...
8+
2. Start the bot
9+
3. Visit (Logged in on the BOT Account): http://localhost:4343/oauth?scopes=user:read:chat%20user:write:chat%20user:bot
10+
- You only need to do this once usually ^
11+
4. You can log in as any user and visit: http://localhost:4343/oauth?scopes=channel:bot to allow this bot to chat/commands
12+
13+
Note: you can adjust the scopes however you need.
14+
"""
15+
16+
import asyncio
17+
import json
18+
import logging
19+
20+
import twitchio
21+
from twitchio import eventsub
22+
from twitchio.ext import commands
23+
24+
25+
LOGGER: logging.Logger = logging.getLogger(__name__)
26+
27+
28+
# Store and retrieve these from a .env or similar, but for example showcase you can just full out the below:
29+
CLIENT_ID = ""
30+
CLIENT_SECRET = ""
31+
BOT_ID = ""
32+
OWNER_ID = ""
33+
34+
35+
class Bot(commands.AutoBot):
36+
# AutoBot will automatically create and connect to a Conduit for us, ensuring there are an appropriate number of shards.
37+
# Conduits make it easier to manage subscriptions to EventSub as they only require App Tokens and we don't need to
38+
# ...subscribe continually: The Bot will maintain subscriptions for 72 hours after shutting down.
39+
40+
def __init__(self, subs: list[eventsub.SubscriptionPayload]) -> None:
41+
super().__init__(
42+
client_id=CLIENT_ID,
43+
client_secret=CLIENT_SECRET,
44+
bot_id=BOT_ID,
45+
owner_id=OWNER_ID,
46+
prefix="!",
47+
subscriptions=subs,
48+
)
49+
50+
async def event_ready(self) -> None:
51+
LOGGER.info("Successfully logged in as: %s", self.user)
52+
53+
async def event_oauth_authorized(self, payload: twitchio.authentication.UserTokenPayload) -> None:
54+
await self.add_token(payload.access_token, payload.refresh_token)
55+
56+
if not payload.user_id:
57+
return
58+
59+
if payload.user_id == self.bot_id:
60+
# We usually don't want subscribe to events on the bots channel...
61+
return
62+
63+
subs: list[eventsub.SubscriptionPayload] = [
64+
eventsub.ChatMessageSubscription(broadcaster_user_id=payload.user_id, user_id=self.bot_id),
65+
eventsub.StreamOnlineSubscription(broadcaster_user_id=payload.user_id),
66+
]
67+
68+
resp: twitchio.MultiSubscribePayload = await self.multi_subscribe(subs)
69+
if resp.errors:
70+
LOGGER.warning("Failed to subscribe to: %r, for user: %s", resp.errors, payload.user_id)
71+
72+
async def event_message(self, payload: twitchio.ChatMessage) -> None:
73+
# Just for example purposes...
74+
LOGGER.info("[%s]: %s", payload.chatter, payload.text)
75+
await super().event_message(payload)
76+
77+
78+
def main() -> None:
79+
twitchio.utils.setup_logging(level=logging.INFO)
80+
81+
# For example purposes we are just using the default token storage, but you could store in a database etc..
82+
# Generate a list of subscriptions for each user token we have...
83+
subs: list[eventsub.SubscriptionPayload] = []
84+
85+
with open(".tio.tokens.json", "rb") as fp:
86+
tokens = json.load(fp)
87+
for user_id in tokens:
88+
subs.extend(
89+
[
90+
eventsub.ChatMessageSubscription(broadcaster_user_id=user_id, user_id=BOT_ID),
91+
eventsub.StreamOnlineSubscription(broadcaster_user_id=user_id),
92+
]
93+
)
94+
95+
async def runner() -> None:
96+
async with Bot(subs=subs) as bot:
97+
await bot.start()
98+
99+
try:
100+
asyncio.run(runner())
101+
except KeyboardInterrupt:
102+
LOGGER.warning("Shutting down due to KeyboardInterrupt.")
103+
104+
105+
if __name__ == "__main__":
106+
main()

0 commit comments

Comments
 (0)