Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
207037eaf9 | ||
|
|
3b60afc5c7 |
@@ -4,11 +4,12 @@ Crisis detection and response system for the-door.
|
||||
Stands between a broken man and a machine that would tell him to die.
|
||||
"""
|
||||
|
||||
from .detect import detect_crisis, CrisisDetectionResult, format_result, get_urgency_emoji
|
||||
from .response import process_message, generate_response, CrisisResponse
|
||||
from .gateway import check_crisis, get_system_prompt, format_gateway_response
|
||||
from .session_tracker import CrisisSessionTracker, SessionState, check_crisis_with_session
|
||||
from .metrics import CrisisMetrics, AggregateMetrics
|
||||
from .intake import (
|
||||
handle_intake_submission,
|
||||
IntakeResult,
|
||||
create_intake_issue,
|
||||
close_intake_issue,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"detect_crisis",
|
||||
@@ -26,4 +27,8 @@ __all__ = [
|
||||
"check_crisis_with_session",
|
||||
"CrisisMetrics",
|
||||
"AggregateMetrics",
|
||||
"handle_intake_submission",
|
||||
"IntakeResult",
|
||||
"create_intake_issue",
|
||||
"close_intake_issue",
|
||||
]
|
||||
|
||||
126
crisis/intake.py
126
crisis/intake.py
@@ -4,10 +4,15 @@ Intake submission handler for the-door.
|
||||
Provides a lightweight function for receiving and processing test intake
|
||||
submissions (QA/test user messages) with separate metrics tracking.
|
||||
|
||||
Also provides Gitea issue integration for tracking intake submissions
|
||||
and their processing state.
|
||||
|
||||
Usage:
|
||||
from crisis.intake import handle_intake_submission
|
||||
from crisis.intake import handle_intake_submission, close_intake_issue
|
||||
|
||||
result = handle_intake_submission("This is a test message", test=True)
|
||||
# Later, mark the intake issue as processed
|
||||
close_intake_issue(200, "Processed and logged to test-intake metrics.")
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
@@ -108,6 +113,125 @@ def _log_test_intake(detection, message: str) -> None:
|
||||
f.write(json.dumps(record) + "\n")
|
||||
|
||||
|
||||
def create_intake_issue(message: str, user: str = "Test User") -> dict:
|
||||
"""
|
||||
Create a Gitea issue to track an intake submission.
|
||||
|
||||
Args:
|
||||
message: The user's message text.
|
||||
user: The name to attribute the intake to.
|
||||
|
||||
Returns:
|
||||
dict: The API response with the created issue (contains 'number').
|
||||
|
||||
Requires GITEA_TOKEN environment variable or ~/.config/gitea/token file.
|
||||
"""
|
||||
import os
|
||||
import urllib.request
|
||||
|
||||
# Get token
|
||||
token = os.environ.get("GITEA_TOKEN")
|
||||
if not token:
|
||||
token_file = os.path.expanduser("~/.config/gitea/token")
|
||||
if os.path.exists(token_file):
|
||||
with open(token_file) as f:
|
||||
token = f.read().strip()
|
||||
|
||||
if not token:
|
||||
raise ValueError("GITEA_TOKEN not set and ~/.config/gitea/token not found")
|
||||
|
||||
url = (
|
||||
"https://forge.alexanderwhitestone.com/api/v1/"
|
||||
"repos/Timmy_Foundation/the-door/issues"
|
||||
)
|
||||
body = f"**Message:** {message}"
|
||||
data = json.dumps({
|
||||
"title": f"Intake submission from {user}",
|
||||
"body": body,
|
||||
}).encode()
|
||||
req = urllib.request.Request(
|
||||
url,
|
||||
data=data,
|
||||
headers={
|
||||
"Authorization": f"token {token}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method="POST",
|
||||
)
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
result = json.loads(resp.read())
|
||||
return result
|
||||
|
||||
|
||||
def close_intake_issue(issue_number: int, comment: str = "Processed.") -> dict:
|
||||
"""
|
||||
Close a Gitea intake issue and add a comment.
|
||||
|
||||
Args:
|
||||
issue_number: The Gitea issue number (e.g., 200 for #200)
|
||||
comment: Optional comment to add before closing.
|
||||
|
||||
Returns:
|
||||
dict: The API response from closing the issue.
|
||||
|
||||
Requires GITEA_TOKEN environment variable or ~/.config/gitea/token file.
|
||||
"""
|
||||
import os
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
|
||||
# Get token
|
||||
token = os.environ.get("GITEA_TOKEN")
|
||||
if not token:
|
||||
token_file = os.path.expanduser("~/.config/gitea/token")
|
||||
if os.path.exists(token_file):
|
||||
with open(token_file) as f:
|
||||
token = f.read().strip()
|
||||
|
||||
if not token:
|
||||
raise ValueError("GITEA_TOKEN not set and ~/.config/gitea/token not found")
|
||||
|
||||
# Add comment first
|
||||
if comment:
|
||||
comment_url = (
|
||||
f"https://forge.alexanderwhitestone.com/api/v1/"
|
||||
f"repos/Timmy_Foundation/the-door/issues/{issue_number}/comments"
|
||||
)
|
||||
comment_data = json.dumps({"body": comment}).encode()
|
||||
req = urllib.request.Request(
|
||||
comment_url,
|
||||
data=comment_data,
|
||||
headers={
|
||||
"Authorization": f"token {token}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
pass # Comment added
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to add comment: {e}")
|
||||
|
||||
# Close the issue
|
||||
close_url = (
|
||||
f"https://forge.alexanderwhitestone.com/api/v1/"
|
||||
f"repos/Timmy_Foundation/the-door/issues/{issue_number}"
|
||||
)
|
||||
close_data = json.dumps({"state": "closed"}).encode()
|
||||
req = urllib.request.Request(
|
||||
close_url,
|
||||
data=close_data,
|
||||
headers={
|
||||
"Authorization": f"token {token}",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
method="PATCH",
|
||||
)
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
result = json.loads(resp.read())
|
||||
return result
|
||||
|
||||
|
||||
# ── Quick test interface ────────────────────────────────────────────
|
||||
|
||||
def _interactive():
|
||||
|
||||
@@ -106,5 +106,41 @@ class TestIntakeIntegration(unittest.TestCase):
|
||||
self.assertTrue(result.provide_988)
|
||||
|
||||
|
||||
class TestIntakeGiteaIntegration(unittest.TestCase):
|
||||
"""Tests for Gitea issue integration (requires token)."""
|
||||
|
||||
def test_create_intake_issue_structure(self):
|
||||
"""create_intake_issue returns dict with 'number' key."""
|
||||
# This is a unit test - we mock the API call
|
||||
import unittest.mock as mock
|
||||
import crisis.intake as intake_mod
|
||||
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.read.return_value = b'{"number": 999, "title": "Test"}'
|
||||
|
||||
with mock.patch("urllib.request.urlopen", return_value=mock_response):
|
||||
# Patch token check
|
||||
original_dir = intake_mod._INTAKE_METRICS_DIR
|
||||
try:
|
||||
result = intake_mod.create_intake_issue("Test message", "Test User")
|
||||
self.assertIn("number", result)
|
||||
self.assertEqual(result["number"], 999)
|
||||
finally:
|
||||
pass
|
||||
|
||||
def test_close_intake_issue_structure(self):
|
||||
"""close_intake_issue returns dict with 'state' key."""
|
||||
import unittest.mock as mock
|
||||
import crisis.intake as intake_mod
|
||||
|
||||
mock_response = mock.MagicMock()
|
||||
mock_response.read.return_value = b'{"number": 200, "state": "closed"}'
|
||||
|
||||
with mock.patch("urllib.request.urlopen", return_value=mock_response):
|
||||
result = intake_mod.close_intake_issue(200, "Processed.")
|
||||
self.assertIn("state", result)
|
||||
self.assertEqual(result["state"], "closed")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user