|
| 1 | +import argparse |
| 2 | +import os |
| 3 | +from pathlib import Path |
| 4 | + |
| 5 | +DOC_TEMPLATE = """Simple Zulip bot that will respond to any query with a "beep boop". |
| 6 | +
|
| 7 | +This is a boilerplate bot that can be used as a template for more |
| 8 | +sophisticated/evolved Zulip bots that can be installed separately. |
| 9 | +""" |
| 10 | + |
| 11 | + |
| 12 | +README_TEMPLATE = """This is a boilerplate package for a Zulip bot that can be installed from pip |
| 13 | +and launched using the `zulip-run-bots` command. |
| 14 | +""" |
| 15 | + |
| 16 | +SETUP_TEMPLATE = """import {bot_name} |
| 17 | +from setuptools import find_packages, setup |
| 18 | +
|
| 19 | +package_info = {{ |
| 20 | + "name": "{bot_name}", |
| 21 | + "version": {bot_name}.__version__, |
| 22 | + "entry_points": {{ |
| 23 | + "zulip_bots.registry": ["{bot_name}={bot_name}.{bot_name}"], |
| 24 | + }}, |
| 25 | + "packages": find_packages(), |
| 26 | +}} |
| 27 | +
|
| 28 | +setup(**package_info) |
| 29 | +""" |
| 30 | + |
| 31 | +BOT_MODULE_TEMPLATE = """# See readme.md for instructions on running this code. |
| 32 | +from typing import Any, Dict |
| 33 | +
|
| 34 | +import {bot_name} |
| 35 | +
|
| 36 | +from zulip_bots.lib import BotHandler |
| 37 | +
|
| 38 | +__version__ = {bot_name}.__version__ |
| 39 | +
|
| 40 | +
|
| 41 | +class {handler_name}: |
| 42 | + def usage(self) -> str: |
| 43 | + return \""" |
| 44 | + This is a boilerplate bot that responds to a user query with |
| 45 | + "beep boop", which is robot for "Hello World". |
| 46 | +
|
| 47 | + This bot can be used as a template for other, more |
| 48 | + sophisticated, bots that can be installed separately. |
| 49 | + \""" |
| 50 | +
|
| 51 | + def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler) -> None: |
| 52 | + content = "beep boop" # type: str |
| 53 | + bot_handler.send_reply(message, content) |
| 54 | +
|
| 55 | + emoji_name = "wave" # type: str |
| 56 | + bot_handler.react(message, emoji_name) |
| 57 | +
|
| 58 | +
|
| 59 | +handler_class = {handler_name} |
| 60 | +""" |
| 61 | + |
| 62 | + |
| 63 | +def create_bot_file(path: Path, file_name: str, content: str) -> None: |
| 64 | + with open(Path(path, file_name), "w") as file: |
| 65 | + file.write(content) |
| 66 | + |
| 67 | + |
| 68 | +def parse_args() -> argparse.Namespace: |
| 69 | + usage = """ |
| 70 | + zulip-create-bot <bot_name> |
| 71 | + zulip-create-bot --help |
| 72 | + """ |
| 73 | + |
| 74 | + parser = argparse.ArgumentParser(usage=usage, description="Create a minimal Zulip bot package.") |
| 75 | + |
| 76 | + parser.add_argument("bot", help="the name of the bot to be created") |
| 77 | + |
| 78 | + parser.add_argument("--output", "-o", help="the target directory for the new bot", default=".") |
| 79 | + |
| 80 | + parser.add_argument( |
| 81 | + "--force", |
| 82 | + "-f", |
| 83 | + action="store_true", |
| 84 | + help="forcibly overwrite the existing files in the output directory", |
| 85 | + ) |
| 86 | + |
| 87 | + parser.add_argument("--quiet", "-q", action="store_true", help="turn off logging output") |
| 88 | + |
| 89 | + args = parser.parse_args() |
| 90 | + |
| 91 | + if not args.bot.isidentifier(): |
| 92 | + parser.error(f'"{args.bot}" is not a valid Python identifier') |
| 93 | + |
| 94 | + if args.output is not None and not os.path.isdir(args.output): |
| 95 | + parser.error(f"{args.output} is not a valid path") |
| 96 | + |
| 97 | + return parser.parse_args() |
| 98 | + |
| 99 | + |
| 100 | +def main() -> None: |
| 101 | + args = parse_args() |
| 102 | + |
| 103 | + handler_name = f'{args.bot.title().replace("_", "")}Handler' |
| 104 | + |
| 105 | + bot_path = Path(args.output, args.bot) |
| 106 | + bot_module_path = Path(bot_path, args.bot) |
| 107 | + |
| 108 | + try: |
| 109 | + os.mkdir(bot_path) |
| 110 | + os.mkdir(bot_module_path) |
| 111 | + except FileExistsError as err: |
| 112 | + if not args.force: |
| 113 | + print( |
| 114 | + f'The directory "{err.filename}" already exists\nUse -f or --force to forcibly overwrite the existing files' |
| 115 | + ) |
| 116 | + exit(1) |
| 117 | + |
| 118 | + create_bot_file(bot_path, "README.md", README_TEMPLATE) |
| 119 | + create_bot_file(bot_path, "setup.py", SETUP_TEMPLATE.format(bot_name=args.bot)) |
| 120 | + create_bot_file(bot_module_path, "doc.md", DOC_TEMPLATE.format(bot_name=args.bot)) |
| 121 | + create_bot_file(bot_module_path, "__init__.py", '__version__ = "1.0.0"') |
| 122 | + create_bot_file( |
| 123 | + bot_module_path, |
| 124 | + f"{args.bot}.py", |
| 125 | + BOT_MODULE_TEMPLATE.format(bot_name=args.bot, handler_name=handler_name), |
| 126 | + ) |
| 127 | + |
| 128 | + output_path = os.path.abspath(bot_path) |
| 129 | + if not args.quiet: |
| 130 | + print( |
| 131 | + f"""Successfully set up {args.bot} at {output_path}\n |
| 132 | + You can install it with "pip install -e {output_path}"\n |
| 133 | + and then run it with "zulip-run-bot -r {args.bot} -c CONFIG_FILE" |
| 134 | + """ |
| 135 | + ) |
| 136 | + |
| 137 | + |
| 138 | +if __name__ == "__main__": |
| 139 | + main() |
0 commit comments