import time import datetime import requests from flask import current_app class WebexManager: def __init__(self, db_collection, client_id, client_secret, redirect_uri, team_id, user_id): self.db = db_collection self.client_id = client_id self.client_secret = client_secret self.redirect_uri = redirect_uri self.team_id = team_id self.user_id = user_id self.base_url = "https://webexapis.com/v1" def save_tokens(self, token_data: dict): """Saves or updates Webex OAuth tokens in MongoDB.""" now = time.time() doc = { "_id": "webex_credentials", "access_token": token_data.get("access_token"), "refresh_token": token_data.get("refresh_token"), "expires_at": now + token_data.get("expires_in", 43200) - 300, # Subtract 5 mins buffer "refresh_token_expires_at": now + token_data.get("refresh_token_expires_in", 7776000), "updated_at": datetime.datetime.now(datetime.timezone.utc).isoformat() } self.db.replace_one({"_id": "webex_credentials"}, doc, upsert=True) return doc def get_access_token(self) -> str: """Retrieves a valid access token from MongoDB, auto-refreshing if expired.""" token_doc = self.db.find_one({"_id": "webex_credentials"}) if not token_doc: raise Exception("No Webex OAuth tokens found. Please complete login at /oauth/login.") # Check if access token is expired if time.time() >= token_doc.get("expires_at", 0): current_app.logger.info("Webex access token expired. Refreshing...") return self.refresh_token(token_doc.get("refresh_token")) return token_doc.get("access_token") def exchange_code(self, code: str) -> dict: """Exchanges OAuth code for access and refresh tokens.""" payload = { "grant_type": "authorization_code", "client_id": self.client_id, "client_secret": self.client_secret, "code": code, "redirect_uri": self.redirect_uri } res = requests.post(f"{self.base_url}/access_token", data=payload) res.raise_for_status() return self.save_tokens(res.json()) def refresh_token(self, refresh_token: str) -> str: """Refreshes the Webex access token using the stored refresh_token.""" payload = { "grant_type": "refresh_token", "client_id": self.client_id, "client_secret": self.client_secret, "refresh_token": refresh_token } res = requests.post(f"{self.base_url}/access_token", data=payload) res.raise_for_status() new_doc = self.save_tokens(res.json()) return new_doc["access_token"] def create_support_room(self, client_name: str, client_email: str, alert_type: str, alert_message: str, alert_id: str) -> str: """Creates a team support room and invites the client and yourself.""" access_token = self.get_access_token() headers = { "Authorization": f"Bearer {access_token}", "Content-Type": "application/json" } # 1. Create room under team room_res = requests.post( f"{self.base_url}/rooms", headers=headers, json={ "title": f"Smartwave Support: {client_name} - {alert_type} ({alert_id})", "teamId": self.team_id } ) room_res.raise_for_status() room_details = room_res.json() room_id = room_details["id"] # 2. Add Client via email (Webex sends invite if user does not exist) if client_email: requests.post( f"{self.base_url}/memberships", headers=headers, json={"roomId": room_id, "personEmail": client_email} ) # 3. Add yourself (WEBEX_NINLUC_ID) if self.user_id: requests.post( f"{self.base_url}/memberships", headers=headers, json={"roomId": room_id, "personId": self.user_id} ) # 4. Optional: Send initial welcome message in the room self.send_message( room_id, message=f"Support room initialized for {client_name}. An agent will be with you shortly.", access_token=access_token ) self.send_alert_info_message(room_id, alert_type, alert_message, alert_id, access_token=access_token) return room_details def send_message(self, room_id: str, message: str | None = None, markdown: str | None = None, access_token: str | None = None): # Fetch the token so it auto-refreshes if needed if not access_token: access_token = self.get_access_token() headers = { "Authorization": f"Bearer {access_token}", "Content-Type": "application/json" } requests.post( f"{self.base_url}/messages", headers=headers, json={ "roomId": room_id, "text": message, "markdown": markdown } ).raise_for_status() def send_alert_info_message(self, room_id: str, alert_type: str, alert_message: str, alert_id: str, added_alert_info: bool = False, access_token: str | None = None): """Sends a structured alert info message to the specified Webex room.""" if added_alert_info: message = f"Additional Alert Details:\n- Type: {alert_type}\n- Message: {alert_message}\n- Alert ID: {alert_id}" else: message = f"Alert Details:\n- Type: {alert_type}\n- Message: {alert_message}\n- Alert ID: {alert_id}" self.send_message(room_id, message=message, access_token=access_token)