feat: add Confluence recent pages + Sentry error tracking (MAT-58, MAT-59)
MAT-58: Add recent_confluence_pages tool to both voice and text chat. Shows last 5 recently modified pages so users can pick directly instead of having to search every time. MAT-59: Integrate sentry-sdk in all three entry points (agent.py, bot.py, voice.py). SENTRY_DSN env var, traces at 10% sample rate. Requires creating project in Sentry UI and setting DSN. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
52
bot.py
52
bot.py
@@ -9,6 +9,7 @@ import re
|
||||
import time
|
||||
import uuid
|
||||
|
||||
import sentry_sdk
|
||||
import docx
|
||||
import fitz # pymupdf
|
||||
import httpx
|
||||
@@ -47,6 +48,12 @@ RENAME_STATE_TYPE = "ai.agiliton.auto_rename"
|
||||
|
||||
logger = logging.getLogger("matrix-ai-bot")
|
||||
|
||||
# Sentry error tracking
|
||||
_sentry_dsn = os.environ.get("SENTRY_DSN", "")
|
||||
if _sentry_dsn:
|
||||
sentry_sdk.init(dsn=_sentry_dsn, traces_sample_rate=0.1, environment=os.environ.get("SENTRY_ENV", "production"))
|
||||
logger.info("Sentry initialized for bot")
|
||||
|
||||
HOMESERVER = os.environ["MATRIX_HOMESERVER"]
|
||||
BOT_USER = os.environ["MATRIX_BOT_USER"]
|
||||
BOT_PASS = os.environ["MATRIX_BOT_PASSWORD"]
|
||||
@@ -87,7 +94,7 @@ IMPORTANT RULES — FOLLOW THESE STRICTLY:
|
||||
- You can see and analyze images that users send. Describe what you see when asked about an image.
|
||||
- You can read and analyze PDF documents that users send. Summarize content and answer questions about them.
|
||||
- You can generate images when asked — use the generate_image tool for any image creation, drawing, or illustration requests.
|
||||
- You can search Confluence and Jira using tools. When users ask about documentation, wiki pages, tickets, or tasks, use the appropriate tool.
|
||||
- You can search Confluence and Jira using tools. When users ask about documentation, wiki pages, tickets, or tasks, use the appropriate tool. Use confluence_recent_pages FIRST to show recently edited pages before searching.
|
||||
- When creating Jira issues, always confirm the project key and summary with the user before creating.
|
||||
- If a user's Atlassian account is not connected, tell them to connect it at matrixhost.eu/settings and provide the link.
|
||||
- If user memories are provided, use them to personalize responses. Address users by name if known.
|
||||
@@ -109,6 +116,19 @@ IMAGE_GEN_TOOLS = [{
|
||||
}]
|
||||
|
||||
ATLASSIAN_TOOLS = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "confluence_recent_pages",
|
||||
"description": "List recently modified Confluence pages. Use this FIRST when the user mentions Confluence, documents, or wiki pages — shows the last 5 recently edited pages so the user can pick one directly without searching.",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {"type": "integer", "description": "Max pages to return (default 5)", "default": 5},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
@@ -456,6 +476,32 @@ class AtlassianClient:
|
||||
logger.warning("Failed to fetch Atlassian cloud ID", exc_info=True)
|
||||
return None
|
||||
|
||||
async def confluence_recent_pages(self, token: str, limit: int = 5) -> str:
|
||||
cloud_id = await self._get_cloud_id(token)
|
||||
if not cloud_id:
|
||||
return "Error: Could not determine Atlassian Cloud instance."
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=15.0) as client:
|
||||
resp = await client.get(
|
||||
f"https://api.atlassian.com/ex/confluence/{cloud_id}/wiki/rest/api/content/search",
|
||||
params={"cql": "type=page ORDER BY lastmodified DESC", "limit": str(limit)},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
results = data.get("results", [])
|
||||
if not results:
|
||||
return "No recent Confluence pages found."
|
||||
lines = ["Recently modified pages:"]
|
||||
for i, r in enumerate(results, 1):
|
||||
title = r.get("title", "Untitled")
|
||||
page_id = r.get("id", "")
|
||||
space = r.get("_expandable", {}).get("space", "").split("/")[-1]
|
||||
lines.append(f"{i}. **{title}** (ID: {page_id}, Space: {space})")
|
||||
return "\n".join(lines)
|
||||
except Exception as e:
|
||||
return f"Failed to fetch recent pages: {e}"
|
||||
|
||||
async def confluence_search(self, token: str, query: str, limit: int = 5) -> str:
|
||||
cloud_id = await self._get_cloud_id(token)
|
||||
if not cloud_id:
|
||||
@@ -2011,7 +2057,9 @@ class Bot:
|
||||
if not token:
|
||||
return ATLASSIAN_NOT_CONNECTED_MSG
|
||||
|
||||
if tool_name == "confluence_search":
|
||||
if tool_name == "confluence_recent_pages":
|
||||
return await self.atlassian.confluence_recent_pages(token, args.get("limit", 5))
|
||||
elif tool_name == "confluence_search":
|
||||
return await self.atlassian.confluence_search(token, args["query"], args.get("limit", 5))
|
||||
elif tool_name == "confluence_read_page":
|
||||
return await self.atlassian.confluence_read_page(token, args["page_id"])
|
||||
|
||||
Reference in New Issue
Block a user