def nag_users(self, msg_writer, event, wit_entities, credentials): """ Generates a nagging conversation, and begins nagging the individual specified in the event. Closed by nag_response :return: The Nagging conversation """ user_name_to_nag = get_highest_confidence_entity(wit_entities, 'name')['value'] if not user_name_to_nag: msg_writer.send_message(event['channel'], "I don't know who you want me to nag") return nag_subject = get_highest_confidence_entity(wit_entities, 'randomize_option')['value'] nagger = event['user_name'].get('real_name') dm = None for member in self.users: # This equality might prove buggy. Perhaps some fuzzy matching here to allow for tolerances? if member.get('profile').get('real_name') == user_name_to_nag: dm = self.get_dm_id_from_user_id(member.get('id')) message = "You need to {}. {} said so".format(nag_subject, nagger) if not dm: msg_writer.send_message(event['channel'], "I couldn't find anyone named {} to nag".format(user_name_to_nag)) return thread = StoppableThread(msg_writer.send_message, dm, message, delay=7200) # 2 hour repeat delay msg_writer.send_message(event['channel'], "Nagging {}".format(user_name_to_nag)) thread.start() conversation = NaggingConversation({'user': event['user'], 'channel': event['channel']}, dm, user_name_to_nag, nag_subject, thread) return conversation
def test_get_highest_confidence_entity(self): sample_entities_dict_1 = { u'randomize_option': [{ u'suggested': True, u'confidence': 0.5173704573627974, u'type': u'value', u'value': u'quote' }], u'intent': [{ u'confidence': 0.7794858005199589, u'value': u'movie-quote' }] } sample_entity_1_1 = u'intent' sample_entity_1_2 = u'randomize_option' sample_entity_1_3 = u'other' # Check wholly valid entity self.assertEqual( utils.get_highest_confidence_entity(sample_entities_dict_1, sample_entity_1_1), { u'confidence': 0.7794858005199589, u'value': u'movie-quote' }) # Check extant, but low confidence entity self.assertEqual( utils.get_highest_confidence_entity(sample_entities_dict_1, sample_entity_1_2), None) # Check non-extant entity self.assertEqual( utils.get_highest_confidence_entity(sample_entities_dict_1, sample_entity_1_3), None)
def onboarding_start(msg_writer, event, wit_entities): if event['user'] not in onboarding_authed_user_ids: msg_writer.send_message(event['channel'], "You are not allowed to start onboarding") return new_employee = get_highest_confidence_entity(wit_entities, 'name') start_date = get_highest_confidence_entity(wit_entities, 'date') # abstract from here on msg_writer.send_message('IT', "Please begin setting up accounts for {}, who starts on {}".format(new_employee, start_date)) msg_writer.send_message('Kim', "Please begin setting up the desk for {}, who starts on {}".format(new_employee, start_date)) msg_writer.send_message('Phones', "Please begin setting up phones for {}, who starts on {}".format(new_employee, start_date)) msg_writer.send_message('Email', "Please begin setting up email for {}, who starts on {}".format(new_employee, start_date)) msg_writer.send_message('Sheri', "Please sign {} up for slack, who starts on {}".format(new_employee, start_date)) conversation = { 'id': uuid4(), 'waiting_for': ['accounts-setup', 'desk-setup', 'phones-setup', 'email-setup', 'slack-setup'], 'context': { 'return': {'user': event['user'], 'channel': event['channel']}, 'new_employee_name': new_employee, 'start_date': start_date }, 'done': False } return conversation
def test_get_highest_confidence_entity(self): sample_entities_dict_1 = {u'randomize_option': [{u'suggested': True, u'confidence': 0.5173704573627974, u'type': u'value', u'value': u'quote'}], u'intent': [{u'confidence': 0.7794858005199589, u'value': u'movie-quote'}]} sample_entity_1_1 = u'intent' sample_entity_1_2 = u'randomize_option' sample_entity_1_3 = u'other' # Check wholly valid entity self.assertEqual(utils.get_highest_confidence_entity(sample_entities_dict_1, sample_entity_1_1), {u'confidence': 0.7794858005199589, u'value': u'movie-quote'}) # Check extant, but low confidence entity self.assertEqual(utils.get_highest_confidence_entity(sample_entities_dict_1, sample_entity_1_2), None) # Check non-extant entity self.assertEqual(utils.get_highest_confidence_entity(sample_entities_dict_1, sample_entity_1_3), None)
def create_drive_file(msg_writer, event, wit_entities, credentials): """ :param msg_writer: writer used to write to the slack channel :param event: slack event object :param wit_entities: entity object returned by wit API call :param credentials GoogleCredentials object used to authorize requests :return: None, affirmitive message indicating creation of file with name specified by wit_entities is sent to slack channel """ state_id = uuid4() current_creds = credentials.get_credential(event, state_id, user=event['user']) if current_creds is None: state = WaitState(build_uuid=state_id, intent_value='create-drive-file', event=event, wit_entities=wit_entities, credentials=credentials) return state http = current_creds.authorize(httplib2.Http()) service = discovery.build('drive', 'v3', http=http) desired_file_name = get_highest_confidence_entity(wit_entities, 'randomize_option')['value'] blank_id = service.files().create(body={"name": desired_file_name}).execute() if blank_id: msg_writer.send_message(event['channel'], "Created file '{}'".format(desired_file_name)) else: msg_writer.write_error(event['channel'], "Failure in file creation")
def view_drive_file(msg_writer, event, wit_entities, credentials): """ :param msg_writer: writer used to write to the slack channel :param event: slack event object :param wit_entities: entity object returned by wit API call :param credentials GoogleCredentials object used to authorize requests :return: None, list of drive files with the name specified by wit_entities is written to slack channel """ state_id = uuid4() current_creds = credentials.get_credential(event, state_id, user=event['user']) if current_creds is None: state = WaitState(build_uuid=state_id, intent_value='view-drive-file', event=event, wit_entities=wit_entities, credentials=credentials) return state http = current_creds.authorize(httplib2.Http()) service = discovery.build('drive', 'v3', http=http) files = service.files().list().execute()['files'] file_names = [x['name'] for x in files] try: desired_file_name = get_highest_confidence_entity(wit_entities, 'randomize_option')['value'] except TypeError: msg_writer.send_message(event['channel'], "I don't know what file you're talking about") return likely_file = process.extractOne(desired_file_name, file_names) if likely_file and likely_file[1] >= 75: # Arbitrary probability cutoff likely_file_id = get_id_from_name(files, likely_file[0]) msg_writer.send_message(event['channel'], "```File ID: {}```".format(likely_file_id)) else: msg_writer.send_message(event['channel'], "No file found with that name, sorry")
def send_email(msg_writer, event, wit_entities, credentials): """ :param msg_writer: A message writer used to write output to slack :param event: The triggering event :param wit_entities: The entities of the wit response :param credentials: A Google Credentials object used to validate with google Oauth send_email generates an email from the message text and sends it to the indicated email address :return: A WaitState if the user is not authenticated, nothing if they are """ state_id = uuid4() current_creds = credentials.get_credential(event, state_id, user=event['user']) if current_creds is None: state = WaitState(build_uuid=state_id, intent_value='send-email', event=event, wit_entities=wit_entities, credentials=credentials) return state http = current_creds.authorize(httplib2.Http()) service = discovery.build('gmail', 'v1', http=http) msg_text = event['cleaned_text'] email_string = "<mailto:.*@.*\..*\|.*@.*\..*>" # matches <mailto:[email protected]|[email protected]> string_cleaner = re.compile(email_string) cleaned_msg_text = string_cleaner.sub("", msg_text) msg_to = get_highest_confidence_entity(wit_entities, 'email')['value'] if not msg_to: msg_writer.send_message(event['channel'], "I can't understand where you want me to send the message, sorry") return message = MIMEText(cleaned_msg_text) message['to'] = msg_to message['from'] = "{}@galatea-associates.com".format(event['user_name']['profile']['last_name']) message['subject'] = "Message via Hal from {}".format(event['user_name']['profile']['real_name']) message_encoded = {'raw': base64.urlsafe_b64encode(message.as_string().encode('utf-8')).decode('utf-8')} service.users().messages().send(userId="me", body=message_encoded).execute()
def _handle_message(self, event): if not self._proof_message(event): return msg_txt = event['text'] channel_id = event['channel'] # Remove mention of the bot so that the rest of the code doesn't need to msg_txt = self.clients.remove_mention(msg_txt).strip() # Ask wit to interpret the text and send back a list of entities logger.info("Asking wit to interpret| {}".format(msg_txt)) wit_resp = self.wit_client.interpret(msg_txt) # Add username and channel name, user dm, and cleaned text to the event object user_name = self.clients.get_user_name_from_id(event['user']) if is_direct_message(channel_id): channel_name = "Direct Message" else: channel_name = self.clients.get_channel_name_from_id(channel_id) event.update({ "user_name": user_name, "channel_name": channel_name, "user_dm": self.clients.get_dm_id_from_user_id(event['user']), "cleaned_text": msg_txt }) # Find the intent with the highest confidence that met our default threshold intent_entity = get_highest_confidence_entity(wit_resp['entities'], 'intent') # If we couldn't find an intent entity, let the user know if intent_entity is None: self.msg_writer.write_prompt(channel_id, self.intents) return intent_value = intent_entity['value'] if intent_value in conversation_intent_types: match = self._conversation_match(intent_value, wit_resp, event) if match: event.update({"conversation": match}) if intent_value in self.intents: t = { 'intent': self.intents[intent_value][0], 'msg_writer': self.msg_writer, 'event': event, 'wit_entities': wit_resp['entities'], 'credentials': self.credentials, 'state_q': self.state_updating_q } self.event_processing_q.put(t) else: raise ReferenceError("No function found to handle intent {}".format(intent_value))
def phones_setup(msg_writer, event, wit_entities): info = get_highest_confidence_entity(wit_entities, 'phone_number') conversation = event.get('conversation') context = conversation.get('context') msg_writer.send_message(context.get('return').get('channel'), "Phones for {} setup, with {} number".format(context.get('new_employee_name'), info)) conversation.get('waiting_for').remove('phones_setup') if conversation.get('waiting_for') is None: email_new_hire(msg_writer, conversation) return None return conversation
def email_setup(msg_writer, event, wit_entities): conversation = event.get('conversation') email = get_highest_confidence_entity(wit_entities, 'email') context = conversation.get('context') msg_writer.send_message(context.get('return').get('channel'), "Email for {} setup".format(context.get('new_employee_name'))) conversation.get('waiting_for').remove('email_setup') conversation.get('context').add({'email': email}) if conversation.get('waiting_for') is None: email_new_hire(msg_writer, conversation) return None return conversation
def _handle_message(self, event): # Event won't have a user if slackbot is unfurling messages for you if 'user' not in event: return # Filter out messages from the bot itself if self.clients.is_message_from_me(event['user']): return msg_txt = event['text'] channel_id = event['channel'] # Filter out message unless this bot is mentioned or it is a direct message if not (is_direct_message(channel_id) or self.clients.is_bot_mention(msg_txt)): return # Remove mention of the bot so that the rest of the code doesn't need to msg_txt = self.clients.remove_mention(msg_txt).strip() # Ensure that we don't go to wit with messages posted by an ignored user if event['user'] in user_ignore_list: return # bot_uid = self.clients.bot_user_id() # Ask wit to interpret the text and send back a list of entities logger.info("Asking wit to interpret| {}".format(msg_txt)) wit_resp = self.wit_client.interpret(msg_txt) # Find the intent with the highest confidence that met our default threshold intent_entity = get_highest_confidence_entity(wit_resp['entities'], 'intent') # If we couldn't find an intent entity, let the user know if intent_entity is None: self.msg_writer.write_prompt(channel_id, intents) return intent_value = intent_entity['value'] if intent_value in intents: intents[intent_value][0](self.msg_writer, event, wit_resp['entities']) else: raise ReferenceError("No function found to handle intent {}".format(intent_value))