|
23 | 23 | """
|
24 | 24 |
|
25 | 25 | import argparse
|
| 26 | +import os |
| 27 | +import pathlib |
26 | 28 | import platform
|
27 | 29 | import re
|
28 | 30 | import sys
|
| 31 | +import subprocess |
| 32 | +from typing import Callable |
29 | 33 |
|
30 | 34 | import aiohttp
|
31 | 35 |
|
|
48 | 52 | parser = argparse.ArgumentParser(prog="twitchio")
|
49 | 53 | parser.add_argument("--version", action="store_true", help="Get version and debug information for TwitchIO.")
|
50 | 54 |
|
| 55 | +# TODO: Only uncomment for testing, until complete... |
| 56 | +# new_bot = parser.add_argument_group("Create Bot", "Create and generate bot boilerplate via an interactive walkthrough.") |
| 57 | +# new_bot.add_argument("--create-new", action="store_true", help="Start an interactive walkthrough.") |
| 58 | + |
51 | 59 | args = parser.parse_args()
|
52 | 60 |
|
53 | 61 |
|
| 62 | +COMPONENT = """from typing import TYPE_CHECKING |
| 63 | +
|
| 64 | +import twitchio |
| 65 | +from twitchio.ext import commands |
| 66 | +
|
| 67 | +
|
| 68 | +if TYPE_CHECKING: |
| 69 | + from ..bot import Bot |
| 70 | +
|
| 71 | +
|
| 72 | +class GeneralComponent(commands.Component): |
| 73 | + def __init__(self, bot: Bot) -> None: |
| 74 | + self.bot = bot |
| 75 | +
|
| 76 | + @commands.command() |
| 77 | + async def hi(self, ctx: commands.Context) -> None: |
| 78 | + await ctx.send(f"Hello {ctx.author.mention}") |
| 79 | +
|
| 80 | +
|
| 81 | +async def setup(bot: Bot) -> None: |
| 82 | + await bot.add_component(GeneralComponent(bot)) |
| 83 | +
|
| 84 | +
|
| 85 | +# This is an optional teardown coroutine for miscellaneous clean-up if necessary. |
| 86 | +async def teardown(bot: Bot) -> None: ... |
| 87 | +
|
| 88 | +""" |
| 89 | + |
| 90 | +MAIN = """""" |
| 91 | + |
| 92 | +BOOLS = { |
| 93 | + "y": True, |
| 94 | + "yes": True, |
| 95 | + "n": False, |
| 96 | + "no": False, |
| 97 | + "t": True, |
| 98 | + "true": True, |
| 99 | + "f": False, |
| 100 | + "false": False, |
| 101 | + "1": True, |
| 102 | + "0": False, |
| 103 | +} |
| 104 | + |
| 105 | + |
| 106 | +def bool_check(inp: str) -> bool | None: |
| 107 | + return BOOLS.get(inp.lower()) |
| 108 | + |
| 109 | + |
| 110 | +def bool_validate(inp: str) -> bool: |
| 111 | + return BOOLS.get(inp.lower()) is not None |
| 112 | + |
| 113 | + |
| 114 | +def validate_input(inp: str, check: Callable[[str], bool] | None = None, *, error_msg: str | None = None) -> str: |
| 115 | + error_msg = error_msg or "Invalid input, please try again!" |
| 116 | + |
| 117 | + while True: |
| 118 | + response = input(inp) |
| 119 | + if not check: |
| 120 | + break |
| 121 | + |
| 122 | + try: |
| 123 | + result = check(response) |
| 124 | + except Exception: |
| 125 | + result = False |
| 126 | + |
| 127 | + if result is False: |
| 128 | + print(error_msg, end="\n\n") |
| 129 | + continue |
| 130 | + |
| 131 | + break |
| 132 | + |
| 133 | + return response |
| 134 | + |
| 135 | + |
54 | 136 | def get_version() -> str:
|
55 | 137 | version = ""
|
56 | 138 | with open("twitchio/__init__.py") as f:
|
@@ -99,5 +181,64 @@ def version_info() -> None:
|
99 | 181 | print(info)
|
100 | 182 |
|
101 | 183 |
|
| 184 | +def install_packages(exe: pathlib.Path, starlette: bool | None = False) -> None: |
| 185 | + package = "twitchio" if not starlette else "twitchio[starlette]" |
| 186 | + subprocess.call([exe, "-m", "pip", "install", package, "--upgrade", "--no-cache"]) |
| 187 | + |
| 188 | + |
| 189 | +def generate_venv() -> None: |
| 190 | + # Create the venv... |
| 191 | + subprocess.call([sys.executable, "-m", "venv", ".venv"]) |
| 192 | + |
| 193 | + system = platform.system() |
| 194 | + |
| 195 | + if system == "Windows": |
| 196 | + exe = pathlib.Path(".venv") / "Scripts" / "python.exe" |
| 197 | + elif system in ["Darwin", "Linux"]: |
| 198 | + exe = pathlib.Path(".venv") / "bin" / "python" |
| 199 | + else: |
| 200 | + print("Unsupported operating system... Skipping package installation. Please manually install required packages.") |
| 201 | + return |
| 202 | + |
| 203 | + starlette = bool_check(validate_input("Would you like to install the optional Starlette and Uvicorn packages? (y/N): ", bool_validate,)) |
| 204 | + install_packages(exe, starlette) |
| 205 | + |
| 206 | + |
| 207 | +def generate_bot() -> ...: |
| 208 | + name = validate_input("Project name? (Leave blank to generate files in this directory): ") |
| 209 | + if name: |
| 210 | + dir = pathlib.Path(name) |
| 211 | + dir.mkdir(exist_ok=True) |
| 212 | + os.chdir(dir) |
| 213 | + else: |
| 214 | + dir = pathlib.Path.cwd() |
| 215 | + |
| 216 | + if sys.prefix != sys.base_prefix: |
| 217 | + resp = bool_check(validate_input("No virtual environment used. Would you like to create one? (y/N): ", bool_validate,)) |
| 218 | + |
| 219 | + if resp: |
| 220 | + generate_venv() |
| 221 | + |
| 222 | + components = bool_check(validate_input("Would you like to setup commands.Components? (y/N): ", bool_validate)) |
| 223 | + if components: |
| 224 | + comp_dir = pathlib.Path("components") |
| 225 | + comp_dir.mkdir(exist_ok=True) |
| 226 | + |
| 227 | + with open(comp_dir / "general.py", "w") as fp: |
| 228 | + fp.write(COMPONENT) |
| 229 | + |
| 230 | + # TODO: .env |
| 231 | + # TODO: client details |
| 232 | + # TODO: fetch owner/bot IDs |
| 233 | + # with open(dir / "main.py", "w") as fp: |
| 234 | + # ... |
| 235 | + |
| 236 | + # with open(dir / "bot.py", "w") as fp: |
| 237 | + # ... |
| 238 | + |
| 239 | + |
102 | 240 | if args.version:
|
103 | 241 | version_info()
|
| 242 | + |
| 243 | +elif args.create_new: |
| 244 | + generate_bot() |
0 commit comments