Skip to content

Commit fff413c

Browse files
committed
Bots: Add metadata scheme for bots.
* Name & (short) description * Whether to use any default commands (default to enabled): (about, '' (empty), usage [remove?], help/commands) * Provide a list of user commands, which help/commands uses
1 parent 59c7507 commit fff413c

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

zulip_bots/zulip_bots/bots/wikipedia/wikipedia.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ class WikipediaHandler(object):
1818
kind of external issue tracker as well.
1919
'''
2020

21+
META = {'name': 'Wikipedia',
22+
'description': 'Searches Wikipedia for a term and returns the top article.',
23+
'no_defaults': True, # Let bot handle all messages
24+
}
25+
2126
def usage(self):
2227
return '''
2328
This plugin will allow users to directly search

zulip_bots/zulip_bots/bots/xkcd/xkcd.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
XKCD_TEMPLATE_URL = 'https://xkcd.com/%s/info.0.json'
77
LATEST_XKCD_URL = 'https://xkcd.com/info.0.json'
88

9+
from collections import OrderedDict
10+
911
class XkcdHandler(object):
1012
'''
1113
This plugin provides several commands that can be used for fetch a comic
@@ -14,6 +16,16 @@ class XkcdHandler(object):
1416
commands.
1517
'''
1618

19+
META = {'name': 'XKCD',
20+
'description': 'Fetches comic strips from https://xkcd.com.',
21+
'no_defaults': False,
22+
'commands': OrderedDict([
23+
('latest', "Show the latest comic strip"),
24+
('random', "Show a random comic strip"),
25+
('<comic id>', "Show a comic strip with a specific 'comic id'"),
26+
])
27+
}
28+
1729
def usage(self):
1830
return '''
1931
This plugin allows users to fetch a comic strip provided by

zulip_bots/zulip_bots/lib.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
from zulip import Client
2020

21+
from collections import OrderedDict
22+
2123
def exit_gracefully(signum, frame):
2224
# type: (int, Optional[Any]) -> None
2325
sys.exit(0)
@@ -156,7 +158,58 @@ def run_message_handler_for_bot(lib_module, quiet, config_file, bot_name):
156158

157159
state_handler = StateHandler()
158160

161+
# Bot details and default commands from defaults, then override if provided
162+
bot_details = { 'name': bot_name.capitalize(),
163+
'description': "",
164+
'commands': {},
165+
'no_defaults': False,
166+
}
167+
def def_about():
168+
desc = bot_details['description']
169+
return "**{}**{}".format(bot_details['name'],
170+
"" if desc == "" else ": {}".format(desc))
171+
def def_help():
172+
return ("\n".join("**{}** - {}".format(k, v[1])
173+
for k, v in default_commands.items() if k != '') +
174+
"\n" +
175+
"\n".join("**{}** - {}".format(k, v)
176+
for k, v in bot_details['commands'].items() if k != ''))
177+
def def_commands():
178+
return "**Commands**: {} {}".format(
179+
" ".join(k for k in default_commands if k != ''),
180+
" ".join(k for k in bot_details['commands'] if k != ''))
181+
default_commands = OrderedDict([
182+
('', lambda: ("Oops. Your message was empty.", )),
183+
('about', (def_about, "The type and use of this bot")),
184+
('usage', ((lambda: message_handler.usage(), "Bot-provided usage text"))),
185+
('help', (lambda: "{}\n{}\n{}".format(def_about(), message_handler.usage(), def_help()),
186+
"This help text")),
187+
('commands', (def_commands, "A short list of supported commands"))
188+
])
189+
# Update bot_details from those in class, if present
190+
try:
191+
bot_details.update(lib_module.handler_class.META)
192+
except AttributeError:
193+
pass
194+
# Update default_commands from any changes in bot_details
195+
if bot_details['no_defaults']: # Bot class will handle all commands
196+
default_commands = {}
197+
else:
198+
if len(bot_details['commands']) == 0: # No commands specified, so don't use this feature
199+
del default_commands['commands']
200+
del default_commands['help']
201+
else:
202+
for command in bot_details['commands']: # Bot commands override defaults
203+
if command in default_commands:
204+
del default_commands[command]
205+
# Sync default_commands changes with bot_details
206+
if len(default_commands) == 0:
207+
bot_details['no_defaults'] = True
208+
159209
if not quiet:
210+
print("Running {} Bot:".format(bot_details['name']))
211+
if bot_details['description'] != "":
212+
print("\n{}".format(bot_details['description']))
160213
print(message_handler.usage())
161214

162215
def extract_query_without_mention(message, client):
@@ -198,6 +251,18 @@ def handle_message(message):
198251
return
199252

200253
if is_private_message or is_mentioned:
254+
# Handle any default_commands first
255+
if len(default_commands) > 0:
256+
if '' in default_commands and len(message['content']) == 0:
257+
restricted_client.send_reply(message, default_commands[''][0]())
258+
return
259+
for command in default_commands:
260+
if command == '':
261+
continue
262+
if message['content'].startswith(command):
263+
restricted_client.send_reply(message, default_commands[command][0]())
264+
return
265+
# ...then pass anything else to bot to deal with
201266
message_handler.handle_message(
202267
message=message,
203268
bot_handler=restricted_client,

0 commit comments

Comments
 (0)