bots: Move all bots and the bots API to separate package.

This commit is contained in:
Eeshan Garg 2017-07-18 01:47:16 -02:30
parent 928d5ca16d
commit 879f44ab3a
130 changed files with 183 additions and 144 deletions

1
.gitignore vendored
View file

@ -10,7 +10,6 @@ dist
build build
eggs eggs
parts parts
var
sdist sdist
develop-eggs develop-eggs
.installed.cfg .installed.cfg

View file

@ -1,86 +0,0 @@
#!/usr/bin/env python
from __future__ import print_function
from __future__ import absolute_import
import importlib
import logging
import optparse
import os
import sys
import provision
from types import ModuleType
our_dir = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, our_dir)
from bot_lib import run_message_handler_for_bot
def validate_path(bots_fn):
# type: (str) -> None
bots_fn = os.path.realpath(bots_fn)
if not os.path.dirname(bots_fn).startswith(os.path.normpath(os.path.join(our_dir, "../bots"))):
print('Sorry, we will only import code from api/bots.')
sys.exit(1)
if not bots_fn.endswith('.py'):
print('Please use a .py extension for library files.')
sys.exit(1)
def get_lib_module(bots_fn):
# type: (str) -> ModuleType
base_bots_fn = os.path.basename(os.path.splitext(bots_fn)[0])
sys.path.insert(1, os.path.dirname(bots_fn))
module_name = base_bots_fn
module = importlib.import_module(module_name)
return module
def run():
# type: () -> None
usage = '''
./run.py <lib file>
Example: ./run.py lib/followup.py
(This program loads bot-related code from the
library code and then runs a message loop,
feeding messages to the library code to handle.)
Please make sure you have a current ~/.zuliprc
file with the credentials you want to use for
this bot.
See lib/readme.md for more context.
'''
parser = optparse.OptionParser(usage=usage)
parser.add_option('--quiet', '-q',
action='store_true',
help='Turn off logging output.')
parser.add_option('--config-file',
action='store',
help='(alternate config file to ~/.zuliprc)')
parser.add_option('--provision',
action='store_true',
help='Install dependencies for the bot')
parser.add_option('--force',
action='store_true',
help='Try running the bot even if dependencies install fails.')
(options, args) = parser.parse_args()
if len(args) == 0:
print('You must specify a library!')
sys.exit(1)
bots_fn = args[0]
validate_path(bots_fn)
if options.provision:
print("Provisioning")
provision.provision_bot(os.path.dirname(bots_fn), options.force)
lib_module = get_lib_module(bots_fn)
if not options.quiet:
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
run_message_handler_for_bot(
lib_module=lib_module,
config_file=options.config_file,
quiet=options.quiet
)
if __name__ == '__main__':
run()

View file

@ -17,11 +17,11 @@ def dir_join(dir1, dir2):
if __name__ == '__main__': if __name__ == '__main__':
bots_dir = os.path.dirname(os.path.abspath(__file__)) tools_dir = os.path.dirname(os.path.abspath(__file__))
root_dir = dir_join(bots_dir, '..') bots_dir = os.path.join(tools_dir, '..', 'zulip_bots', 'zulip_bots')
bots_test_dir = dir_join(bots_dir, '../bots') bots_test_dir = dir_join(bots_dir, 'bots')
sys.path.insert(0, root_dir) sys.path.insert(0, bots_dir)
sys.path.insert(0, bots_test_dir) sys.path.insert(0, bots_test_dir)
btd = bots_test_dir btd = bots_test_dir
@ -57,7 +57,7 @@ if __name__ == '__main__':
sys.path.insert(0, dep_path) sys.path.insert(0, dep_path)
try: try:
suites.append(loader.discover(start_dir = dir_join(bots_test_dir, bot_to_test), suites.append(loader.discover(start_dir = dir_join(bots_test_dir, bot_to_test),
top_level_dir = root_dir)) top_level_dir = bots_dir))
except ImportError: except ImportError:
logging.error("Bot %s requires __init__.py to be added" % (bot_to_test)) logging.error("Bot %s requires __init__.py to be added" % (bot_to_test))
failed = True failed = True

5
zulip_bots/MANIFEST.in Normal file
View file

@ -0,0 +1,5 @@
include zulip_bots/bots/giphy/fixtures/test_1.json
include zulip_bots/bots/github_detail/fixtures/test_404.json
include zulip_bots/bots/github_detail/fixtures/test_issue.json
include zulip_bots/bots/github_detail/fixtures/test_pull.json

