Skip to content

Commit 39fd76f

Browse files
committed
Complete initial create-new scripts
1 parent 5cd91ef commit 39fd76f

File tree

1 file changed

+152
-45
lines changed

1 file changed

+152
-45
lines changed

twitchio/__main__.py

Lines changed: 152 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"""
2424

2525
import argparse
26+
import asyncio
2627
import getpass
2728
import os
2829
import pathlib
@@ -54,15 +55,16 @@
5455
parser.add_argument("--version", action="store_true", help="Get version and debug information for TwitchIO.")
5556

5657
# TODO: Only uncomment for testing, until complete...
57-
# new_bot = parser.add_argument_group("Create Bot", "Create and generate bot boilerplate via an interactive walkthrough.")
58-
# new_bot.add_argument("--create-new", action="store_true", help="Start an interactive walkthrough.")
58+
new_bot = parser.add_argument_group("Create Bot", "Create and generate bot boilerplate via an interactive walkthrough.")
59+
new_bot.add_argument("--create-new", action="store_true", help="Start an interactive walkthrough.")
5960

6061
args = parser.parse_args()
6162

6263

63-
COMPONENT = """from typing import TYPE_CHECKING
64+
COMPONENT = """from __future__ import annotations
65+
66+
from typing import TYPE_CHECKING
6467
65-
import twitchio
6668
from twitchio.ext import commands
6769
6870
@@ -88,7 +90,96 @@ async def teardown(bot: Bot) -> None: ...
8890
8991
"""
9092

91-
MAIN = """"""
93+
MAIN = """import asyncio
94+
import logging
95+
import tomllib
96+
97+
from bot import Bot
98+
99+
import twitchio
100+
101+
102+
LOGGER: logging.Logger = logging.getLogger(__name__)
103+
104+
105+
def main() -> None:
106+
twitchio.utils.setup_logging(level=logging.INFO)
107+
108+
with open("config.toml", "rb") as fp:
109+
config = tomllib.load(fp)
110+
111+
async def runner() -> None:
112+
async with Bot(**config["bot"]) as bot:
113+
await bot.start()
114+
115+
try:
116+
asyncio.run(runner())
117+
except KeyboardInterrupt:
118+
LOGGER.warning("Shutting down due to Keyboard Interrupt.")
119+
120+
121+
if __name__ == "__main__":
122+
main()
123+
124+
"""
125+
126+
BOT = """from __future__ import annotations
127+
128+
import json
129+
import logging
130+
from typing import TYPE_CHECKING, Any
131+
132+
from twitchio import eventsub
133+
from twitchio.ext import commands
134+
135+
136+
if TYPE_CHECKING:
137+
from twitchio.authentication import UserTokenPayload
138+
139+
140+
LOGGER: logging.Logger = logging.getLogger("Bot")
141+
142+
143+
class Bot(commands.AutoBot):
144+
def __init__(self, *args: Any, **kwargs: Any) -> None:
145+
super().__init__(*args, **kwargs)
146+
147+
async def setup_hook(self) -> None:
148+
subs: list[eventsub.SubscriptionPayload] = []
149+
150+
with open(".tio.tokens.json", "rb") as fp:
151+
tokens = json.load(fp)
152+
153+
for user_id in tokens:
154+
subs.extend(self.generate_subs(user_id))
155+
156+
if subs:
157+
await self.multi_subscribe(subs)
158+
159+
{COMP}
160+
161+
async def event_ready(self) -> None:
162+
LOGGER.info("Logged in as: %s. Owner: %s", self.user, self.owner)
163+
164+
def generate_subs(self, user_id: str) -> tuple[eventsub.SubscriptionPayload, ...]:
165+
# Add the required eventsub subscriptions for each user...
166+
assert self.user
167+
168+
return (eventsub.ChatMessageSubscription(broadcaster_user_id=user_id, user_id=self.user.id),)
169+
170+
async def event_oauth_authorized(self, payload: UserTokenPayload) -> None:
171+
await self.add_token(payload.access_token, payload.refresh_token)
172+
173+
if not payload.user_id:
174+
return
175+
176+
if payload.user_id == self.bot_id:
177+
# Don't subscribe to events for the bot user
178+
return
179+
180+
await self.multi_subscribe(self.generate_subs(payload.user_id))
181+
182+
"""
92183

