def SymphonyREST(method, endpoint, body): retVal = SymphonyAgentResponse() # Allowing for reauth from the async process if method != 'AUTH' and 'sessionToken' not in agentSession.headers: SymphonyReAuth() try: if method == 'GET': response = agentSession.get(endpoint) elif method == 'POST': response = agentSession.post(endpoint, data=body) elif method == 'POSTV2': response = PostV2(endpoint, body) elif method == 'POSTV2_1': response = PostV2_1(endpoint, body) elif method == 'AUTH': response = agentSession.post(endpoint) else: raise MethodNotImplementedException(method + ' is not yet implemented.') retVal.ResponseText = response.text retVal.ResponseCode = response.status_code if response.status_code == 200: retVal.Success = True retVal.ParseResponseJSON() elif response.status_code // 100 == 2: # Any other 200 code, not success but don't throw exception retVal.Success = True else: response.raise_for_status() except requests.exceptions.HTTPError as httpex: errorStr = "Symphony REST Exception (http): " + str(httpex) botlog.LogConsoleInfo("Response Code: " + str(response.status_code)) botlog.LogConsoleInfo("Response Message: " + response.text) retVal.ErrorMessage = errorStr stackTrace = 'Stack Trace: ' + ''.join(traceback.format_stack()) botlog.LogSymphonyError(errorStr) botlog.LogSymphonyError(stackTrace) except requests.exceptions.RequestException as connex: errorStr = "Symphony REST Exception (connection - Status Code " + str(response.status_code) + \ "): " + str(connex) retVal.ErrorMessage = errorStr stackTrace = 'Stack Trace: ' + ''.join(traceback.format_stack()) botlog.LogSymphonyError(errorStr) botlog.LogSymphonyError(stackTrace) except Exception as ex: errorStr = "Symphony REST Exception (system): " + str(ex) retVal.ErrorMessage = errorStr stackTrace = 'Stack Trace: ' + ''.join(traceback.format_stack()) botlog.LogSystemError(errorStr) botlog.LogSystemError(stackTrace) finally: return retVal
def Authenticate(self): botlog.LogConsoleInfo('Authenticating...') self.SessionToken = callout.GetSessionToken() if self.SessionToken != '': botlog.LogSymphonyInfo('Success! Session token obtained.') self.KeyAuthToken = callout.GetKeyManagerToken() if self.KeyAuthToken != '': botlog.LogSymphonyInfo('Success! Key Manager token obtained.') self.SessionExpirationDate = datetime.date.today() + datetime.timedelta(days=7) # self.RESTHeaders = {"sessionToken": self.SessionToken, "keyManagerToken": self.KeyAuthToken, # "Content-Type": "application/json"} self.RESTHeaders = callout.BuildHeaders(self.SessionToken, self.KeyAuthToken) # Attempting to use requests.Session callout.agentSession.headers.update(self.RESTHeaders) botlog.LogSymphonyInfo('Session expires on ' + str(self.SessionExpirationDate)) self.IsAuthenticated = True else: botlog.LogSystemError("Failed to obtain KM Token") else: botlog.LogSystemError("Failed to obtain Session Token")
def LogSearchStart(engine: str, search: str): global search_index search_index += 1 log.LogConsoleInfo("(" + str(search_index) + ") Searching " + engine + " for " + search) return search_index
def MatchEpic(bodytext: str): searchtext = bodytext.lower() epic_match = {} freq_dict = GetFrequencyDict(searchtext) for epic_key in EpicList: epic = EpicList.get(epic_key) for keyword in epic['keywords']: if keyword in freq_dict: count = epic_match.get(epic['name'], 0) epic_match[epic['name']] = count + freq_dict[keyword] # returns the epic name with the largest match count # If there are multiples with the same max count, returns the first one # I need to figure out a way to look at all the max values and pick the one with the # higher priority if len(epic_match) > 0: # log match dict to console log.LogConsoleInfo(json.dumps(epic_match)) epic_name = max(epic_match.keys(), key=(lambda k: epic_match[k])) return EpicList.get(epic_name, DefaultItem) else: return DefaultItem
def ReparseRoomFeedback(messageDetail): streamId = messageDetail.StreamId if len(messageDetail.Command.UnnamedParams) > 0: timeStr = ''.join(messageDetail.Command.UnnamedParams) offsetSeconds = utdt.ConvertShorthandToSeconds(timeStr) if offsetSeconds <= 0: offsetSeconds = 15 * 60 else: # Default to 15 mins if no valid time string is provided offsetSeconds = 15 * 60 dtReparse = datetime.datetime.now() + datetime.timedelta(seconds=-1 * offsetSeconds) messageList = symreader.GetSymphonyMessagesSinceDateTime( streamId, dtReparse) botlog.LogConsoleInfo('Attempting to resubmit (' + str(len(messageList)) + ') messages.') from modules.plugins.Salesforce.commands import SubmitUserFeedbackCollection SubmitUserFeedbackCollection(messageList)
def __init__(self, respItem): self.MessageId = msg.FormatSymphonyId(respItem.id) if hasattr( respItem, 'id') else '-1' # I REALLY don't like the idea of EAFP. Sometimes it's just better to check things first # instead of just throwing an exception like a big 'ol tantrum. self.FromUserId = str(respItem.fromUserId) if hasattr( respItem, 'fromUserId') else '-1' self.StreamId = msg.FormatSymphonyId(respItem.streamId) if hasattr( respItem, 'streamId') else '-1' # Some of the message types (like room additions) have no message self.MessageRaw = respItem.message if hasattr(respItem, 'message') else None self.Type = respItem.v2messageType if hasattr(respItem, 'v2messageType') else '' self.Attachments = self.ParseAttachments(respItem) # respItem.attachments if hasattr(respItem, 'attachments') else [] self.IsValid = self.Type == 'V2Message' self.Sender = None self.ChatRoom = None self.Command = None if len(self.Attachments) > 0: log.LogConsoleInfo(respItem)
def GetSymphonyMessages(endpoint): response = callout.SymphonyGET(endpoint) # Messages coming from the API are formatted as an array of JSON objects # Thus, I need to break up the array, parse the individual objects, and pass # the list of python objects back to the engine messageItems = [] if response.Success: for respItem in response.ResponseData: # Hopefully this will try: if respItem.v2messageType and respItem.v2messageType == 'V2Message': detail = msg.MessageDetail(respItem) detail.Sender = user.GetSymphonyUserDetail( detail.FromUserId) detail.ChatRoom = stream.GetStreamInfo(respItem.streamId) botlog.LogSymphonyInfo(detail.GetConsoleLogLine()) if detail.Sender and detail.Sender.IsValidSender: detail.InitiateCommandParsing() messageItems.append(detail) elif respItem.v2messageType != 'V2Message': botlog.LogConsoleInfo('Non-chat Message Type: ' + respItem.v2messageType) else: botlog.LogConsoleInfo('Non-chat Message Type: unknown') except SystemExit: botlog.LogConsoleInfo('Exiting Ares.') except Exception as ex: errorStr = "Symphony REST Exception (system): " + str(ex) # stackTrace = 'Stack Trace: ' + ''.join(traceback.format_stack()) exInfo = sys.exc_info() stackTrace = 'Stack Trace: ' + ''.join( traceback.format_exception(exInfo[0], exInfo[1], exInfo[2])) botlog.LogSystemError(errorStr) botlog.LogSystemError(stackTrace) botlog.LogConsoleInfo(response.ResponseText) return messageItems
def PollDataFeed(datafeedId): datafeedEP = config.SymphonyBaseURL + '/agent/v2/datafeed/' + datafeedId + '/read' #datafeedEP = config.SymphonyBaseURL + '/agent/v4/datafeed/' + datafeedId + '/read' response = callout.SymphonyGET(datafeedEP) # Messages coming from the API are formatted as an array of JSON objects # Thus, I need to break up the array, parse the individual objects, and pass # the list of python objects back to the engine messageItems = [] if response.Success: for respItem in response.ResponseData: # Hopefully this will try: if respItem.v2messageType and respItem.v2messageType == 'V2Message': detail = msg.MessageDetail(respItem) detail.Sender = user.GetSymphonyUserDetail( detail.FromUserId) detail.ChatRoom = stream.GetStreamInfo(respItem.streamId) botlog.LogSymphonyInfo(detail.GetConsoleLogLine()) if detail.Sender and detail.Sender.IsValidSender: detail.InitiateCommandParsing() messageItems.append(detail) elif respItem.v2messageType != 'V2Message': botlog.LogConsoleInfo('Non-chat Message Type: ' + respItem.v2messageType) else: botlog.LogConsoleInfo('Non-chat Message Type: unknown') except SystemExit: botlog.LogConsoleInfo('Exiting Symphony Zendesk Bot.') #messaging.SendSymphonyMessage(_configDef['BotStreamForPing'], "Exiting Symphony Zendesk Bot.") except Exception as ex: errorStr = "Symphony REST Exception (system): " + str(ex) #messaging.SendSymphonyMessage(_configDef['BotStreamForPing'], "Symphony REST Exception (system): " + str(ex)) # stackTrace = 'Stack Trace: ' + ''.join(traceback.format_stack()) exInfo = sys.exc_info() stackTrace = 'Stack Trace: ' + ''.join( traceback.format_exception(exInfo[0], exInfo[1], exInfo[2])) botlog.LogSystemError(errorStr) botlog.LogSystemError(stackTrace) botlog.LogConsoleInfo(response.ResponseText) #messaging.SendSymphonyMessage(_configDef['BotStreamForPing'], response.ResponseText) elif response.ResponseCode == 204: return [] else: botlog.LogConsoleInfo("datafeed.py error - Response Code: " + str(response.ResponseCode)) botlog.LogConsoleInfo("Response Message: " + response.ResponseText) #messaging.SendSymphonyMessage(_configDef['BotStreamForPing'], "datafeed.py error - Response Code: " + str(response.ResponseCode) + " Response Message: " + response.ResponseText) # if the response is not successful, return None. This way, I can tell the datafeed call was bad # and attempt to reconnect to the server. return None return messageItems
def ConnectDatafeed(self): self.BotUserId = user.GetBotUserId() botlog.LogSymphonyInfo('Bot User Id: ' + str(self.BotUserId)) if self.DataFeedId == '': botlog.LogSymphonyInfo('Creating Datafeed...') self.DataFeedId = datafeed.CreateDataFeed() else: botlog.LogSymphonyInfo('Attempting to Reuse Existing Datafeed in 5 seconds...') for sleepIndex in range(0, 5): botlog.LogConsoleInfo(str(sleepIndex) + '...') time.sleep(1) botlog.LogSymphonyInfo('Reconnecting to Datafeed...') if self.DataFeedId != '': botlog.LogSymphonyInfo('Datafeed Connected! Id: ' + self.DataFeedId) self.IsDatafeedConnected = True else: botlog.LogSymphonyError('Failed to connect to Datafeed.')
def __init__(self, respItem): self.MessageId = msg.FormatSymphonyId(respItem.id) if hasattr( respItem, 'id') else '-1' #self.MessageId = msg.FormatSymphonyId(respItem.messageId) if hasattr(respItem, 'messageId') else '-1' # I REALLY don't like the idea of EAFP. Sometimes it's just better to check things first # instead of just throwing an exception like a big 'ol tantrum. self.FromUserId = str(respItem.fromUserId) if hasattr( respItem, 'fromUserId') else '-1' #self.FromUserId = str(respItem.initiator.user.userId) if hasattr(respItem, 'initiator') else '-1' self.StreamId = msg.FormatSymphonyId(respItem.streamId) if hasattr( respItem, 'streamId') else '-1' #self.StreamId = msg.FormatSymphonyId(respItem.payload.messageSent.message.stream.streamId) if hasattr(respItem, 'payload') else '-1' #self.externalRecipients = str(respItem.payload.messageSent.message.externalRecipients) if hasattr(respItem, 'payload') else '-1' # Some of the message types (like room additions) have no message self.MessageRaw = respItem.message if hasattr(respItem, 'message') else None # if hasattr(respItem, 'payload'): # test = str(respItem.payload.messageSent.message.message) # soup = BeautifulSoup(test, "lxml") # for hit in soup.findAll('div'): # hit = hit.text.strip() # hit = hit.replace('<', '<') # # test = re.search("(?<=<p>)(.*?)(?=</p>)", test, flags=re.IGNORECASE).group(0) # hit = "<messageML>" + hit + "</messageML>" # self.MessageRaw = hit # else: None self.Type = respItem.v2messageType if hasattr(respItem, 'v2messageType') else '' self.Attachments = self.ParseAttachments(respItem) # respItem.attachments if hasattr(respItem, 'attachments') else [] self.IsValid = self.Type == 'V2Message' #self.IsValid = self.Type == 'MESSAGESENT' self.Sender = None self.ChatRoom = None self.Command = None if len(self.Attachments) > 0: log.LogConsoleInfo(respItem)
def GetKeyManagerToken(): botlog.LogConsoleInfo('Getting Key Manager Token...') return GetSymphonyAuthToken(config.KeyManagerEP)
def GetSessionToken(): botlog.LogConsoleInfo('Getting Session Token...') return GetSymphonyAuthToken(config.SessionAuthEP)
def SubmitFeedbackJIRAv2(messageDetail): try: issueFieldsDict = {} watcherList = [] labelSet = set(messageDetail.Command.Hashtags) reporter_field = { "name": util.FindJIRAUserByEmailV2(messageDetail.Sender.Email) } issueFieldsDict['reporter'] = reporter_field project = 'SFDC' type_name = 'Unknown' bodyDetail: str = messageDetail.Command.MessageFlattened # Conduct Epic Search epic_item = fbmatch.MatchEpic(bodyDetail) if epic_item is not None: issueFieldsDict['assignee'] = {"name": epic_item['assignTo']} if epic_item['epic'] != "": issueFieldsDict['customfield_10200'] = epic_item['epic'] # Union (|=) the epic specific labels with the hashtag labels labelSet |= set(epic_item['labels']) watcherList = epic_item['mention'] else: log.LogConsoleInfo('No epic match was returned!') # convert label set to list for JSON serialization issueFieldsDict['labels'] = list(labelSet) # Determine what type the feedback is type_dict = { "bug": "Bug", 'bugs': 'Bug', 'defect': 'Bug', 'defects': 'Bug', 'problem': 'Bug', 'problems': 'Bug', 'feature': 'New Feature', 'features': 'New Feature', 'featurerequest': 'New Feature', 'missing': 'New Feature', 'needed': 'New Feature', 'required': 'New Feature', 'newfeature': 'New Feature', 'newfeaturerequest': 'New Feature', 'usability': 'Usability Issue', 'useability': 'Usability Issue', 'performance': 'Usability Issue', 'unstable': 'Usability Issue', 'awkward': 'Usability Issue' } for tag in messageDetail.Command.Hashtags: if tag.lower() in type_dict: type_name = type_dict[tag.lower()] break # Regex to identify hash tags. regexHashtags = r"(\#[a-zA-Z]+\b)" summary = re.sub(regexHashtags, '', messageDetail.Command.MessageFlattened) # Build dict of UID/Users for uid in messageDetail.Command.Mentions: try: userStr = '_u_' + uid userObj = user.GetSymphonyUserDetail(uid) if userObj.Id != '-1': bodyReplace = userObj.FullName + '(' + userObj.Email + ')' bodyDetail = bodyDetail.replace(userStr, bodyReplace) # no need to include the mentioned users in the Summary summary = summary.replace(userStr, '') jiraUser = util.FindJIRAUserByEmailV2(userObj.Email) if jiraUser is not None: watcherList.append(jiraUser) except Exception as ex: log.LogSystemError(str(ex)) # clean up summary # Regex to replace extra spaces with single space regexSpace = "\s\s+" summary = re.sub(regexSpace, ' ', summary)[:100] if messageDetail.Command.CommandRaw is not None: summary = summary.replace(messageDetail.Command.CommandRaw, '') summary = summary.strip() nosubmit = False debug = False for tag in messageDetail.Command.Hashtags: if tag.lower() == 'nosubmit': nosubmit = True elif tag.lower() == 'debug': debug = True if not nosubmit: new_issue = util.CreateIssueV2(projectKey=project, summary=summary, desc=bodyDetail, issueTypeName=type_name, jiraFields=issueFieldsDict) # add watchers util.AddWatchersV2(new_issue, watcherList) msg = 'JIRA created successfully.<br/>Key: ' + new_issue.key + "<br/>JIRA Link: <a href='" + \ new_issue.permalink() + "'/>" messageDetail.ReplyToSenderv2(msg) else: msg = 'Feedback received but #nosubmit was included. Issue not sent to JIRA.' messageDetail.ReplyToSenderv2(msg) if debug: issueFieldsDict['project'] = project issueFieldsDict['summary'] = summary issueFieldsDict['description'] = bodyDetail issueFieldsDict['issueType'] = type_name from modules.symphony.messaging import FormatDicttoMML2 as json_format json_str = json_format(issueFieldsDict) messageDetail.ReplyToSenderv2(json_str) except Exception as ex: errStr = 'Unable to submit JIRA. Error: ' + str(ex) messageDetail.ReplyToSenderv2(errStr) stackTrace = 'Stack Trace: ' + ''.join(traceback.format_exc()) log.LogSymphonyError(errStr) log.LogSymphonyError(stackTrace)
def LogSearchEnd(s_index: int): log.LogConsoleInfo("(" + str(s_index) + ") Search Complete.")