| | from fastapi import FastAPI, Request |
| | from pydantic import BaseModel |
| | import requests |
| | import logging |
| | from datetime import datetime |
| |
|
| | |
| | logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') |
| |
|
| | app = FastAPI() |
| |
|
| | |
| | CLICKUP_URL_BASE = "https://api.clickup.com/api/v2" |
| |
|
| | |
| | WHATSAPP_URL = "https://7105.api.greenapi.com/waInstance7105210836/sendMessage/805b69f6c85d4e6caea0edaba692b889abecc9e6bb8b457e8f" |
| |
|
| | |
| | ACCESS_TOKEN = "2144425825_36f2249dc27c5aca075ac5442b1bbcdf01c3a29b9e41b86bda46a6cf651acd0f" |
| |
|
| | |
| | headers = { |
| | "Authorization": ACCESS_TOKEN, |
| | "Content-Type": "application/json" |
| | } |
| |
|
| | |
| | whatsapp_headers = { |
| | "Content-Type": "application/json" |
| | } |
| |
|
| | class TaskData(BaseModel): |
| | task_name: str |
| | task_type: str |
| | campaign_name: str |
| | platforms: list[str] |
| | assignees: list[int] |
| | due_date: int |
| |
|
| | @app.post("/singletask") |
| | async def create_task(request: Request): |
| | data = await request.json() |
| | logging.info(f"Received task data: {data}") |
| |
|
| | |
| | team = data.get('team', '') |
| | task_type = data.get('taskType', '') |
| | task_title = data.get('taskTitle', '') |
| | assignees = data.get('assignees', '') |
| | platforms = data.get('platforms', []) |
| | deadline = data.get('deadline', '') |
| | goal = data.get('goal', '') |
| | description = data.get('description', '') |
| | creative_type = data.get('creativeType', '') |
| | ad_content = data.get('adContent', '') |
| | posting_content = data.get('postingContent', '') |
| | attachment_link = data.get('attachmentLink', '') |
| | start_date = data.get('startDate', '') |
| | end_date = data.get('endDate', '') |
| |
|
| | |
| | print(f"Team: {team}") |
| | print(f"Task Type: {task_type}") |
| | print(f"Task Title: {task_title}") |
| | print(f"Assignees: {assignees}") |
| | print(f"Platforms: {platforms}") |
| | print(f"Deadline: {deadline}") |
| | print(f"Goal: {goal}") |
| | print(f"Description: {description}") |
| | print(f"Creative Type: {creative_type}") |
| | print(f"Ad Content: {ad_content}") |
| | print(f"Posting Content: {posting_content}") |
| | print(f"Attachment Link: {attachment_link}") |
| | print(f"Start Date: {start_date}") |
| | print(f"End Date: {end_date}") |
| |
|
| |
|
| | web_app_url = "https://script.google.com/macros/s/AKfycbzL6OVq4WNKb1mX0BWEtvxYNRKUdjmzp-gjCbhHOZa_y3N9XB9IF_RWZxlgB_Dx07Aw/exec" |
| | params = { |
| | "mode": "extended", |
| | "taskType": task_type, |
| | "company": team, |
| | "assignees": ','.join(assignees) |
| | } |
| |
|
| | response = requests.get(web_app_url, params=params) |
| | try: |
| | response_data = response.json() |
| | except ValueError: |
| | print("ERROR: Failed to decode JSON from web app response") |
| | return {"error": "Invalid response from Google Apps Script"} |
| |
|
| | print(f"Web App Response: {response_data}") |
| |
|
| | list_id = response_data.get("listId") |
| | print(f"List ID: {list_id}") |
| |
|
| | assignee_ids = response_data.get("assigneeIds", []) |
| | print(f"Assignee IDs: {assignee_ids}") |
| |
|
| | assignee_numbers = response_data.get("assigneeNumbers", []) |
| | manager_numbers = response_data.get("managerNumbers", []) |
| |
|
| | print(f"Assignee Numbers: {assignee_numbers}") |
| | print(f"Manager Numbers: {manager_numbers}") |
| | |
| | |
| | description_text = "" |
| |
|
| | task_type_lower = task_type.lower() |
| |
|
| | if task_type_lower == "strategy": |
| | status = "to do" |
| | description_text = description |
| |
|
| | elif task_type_lower == "content": |
| | status = "backlog" |
| | description_text = f"{goal.strip()}".strip() |
| | if platforms: |
| | description_text += "\n\nPlatforms: " + ", ".join(platforms) |
| |
|
| | elif task_type_lower == "creative": |
| | status = "to do" |
| | description_text = f"{creative_type.strip()}".strip() |
| | if platforms: |
| | description_text += "\n\nPlatforms: " + ", ".join(platforms) |
| |
|
| | elif task_type_lower == "ads": |
| | status = "ready" |
| | parts = [] |
| | if ad_content: |
| | parts.append(ad_content.strip()) |
| | if attachment_link: |
| | parts.append(f"Attachment: {attachment_link.strip()}") |
| | if platforms: |
| | parts.append("Platforms: " + ", ".join(platforms)) |
| | description_text = "\n\n".join(parts) |
| |
|
| | elif task_type_lower == "posting": |
| | status = "ready for posting" |
| | parts = [] |
| | if posting_content: |
| | parts.append(posting_content.strip()) |
| | if platforms: |
| | parts.append("Platforms: " + ", ".join(platforms)) |
| | if attachment_link: |
| | parts.append(f"Attachment: {attachment_link.strip()}") |
| | description_text = "\n\n".join(parts) |
| |
|
| | elif task_type_lower == "ads report": |
| | status = "ready" |
| | platforms_text = ", ".join(platforms) if platforms else "" |
| | description_text = f"Prepare an ads report for {platforms_text}" |
| | if start_date and end_date: |
| | description_text += f" from {start_date} to {end_date}" |
| |
|
| | else: |
| | return {"error": f"Unsupported task type: {task_type}"} |
| | |
| | |
| | payload = { |
| | "name": task_title, |
| | "description": description_text, |
| | "assignees": [int(uid) for uid in assignee_ids], |
| | "status": status |
| | } |
| |
|
| | |
| | if deadline: |
| | try: |
| | due_timestamp = int(datetime.strptime(deadline, "%Y-%m-%d").timestamp() * 1000) |
| | payload["due_date"] = due_timestamp |
| | print(f"Due Date (timestamp): {due_timestamp}") |
| | except ValueError: |
| | print("Invalid deadline format. Skipping due_date.") |
| |
|
| | |
| | create_url = f"{CLICKUP_URL_BASE}/list/{list_id}/task" |
| | clickup_response = requests.post(create_url, headers=headers, json=payload) |
| | clickup_data = clickup_response.json() |
| | print(f"ClickUp Response: {clickup_response.status_code}, {clickup_data}") |
| |
|
| | |
| | |
| | |
| |
|
| | return {"status": "Task data received and parsed"} |
| |
|
| | |
| | def get_task_name(task_id): |
| | """Fetch task details from ClickUp API using task_id.""" |
| | url = f"https://api.clickup.com/api/v2/task/{task_id}" |
| | headers = { |
| | "Authorization": ACCESS_TOKEN |
| | } |
| | |
| | |
| | response = requests.get(url, headers=headers) |
| |
|
| | |
| | if response.status_code == 200: |
| | |
| | |
| | task_name = response.json()["name"] |
| | return task_name |
| | else: |
| | task_name = "Task name wasn't not found" |
| | return task_name |
| |
|
| | def get_task_details(task_id): |
| | url = f"https://api.clickup.com/api/v2/task/{task_id}" |
| | headers = {"Authorization": ACCESS_TOKEN} |
| | |
| | response = requests.get(url, headers=headers) |
| | |
| | if response.status_code == 200: |
| | task_data = response.json() |
| | else: |
| | print(f"Error: {response.status_code}, {response.text}") |
| | task_data = "invalid data" |
| |
|
| | return task_data |
| |
|
| | def send_whatsapp_notification(chat_id: str, message: str): |
| | payload = { |
| | "chatId": chat_id, |
| | "message": message |
| | } |
| | try: |
| | response = requests.post(WHATSAPP_URL, json=payload, headers=whatsapp_headers) |
| | response.raise_for_status() |
| | logging.info(f"WhatsApp sent to {chat_id} - {response.status_code}: {response.text}") |
| | except requests.RequestException as e: |
| | logging.error(f"WhatsApp send failed for {chat_id}: {e}") |
| |
|
| | @app.post("/updates") |
| | async def task_update(request: Request): |
| | data = await request.json() |
| | logging.info(f"Received task update from ClickUp: {data}") |
| |
|
| | event_type = data.get("event") |
| | task_id = data.get("task_id", "Unknown ID") |
| | |
| | |
| | task_name = get_task_name(task_id) |
| | task_link = f"https://app.clickup.com/t/{task_id}" |
| |
|
| | if event_type == "taskUpdated": |
| | history_items = data.get("history_items", []) |
| |
|
| | for item in history_items: |
| | if item.get("field") == "status": |
| | after_status = item.get("after", {}).get("status") |
| | action_timestamp = item.get("date", 0) |
| |
|
| | if not after_status: |
| | logging.warning(f"Task {task_id} update ignored: No status change detected.") |
| | continue |
| |
|
| | action_date_human = datetime.utcfromtimestamp(int(action_timestamp) / 1000).strftime('%Y-%m-%d %H:%M:%S') if action_timestamp else "Unknown Date" |
| |
|
| | logging.info(f"Task: {task_name}, New Status: {after_status}, Action Date: {action_date_human}") |
| |
|
| | if after_status.lower() == "ready for review": |
| | chat_id = "201092003112@c.us" |
| | message = ( |
| | f"📝 *Task Submitted for Review!*\n\n" |
| | f"📌 *Task:* {task_name}\n" |
| | f"📅 *Submitted On:* {action_date_human}\n\n" |
| | f"🔗 *View Task:* {task_link}\n\n" |
| | f"Please review the task and provide feedback." |
| | ) |
| | send_whatsapp_notification(chat_id, message) |
| |
|
| | elif event_type == "taskTagUpdated": |
| | history_items = data.get("history_items", []) |
| | if history_items: |
| | for history_item in history_items: |
| | if "after" in history_item: |
| | for tag in history_item["after"]: |
| | tag_name = tag.get("name") |
| | if tag_name and tag_name.lower() == "missed due date": |
| | |
| | task_details = get_task_details(task_id) |
| | print(f"Task details: {task_details}") |
| | |
| | assignee_usernames = [assignee['username'] for assignee in task_details.get('assignees', [])] |
| | |
| | print(assignee_usernames) |
| | due_date_timestamp = task_details.get("due_date") |
| | due_date = datetime.utcfromtimestamp(int(due_date_timestamp) / 1000).strftime('%Y-%m-%d') if due_date_timestamp else "Unknown Due Date" |
| |
|
| | logging.info(f"Missed Due Date Tag Added for Task: {task_name}, Due Date: {due_date}") |
| |
|
| | chat_id = "201092003112@c.us" |
| | message = ( |
| | f"⚠️ *Task Missed Due Date!*\n\n" |
| | f"📌 *Task:* {task_name}\n" |
| | f"📅 *Due Date:* {due_date}\n\n" |
| | f"🔗 *View Task:* {task_link}\n\n" |
| | f"Please take action immediately." |
| | ) |
| | send_whatsapp_notification(chat_id, message) |
| |
|
| | return {"status": "Update received"} |