def outgoing(request) -> MegatronResponse: user_id = request.data['user'] message = request.data['message'] try: message['attachments'] = json.loads(message['attachments']) except TypeError: pass try: megatron_channel = MegatronChannel.objects.get( platform_user_id=user_id) except MegatronChannel.DoesNotExist: return MegatronResponse({'ok': True, 'track': False}, 200) if megatron_channel.is_archived: return MegatronResponse({'ok': True, 'track': False}, 200) megatron_integration = megatron_channel.megatron_integration interpreter = IntegrationService(megatron_integration).get_interpreter() customer_workspace_id = megatron_channel.workspace.platform_id response = interpreter.outgoing(message, megatron_channel) if response.get('ok'): if response.get('watched_channel'): front_integration.outgoing_message.delay(user_id, customer_workspace_id, message) megatron_msg = MegatronMessage(integration_msg_id=response['ts'], customer_msg_id=request.data['ts'], megatron_channel=megatron_channel) megatron_msg.save() return MegatronResponse({'ok': True, 'track': True}, 200) return MegatronResponse({ 'error': response.get('error'), 'track': False }, 500)
def open_channel( megatron_user_id: int, serialized_request_data: dict, arguments: dict ) -> dict: request_data = RequestData(**serialized_request_data) megatron_user = MegatronUser.objects.get(id=megatron_user_id) platform_user_id = arguments["targeted_platform_id"] integration = megatron_user.megatronintegration_set.first() connection = IntegrationService(integration).get_connection(as_user=False) # Ensure platform user exists if not PlatformUser.objects.filter(platform_id=platform_user_id).exists(): user_info = connection._get_user_info(platform_user_id) try: workspace = CustomerWorkspace.objects.get(platform_id=user_info["user"]["team_id"]) except (CustomerWorkspace.DoesNotExist, KeyError): return {"ok": False, "error": "Customer Workspace not found"} WorkspaceService(workspace).get_or_create_user_by_id(platform_user_id) new_msg = formatting.user_titled(platform_user_id, "Connecting...") response = connection.respond_to_url(request_data.response_url, new_msg) if not response.get("ok"): return response _update_channel_link.delay( megatron_user.id, platform_user_id, request_data.response_url ) return {"ok": True}
def clear_context( megatron_user_id: int, serialized_request_data: dict, arguments: dict ) -> dict: request_data = RequestData(**serialized_request_data) megatron_user = MegatronUser.objects.get(id=megatron_user_id) platform_user_id = User.objects.get(id=arguments["platform_user_id"]) command_url = getattr(megatron_user, "command_url", None) if not command_url: return {"ok": False, "error": "MegatronUser has not provided a command url."} # TODO: Use the command that called this to standardize the 'command' param response = requests.post( megatron_user.command_url, json={ "command": "clear-context", "platform_user_id": platform_user_id, "megatron_verification_token": megatron_user.verification_token, }, ) if not response.status_code == 200: return {"ok": False, "error": response.content} # TODO: This is probably better suited to being part of the integration itself integration = megatron_user.megatronintegration_set.first() integration_connection = IntegrationService(integration).get_connection( as_user=False ) platform_username = PlatformUser.objects.get(platform_id=platform_user_id).username msg = {"text": f"Context cleared for *{platform_username}*."} integration_response = integration_connection.ephemeral_message(request_data, msg) if not integration_response.get("ok"): return {"ok": False, "error": "Failed to post confirmation to slack."} return {"ok": True}
def _check_and_send_paused_warning(megatron_channel: MegatronChannel, user_id: str): if megatron_channel.is_paused: return last_warning = cache.get(PAUSE_WARNING_PREFIX + str(megatron_channel.id)) if last_warning: last_warning = datetime.strptime(last_warning, '%d-%m-%Y:%H:%M:%S') minutes_elapsed = (datetime.now() - last_warning).total_seconds() / 60 if minutes_elapsed < TIME_TIL_NEXT_WARNING: return workspace_id = megatron_channel.workspace.platform_id platform_user_id = megatron_channel.platform_user_id msg = formatting.get_unpaused_warning(workspace_id, platform_user_id) channel_id = megatron_channel.platform_channel_id integration_connection = IntegrationService( megatron_channel.megatron_integration).get_connection(as_user=False) message_action = Action(ActionType.POST_MESSAGE, { 'channel': channel_id, 'message': msg }) integration_connection.take_action(message_action) last_warning = datetime.now().strftime('%d-%m-%Y:%H:%M:%S') cache.set(PAUSE_WARNING_PREFIX + str(megatron_channel.id), last_warning)
def _image_passthrough_message(event): tracked_channel = MegatronChannel.objects.filter( platform_channel_id=event['channel']).first() connection = IntegrationService( tracked_channel.megatron_integration).get_connection(as_user=False) user = PlatformUser.objects.get(platform_id=event['user']) msg = connection.build_img_attach(event, user) return msg
def _change_pause_state( megatron_user: MegatronUser, platform_user: User, request_data: RequestData, pause=False, ) -> dict: workspace = platform_user.workspace if not getattr(megatron_user, "command_url", None): return {"ok": False, "error": "No command url provided for workspace."} customer_connection = WorkspaceService(workspace).get_connection(as_user=False) response = customer_connection.open_im(platform_user.platform_id) if not response["ok"]: return { "ok": False, "error": "Failed to open get im channel from " f"slack: {response['error']}", } channel_id = response["channel"]["id"] data = { "megatron_verification_token": settings.MEGATRON_VERIFICATION_TOKEN, "command": "pause", "channel_id": channel_id, "platform_user_id": platform_user.platform_id, "team_id": workspace.platform_id, "paused": pause, } response = requests.post(megatron_user.command_url, json=data) # TODO: This response is 200 even on failure to find user if not response.status_code == 200: return {"ok": False, "error": "Failed to pause bot for user."} megatron_channel = MegatronChannel.objects.get( workspace=workspace, platform_user_id=platform_user.platform_id ) megatron_channel.is_paused = pause megatron_channel.save() # TODO: This is probably better suited to being part of the integration itself integration = megatron_user.megatronintegration_set.first() integration_connection = IntegrationService(integration).get_connection( as_user=False ) paused_word = "paused" if pause else "unpaused" msg = {"text": f"Bot *{paused_word}* for user: {platform_user}."} channel = request_data.channel_id message_action = Action( ActionType.POST_MESSAGE, {"channel": channel, "message": msg} ) integration_connection.take_action(message_action) return {"ok": True}
def open_channel(megatron_user_id: int, serialized_request_data: dict, arguments: dict) -> dict: request_data = RequestData(**serialized_request_data) megatron_user = MegatronUser.objects.get(id=megatron_user_id) platform_user_id = arguments['targeted_platform_id'] integration = megatron_user.megatronintegration_set.first() connection = IntegrationService(integration).get_connection(as_user=False) new_msg = formatting.user_titled(platform_user_id, "Connecting....") response = connection.respond_to_url(request_data.response_url, new_msg) if not response.get('ok'): return response _update_channel_link.delay(megatron_user.id, platform_user_id, request_data.response_url) return {'ok': True}
def _get_slack_user_data(channel_id, slack_id): # TODO: Verify responses/that user exists incoming_channel = MegatronChannel.objects.get( platform_channel_id=channel_id) integration_connection = IntegrationService( incoming_channel.megatron_integration).get_connection() action = Action(ActionType.GET_USER_INFO, {'user_id': slack_id}) response = integration_connection.take_action(action) user_data = response['user'] from_user = { 'user_name': user_data['profile']['real_name'], 'user_icon_url': user_data['profile']['image_24'] } return from_user
def _get_slack_user_data(channel_id, slack_id): # TODO: Verify responses/that user exists incoming_channel = MegatronChannel.objects.get( platform_channel_id=channel_id) integration_connection = IntegrationService( incoming_channel.megatron_integration).get_connection() action = Action(ActionType.GET_USER_INFO, {"user_id": slack_id}) response = integration_connection.take_action(action) user_data = response["user"] from_user = { "user_name": user_data["profile"]["real_name"], "user_icon_url": user_data["profile"]["image_24"], } return from_user
def unpause_reminder(): channels = MegatronChannel.objects.all() for channel in channels: if not channel.is_paused: continue workspace_id = channel.workspace.id platform_user_id = channel.platform_user_id time_since_last_message = datetime.now() - channel.last_message_sent if PAUSE_WARNING_START < time_since_last_message <= PAUSE_WARNING_STOP: integration = MegatronIntegration.objects.get( megatron_user=channel.megatron_user) connection = IntegrationService(integration).get_connection( as_user=False) msg = formatting.get_pause_warning(workspace_id, platform_user_id) connection.message(channel.platform_channel_id, msg)
def _change_pause_state(megatron_user: MegatronUser, platform_user: User, request_data: RequestData, pause=False ) -> dict: workspace = platform_user.workspace if not getattr(megatron_user, 'command_url', None): return {'ok': False, 'error': 'No command url provided for workspace.'} customer_connection = WorkspaceService( workspace).get_connection(as_user=False) response = customer_connection.open_im(platform_user.platform_id) if not response['ok']: return { 'ok': False, 'error': 'Failed to open get im channel from ' f"slack: {response['error']}" } channel_id = response['channel']['id'] data = { 'megatron_verification_token': settings.MEGATRON_VERIFICATION_TOKEN, 'command': 'pause', 'channel_id': channel_id, 'platform_user_id': platform_user.platform_id, 'team_id': workspace.platform_id, 'paused': pause, } response = requests.post(megatron_user.command_url, json=data) # TODO: This response is 200 even on failure to find user if not response.status_code == 200: return {'ok': False, 'error': 'Failed to pause bot for user.'} megatron_channel = MegatronChannel.objects.get( workspace=workspace, platform_user_id=platform_user.platform_id) megatron_channel.is_paused = pause megatron_channel.save() # TODO: This is probably better suited to being part of the integration itself integration = megatron_user.megatronintegration_set.first() integration_connection = IntegrationService( integration).get_connection(as_user=False) paused_word = 'paused' if pause else 'unpaused' msg = { "text": f"Bot *{paused_word}* for user: {platform_user}." } channel = request_data.channel_id message_action = Action(ActionType.POST_MESSAGE, {'channel': channel, 'message': msg}) integration_connection.take_action(message_action) return {'ok': True}
def edit(request): message = json.loads(request.body) # Warning! Currently the only way to identify msgs sent by Megatron try: footer_text = message["message"]["attachments"][-1]["footer"] if footer_text.startswith("sent by"): return OK_RESPONSE except KeyError: pass if message.get("user"): megatron_channel = MegatronChannel.objects.filter( platform_user_id=message["message"]["user"]).first() elif message.get("channel"): megatron_channel = MegatronChannel.objects.filter( platform_channel_id=message["channel"]).first() else: return OK_RESPONSE if not megatron_channel: return OK_RESPONSE team_connection = IntegrationService( megatron_channel.megatron_integration).get_connection(as_user=False) existing_message = MegatronMessage.objects.filter( customer_msg_id=message["previous_message"]["ts"], megatron_channel=megatron_channel, ).first() if not megatron_channel or not existing_message: return OK_RESPONSE new_message = { "text": message["message"].get("text", ""), "attachments": message["message"].get("attachments"), } old_message = { "channel_id": megatron_channel.platform_channel_id, "ts": existing_message.integration_msg_id, } params = {"new_message": new_message, "old_message": old_message} update_action = Action(ActionType.UPDATE_MESSAGE, params) response = team_connection.take_action(update_action) existing_message.customer_msg_id = message["message"]["ts"] existing_message.integration_msg_id = response["ts"] existing_message.save() return OK_RESPONSE
def edit(request): message = json.loads(request.body) # Warning! Currently the only way to identify msgs sent by Megatron try: footer_text = message['message']['attachments'][-1]['footer'] if footer_text.startswith('sent by'): return OK_RESPONSE except KeyError: pass if message.get('user'): megatron_channel = MegatronChannel.objects.filter( platform_user_id=message['message']['user']).first() elif message.get('channel'): megatron_channel = MegatronChannel.objects.filter( platform_channel_id=message['channel']).first() else: return OK_RESPONSE if not megatron_channel: return OK_RESPONSE team_connection = IntegrationService( megatron_channel.megatron_integration).get_connection(as_user=False) existing_message = MegatronMessage.objects.filter( customer_msg_id=message['previous_message']['ts'], megatron_channel=megatron_channel).first() if not megatron_channel or not existing_message: return OK_RESPONSE new_message = { 'text': message['message'].get('text', ''), 'attachments': message['message'].get('attachments') } old_message = { 'channel_id': megatron_channel.platform_channel_id, 'ts': existing_message.integration_msg_id } params = {'new_message': new_message, 'old_message': old_message} update_action = Action(ActionType.UPDATE_MESSAGE, params) response = team_connection.take_action(update_action) existing_message.customer_msg_id = message['message']['ts'] existing_message.integration_msg_id = response['ts'] existing_message.save() return OK_RESPONSE
def _update_channel_link( megatron_user_id: int, platform_user_id: str, response_url: str ): megatron_user = MegatronUser.objects.get(id=megatron_user_id) integration = megatron_user.megatronintegration_set.first() connection = IntegrationService(integration).get_connection(as_user=False) platform_user = PlatformUser.objects.get(platform_id=platform_user_id) megatron_user = MegatronUser.objects.get(id=megatron_user_id) megatron_channel = MegatronChannel.objects.filter( platform_user_id=platform_user_id ).first() if not megatron_channel: username = platform_user.username + "_" + platform_user.workspace.domain username = re.sub(r"[^\w-]", "", username.lower()) response = connection.create_channel(f"{settings.CHANNEL_PREFIX}{username}") if not response: response = connection.respond_to_url( response_url, {"text": "Error creating channel."} ) if not response.get("ok"): LOGGER.error(f"Problem updating slack message: {response['error']}") return megatron_channel, created = _create_or_update_channel( megatron_user, response["channel"], platform_user ) if created: _get_conversation_history(megatron_channel) elif megatron_channel.is_archived: MegatronChannelService(megatron_channel).unarchive() _get_conversation_history(megatron_channel) channel_link = _get_channel_link( megatron_user, megatron_channel.platform_channel_id ) join_message = formatting.user_titled( platform_user_id, f"<{channel_link}|Go to slack conversation>" ) response = connection.respond_to_url(response_url, join_message) if not response.get("ok"): LOGGER.error(f"Problem updating slack message: {response['error']}")
def notify_user(request) -> MegatronResponse: msg = request.data["message"] user_id = request.data["user_id"] channel_id = request.data["channel_id"] platform_type = request.data["platform_type"] request_data = RequestData(channel_id=channel_id, user_id=user_id, response_url="") platform_type = PlatformType[platform_type.capitalize()].value megatron_channel = MegatronChannel.objects.get( platform_channel_id=channel_id, workspace__platform_type=platform_type) connection = IntegrationService( megatron_channel.megatron_integration).get_connection(as_user=False) response = connection.ephemeral_message(request_data, msg) if response.get("ok"): return MegatronResponse({"ok": True, "track": True}, 200) return MegatronResponse({ "error": response.get("error"), "track": False }, 500)
def notify_user(request) -> MegatronResponse: msg = request.data['message'] user_id = request.data['user_id'] channel_id = request.data['channel_id'] platform_type = request.data['platform_type'] request_data = RequestData(channel_id=channel_id, user_id=user_id, response_url="") platform_type = PlatformType[platform_type.capitalize()].value megatron_channel = MegatronChannel.objects.get( platform_channel_id=channel_id, workspace__platform_type=platform_type) connection = IntegrationService( megatron_channel.megatron_integration).get_connection(as_user=False) response = connection.ephemeral_message(request_data, msg) if response.get('ok'): return MegatronResponse({'ok': True, 'track': True}, 200) return MegatronResponse({ 'error': response.get('error'), 'track': False }, 500)
def get_a_human(request): payload = json.loads(request.body) requesting_user = payload['requesting_user'] workspace_id = payload['workspace_id'] integration = MegatronIntegration.objects.get( megatron_user__id=request.user.id) team_connection = IntegrationService(integration).get_connection( as_user=False) # TODO: This should all be integration dependent channel = settings.NOTIFICATIONS_CHANNELS[ NotificationChannels.customer_service] slack_name = requesting_user['with_team_domain'] attach = { "color": '1f355e', "text": f"🆘 *{slack_name}* requested some help!", "callback_id": f"open", "actions": [{ "name": "categorize", "text": "Open engagement channel", "type": "button", "value": f'{workspace_id}-{requesting_user["slack_id"]}' }], "mrkdwn_in": ["text"] } msg = ({'text': ' ', 'attachments': [attach]}) msg_action = Action(ActionType.POST_MESSAGE, { 'channel': channel, 'message': msg }) response = team_connection.take_action(msg_action) if response.get('ok'): return OK_RESPONSE return MegatronResponse(response.get('error'), response.get('status'))
def incoming(request) -> MegatronResponse: msg = request.data['message'] megatron_user = request.user integration = megatron_user.megatronintegration_set.first() team_interpreter = IntegrationService(integration).get_interpreter() channel = MegatronChannel.objects.filter( platform_user_id=msg['user']).first() if not channel or channel.is_archived: return MegatronResponse({'ok': True, 'track': False}, 200) response = team_interpreter.incoming(msg, channel) if response.get('ok'): if response.get('watched_channel'): front_integration.incoming_message.delay(msg) channel = MegatronChannel.objects.filter( platform_user_id=request.data['message']['user']).first() MegatronMessage.objects.create( integration_msg_id=response['ts'], customer_msg_id=request.data['message']['ts'], megatron_channel=channel) return OK_RESPONSE return MegatronResponse(response.get('error'), 500)
def incoming(request) -> MegatronResponse: msg = request.data["message"] megatron_user = request.user integration = megatron_user.megatronintegration_set.first() team_interpreter = IntegrationService(integration).get_interpreter() channel = MegatronChannel.objects.filter( platform_user_id=msg["user"]).first() if not channel or channel.is_archived: return MegatronResponse({"ok": True, "track": False}, 200) response = team_interpreter.incoming(msg, channel) if response.get("ok"): if response.get("watched_channel"): front_integration.incoming_message.delay(msg) channel = MegatronChannel.objects.filter( platform_user_id=request.data["message"]["user"]).first() MegatronMessage.objects.create( integration_msg_id=response["ts"], customer_msg_id=request.data["message"]["ts"], megatron_channel=channel, ) return OK_RESPONSE return MegatronResponse(response.get("error"), 500)
def outgoing(request) -> MegatronResponse: user_id = request.data["user"] message = request.data["message"] try: message["attachments"] = json.loads(message["attachments"]) except TypeError: pass try: megatron_channel = MegatronChannel.objects.get( platform_user_id=user_id) except MegatronChannel.DoesNotExist: return MegatronResponse({"ok": True, "track": False}, 200) if megatron_channel.is_archived: return MegatronResponse({"ok": True, "track": False}, 200) megatron_integration = megatron_channel.megatron_integration interpreter = IntegrationService(megatron_integration).get_interpreter() customer_workspace_id = megatron_channel.workspace.platform_id response = interpreter.outgoing(message, megatron_channel) if response.get("ok"): if response.get("watched_channel"): front_integration.outgoing_message.delay(user_id, customer_workspace_id, message) megatron_msg = MegatronMessage( integration_msg_id=response["ts"], customer_msg_id=request.data["ts"], megatron_channel=megatron_channel, ) megatron_msg.save() return MegatronResponse({"ok": True, "track": True}, 200) return MegatronResponse({ "error": response.get("error"), "track": False }, 500)
def _get_conversation_history(channel: MegatronChannel): connection = WorkspaceService(channel.workspace).get_connection() response = connection.open_im(channel.platform_user_id) channel_id = response['channel']['id'] prev_messages = connection.im_history(channel_id, 10) integration_interpreter = IntegrationService( channel.megatron_integration).get_interpreter() messages = prev_messages['messages'] messages.sort(key=lambda message: message['ts']) previous_ts = None for message in messages: timestamp = datetime.fromtimestamp(int(message['ts'].split('.')[0])) formatted_timestamp = _format_slack_timestamp(timestamp, previous_ts) message['text'] = f"{formatted_timestamp}{message['text']}" previous_ts = timestamp if message.get('bot_id'): integration_interpreter.outgoing(message, channel) else: integration_interpreter.incoming(message, channel) return prev_messages