23
23
"""
24
24
25
25
import argparse
26
+ import asyncio
26
27
import getpass
27
28
import os
28
29
import pathlib
54
55
parser .add_argument ("--version" , action = "store_true" , help = "Get version and debug information for TwitchIO." )
55
56
56
57
# 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." )
59
60
60
61
args = parser .parse_args ()
61
62
62
63
63
- COMPONENT = """from typing import TYPE_CHECKING
64
+ COMPONENT = """from __future__ import annotations
65
+
66
+ from typing import TYPE_CHECKING
64
67
65
- import twitchio
66
68
from twitchio.ext import commands
67
69
68
70
@@ -88,7 +90,96 @@ async def teardown(bot: Bot) -> None: ...
88
90
89
91
"""
90
92
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
+ """
92
183
93
184
BOOLS = {
94
185
"y" : True ,
@@ -210,7 +301,7 @@ def generate_venv() -> None:
210
301
install_packages (exe , starlette )
211
302
212
303
213
- def generate_bot () -> ...:
304
+ async def generate_bot () -> ...:
214
305
name = validate_input ("Project name? (Leave blank to generate files in this directory): " )
215
306
if name :
216
307
_dir = pathlib .Path (name )
@@ -230,67 +321,83 @@ def generate_bot() -> ...:
230
321
if resp :
231
322
generate_venv ()
232
323
233
- components = bool_check (validate_input ("Would you like to setup commands.Components? (y/N): " , bool_validate ))
324
+ components = bool_check (validate_input ("\n Would you like to setup commands.Components? (y/N): " , bool_validate ))
234
325
if components :
235
326
comp_dir = pathlib .Path ("components" )
236
327
comp_dir .mkdir (exist_ok = True )
237
328
238
329
with open (comp_dir / "general.py" , "w" ) as fp :
239
330
fp .write (COMPONENT )
240
331
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 : " )
244
335
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
249
339
250
- if client_id != cid_reenter :
251
- print ("Client-ID does not match, please try again..." , end = "\n \n " )
252
- continue
340
+ break
253
341
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: " )
255
345
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
259
349
260
- if client_sec != csec_reenter :
261
- print ("Client-Secret does not match, please try again..." , end = "\n \n " )
262
- continue
350
+ break
263
351
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 } '\n Bot Name: '{ bot_name } '"
265
356
266
- config_data = f"""[secrets]\n client_id = \" { client_id } \" \n client_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
269
362
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 } '\n Bot Name: '{ bot_name } '"
363
+ import twitchio
275
364
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 ()
279
368
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]\n client_id = "{ client_id } "\n client_secret = "{ client_sec } "\n '
382
+ f'owner_id = "{ owner .id if owner else "" } "\n bot_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 )
281
391
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 ))
287
395
288
- # with open(dir / "bot.py", "w") as fp:
289
- # ...
396
+ print ("\n \n Successfully created Bot boilerplate with the provided details!" )
290
397
291
398
292
399
if args .version :
293
400
version_info ()
294
401
295
402
elif args .create_new :
296
- generate_bot ()
403
+ asyncio . run ( generate_bot () )
0 commit comments