python-zulip-api/zulip_bots/zulip_bots/run.py
2021-07-29 11:08:33 -07:00

200 lines
6 KiB
Python
Executable file

#!/usr/bin/env python3
import argparse
import logging
import os
import sys
from typing import Optional
from zulip_bots import finder
from zulip_bots.lib import (
NoBotConfigException,
run_message_handler_for_bot,
zulip_env_vars_are_present,
)
from zulip_bots.provision import provision_bot
current_dir = os.path.dirname(os.path.abspath(__file__))
def parse_args() -> argparse.Namespace:
usage = """
zulip-run-bot <bot_name> --config-file ~/zuliprc
zulip-run-bot --help
"""
parser = argparse.ArgumentParser(usage=usage)
parser.add_argument("bot", action="store", help="the name or path of an existing bot to run")
parser.add_argument("--quiet", "-q", action="store_true", help="turn off logging output")
parser.add_argument(
"--config-file",
"-c",
action="store",
help="zulip configuration file (e.g. ~/Downloads/zuliprc)",
)
parser.add_argument(
"--bot-config-file",
"-b",
action="store",
help="third party configuration file (e.g. ~/giphy.conf",
)
parser.add_argument(
"--force",
action="store_true",
help="try running the bot even if dependencies install fails",
)
parser.add_argument(
"--registry",
"-r",
action="store_true",
help="run the bot via zulip_bots registry",
)
parser.add_argument("--provision", action="store_true", help="install dependencies for the bot")
args = parser.parse_args()
return args
def exit_gracefully_if_zulip_config_is_missing(config_file: Optional[str]) -> None:
error_msg = None
if config_file:
if os.path.exists(config_file):
# We're good. (There may be problems with the config file,
# but we'll catch those later.
return
else:
error_msg = f"ERROR: {config_file} does not exist."
else:
if zulip_env_vars_are_present():
return
else:
error_msg = "ERROR: You did not supply a Zulip config file."
if error_msg:
print("\n")
print(error_msg)
print(
"""
You may need to download a config file from the Zulip app, or
if you have already done that, you need to specify the file
location correctly on the command line.
If you don't want to use a config file, you must set
these env vars: ZULIP_EMAIL, ZULIP_API_KEY, ZULIP_SITE.
"""
)
sys.exit(1)
def exit_gracefully_if_bot_config_file_does_not_exist(bot_config_file: Optional[str]) -> None:
if bot_config_file is None:
# This is a common case, just so succeed quietly. (Some
# bots don't have third party configuration.)
return
if not os.path.exists(bot_config_file):
print(
"""
ERROR: %s does not exist.
You probably just specified the wrong file location here.
"""
% (bot_config_file,)
)
sys.exit(1)
def main() -> None:
args = parse_args()
if args.registry:
try:
bot_source, lib_module = finder.import_module_from_zulip_bot_registry(args.bot)
except finder.DuplicateRegisteredBotName as error:
print(
f'ERROR: Found duplicate entries for "{error}" in zulip bots registry.\n'
"Make sure that you don't install bots using the same entry point. Exiting now."
)
sys.exit(1)
if lib_module:
bot_name = args.bot
else:
result = finder.resolve_bot_path(args.bot)
if result:
bot_path, bot_name = result
sys.path.insert(0, os.path.dirname(bot_path))
if args.provision:
provision_bot(os.path.dirname(bot_path), args.force)
try:
lib_module = finder.import_module_from_source(bot_path.as_posix(), bot_name)
except ImportError:
req_path = os.path.join(os.path.dirname(bot_path), "requirements.txt")
with open(req_path) as fp:
deps_list = fp.read()
dep_err_msg = (
"ERROR: The following dependencies for the {bot_name} bot are not installed:\n\n"
"{deps_list}\n"
"If you'd like us to install these dependencies, run:\n"
" zulip-run-bot {bot_name} --provision"
)
print(dep_err_msg.format(bot_name=bot_name, deps_list=deps_list))
sys.exit(1)
bot_source = "source"
else:
lib_module = finder.import_module_by_name(args.bot)
if lib_module:
bot_name = lib_module.__name__
bot_source = "named module"
if args.provision:
print("ERROR: Could not load bot's module for '{}'. Exiting now.")
sys.exit(1)
if lib_module is None:
print("ERROR: Could not load bot module. Exiting now.")
sys.exit(1)
if not args.quiet:
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
# It's a bit unfortunate that we have two config files, but the
# alternative would be way worse for people running multiple bots
# or testing against multiple Zulip servers.
exit_gracefully_if_zulip_config_is_missing(args.config_file)
exit_gracefully_if_bot_config_file_does_not_exist(args.bot_config_file)
try:
run_message_handler_for_bot(
lib_module=lib_module,
config_file=args.config_file,
bot_config_file=args.bot_config_file,
quiet=args.quiet,
bot_name=bot_name,
bot_source=bot_source,
)
except NoBotConfigException:
print(
"""
ERROR: Your bot requires you to specify a third party
config file with the --bot-config-file option.
Exiting now.
"""
)
sys.exit(1)
if __name__ == "__main__":
main()