Skip to content

Commit bad0a5a

Browse files
author
Eviee Py
committed
Add alpha example
1 parent b45fe9f commit bad0a5a

File tree

2 files changed

+169
-2
lines changed

2 files changed

+169
-2
lines changed

examples/alpha_example/main.py

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import asyncio
2+
import logging
3+
import sqlite3
4+
5+
import asqlite
6+
import twitchio
7+
from twitchio.ext import commands
8+
from twitchio import eventsub
9+
10+
11+
LOGGER: logging.Logger = logging.getLogger("Bot")
12+
13+
# Simple example for TwitchIO V3 Alpha...
14+
# Instructions:
15+
16+
# You need to install: https://github.com/Rapptz/asqlite
17+
# pip install -U git+https://github.com/Rapptz/asqlite.git
18+
19+
# 1.) Comment out lines: 54-60 (The subscriptions)
20+
# 2.) Add the Twitch Developer Console and Create an Application
21+
# 3.) Add: http://localhost:4343/oauth/callback as the callback URL
22+
# 4.) Enter your CLIENT_ID, CLIENT_SECRET, BOT_ID and OWNER_ID
23+
# 5.) Run the bot.
24+
# 6.) Logged in the bots user account, visit: http://localhost:4343/oauth?scopes=user:read:chat%20user:write:chat%20user:bot
25+
# 7.) Logged in as your personal user account, visit: http://localhost:4343/oauth?scopes=channel:bot
26+
# 8.) Uncomment lines: 54-60 (The subscriptions)
27+
# 9.) Restart the bot.
28+
# You only have to do the above once for this example.
29+
30+
31+
CLIENT_ID: str = "..."
32+
CLIENT_SECRET: str = "..."
33+
BOT_ID = "..." # The Account ID of the bot user...
34+
OWNER_ID = "..." # Your personal User ID..
35+
36+
37+
class Bot(commands.Bot):
38+
def __init__(self, *, token_database: asqlite.Pool) -> None:
39+
self.token_database = token_database
40+
super().__init__(
41+
client_id=CLIENT_ID,
42+
client_secret=CLIENT_SECRET,
43+
bot_id=BOT_ID,
44+
owner_id=OWNER_ID,
45+
prefix="!",
46+
)
47+
48+
async def setup_hook(self) -> None:
49+
# Add our component which contains our commands...
50+
await self.add_component(MyComponent(self))
51+
52+
# Subscribe to read chat (event_message) from our channel as the bot...
53+
# This creates and opens a websocket to Twitch EventSub...
54+
subscription = eventsub.ChatMessageSubscription(broadcaster_user_id=OWNER_ID, user_id=BOT_ID)
55+
await self.subscribe_websocket(payload=subscription)
56+
57+
# Subscribe and listen to when a stream goes live..
58+
# For this example listen to our own stream...
59+
subscription = eventsub.StreamOnlineSubscription(broadcaster_user_id=OWNER_ID)
60+
await self.subscribe_websocket(payload=subscription)
61+
62+
async def add_token(self, token: str, refresh: str) -> twitchio.authentication.ValidateTokenPayload:
63+
# Make sure to call super() as it will add the tokens interally and return us some data...
64+
resp: twitchio.authentication.ValidateTokenPayload = await super().add_token(token, refresh)
65+
66+
# Store our tokens in a simle SQLite Database when they are authorized...
67+
query = """
68+
INSERT INTO tokens (user_id, token, refresh)
69+
VALUES (?, ?, ?)
70+
ON CONFLICT(user_id)
71+
DO UPDATE SET
72+
token = excluded.token,
73+
refresh = excluded.refresh;
74+
"""
75+
76+
async with self.token_database.acquire() as connection:
77+
await connection.execute(query, (resp.user_id, token, refresh))
78+
79+
LOGGER.info("Added token to the database for user: %s", resp.user_id)
80+
return resp
81+
82+
async def load_tokens(self, path: str | None = None) -> None:
83+
# We don't need to call this manually, it is called in .login() from .start() internally...
84+
85+
async with self.token_database.acquire() as connection:
86+
rows: list[sqlite3.Row] = await connection.fetchall("""SELECT * from tokens""")
87+
88+
for row in rows:
89+
await self.add_token(row["token"], row["refresh"])
90+
91+
async def setup_database(self) -> None:
92+
# Create our token table, if it doesn't exist..
93+
query = """CREATE TABLE IF NOT EXISTS tokens(user_id TEXT PRIMARY KEY, token TEXT NOT NULL, refresh TEXT NOT NULL)"""
94+
async with self.token_database.acquire() as connection:
95+
await connection.execute(query)
96+
97+
async def event_ready(self) -> None:
98+
LOGGER.info("Successfully logged in as: %s", self.bot_id)
99+
100+
101+
class MyComponent(commands.Component):
102+
def __init__(self, bot: Bot):
103+
# Passing args is not required...
104+
# We pass bot here as an example...
105+
self.bot = bot
106+
107+
@commands.command(aliases=["hello", "howdy", "hey"])
108+
async def hi(self, ctx: commands.Context) -> None:
109+
"""Simple command that says hello!
110+
111+
!hi, !hello, !howdy, !hey
112+
"""
113+
await ctx.reply(f"Hello {ctx.chatter.mention}!")
114+
115+
@commands.group(invoke_fallback=True)
116+
async def socials(self, ctx: commands.Context) -> None:
117+
"""Group command for our social links.
118+
119+
!socials
120+
"""
121+
await ctx.send("discord.gg/..., youtube.com/..., twitch.tv/...")
122+
123+
@socials.command(name="discord")
124+
async def socials_discord(self, ctx: commands.Context) -> None:
125+
"""Sub command of socials that sends only our discord invite.
126+
127+
!socials discord
128+
"""
129+
await ctx.send("discord.gg/...")
130+
131+
@commands.command(aliases=["repeat"])
132+
@commands.is_moderator()
133+
async def say(self, ctx: commands.Context, *, content: str) -> None:
134+
"""Moderator only command which repeats back what you say.
135+
136+
!say hello world, !repeat I am cool LUL
137+
"""
138+
await ctx.send(content)
139+
140+
@commands.Component.listener()
141+
async def event_stream_online(self, payload: twitchio.StreamOnline) -> None:
142+
# Event dispatched when a user goes live from the subscription we made above...
143+
144+
# Keep in mind we are assuming this is for ourselves
145+
# others may not want your bot randomly sending messages...
146+
await payload.broadcaster.send_message(
147+
sender=self.bot.bot_id,
148+
message=f"Hi... {payload.broadcaster}! You are live!",
149+
)
150+
151+
152+
def main() -> None:
153+
twitchio.utils.setup_logging(level=logging.INFO)
154+
155+
async def runner() -> None:
156+
async with asqlite.create_pool("tokens.db") as tdb, Bot(token_database=tdb) as bot:
157+
await bot.setup_database()
158+
await bot.start()
159+
160+
try:
161+
asyncio.run(runner())
162+
except KeyboardInterrupt:
163+
LOGGER.warning("Shutting down due to KeyboardInterrupt...")
164+
165+
166+
if __name__ == "__main__":
167+
main()

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ dev = ["ruff", "pyright", "isort"]
6161
[tool.ruff]
6262
line-length = 125
6363
indent-width = 4
64-
exclude = ["venv", "docs"]
64+
exclude = ["venv", "docs", "examples"]
6565

6666

6767
[tool.ruff.lint]
@@ -117,7 +117,7 @@ skip-magic-trailing-comma = false
117117
line-ending = "auto"
118118

119119
[tool.pyright]
120-
exclude = ["venv", "docs"]
120+
exclude = ["venv", "docs", "examples"]
121121
useLibraryCodeForTypes = true
122122
typeCheckingMode = "strict"
123123
reportImportCycles = false

0 commit comments

Comments
 (0)