View file

@ -24,13 +24,17 @@ to messages in any of the following settings:
account specific authentication, for example: a gmail bot that lets account specific authentication, for example: a gmail bot that lets
one send emails directly through Zulip. one send emails directly through Zulip.
## Installing
To install the bots and bots API, run:
python setup.py install
## Running bots ## Running bots
Here is an example of running the "follow-up" bot from Here is an example of running the "follow-up" bot:
inside a Zulip repo (and in your remote instance):
cd ~/zulip/api zulip-run-bot followup --config-file ~/.zuliprc-prod
bots_api/run.py bots/followup/followup.py --config-file ~/.zuliprc-prod
Once the bot code starts running, you will see a Once the bot code starts running, you will see a
message explaining how to use the bot, as well as message explaining how to use the bot, as well as

78
zulip_bots/setup.py Executable file
View file

@ -0,0 +1,78 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
import sys
# We should be installable with either setuptools or distutils.
package_info = dict(
name='zulip_bots',
version='0.3.1',
description='Zulip\'s Bot framework',
author='Zulip Open Source Project',
author_email='zulip-devel@googlegroups.com',
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Topic :: Communications :: Chat',
],
url='https://www.zulip.org/',
entry_points={
'console_scripts': [
'zulip-run-bot=zulip_bots.run:main',
],
},
include_package_data=True,
) # type: Dict[str, Any]
setuptools_info = dict(
install_requires=[
'zulip>=0.3.1',
'mock>=2.0.0',
'html2text', # for bots/define
'PyDictionary', # for bots/thesaurus
],
)
try:
from setuptools import setup, find_packages
package_info.update(setuptools_info)
package_info['packages'] = find_packages()
except ImportError:
from distutils.core import setup
from distutils.version import LooseVersion
from importlib import import_module
# Manual dependency check
def check_dependency_manually(module_name, version=None):
try:
module = import_module(module_name)
if version is not None:
assert(LooseVersion(module.__version__) >= LooseVersion(version))
except (ImportError, AssertionError):
if version is not None:
print("{name}>={version} is not installed.".format(
req=req, version=version), file=sys.stderr)
else:
print("{name} is not installed.".format(name=module_name), file=sys.stderr)
sys.exit(1)
check_dependency_manually('zulip', '0.3.1')
check_dependency_manually('mock', '2.0.0')
check_dependency_manually('html2text')
check_dependency_manually('PyDictionary')
# Include all submodules under bots/
package_list = ['zulip_bots']
dirs = os.listdir('zulip_bots/bots/')
for dir_name in dirs:
if os.path.isdir(os.path.join('zulip_bots/bots/', dir_name)):
package_list.append('zulip_bots.bots.' + dir_name)
package_info['packages'] = package_list
setup(**package_info)

View file

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View file

@ -5,11 +5,10 @@ from __future__ import division
import copy import copy
import importlib import importlib
import sys
from math import log10, floor from math import log10, floor
import utils
import re import re
from zulip_bots.bots.converter import utils
def is_float(value): def is_float(value):
try: try:

View file

@ -3,7 +3,7 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from bots_test_lib import BotTestCase from zulip_bots.test_lib import BotTestCase
class TestConverterBot(BotTestCase): class TestConverterBot(BotTestCase):
bot_name = "converter" bot_name = "converter"

View file

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View file

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -3,7 +3,7 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from bots_test_lib import BotTestCase from zulip_bots.test_lib import BotTestCase
class TestDefineBot(BotTestCase): class TestDefineBot(BotTestCase):
bot_name = "define" bot_name = "define"

View file

Before

Width:  |  Height:  |  Size: 287 KiB

After

Width:  |  Height:  |  Size: 287 KiB

View file

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 180 KiB

View file

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

View file

Before

Width:  |  Height:  |  Size: 170 KiB

After

Width:  |  Height:  |  Size: 170 KiB

View file

@ -3,7 +3,7 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from bots_test_lib import BotTestCase from zulip_bots.test_lib import BotTestCase
class TestEncryptBot(BotTestCase): class TestEncryptBot(BotTestCase):
bot_name = "encrypt" bot_name = "encrypt"

View file

@ -3,7 +3,7 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from bots_test_lib import BotTestCase from zulip_bots.test_lib import BotTestCase
class TestFollowUpBot(BotTestCase): class TestFollowUpBot(BotTestCase):
bot_name = "followup" bot_name = "followup"

