python-zulip-api/zulip_bots/zulip_bots/bots/salesforce/salesforce.py
LoopThrough-i-j 1fb3d529a9 zulip-bots: Set bot_handler type to BotHandler.
- Set `bot_handler` type to `BotHandler`.
- Fix mypy issues in improperly typed variables, params and returns.

Fixes part of #639
2021-03-03 13:31:50 -08:00

175 lines
6.8 KiB
Python

# See readme.md for instructions on running this code.
import simple_salesforce
from typing import Dict, Any, List
from zulip_bots.lib import BotHandler
import re
import logging
from zulip_bots.bots.salesforce.utils import commands, object_types, link_query, default_query
base_help_text = '''Salesforce bot
This bot can do simple salesforce query requests
**All commands must be @-mentioned to the bot.**
Commands:
{}
Arguments:
**-limit <num>**: the maximum number of entries sent (default: 5)
**-show**: show all the properties of each entry (default: false)
This bot can also show details about any Salesforce links sent to it.
Supported Object types:
These are the types of Salesforce object supported by this bot.
The bot cannot show the details of any other object types.
{}'''
login_url = 'https://login.salesforce.com/'
def get_help_text() -> str:
command_text = ''
for command in commands:
if 'template' in command.keys() and 'description' in command.keys():
command_text += '**{}**: {}\n'.format('{} [arguments]'.format(
command['template']), command['description'])
object_type_text = ''
for object_type in object_types.values():
object_type_text += '{}\n'.format(object_type['table'])
return base_help_text.format(command_text, object_type_text)
def format_result(
result: Dict[str, Any],
exclude_keys: List[str] = [],
force_keys: List[str] = [],
rank_output: bool = False,
show_all_keys: bool = False
) -> str:
exclude_keys += ['Name', 'attributes', 'Id']
output = ''
if result['totalSize'] == 0:
return 'No records found.'
if result['totalSize'] == 1:
record = result['records'][0]
output += '**[{}]({}{})**\n'.format(record['Name'],
login_url, record['Id'])
for key, value in record.items():
if key not in exclude_keys:
output += '>**{}**: {}\n'.format(key, value)
else:
for i, record in enumerate(result['records']):
if rank_output:
output += '{}) '.format(i + 1)
output += '**[{}]({}{})**\n'.format(record['Name'],
login_url, record['Id'])
added_keys = False
for key, value in record.items():
if key in force_keys or (show_all_keys and key not in exclude_keys):
added_keys = True
output += '>**{}**: {}\n'.format(key, value)
if added_keys:
output += '\n'
return output
def query_salesforce(arg: str, salesforce: simple_salesforce.Salesforce, command: Dict[str, Any]) -> str:
arg = arg.strip()
qarg = arg.split(' -', 1)[0]
split_args = [] # type: List[str]
raw_arg = ''
if len(arg.split(' -', 1)) > 1:
raw_arg = ' -' + arg.split(' -', 1)[1]
split_args = raw_arg.split(' -')
limit_num = 5
re_limit = re.compile(r'-limit \d+')
limit = re_limit.search(raw_arg)
if limit:
limit_num = int(limit.group().rsplit(' ', 1)[1])
logging.info('Searching with limit {}'.format(limit_num))
query = default_query
if 'query' in command.keys():
query = command['query']
object_type = object_types[command['object']]
res = salesforce.query(query.format(
object_type['fields'], object_type['table'], qarg, limit_num))
exclude_keys = [] # type: List[str]
if 'exclude_keys' in command.keys():
exclude_keys = command['exclude_keys']
force_keys = [] # type: List[str]
if 'force_keys' in command.keys():
force_keys = command['force_keys']
rank_output = False
if 'rank_output' in command.keys():
rank_output = command['rank_output']
show_all_keys = 'show' in split_args
if 'show_all_keys' in command.keys():
show_all_keys = command['show_all_keys'] or 'show' in split_args
return format_result(res, exclude_keys=exclude_keys, force_keys=force_keys, rank_output=rank_output, show_all_keys=show_all_keys)
def get_salesforce_link_details(link: str, sf: Any) -> str:
re_id = re.compile('/[A-Za-z0-9]{18}')
re_id_res = re_id.search(link)
if re_id_res is None:
return 'Invalid salesforce link'
id = re_id_res.group().strip('/')
for object_type in object_types.values():
res = sf.query(link_query.format(
object_type['fields'], object_type['table'], id))
if res['totalSize'] == 1:
return format_result(res)
return 'No object found. Make sure it is of the supported types. Type `help` for more info.'
class SalesforceHandler:
def usage(self) -> str:
return '''
This is a Salesforce bot, which can search for Contacts,
Accounts and Opportunities. And can be configured for any
other object types.
It will also show details of any Salesforce links posted.
@-mention the bot with 'help' to see available commands.
'''
def get_salesforce_response(self, content: str) -> str:
content = content.strip()
if content == '' or content == 'help':
return get_help_text()
if content.startswith('http') and 'force' in content:
return get_salesforce_link_details(content, self.sf)
for command in commands:
for command_keyword in command['commands']:
if content.startswith(command_keyword):
args = content.replace(command_keyword, '').strip()
if args is not None and args != '':
if 'callback' in command.keys():
return command['callback'](args, self.sf, command)
else:
return query_salesforce(args, self.sf, command)
else:
return 'Usage: {} [arguments]'.format(command['template'])
return get_help_text()
def initialize(self, bot_handler: BotHandler) -> None:
self.config_info = bot_handler.get_config_info('salesforce')
try:
self.sf = simple_salesforce.Salesforce(
username=self.config_info['username'],
password=self.config_info['password'],
security_token=self.config_info['security_token']
)
except simple_salesforce.exceptions.SalesforceAuthenticationFailed as err:
bot_handler.quit('Failed to log in to Salesforce. {} {}'.format(err.code, err.message))
def handle_message(self, message: Dict[str, Any], bot_handler: BotHandler) -> None:
try:
bot_response = self.get_salesforce_response(message['content'])
bot_handler.send_reply(message, bot_response)
except Exception as e:
bot_handler.send_reply(message, 'Error. {}.'.format(e), bot_response)
handler_class = SalesforceHandler