93184
BOOLS = {
94185
"y": True,
@@ -210,7 +301,7 @@ def generate_venv() -> None:
210301
install_packages(exe, starlette)
211302

212303

213-
def generate_bot() -> ...:
304+
async def generate_bot() -> ...:
214305
name = validate_input("Project name? (Leave blank to generate files in this directory): ")
215306
if name:
216307
_dir = pathlib.Path(name)
@@ -230,67 +321,83 @@ def generate_bot() -> ...:
230321
if resp:
231322
generate_venv()
232323

233-
components = bool_check(validate_input("Would you like to setup commands.Components? (y/N): ", bool_validate))
324+
components = bool_check(validate_input("\nWould you like to setup commands.Components? (y/N): ", bool_validate))
234325
if components:
235326
comp_dir = pathlib.Path("components")
236327
comp_dir.mkdir(exist_ok=True)
237328

238329
with open(comp_dir / "general.py", "w") as fp:
239330
fp.write(COMPONENT)
240331

241-
client_id = None
242-
client_sec = None
243-
config = bool_check(validate_input("Would you like to create a config? (y/N): ", bool_validate))
332+
while True:
333+
client_id = validate_input("Please enter your Client-ID: ")
334+
cid_reenter = validate_input("Please re-enter your Client-ID: ")
244335

245-
if config:
246-
while True:
247-
client_id = validate_input("Please enter your Client-ID: ")
248-
cid_reenter = validate_input("Please re-enter your Client-ID: ")
336+
if client_id != cid_reenter:
337+
print("Client-ID does not match, please try again...", end="\n\n")
338+
continue
249339

250-
if client_id != cid_reenter:
251-
print("Client-ID does not match, please try again...", end="\n\n")
252-
continue
340+
break
253341

254-
break
342+
while True:
343+
client_sec = getpass.getpass("Please enter your Client-Secret: ")
344+
csec_reenter = getpass.getpass("Please re-enter your Client-Secret: ")
255345

256-
while True:
257-
client_sec = getpass.getpass("Please enter your Client-Secret: ")
258-
csec_reenter = getpass.getpass("Please re-enter your Client-Secret: ")
346+
if client_sec != csec_reenter:
347+
print("Client-Secret does not match, please try again...", end="\n\n")
348+
continue
259349

260-
if client_sec != csec_reenter:
261-
print("Client-Secret does not match, please try again...", end="\n\n")
262-
continue
350+
break
263351

264-
break
352+
while True:
353+
owner_name = validate_input("Please enter the Twitch username of the owner of this Bot (E.g. chillymosh): ")
354+
bot_name = validate_input("Please enter the Twitch username of the Bot Account (E.g. chillybot): ")
355+
names = f"Owner Name: '{owner_name}'\nBot Name: '{bot_name}'"
265356

266-
config_data = f"""[secrets]\nclient_id = \"{client_id}\"\nclient_secret = \"{client_sec}\""""
267-
with open("config.toml", "w") as fp:
268-
fp.write(config_data)
357+
correct = bool_check(validate_input(f"Is this information correct? (y/N)\n\n{names}\n", bool_validate))
358+
if not correct:
359+
continue
360+
361+
break
269362

270-
if client_id and client_sec:
271-
while True:
272-
owner_name = validate_input("Please enter the Twitch username of the owner of this Bot (E.g. chillymosh): ")
273-
bot_name = validate_input("Please enter the Twitch username of the Bot Account (E.g. chillybot): ")
274-
names = f"Owner Name: '{owner_name}'\nBot Name: '{bot_name}'"
363+
import twitchio
275364

276-
correct = bool_check(validate_input(f"Is this information correct? (y/N)\n\n{names}\n", bool_validate))
277-
if not correct:
278-
continue
365+
client = twitchio.Client(client_id=client_id, client_secret=client_sec)
366+
async with client:
367+
await client.login()
279368

280-
break
369+
owner = bot = None
370+
try:
371+
owner, bot = await client.fetch_users(logins=[owner_name, bot_name])
372+
except twitchio.HTTPException:
373+
print(
374+
"Error fetching IDs of provided users. Please do this manually and enter them into the generated config.toml"
375+
)
376+
377+
prefixr = validate_input("Please enter a command prefix for the Bot (Leave blank for '!'): ")
378+
prefix = prefixr or "!"
379+
380+
config_data = (
381+
f'[bot]\nclient_id = "{client_id}"\nclient_secret = "{client_sec}"\n'
382+
f'owner_id = "{owner.id if owner else ""}"\nbot_id = "{bot.id if bot else ""}"\n'
383+
f'prefix = "{prefix}"'
384+
)
385+
386+
with open("config.toml", "w") as fp:
387+
fp.write(config_data)
388+
389+
with open("main.py", "w") as fp:
390+
fp.write(MAIN)
281391

282-
# TODO: .env
283-
# TODO: client details
284-
# TODO: fetch owner/bot IDs
285-
# with open(dir / "main.py", "w") as fp:
286-
# ...
392+
with open("bot.py", "w") as fp:
393+
comp = 'await self.load_module("components.general")' if components else ""
394+
fp.write(BOT.format(COMP=comp))
287395

288-
# with open(dir / "bot.py", "w") as fp:
289-
# ...
396+
print("\n\nSuccessfully created Bot boilerplate with the provided details!")
290397

291398

292399
if args.version:
293400
version_info()
294401

295402
elif args.create_new:
296-
generate_bot()
403+
asyncio.run(generate_bot())

0 commit comments

Comments
 (0)