View file

@ -5,7 +5,7 @@ from __future__ import print_function
import json import json
from bots_test_lib import BotTestCase from zulip_bots.test_lib import BotTestCase
class TestGiphyBot(BotTestCase): class TestGiphyBot(BotTestCase):
bot_name = "giphy" bot_name = "giphy"

View file

@ -27,7 +27,7 @@ Here is an example of running the `git_hub_comment` bot from
inside a Zulip repo: inside a Zulip repo:
`cd ~/zulip/api` `cd ~/zulip/api`
`bots_api/run.py bots/git_hub_comment/git_hub_comment.py --config-file ~/.zuliprc-prod` `zulip-run-bot git_hub_comment --config-file ~/.zuliprc-prod`
Once the bot code starts running, you will see a Once the bot code starts running, you will see a
message explaining how to use the bot, as well as message explaining how to use the bot, as well as
@ -49,5 +49,3 @@ page to create a user-owned bot.
email=someuser@example.com email=someuser@example.com
key=<your api key> key=<your api key>
site=https://zulip.somewhere.com site=https://zulip.somewhere.com

View file

@ -3,16 +3,9 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
import os
import sys
import json import json
our_dir = os.path.dirname(os.path.abspath(__file__)) from zulip_bots.test_lib import BotTestCase
sys.path.insert(0, os.path.normpath(os.path.join(our_dir)))
# For dev setups, we can find the API in the repo itself.
if os.path.exists(os.path.join(our_dir, '..')):
sys.path.insert(0, '..')
from bots_test_lib import BotTestCase
class TestGithubDetailBot(BotTestCase): class TestGithubDetailBot(BotTestCase):
bot_name = "github_detail" bot_name = "github_detail"

View file

@ -5,7 +5,7 @@ from __future__ import print_function
from six.moves import zip from six.moves import zip
from bots_test_lib import BotTestCase from zulip_bots.test_lib import BotTestCase
class TestHelloWorldBot(BotTestCase): class TestHelloWorldBot(BotTestCase):
bot_name = "helloworld" bot_name = "helloworld"

View file

@ -5,7 +5,7 @@ from __future__ import print_function
from six.moves import zip from six.moves import zip
from bots_test_lib import BotTestCase from zulip_bots.test_lib import BotTestCase
class TestHelpBot(BotTestCase): class TestHelpBot(BotTestCase):
bot_name = "help" bot_name = "help"

View file

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

View file

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

View file

Before

Width:  |  Height:  |  Size: 9 KiB

After

Width:  |  Height:  |  Size: 9 KiB

View file

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View file

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

View file

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View file

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View file

@ -1,6 +1,5 @@
import json import json
import os import os
import sys
from random import choice from random import choice
@ -12,9 +11,6 @@ except ImportError:
Please: pip install chatterbot""") Please: pip install chatterbot""")
BOTS_DIR = os.path.dirname(os.path.abspath(__file__)) BOTS_DIR = os.path.dirname(os.path.abspath(__file__))
os.chdir(os.path.dirname(BOTS_DIR))
sys.path.insert(0, os.path.dirname(BOTS_DIR))
JOKES_PATH = os.path.join(BOTS_DIR, 'assets/var/jokes.json') JOKES_PATH = os.path.join(BOTS_DIR, 'assets/var/jokes.json')
DATABASE_PATH = os.path.join(BOTS_DIR, 'assets/var/database.db') DATABASE_PATH = os.path.join(BOTS_DIR, 'assets/var/database.db')
DIRECTORY_PATH = os.path.join(BOTS_DIR, 'assets') DIRECTORY_PATH = os.path.join(BOTS_DIR, 'assets')

View file

@ -3,7 +3,7 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from bots_test_lib import BotTestCase from zulip_bots.test_lib import BotTestCase
class TestThesaurusBot(BotTestCase): class TestThesaurusBot(BotTestCase):
bot_name = "thesaurus" bot_name = "thesaurus"

View file

@ -3,7 +3,7 @@
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import print_function from __future__ import print_function
from bots_test_lib import BotTestCase from zulip_bots.test_lib import BotTestCase
class TestVirtualFsBot(BotTestCase): class TestVirtualFsBot(BotTestCase):
bot_name = "virtual_fs" bot_name = "virtual_fs"

View file

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Some files were not shown because too many files have changed in this diff Show more