zulip_bots: Make StateHandler optional.
This has the convenient side-effect of making
the bot_handler.storage attribute read-only.
MODIFY
zulip_bots/zulip_bots/bots/incrementor/incrementor.py
zulip_bots/zulip_bots/bots/incrementor/incrementor.py
class IncrementorHandler(object):
+ META = {
+ 'name': 'Incrementor',
+ 'description': 'Example bot to test the update_message() function.',
+ 'uses_storage': True,
+ }
def usage(self):
return '''

MODIFY
zulip_bots/zulip_bots/bots/tictactoe/tictactoe.py
zulip_bots/zulip_bots/bots/tictactoe/tictactoe.py
@@ -267,6 +267,11 @@ class ticTacToeHandler(object):
tic-tac-toe bot! Make sure your message starts with
"@mention-bot".
'''
+ META = {
+ 'name': 'TicTacToe',
+ 'description': 'Lets you play Tic-tac-toe against a computer.',
+ 'uses_storage': True,
+ }
def usage(self):
return '''

MODIFY
zulip_bots/zulip_bots/bots/virtual_fs/virtual_fs.py
zulip_bots/zulip_bots/bots/virtual_fs/virtual_fs.py
@@ -4,6 +4,12 @@ import re
import os
class VirtualFsHandler(object):
+ META = {
+ 'name': 'VirtualFs',
+ 'description': 'Provides a simple, permanent file system to store and retrieve strings.',
+ 'uses_storage': True,
+ }
+
def usage(self):
return get_help()

MODIFY
zulip_bots/zulip_bots/lib.py
zulip_bots/zulip_bots/lib.py
@@ -92,14 +92,15 @@ class StateHandler(object):
raise StateHandlerError("Error updating state: {}".format(str(response)))
class ExternalBotHandler(object):
- def __init__(self, client, root_dir):
- # type: (Client, str) -> None
+ def __init__(self, client, root_dir, bot_details={}):
+ # type: (Client, str, Dict[str, Any]) -> None
# Only expose a subset of our Client's functionality
user_profile = client.get_profile()
self._rate_limit = RateLimit(20, 5)
self._client = client
self._root_dir = root_dir
- self.storage = StateHandler(client)
+ self.bot_details = bot_details
+ self._storage = StateHandler(client) if self.bot_details.get('uses_storage', False) else None
try:
self.user_id = user_profile['user_id']
self.full_name = user_profile['full_name']
@@ -109,6 +110,20 @@ class ExternalBotHandler(object):
' up the zuliprc file correctly.')
sys.exit(1)
+ @property
+ def storage(self):
+ # type: () -> StateHandler
+ if not self._storage:
+ raise AttributeError("""Bot tried to access storage, but has not enabled
+storage access. To enable storage access, add
+ META = {
+ 'uses_storage': True,
+ }
+to your bot handler class. Check out the incrementor
+bot for an example on how to do this.
+""")
+ return self._storage
+
def send_message(self, message):
# type: (Dict[str, Any]) -> Dict[str, Any]
if self._rate_limit.is_legal():
@@ -194,22 +209,21 @@ def run_message_handler_for_bot(lib_module, quiet, config_file, bot_name):
# inherit from a common prototype specifying the handle_message
# function.
#
+ # Set default bot_details, then override from class, if provided
+ bot_details = {
+ 'name': bot_name.capitalize(),
+ 'description': "",
+ }
+ bot_details.update(getattr(lib_module.handler_class, 'META', {}))
# Make sure you set up your ~/.zuliprc
client = Client(config_file=config_file, client="Zulip{}Bot".format(bot_name.capitalize()))
bot_dir = os.path.dirname(lib_module.__file__)
- restricted_client = ExternalBotHandler(client, bot_dir)
+ restricted_client = ExternalBotHandler(client, bot_dir, bot_details)
message_handler = lib_module.handler_class()
if hasattr(message_handler, 'initialize'):
message_handler.initialize(bot_handler=restricted_client)
- # Set default bot_details, then override from class, if provided
- bot_details = {
- 'name': bot_name.capitalize(),
- 'description': "",
- }
- bot_details.update(getattr(lib_module.handler_class, 'META', {}))
-
if not quiet:
print("Running {} Bot:".format(bot_details['name']))
if bot_details['description'] != "":

zulip_bots: Make StateHandler optional.

This has the convenient side-effect of making
the bot_handler.storage attribute read-only.