Remove triage_message() function from all the contrib-bots.

To make all bots consistent add shared function in bot_lib.py
to check if this bot is called. All bots respond to at-mention of
the bot instead of their specific names.
This commit is contained in:
Abhijeet Kaur 2017-02-17 03:26:32 +05:30 committed by showell
parent a87ae4b1c4
commit 9a8dc7c622
18 changed files with 134 additions and 281 deletions

View file

@ -5,6 +5,7 @@ import os
import signal import signal
import sys import sys
import time import time
import re
our_dir = os.path.dirname(os.path.abspath(__file__)) our_dir = os.path.dirname(os.path.abspath(__file__))
@ -77,10 +78,49 @@ def run_message_handler_for_bot(lib_module, quiet, config_file):
if not quiet: if not quiet:
print(message_handler.usage()) print(message_handler.usage())
def extract_message_if_mentioned(message, client):
bot_mention = r'^@(\*\*{0}\*\*\s|{0}\s)(?=.*)'.format(client.full_name)
start_with_mention = re.compile(bot_mention).match(message['content'])
if start_with_mention:
query = message['content'][len(start_with_mention.group()):]
return query
else:
bot_response = 'Please mention me first, then type the query.'
if message['type'] == 'private':
client.send_message(dict(
type='private',
to=message['sender_email'],
content=bot_response,
))
else:
client.send_message(dict(
type='stream',
to=message['display_recipient'],
subject=message['subject'],
content=bot_response,
))
return None
def is_private(message, client):
# bot will not reply if the sender name is the same as the bot name
# to prevent infinite loop
if message['type'] == 'private':
return client.full_name != message['sender_full_name']
return False
def handle_message(message): def handle_message(message):
logging.info('waiting for next message') logging.info('waiting for next message')
if message_handler.triage_message(message=message,
client=restricted_client): is_mentioned = message['is_mentioned']
is_private_message = is_private(message, restricted_client)
# Strip at-mention botname from the message
if is_mentioned:
message['content'] = extract_message_if_mentioned(message=message, client=restricted_client)
if message['content'] is None:
return
if is_private_message or is_mentioned:
message_handler.handle_message( message_handler.handle_message(
message=message, message=message,
client=restricted_client, client=restricted_client,

View file

@ -77,16 +77,6 @@ class CommuteHandler(object):
you can use the timezone bot. you can use the timezone bot.
''' '''
def triage_message(self, message, client):
original_content = message['content']
# This next line of code is defensive, as we
# never want to get into an infinite loop of posting follow
# ups for own follow ups!
if message['display_recipient'] == 'commute':
return False
is_commute = original_content.startswith('@commute')
return is_commute
# adds API Authentication Key to url request # adds API Authentication Key to url request
def get_api_key(self): def get_api_key(self):
# commute_bot.config must have been moved from # commute_bot.config must have been moved from

View file

@ -31,8 +31,8 @@ class ConverterHandler(object):
This plugin allows users to make conversions between various units, This plugin allows users to make conversions between various units,
e.g. Celsius to Fahrenheit, or kilobytes to gigabytes. e.g. Celsius to Fahrenheit, or kilobytes to gigabytes.
It looks for messages of the format It looks for messages of the format
'@convert <number> <unit_from> <unit_to>' '@mention-bot <number> <unit_from> <unit_to>'
The message '@convert help' posts a short description of how to use The message '@mention-bot help' posts a short description of how to use
the plugin, along with a list of all supported units. the plugin, along with a list of all supported units.
''' '''
@ -41,20 +41,18 @@ class ConverterHandler(object):
This plugin allows users to make conversions between This plugin allows users to make conversions between
various units, e.g. Celsius to Fahrenheit, various units, e.g. Celsius to Fahrenheit,
or kilobytes to gigabytes. It looks for messages of or kilobytes to gigabytes. It looks for messages of
the format '@convert <number> <unit_from> <unit_to>' the format '@mention-bot <number> <unit_from> <unit_to>'
The message '@convert help' posts a short description of The message '@mention-bot help' posts a short description of
how to use the plugin, along with a list of how to use the plugin, along with a list of
all supported units. all supported units.
''' '''
def triage_message(self, message, client):
return '@convert' in message['content']
def handle_message(self, message, client, state_handler): def handle_message(self, message, client, state_handler):
content = message['content'] content = message['content']
words = content.lower().split() words = content.lower().split()
convert_indexes = [i for i, word in enumerate(words) if word == "@convert"] convert_indexes = [i for i, word in enumerate(words) if word == "@convert"]
convert_indexes = [-1] + convert_indexes
results = [] results = []
for convert_index in convert_indexes: for convert_index in convert_indexes:

View file

@ -7,7 +7,7 @@ import html2text
class DefineHandler(object): class DefineHandler(object):
''' '''
This plugin define a word that the user inputs. It This plugin define a word that the user inputs. It
looks for messages starting with '@define'. looks for messages starting with '@mention-bot'.
''' '''
DEFINITION_API_URL = 'https://owlbot.info/api/v1/dictionary/{}?format=json' DEFINITION_API_URL = 'https://owlbot.info/api/v1/dictionary/{}?format=json'
@ -18,28 +18,19 @@ class DefineHandler(object):
def usage(DefineHandler): def usage(DefineHandler):
return ''' return '''
This plugin will allow users to define a word. Users should preface This plugin will allow users to define a word. Users should preface
messages with "@define". messages with @mention-bot.
''' '''
def triage_message(DefineHandler, message, client):
original_content = message['content']
# This next line of code is defensive, as we
# never want to get into an infinite loop of posting follow
# ups for own follow ups!
is_define = original_content.startswith('@define')
return is_define
def _handle_definition(DefineHandler, original_content): def _handle_definition(DefineHandler, original_content):
# Remove '@define' from the message and extract the rest of the message, the # Remove '@define' from the message and extract the rest of the message, the
# word to define. # word to define.
split_content = original_content.split(' ') split_content = original_content.split(' ')
# If there are more than one word (a phrase) # If there are more than one word (a phrase)
if len(split_content) > 2: if len(split_content) > 1:
return DefineHandler.PHRASE_ERROR_MESSAGE return DefineHandler.PHRASE_ERROR_MESSAGE
to_define = split_content[1].strip() to_define = split_content[0].strip()
to_define_lower = to_define.lower() to_define_lower = to_define.lower()
# No word was entered. # No word was entered.

View file

@ -18,28 +18,19 @@ def encrypt(text):
class EncryptHandler(object): class EncryptHandler(object):
''' '''
This bot allows users to quickly encrypt messages using ROT13 encryption. This bot allows users to quickly encrypt messages using ROT13 encryption.
It encrypts/decrypts messages starting with @encrypt. It encrypts/decrypts messages starting with @mention-bot.
''' '''
def usage(self): def usage(self):
return ''' return '''
This bot uses ROT13 encryption for its purposes. This bot uses ROT13 encryption for its purposes.
It responds to me starting with @encrypt. It responds to me starting with @mention-bot.
Feeding encrypted messages into the bot decrypts them. Feeding encrypted messages into the bot decrypts them.
''' '''
def triage_message(self, message, client):
original_content = message['content']
# This makes sure that the bot only replies to messages it supposed to reply to.
should_be_encrypted = original_content.startswith('@encrypt')
return should_be_encrypted
def handle_message(self, message, client, state_handler): def handle_message(self, message, client, state_handler):
original_content = message['content'] original_content = message['content']
temp_content = encrypt(original_content.replace('@encrypt', '')) temp_content = encrypt(original_content)
send_content = "Encrypted/Decrypted text: " + temp_content send_content = "Encrypted/Decrypted text: " + temp_content
client.send_message(dict( client.send_message(dict(

View file

@ -4,7 +4,7 @@ class FollowupHandler(object):
''' '''
This plugin facilitates creating follow-up tasks when This plugin facilitates creating follow-up tasks when
you are using Zulip to conduct a virtual meeting. It you are using Zulip to conduct a virtual meeting. It
looks for messages starting with '@followup'. looks for messages starting with '@mention-bot'.
In this example, we write follow up items to a special In this example, we write follow up items to a special
Zulip stream called "followup," but this code could Zulip stream called "followup," but this code could
@ -16,30 +16,17 @@ class FollowupHandler(object):
return ''' return '''
This plugin will allow users to flag messages This plugin will allow users to flag messages
as being follow-up items. Users should preface as being follow-up items. Users should preface
messages with "@followup". messages with "@mention-bot".
Before running this, make sure to create a stream Before running this, make sure to create a stream
called "followup" that your API user can send to. called "followup" that your API user can send to.
''' '''
def triage_message(self, message, client):
original_content = message['content']
# This next line of code is defensive, as we
# never want to get into an infinite loop of posting follow
# ups for own follow ups!
if message['display_recipient'] == 'followup':
return False
is_follow_up = (original_content.startswith('@followup') or
original_content.startswith('@follow-up'))
return is_follow_up
def handle_message(self, message, client, state_handler): def handle_message(self, message, client, state_handler):
original_content = message['content'] original_content = message['content']
original_sender = message['sender_email'] original_sender = message['sender_email']
new_content = original_content.replace('@followup', temp_content = 'from %s:' % (original_sender,)
'from %s:' % (original_sender,)) new_content = temp_content + original_content
client.send_message(dict( client.send_message(dict(
type='stream', type='stream',

View file

@ -30,13 +30,13 @@ class FoursquareHandler(object):
This plugin allows users to search for restaurants nearby an inputted This plugin allows users to search for restaurants nearby an inputted
location to a limit of 3 venues for every location. The name, address location to a limit of 3 venues for every location. The name, address
and description of the restaurant will be outputted. and description of the restaurant will be outputted.
It looks for messages starting with '@foursquare'. It looks for messages starting with '@mention-bot'.
If you need help, simply type: If you need help, simply type:
@foursquare /help into the Compose Message box @mention-bot /help into the Compose Message box
Sample input: Sample input:
@foursquare Chicago, IL @mention-bot Chicago, IL
@foursquare help @mention-bot help
''' '''
help_info = ''' help_info = '''
@ -45,18 +45,10 @@ cusine of a restaurant in that exact order.
Please note the required use of quotes in the search location. Please note the required use of quotes in the search location.
Example Inputs: Example Inputs:
@foursquare 'Millenium Park' 8000 donuts @mention-bot 'Millenium Park' 8000 donuts
@foursquare 'Melbourne, Australia' 40000 seafood @mention-bot 'Melbourne, Australia' 40000 seafood
''' '''
def triage_message(self, message, client):
callers = ['@FourSquare', '@Foursquare', '@foursquare', '@4square', '@4sq']
for call in callers:
if call in message['content']:
return True
break
return False
def format_json(self, venues): def format_json(self, venues):
def format_venue(venue): def format_venue(venue):
name = venue['name'] name = venue['name']
@ -98,7 +90,7 @@ Example Inputs:
pass pass
# Optional params for HTTP request. # Optional params for HTTP request.
if len(words) >= 2: if len(words) >= 1:
try: try:
params['radius'] = re.search('([0-9]){3,}', message['content']).group(0) params['radius'] = re.search('([0-9]){3,}', message['content']).group(0)
except AttributeError: except AttributeError:
@ -115,7 +107,7 @@ Example Inputs:
received_json = response.json() received_json = response.json()
else: else:
self.send_info(message, self.send_info(message,
"Invalid Request\nIf stuck, try '@foursquare help'.", "Invalid Request\nIf stuck, try '@mention-bot help'.",
client) client)
return return
@ -127,7 +119,7 @@ Example Inputs:
return return
self.send_info(message, self.send_info(message,
"Invalid Request\nIf stuck, try '@foursquare help'.", "Invalid Request\nIf stuck, try '@mention-bot help'.",
client) client)
return return

View file

@ -22,29 +22,17 @@ class GiphyHandler(object):
''' '''
This plugin posts a GIF in response to the keywords provided by the user. This plugin posts a GIF in response to the keywords provided by the user.
Images are provided by Giphy, through the public API. Images are provided by Giphy, through the public API.
The bot looks for messages starting with "@giphy" or @mention of the bot The bot looks for messages starting with @mention of the bot
and responds with a message with the GIF based on provided keywords. and responds with a message with the GIF based on provided keywords.
It also responds to private messages. It also responds to private messages.
''' '''
def usage(self): def usage(self):
return ''' return '''
This plugin allows users to post GIFs provided by Giphy. This plugin allows users to post GIFs provided by Giphy.
Users should preface keywords with "@giphy" or the Giphy-bot @mention. Users should preface keywords with the Giphy-bot @mention.
The bot responds also to private messages. The bot responds also to private messages.
''' '''
def triage_message(self, message, client):
# To prevent infinite loop in private message, bot will detect
# if the sender name is the bot name it will return false.
if message['type'] == 'private':
return client.full_name != message['sender_full_name']
original_content = message['content']
is_giphy_called = (original_content.startswith('@giphy ') or
message['is_mentioned'])
return is_giphy_called
def handle_message(self, message, client, state_handler): def handle_message(self, message, client, state_handler):
bot_response = get_bot_giphy_response(message, client) bot_response = get_bot_giphy_response(message, client)
@ -99,23 +87,9 @@ def get_url_gif_giphy(keyword, api_key):
def get_bot_giphy_response(message, client): def get_bot_giphy_response(message, client):
# Handle the message that called through mention.
if message['is_mentioned']:
bot_mention = r'^@(\*\*{0}\*\*\s|{0}\s)(?=.*)'.format(client.full_name)
start_with_mention = re.compile(bot_mention).match(message['content'])
if start_with_mention:
keyword = message['content'][len(start_with_mention.group()):]
else:
return 'Please mention me first, then type the keyword.'
# Handle the message that called through the specified keyword.
elif message['content'].startswith('@giphy '):
keyword = message['content'][len('@giphy '):]
# Handle the private message.
elif message['type'] == 'private':
keyword = message['content']
# Each exception has a specific reply should "gif_url" return a number. # Each exception has a specific reply should "gif_url" return a number.
# The bot will post the appropriate message for the error. # The bot will post the appropriate message for the error.
keyword = message['content']
try: try:
gif_url = get_url_gif_giphy(keyword, get_giphy_api_key_from_config()) gif_url = get_url_gif_giphy(keyword, get_giphy_api_key_from_config())
except requests.exceptions.ConnectionError: except requests.exceptions.ConnectionError:

View file

@ -15,13 +15,13 @@ class InputError(IndexError):
class GitHubHandler(object): class GitHubHandler(object):
''' '''
This plugin allows you to comment on a GitHub issue, under a certain repository. This plugin allows you to comment on a GitHub issue, under a certain repository.
It looks for messages starting with '@comment' or '@gcomment'. It looks for messages starting with '@mention-bot'.
''' '''
def usage(self): def usage(self):
return ''' return '''
This bot will allow users to comment on a GitHub issue. This bot will allow users to comment on a GitHub issue.
Users should preface messages with '@comment' or '@gcomment'. Users should preface messages with '@mention-bot'.
You will need to have a GitHub account. You will need to have a GitHub account.
Before running this, make sure to get a GitHub OAuth token. Before running this, make sure to get a GitHub OAuth token.
@ -39,25 +39,11 @@ class GitHubHandler(object):
'<repository_owner>/<repository>/<issue_number>/<your_comment>'. '<repository_owner>/<repository>/<issue_number>/<your_comment>'.
''' '''
def triage_message(self, message, client):
original_content = message['content']
is_comment = (original_content.startswith('@comment') or
original_content.startswith('@gcomment'))
return is_comment
def handle_message(self, message, client, state_handler): def handle_message(self, message, client, state_handler):
original_content = message['content'] original_content = message['content']
original_sender = message['sender_email'] original_sender = message['sender_email']
# this handles the message if its starts with @comment handle_input(client, original_content, original_sender)
if original_content.startswith('@comment'):
handle_input(client, original_content, original_sender)
# handle if message starts with @gcomment
elif original_content.startswith('@gcomment'):
handle_input(client, original_content, original_sender)
handler_class = GitHubHandler handler_class = GitHubHandler

View file

@ -13,7 +13,7 @@ import urllib.request
class IssueHandler(object): class IssueHandler(object):
''' '''
This plugin facilitates sending issues to github, when This plugin facilitates sending issues to github, when
an item is prefixed with '@issue' or '@bug' an item is prefixed with '@mention-bot'.
It will also write items to the issues stream, as well It will also write items to the issues stream, as well
as reporting it to github as reporting it to github
@ -30,7 +30,7 @@ class IssueHandler(object):
def usage(self): def usage(self):
return ''' return '''
This plugin will allow users to flag messages This plugin will allow users to flag messages
as being issues with Zulip by using te prefix '@issue' as being issues with Zulip by using te prefix '@mention-bot'.
Before running this, make sure to create a stream Before running this, make sure to create a stream
called "issues" that your API user can send to. called "issues" that your API user can send to.
@ -47,30 +47,20 @@ class IssueHandler(object):
github_token = <oauth_token> (The personal access token for the GitHub bot) github_token = <oauth_token> (The personal access token for the GitHub bot)
''' '''
def triage_message(self, message, client):
original_content = message['content']
# This next line of code is defensive, as we
# never want to get into an infinite loop of posting follow
# ups for own follow ups!
if message['display_recipient'] == 'issue':
return False
is_issue = original_content.startswith('@issue')
return is_issue
def handle_message(self, message, client, state_handler): def handle_message(self, message, client, state_handler):
original_content = message['content'] original_content = message['content']
original_sender = message['sender_email'] original_sender = message['sender_email']
new_content = original_content.replace('@issue', 'by {}:'.format(original_sender,)) temp_content = 'by {}:'.format(original_sender,)
new_content = temp_content + original_content
# gets the repo url # gets the repo url
url_new = self.URL.format(self.REPO_OWNER, self.REPO_NAME) url_new = self.URL.format(self.REPO_OWNER, self.REPO_NAME)
# signs into github using the provided username and password # signs into github using the provided username and password
session = github.auth() session = github.auth()
# Gets rid of the @issue in the issue title issue_title = message['content'].strip()
issue_title = message['content'].replace('@issue', '').strip()
issue_content = '' issue_content = ''
new_issue_title = '' new_issue_title = ''
for part_of_title in issue_title.split(): for part_of_title in issue_title.split():

View file

@ -30,16 +30,16 @@ class HowdoiHandler(object):
in your questions. in your questions.
There are two possible commands: There are two possible commands:
* @howdowe > This would return the answer to the same * @mention-bot howdowe > This would return the answer to the same
stream that it was called from. stream that it was called from.
* @howdoi > The bot would send a private message to the * @mention-bot howdoi > The bot would send a private message to the
user containing the answer. user containing the answer.
By default, howdoi only returns the coding section of the By default, howdoi only returns the coding section of the
first search result if possible, to see the full answer first search result if possible, to see the full answer
from Stack Overflow, append a '!' to the commands. from Stack Overflow, append a '!' to the commands.
(ie '@howdoi!', '@howdowe!') (ie '@mention-bot howdoi!', '@mention-bot howdowe!')
''' '''
MAX_LINE_LENGTH = 85 MAX_LINE_LENGTH = 85
@ -50,26 +50,12 @@ class HowdoiHandler(object):
answers from Stackoverflow. Users should preface answers from Stackoverflow. Users should preface
their questions with one of the following: their questions with one of the following:
* @howdowe > Answer to the same stream * @mention-bot howdowe > Answer to the same stream
* @howdoi > Answer via private message * @mention-bot howdoi > Answer via private message
* @howdowe! OR @howdoi! > Full answer from SO * @mention-bot howdowe! OR @mention-bot howdoi! > Full answer from SO
''' '''
def triage_message(self, message, client):
cmd_list = ['@howdowe', '@howdoi', '@howdowe!', '@howdoi!']
question = message['content']
# This next line of code is defensive, as we never want
# to get into an infinite loop of searching answers
# from Stackoverflow!
if message['sender_email'].startswith('howdoi'):
return False
is_howdoi = any([question.startswith(cmd) for cmd in cmd_list])
return is_howdoi
def line_wrap(self, string, length): def line_wrap(self, string, length):
lines = string.split("\n") lines = string.split("\n")
@ -94,36 +80,36 @@ class HowdoiHandler(object):
return answer return answer
def handle_message(self, message, client, state_handler): def handle_message(self, message, client, state_handler):
question = message['content'] question = message['content'].strip()
if question.startswith('@howdowe!'): if question.startswith('howdowe!'):
client.send_message(dict( client.send_message(dict(
type='stream', type='stream',
to=message['display_recipient'], to=message['display_recipient'],
subject=message['subject'], subject=message['subject'],
content=self.get_answer('@howdowe!', question) content=self.get_answer('howdowe!', question)
)) ))
elif question.startswith('@howdoi!'): elif question.startswith('howdoi!'):
client.send_message(dict( client.send_message(dict(
type='private', type='private',
to=message['sender_email'], to=message['sender_email'],
content=self.get_answer('@howdoi!', question) content=self.get_answer('howdoi!', question)
)) ))
elif question.startswith('@howdowe'): elif question.startswith('howdowe'):
client.send_message(dict( client.send_message(dict(
type='stream', type='stream',
to=message['display_recipient'], to=message['display_recipient'],
subject=message['subject'], subject=message['subject'],
content=self.get_answer('@howdowe', question) content=self.get_answer('howdowe', question)
)) ))
elif question.startswith('@howdoi'): elif question.startswith('howdoi'):
client.send_message(dict( client.send_message(dict(
type='private', type='private',
to=message['sender_email'], to=message['sender_email'],
content=self.get_answer('@howdoi', question) content=self.get_answer('howdoi', question)
)) ))

View file

@ -113,11 +113,6 @@ class JohnHandler(object):
called "VirtualHelp" that your API user can send to. called "VirtualHelp" that your API user can send to.
''' '''
def triage_message(self, message, client):
original_content = message['content'].lower()
return (original_content.startswith("@john") or
original_content.startswith("@**john**"))
def handle_message(self, message, client, state_handler): def handle_message(self, message, client, state_handler):
original_content = message['content'] original_content = message['content']
client.send_message(dict( client.send_message(dict(

View file

@ -26,16 +26,16 @@ def get_thesaurus_result(original_content):
search_keyword = original_content.strip().split(' ', 1)[1] search_keyword = original_content.strip().split(' ', 1)[1]
if search_keyword == 'help': if search_keyword == 'help':
help_message = "To use this bot, start messages with either \ help_message = "To use this bot, start messages with either \
@synonym (to get the synonyms of a given word) \ @mention-bot synonym (to get the synonyms of a given word) \
or @antonym (to get the antonyms of a given word). \ or @mention-bot antonym (to get the antonyms of a given word). \
Phrases are not accepted so only use single words \ Phrases are not accepted so only use single words \
to search. For example you could search '@synonym hello' \ to search. For example you could search '@mention-bot synonym hello' \
or '@antonym goodbye'." or '@mention-bot antonym goodbye'."
return help_message return help_message
elif original_content.startswith('@synonym'): elif original_content.startswith('synonym'):
result = get_clean_response(search_keyword, method = Dictionary.synonym) result = get_clean_response(search_keyword, method = Dictionary.synonym)
return result return result
elif original_content.startswith('@antonym'): elif original_content.startswith('antonym'):
result = get_clean_response(search_keyword, method = Dictionary.antonym) result = get_clean_response(search_keyword, method = Dictionary.antonym)
return result return result
@ -45,7 +45,7 @@ class ThesaurusHandler(object):
and get synonyms, and antonyms, for that word sent and get synonyms, and antonyms, for that word sent
back to the context (stream or private) in which back to the context (stream or private) in which
it was sent. It looks for messages starting with it was sent. It looks for messages starting with
@synonym or @antonym. '@mention-bot synonym' or '@mention-bot @antonym'.
''' '''
def usage(self): def usage(self):
@ -53,21 +53,13 @@ class ThesaurusHandler(object):
This plugin will allow users to get both synonyms This plugin will allow users to get both synonyms
and antonyms for a given word from zulip. To use this and antonyms for a given word from zulip. To use this
plugin, users need to install the PyDictionary module plugin, users need to install the PyDictionary module
using 'pip install PyDictionary'.Use '@synonym help' or using 'pip install PyDictionary'.Use '@mention-bot synonym help' or
'@antonym help' for more usage information. Users should '@mention-bot antonym help' for more usage information. Users should
preface messages with @synonym or @antonym. preface messages with @mention-bot synonym or @mention-bot antonym.
''' '''
def triage_message(self, message, client):
original_content = message['content']
is_thesaurus = (original_content.startswith('@synonym') or
original_content.startswith('@antonym'))
return is_thesaurus
def handle_message(self, message, client, state_handler): def handle_message(self, message, client, state_handler):
original_content = message['content'] original_content = message['content'].strip()
original_sender = message['sender_email'] original_sender = message['sender_email']
new_content = get_thesaurus_result(original_content) new_content = get_thesaurus_result(original_content)

View file

@ -34,16 +34,16 @@ class TicTacToeGame(object):
positions = "Coordinates are entered in a (row, column) format. Numbering is from top to bottom and left to right.\n" \ positions = "Coordinates are entered in a (row, column) format. Numbering is from top to bottom and left to right.\n" \
"Here are the coordinates of each position. (Parentheses and spaces are optional.) \n" \ "Here are the coordinates of each position. (Parentheses and spaces are optional.) \n" \
"(1, 1) (1, 2) (1, 3) \n(2, 1) (2, 2) (2, 3) \n(3, 1) (3, 2) (3, 3) \n " \ "(1, 1) (1, 2) (1, 3) \n(2, 1) (2, 2) (2, 3) \n(3, 1) (3, 2) (3, 3) \n " \
"Your move would be one of these. To make a move, type @tictactoe or @ttt " \ "Your move would be one of these. To make a move, type @mention-bot " \
"followed by a space and the coordinate." "followed by a space and the coordinate."
detailed_help_message = "*Help for Tic-Tac-Toe bot* \n" \ detailed_help_message = "*Help for Tic-Tac-Toe bot* \n" \
"The bot responds to messages starting with @tictactoe or @ttt.\n" \ "The bot responds to messages starting with @mention-bot.\n" \
"**@tictactoe new** (or **@ttt new**) will start a new game (but not if you're " \ "**@mention-bot new** will start a new game (but not if you're " \
"already in the middle of a game). You must type this first to start playing!\n" \ "already in the middle of a game). You must type this first to start playing!\n" \
"**@tictactoe help** (or **@ttt help**) will return this help function.\n" \ "**@mention-bot help** will return this help function.\n" \
"**@tictactoe quit** (or **@ttt quit**) will quit from the current game.\n" \ "**@mention-bot quit** will quit from the current game.\n" \
"**@tictactoe <coordinate>** (or **@ttt <coordinate>**) will make a move at the given coordinate.\n" \ "**@mention-bot <coordinate>** will make a move at the given coordinate.\n" \
"Coordinates are entered in a (row, column) format. Numbering is from " \ "Coordinates are entered in a (row, column) format. Numbering is from " \
"top to bottom and left to right. \n" \ "top to bottom and left to right. \n" \
"Here are the coordinates of each position. (Parentheses and spaces are optional). \n" \ "Here are the coordinates of each position. (Parentheses and spaces are optional). \n" \
@ -265,24 +265,17 @@ class ticTacToeHandler(object):
''' '''
You can play tic-tac-toe in a private message with You can play tic-tac-toe in a private message with
tic-tac-toe bot! Make sure your message starts with tic-tac-toe bot! Make sure your message starts with
"@tictactoe or @ttt". "@mention-bot".
''' '''
def usage(self): def usage(self):
return ''' return '''
You can play tic-tac-toe with the computer now! Make sure your You can play tic-tac-toe with the computer now! Make sure your
message starts with @tictactoe or @ttt. message starts with @mention-bot.
''' '''
def triage_message(self, message, client):
original_content = message['content']
is_tictactoe = (original_content.startswith('@tictactoe') or
original_content.startswith('@ttt'))
return is_tictactoe
def handle_message(self, message, client, state_handler): def handle_message(self, message, client, state_handler):
original_content = message['content'] command_list = message['content']
command_list = original_content.split()[1:]
command = "" command = ""
for val in command_list: for val in command_list:
command += val command += val

View file

@ -7,19 +7,8 @@ class VirtualFsHandler(object):
def usage(self): def usage(self):
return get_help() return get_help()
def triage_message(self, message, client):
if message['type'] != 'stream':
return False
original_content = message['content']
return (original_content.startswith('fs ') or
original_content.startswith('@fs '))
def handle_message(self, message, client, state_handler): def handle_message(self, message, client, state_handler):
assert self.triage_message(message, client) command = message['content']
original_content = message['content']
command = original_content[len('fs '):]
stream = message['display_recipient'] stream = message['display_recipient']
topic = message['subject'] topic = message['subject']
sender = message['sender_email'] sender = message['sender_email']

View file

@ -9,8 +9,7 @@ class WikipediaHandler(object):
''' '''
This plugin facilitates searching Wikipedia for a This plugin facilitates searching Wikipedia for a
specific key term and returns the top article from the specific key term and returns the top article from the
search. It looks for messages starting with '@wikipedia' search. It looks for messages starting with '@mention-bot'
or '@wiki'.
In this example, we write all Wikipedia searches into In this example, we write all Wikipedia searches into
the same stream that it was called from, but this code the same stream that it was called from, but this code
@ -23,31 +22,12 @@ class WikipediaHandler(object):
This plugin will allow users to directly search This plugin will allow users to directly search
Wikipedia for a specific key term and get the top Wikipedia for a specific key term and get the top
article that is returned from the search. Users article that is returned from the search. Users
should preface searches with "@wikipedia" or should preface searches with "@mention-bot".
"@wiki".
''' '''
def triage_message(self, message, client):
original_content = message['content']
# This next line of code is defensive, as we
# never want to get into an infinite loop of posting Wikipedia
# searches for own Wikipedia searches!
if message['sender_full_name'] == 'wikipedia-bot':
return False
is_wikipedia = (original_content.startswith('@wiki') or
original_content.startswith('@wikipedia'))
return is_wikipedia
def handle_message(self, message, client, state_handler): def handle_message(self, message, client, state_handler):
query = message['content'] query = message['content']
for prefix in ['@wikipedia', '@wiki']:
if query.startswith(prefix):
query = query[len(prefix)+1:]
break
query_wiki_link = ('https://en.wikipedia.org/w/api.php?action=query&' query_wiki_link = ('https://en.wikipedia.org/w/api.php?action=query&'
'list=search&srsearch=%s&format=json' % (query,)) 'list=search&srsearch=%s&format=json' % (query,))
try: try:

View file

@ -10,30 +10,23 @@ class XkcdHandler(object):
''' '''
This plugin provides several commands that can be used for fetch a comic This plugin provides several commands that can be used for fetch a comic
strip from https://xkcd.com. The bot looks for messages starting with strip from https://xkcd.com. The bot looks for messages starting with
"@xkcd" and responds with a message with the comic based on provided "@mention-bot" and responds with a message with the comic based on provided
commands. commands.
''' '''
def usage(self): def usage(self):
return ''' return '''
This plugin allows users to fetch a comic strip provided by This plugin allows users to fetch a comic strip provided by
https://xkcd.com. Users should preface the command with "@xkcd". https://xkcd.com. Users should preface the command with "@mention-bot".
There are several commands to use this bot: There are several commands to use this bot:
- @xkcd help -> To show all commands the bot supports. - @mention-bot help -> To show all commands the bot supports.
- @xkcd latest -> To fetch the latest comic strip from xkcd. - @mention-bot latest -> To fetch the latest comic strip from xkcd.
- @xkcd random -> To fetch a random comic strip from xkcd. - @mention-bot random -> To fetch a random comic strip from xkcd.
- @xkcd <comic_id> -> To fetch a comic strip based on - @mention-bot <comic_id> -> To fetch a comic strip based on
`<comic_id>`, e.g `@xkcd 1234`. `<comic_id>`, e.g `@mention-bot 1234`.
''' '''
def triage_message(self, message, client):
original_content = message['content']
is_xkcd_called = original_content.startswith('@xkcd ')
is_xkcd_called_without_command = original_content == '@xkcd'
return is_xkcd_called or is_xkcd_called_without_command
def handle_message(self, message, client, state_handler): def handle_message(self, message, client, state_handler):
xkcd_bot_response = get_xkcd_bot_response(message) xkcd_bot_response = get_xkcd_bot_response(message)
@ -57,10 +50,7 @@ class XkcdServerError(Exception):
def get_xkcd_bot_response(message): def get_xkcd_bot_response(message):
original_content = message['content'].strip() original_content = message['content'].strip()
cropped = original_content[len('@xkcd '):] command = original_content.strip()
command = cropped.strip()
xkcd_called_without_command = original_content == '@xkcd'
commands_help = ("%s" commands_help = ("%s"
"\n* `@xkcd help` to show this help message." "\n* `@xkcd help` to show this help message."
@ -70,14 +60,14 @@ def get_xkcd_bot_response(message):
"e.g `@xkcd 1234`.") "e.g `@xkcd 1234`.")
try: try:
if command == 'help' or xkcd_called_without_command: if command == 'help':
return commands_help % ('xkcd bot supports these commands:') return commands_help % ('xkcd bot supports these commands:')
elif command == 'latest': elif command == 'latest':
fetched = fetch_xkcd_query(XkcdBotCommand.LATEST) fetched = fetch_xkcd_query(XkcdBotCommand.LATEST)
elif command == 'random': elif command == 'random':
fetched = fetch_xkcd_query(XkcdBotCommand.RANDOM) fetched = fetch_xkcd_query(XkcdBotCommand.RANDOM)
elif command.isdigit(): elif command.isdigit():
fetched = fetch_xkcd_query(XkcdBotCommand.COMIC_ID, cropped.strip()) fetched = fetch_xkcd_query(XkcdBotCommand.COMIC_ID, command)
else: else:
return commands_help % ('xkcd bot only supports these commands:') return commands_help % ('xkcd bot only supports these commands:')
except (requests.exceptions.ConnectionError, XkcdServerError): except (requests.exceptions.ConnectionError, XkcdServerError):

View file

@ -14,14 +14,14 @@ except ImportError as e:
HELP_MESSAGE = ''' HELP_MESSAGE = '''
This bot allows users to translate a sentence into This bot allows users to translate a sentence into
'Yoda speak'. 'Yoda speak'.
Users should preface messages with '@yoda'. Users should preface messages with '@mention-bot'.
Before running this, make sure to get a Mashape Api token. Before running this, make sure to get a Mashape Api token.
Instructions are in the 'readme.md' file. Instructions are in the 'readme.md' file.
Store it in the 'yoda_bot.config' file. Store it in the 'yoda_bot.config' file.
The 'yoda_bot.config' file should be located at '~/yoda_bot.config'. The 'yoda_bot.config' file should be located at '~/yoda_bot.config'.
Example input: Example input:
@yoda You will learn how to speak like me someday. @mention-bot You will learn how to speak like me someday.
''' '''
@ -32,36 +32,29 @@ class ApiKeyError(Exception):
class YodaSpeakHandler(object): class YodaSpeakHandler(object):
''' '''
This bot will allow users to translate a sentence into 'Yoda speak'. This bot will allow users to translate a sentence into 'Yoda speak'.
It looks for messages starting with '@yoda'. It looks for messages starting with '@mention-bot'.
''' '''
def usage(self): def usage(self):
return ''' return '''
This bot will allow users to translate a sentence into This bot will allow users to translate a sentence into
'Yoda speak'. 'Yoda speak'.
Users should preface messages with '@yoda'. Users should preface messages with '@mention-bot'.
Before running this, make sure to get a Mashape Api token. Before running this, make sure to get a Mashape Api token.
Instructions are in the 'readme.md' file. Instructions are in the 'readme.md' file.
Store it in the 'yoda_bot.config' file. Store it in the 'yoda_bot.config' file.
The 'yoda_bot.config' file should be located at '~/yoda_bot.config'. The 'yoda_bot.config' file should be located at '~/yoda_bot.config'.
Example input: Example input:
@yoda You will learn how to speak like me someday. @mention-bot You will learn how to speak like me someday.
''' '''
def triage_message(self, message):
original_content = message['content']
return original_content.startswith('@yoda')
def handle_message(self, message, client, state_handler): def handle_message(self, message, client, state_handler):
original_content = message['content'] original_content = message['content']
stream = message['display_recipient'] stream = message['display_recipient']
subject = message['subject'] subject = message['subject']
# this handles the message if its starts with @yoda handle_input(client, original_content, stream, subject)
if original_content.startswith('@yoda'):
handle_input(client, original_content, stream, subject)
handler_class = YodaSpeakHandler handler_class = YodaSpeakHandler
@ -90,10 +83,8 @@ def send_to_yoda_api(sentence, api_key):
def format_input(original_content): def format_input(original_content):
# replaces the '@yoda' with nothing, so that '@yoda' doesn't get sent to the api
message_content = original_content.replace('@yoda', '')
# gets rid of whitespace around the edges, so that they aren't a problem in the future # gets rid of whitespace around the edges, so that they aren't a problem in the future
message_content = message_content.strip() message_content = original_content.strip()
# replaces all spaces with '+' to be in the format the api requires # replaces all spaces with '+' to be in the format the api requires
sentence = message_content.replace(' ', '+') sentence = message_content.replace(' ', '+')
return sentence return sentence
@ -140,10 +131,8 @@ def send_message(client, message, stream, subject):
def is_help(original_content): def is_help(original_content):
# replaces the '@yoda' with nothing, so that '@yoda' doesn't get sent to the api
message_content = original_content.replace('@yoda', '')
# gets rid of whitespace around the edges, so that they aren't a problem in the future # gets rid of whitespace around the edges, so that they aren't a problem in the future
message_content = message_content.strip() message_content = original_content.strip()
if message_content == 'help': if message_content == 'help':
return True return True
else: else: