Implement AI workspace and mitigation workflow
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
from rest_framework import status
|
||||
|
||||
import requests
|
||||
from requests.exceptions import RequestException
|
||||
import orjson
|
||||
from django.conf import settings
|
||||
import aiohttp
|
||||
import base64
|
||||
import asyncio
|
||||
import base64
|
||||
|
||||
import aiohttp
|
||||
import orjson
|
||||
import requests
|
||||
from django.conf import settings
|
||||
from requests.exceptions import RequestException
|
||||
from rest_framework import status
|
||||
|
||||
|
||||
async def start_typing(uuid):
|
||||
@@ -18,6 +18,7 @@ async def start_typing(uuid):
|
||||
async with session.put(url, json=data) as response:
|
||||
return await response.text() # Optional: Return response content
|
||||
|
||||
|
||||
async def stop_typing(uuid):
|
||||
base = getattr(settings, "SIGNAL_HTTP_URL", "http://signal:8080").rstrip("/")
|
||||
url = f"{base}/v1/typing_indicator/{settings.SIGNAL_NUMBER}"
|
||||
@@ -27,6 +28,7 @@ async def stop_typing(uuid):
|
||||
async with session.delete(url, json=data) as response:
|
||||
return await response.text() # Optional: Return response content
|
||||
|
||||
|
||||
async def download_and_encode_base64(file_url, filename, content_type):
|
||||
"""
|
||||
Downloads a file from a given URL asynchronously, converts it to Base64,
|
||||
@@ -51,12 +53,15 @@ async def download_and_encode_base64(file_url, filename, content_type):
|
||||
base64_encoded = base64.b64encode(file_data).decode("utf-8")
|
||||
|
||||
# Format according to Signal's expected structure
|
||||
return f"data:{content_type};filename={filename};base64,{base64_encoded}"
|
||||
return (
|
||||
f"data:{content_type};filename={filename};base64,{base64_encoded}"
|
||||
)
|
||||
|
||||
except aiohttp.ClientError as e:
|
||||
# log.error(f"Failed to download file: {file_url}, error: {e}")
|
||||
return None
|
||||
|
||||
|
||||
async def send_message_raw(recipient_uuid, text=None, attachments=[]):
|
||||
"""
|
||||
Sends a message using the Signal REST API, ensuring attachment links are not included in the text body.
|
||||
@@ -75,11 +80,14 @@ async def send_message_raw(recipient_uuid, text=None, attachments=[]):
|
||||
data = {
|
||||
"recipients": [recipient_uuid],
|
||||
"number": settings.SIGNAL_NUMBER,
|
||||
"base64_attachments": []
|
||||
"base64_attachments": [],
|
||||
}
|
||||
|
||||
# Asynchronously download and encode all attachments
|
||||
tasks = [download_and_encode_base64(att["url"], att["filename"], att["content_type"]) for att in attachments]
|
||||
tasks = [
|
||||
download_and_encode_base64(att["url"], att["filename"], att["content_type"])
|
||||
for att in attachments
|
||||
]
|
||||
encoded_attachments = await asyncio.gather(*tasks)
|
||||
|
||||
# Filter out failed downloads (None values)
|
||||
@@ -87,7 +95,7 @@ async def send_message_raw(recipient_uuid, text=None, attachments=[]):
|
||||
|
||||
# Remove the message body if it only contains an attachment link
|
||||
if text and (text.strip() in [att["url"] for att in attachments]):
|
||||
#log.info("Removing message body since it only contains an attachment link.")
|
||||
# log.info("Removing message body since it only contains an attachment link.")
|
||||
text = None # Don't send the link as text
|
||||
|
||||
if text:
|
||||
@@ -103,6 +111,7 @@ async def send_message_raw(recipient_uuid, text=None, attachments=[]):
|
||||
return ts if ts else False
|
||||
return False
|
||||
|
||||
|
||||
async def fetch_signal_attachment(attachment_id):
|
||||
"""
|
||||
Asynchronously fetches an attachment from Signal.
|
||||
@@ -111,7 +120,7 @@ async def fetch_signal_attachment(attachment_id):
|
||||
attachment_id (str): The Signal attachment ID.
|
||||
|
||||
Returns:
|
||||
dict | None:
|
||||
dict | None:
|
||||
{
|
||||
"content": <binary file data>,
|
||||
"content_type": <MIME type>,
|
||||
@@ -128,7 +137,9 @@ async def fetch_signal_attachment(attachment_id):
|
||||
if response.status != 200:
|
||||
return None # Failed request
|
||||
|
||||
content_type = response.headers.get("Content-Type", "application/octet-stream")
|
||||
content_type = response.headers.get(
|
||||
"Content-Type", "application/octet-stream"
|
||||
)
|
||||
content = await response.read()
|
||||
size = int(response.headers.get("Content-Length", len(content)))
|
||||
|
||||
@@ -150,7 +161,6 @@ async def fetch_signal_attachment(attachment_id):
|
||||
return None # Network error
|
||||
|
||||
|
||||
|
||||
def download_and_encode_base64_sync(file_url, filename, content_type):
|
||||
"""
|
||||
Downloads a file from a given URL, converts it to Base64, and returns it in Signal's expected format.
|
||||
@@ -173,7 +183,7 @@ def download_and_encode_base64_sync(file_url, filename, content_type):
|
||||
# Format according to Signal's expected structure
|
||||
return f"data:{content_type};filename={filename};base64,{base64_encoded}"
|
||||
except requests.RequestException as e:
|
||||
#log.error(f"Failed to download file: {file_url}, error: {e}")
|
||||
# log.error(f"Failed to download file: {file_url}, error: {e}")
|
||||
return None
|
||||
|
||||
|
||||
@@ -193,18 +203,20 @@ def send_message_raw_sync(recipient_uuid, text=None, attachments=[]):
|
||||
data = {
|
||||
"recipients": [recipient_uuid],
|
||||
"number": settings.SIGNAL_NUMBER,
|
||||
"base64_attachments": []
|
||||
"base64_attachments": [],
|
||||
}
|
||||
|
||||
# Convert attachments to Base64
|
||||
for att in attachments:
|
||||
base64_data = download_and_encode_base64_sync(att["url"], att["filename"], att["content_type"])
|
||||
base64_data = download_and_encode_base64_sync(
|
||||
att["url"], att["filename"], att["content_type"]
|
||||
)
|
||||
if base64_data:
|
||||
data["base64_attachments"].append(base64_data)
|
||||
|
||||
# Remove the message body if it only contains an attachment link
|
||||
if text and (text.strip() in [att["url"] for att in attachments]):
|
||||
#log.info("Removing message body since it only contains an attachment link.")
|
||||
# log.info("Removing message body since it only contains an attachment link.")
|
||||
text = None # Don't send the link as text
|
||||
|
||||
if text:
|
||||
@@ -214,10 +226,12 @@ def send_message_raw_sync(recipient_uuid, text=None, attachments=[]):
|
||||
response = requests.post(url, json=data, timeout=10)
|
||||
response.raise_for_status()
|
||||
except requests.RequestException as e:
|
||||
#log.error(f"Failed to send Signal message: {e}")
|
||||
# log.error(f"Failed to send Signal message: {e}")
|
||||
return False
|
||||
|
||||
if response.status_code == status.HTTP_201_CREATED: # Signal server returns 201 on success
|
||||
if (
|
||||
response.status_code == status.HTTP_201_CREATED
|
||||
): # Signal server returns 201 on success
|
||||
try:
|
||||
ts = orjson.loads(response.text).get("timestamp", None)
|
||||
return ts if ts else False
|
||||
|
||||
Reference in New Issue
Block a user