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:
Christian Gick
2026-02-27 08:44:57 +02:00
parent db10e435bc
commit 7791a5ba8e
5 changed files with 114 additions and 4 deletions

52
bot.py
View File

@@ -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"])