Hausmeister-Bot: Matomo Analytics Integration — Besucherzahlen, Trend, Top-Seiten, Traffic-Quellen per Chat abfragbar
This commit is contained in:
parent
ef5097ab71
commit
402c2eac42
4 changed files with 205 additions and 1 deletions
|
|
@ -7,7 +7,7 @@ import re
|
||||||
|
|
||||||
sys.path.insert(0, os.path.dirname(__file__))
|
sys.path.insert(0, os.path.dirname(__file__))
|
||||||
from core import config, loki_client, proxmox_client, wordpress_client, prometheus_client
|
from core import config, loki_client, proxmox_client, wordpress_client, prometheus_client
|
||||||
from core import forgejo_client, seafile_client, pbs_client, mail_client
|
from core import forgejo_client, seafile_client, pbs_client, mail_client, matomo_client
|
||||||
|
|
||||||
|
|
||||||
def _load_config():
|
def _load_config():
|
||||||
|
|
@ -180,6 +180,20 @@ def _tool_get_wordpress_stats() -> str:
|
||||||
return wordpress_client.format_overview(cfg)
|
return wordpress_client.format_overview(cfg)
|
||||||
|
|
||||||
|
|
||||||
|
def _tool_get_matomo_analytics() -> str:
|
||||||
|
cfg = _load_config()
|
||||||
|
if not matomo_client.init(cfg):
|
||||||
|
return "Matomo nicht konfiguriert (MATOMO_URL/MATOMO_TOKEN fehlt in homelab.conf)"
|
||||||
|
return matomo_client.format_analytics()
|
||||||
|
|
||||||
|
|
||||||
|
def _tool_get_matomo_trend(days: int = 30) -> str:
|
||||||
|
cfg = _load_config()
|
||||||
|
if not matomo_client.init(cfg):
|
||||||
|
return "Matomo nicht konfiguriert (MATOMO_URL/MATOMO_TOKEN fehlt in homelab.conf)"
|
||||||
|
return matomo_client.format_trend(days=days)
|
||||||
|
|
||||||
|
|
||||||
def _tool_create_issue(title: str, body: str = "") -> str:
|
def _tool_create_issue(title: str, body: str = "") -> str:
|
||||||
cfg = _load_config()
|
cfg = _load_config()
|
||||||
forgejo_client.init(cfg)
|
forgejo_client.init(cfg)
|
||||||
|
|
@ -408,6 +422,8 @@ def get_tool_handlers(session_id: str = None) -> dict:
|
||||||
"search_mail": lambda query, days=30: _tool_search_mail(query, days=days),
|
"search_mail": lambda query, days=30: _tool_search_mail(query, days=days),
|
||||||
"get_todays_mails": lambda: _tool_get_todays_mails(),
|
"get_todays_mails": lambda: _tool_get_todays_mails(),
|
||||||
"get_smart_mail_digest": lambda hours=24: _tool_get_smart_mail_digest(hours=hours),
|
"get_smart_mail_digest": lambda hours=24: _tool_get_smart_mail_digest(hours=hours),
|
||||||
|
"get_matomo_analytics": lambda: _tool_get_matomo_analytics(),
|
||||||
|
"get_matomo_trend": lambda days=30: _tool_get_matomo_trend(days=days),
|
||||||
"memory_read": lambda scope="": _tool_memory_read(scope),
|
"memory_read": lambda scope="": _tool_memory_read(scope),
|
||||||
"memory_suggest": lambda scope, kind, content, memory_type="fact", confidence="high", expires_at=None: _tool_memory_suggest(scope, kind, content, memory_type=memory_type, confidence=confidence, expires_at=expires_at),
|
"memory_suggest": lambda scope, kind, content, memory_type="fact", confidence="high", expires_at=None: _tool_memory_suggest(scope, kind, content, memory_type=memory_type, confidence=confidence, expires_at=expires_at),
|
||||||
"session_search": lambda query: _tool_session_search(query),
|
"session_search": lambda query: _tool_session_search(query),
|
||||||
|
|
|
||||||
163
homelab-ai-bot/core/matomo_client.py
Normal file
163
homelab-ai-bot/core/matomo_client.py
Normal file
|
|
@ -0,0 +1,163 @@
|
||||||
|
"""Matomo Analytics API Client — Besucherstatistiken fuer arakavanews.com."""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
MATOMO_URL = ""
|
||||||
|
MATOMO_TOKEN = ""
|
||||||
|
MATOMO_SITE_ID = "1"
|
||||||
|
|
||||||
|
|
||||||
|
def init(cfg):
|
||||||
|
global MATOMO_URL, MATOMO_TOKEN, MATOMO_SITE_ID
|
||||||
|
MATOMO_URL = cfg.raw.get("MATOMO_URL", "")
|
||||||
|
MATOMO_TOKEN = cfg.raw.get("MATOMO_TOKEN", "")
|
||||||
|
MATOMO_SITE_ID = cfg.raw.get("MATOMO_SITE_ID", "1")
|
||||||
|
return bool(MATOMO_URL and MATOMO_TOKEN)
|
||||||
|
|
||||||
|
|
||||||
|
def _api(method: str, **params) -> dict | list | None:
|
||||||
|
try:
|
||||||
|
p = {
|
||||||
|
"module": "API",
|
||||||
|
"method": method,
|
||||||
|
"idSite": MATOMO_SITE_ID,
|
||||||
|
"format": "json",
|
||||||
|
"token_auth": MATOMO_TOKEN,
|
||||||
|
}
|
||||||
|
p.update(params)
|
||||||
|
resp = requests.get(f"{MATOMO_URL}/index.php", params=p, timeout=15)
|
||||||
|
resp.raise_for_status()
|
||||||
|
return resp.json()
|
||||||
|
except Exception as e:
|
||||||
|
return {"error": str(e)}
|
||||||
|
|
||||||
|
|
||||||
|
def get_summary(period: str = "day", date: str = "today") -> dict:
|
||||||
|
"""Besucher-Zusammenfassung (Visits, Unique, Actions, Bounce, Avg Time)."""
|
||||||
|
return _api("VisitsSummary.get", period=period, date=date)
|
||||||
|
|
||||||
|
|
||||||
|
def get_visitor_trend(days: int = 30) -> dict:
|
||||||
|
"""Tageweise Besucherzahlen der letzten N Tage."""
|
||||||
|
return _api("VisitsSummary.get", period="day", date=f"last{days}")
|
||||||
|
|
||||||
|
|
||||||
|
def get_top_pages(period: str = "day", date: str = "today", limit: int = 10) -> list:
|
||||||
|
"""Meistbesuchte Seiten."""
|
||||||
|
result = _api("Actions.getPageUrls", period=period, date=date,
|
||||||
|
filter_limit=limit, flat=1)
|
||||||
|
if isinstance(result, dict) and "error" in result:
|
||||||
|
return []
|
||||||
|
return result if isinstance(result, list) else []
|
||||||
|
|
||||||
|
|
||||||
|
def get_referrers(period: str = "day", date: str = "today", limit: int = 10) -> list:
|
||||||
|
"""Woher kommen Besucher (Suchmaschinen, Social, Direkt)."""
|
||||||
|
result = _api("Referrers.getReferrerType", period=period, date=date,
|
||||||
|
filter_limit=limit)
|
||||||
|
if isinstance(result, dict) and "error" in result:
|
||||||
|
return []
|
||||||
|
return result if isinstance(result, list) else []
|
||||||
|
|
||||||
|
|
||||||
|
def get_countries(period: str = "day", date: str = "today", limit: int = 10) -> list:
|
||||||
|
"""Besucher nach Laendern."""
|
||||||
|
result = _api("UserCountry.getCountry", period=period, date=date,
|
||||||
|
filter_limit=limit)
|
||||||
|
if isinstance(result, dict) and "error" in result:
|
||||||
|
return []
|
||||||
|
return result if isinstance(result, list) else []
|
||||||
|
|
||||||
|
|
||||||
|
def get_devices(period: str = "day", date: str = "today") -> list:
|
||||||
|
"""Besucher nach Geraetetyp (Desktop, Mobile, Tablet)."""
|
||||||
|
result = _api("DevicesDetection.getType", period=period, date=date)
|
||||||
|
if isinstance(result, dict) and "error" in result:
|
||||||
|
return []
|
||||||
|
return result if isinstance(result, list) else []
|
||||||
|
|
||||||
|
|
||||||
|
def format_analytics(period: str = "day", date: str = "today") -> str:
|
||||||
|
"""Kompakter Analytics-Report fuer den Hausmeister-Bot."""
|
||||||
|
lines = []
|
||||||
|
|
||||||
|
summary = get_summary(period, date)
|
||||||
|
if isinstance(summary, dict) and "error" not in summary:
|
||||||
|
visitors = summary.get("nb_uniq_visitors", 0)
|
||||||
|
visits = summary.get("nb_visits", 0)
|
||||||
|
actions = summary.get("nb_actions", 0)
|
||||||
|
bounce = summary.get("bounce_rate", "?")
|
||||||
|
avg_time = summary.get("avg_time_on_site", 0)
|
||||||
|
avg_min = int(avg_time) // 60
|
||||||
|
avg_sec = int(avg_time) % 60
|
||||||
|
lines.append(f"Besucher: {visitors} unique, {visits} visits, {actions} Seitenaufrufe")
|
||||||
|
lines.append(f"Bounce Rate: {bounce}, Verweildauer: {avg_min}m {avg_sec}s")
|
||||||
|
else:
|
||||||
|
return f"Matomo nicht erreichbar: {summary}"
|
||||||
|
|
||||||
|
trend = get_visitor_trend(14)
|
||||||
|
if isinstance(trend, dict) and "error" not in trend:
|
||||||
|
trend_lines = []
|
||||||
|
for date_str, data in sorted(trend.items()):
|
||||||
|
if isinstance(data, dict):
|
||||||
|
v = data.get("nb_uniq_visitors", 0)
|
||||||
|
trend_lines.append(f" {date_str}: {v} Besucher")
|
||||||
|
else:
|
||||||
|
trend_lines.append(f" {date_str}: 0")
|
||||||
|
if trend_lines:
|
||||||
|
lines.append("\nTrend (14 Tage):")
|
||||||
|
lines.extend(trend_lines)
|
||||||
|
|
||||||
|
pages = get_top_pages(period, "today", 5)
|
||||||
|
if pages:
|
||||||
|
lines.append("\nTop Seiten (heute):")
|
||||||
|
for p in pages[:5]:
|
||||||
|
label = p.get("label", "?")
|
||||||
|
hits = p.get("nb_hits", 0)
|
||||||
|
lines.append(f" {label}: {hits}x")
|
||||||
|
|
||||||
|
referrers = get_referrers(period, "today", 5)
|
||||||
|
if referrers:
|
||||||
|
lines.append("\nTraffic-Quellen (heute):")
|
||||||
|
for r in referrers[:5]:
|
||||||
|
label = r.get("label", "?")
|
||||||
|
visits = r.get("nb_visits", 0)
|
||||||
|
lines.append(f" {label}: {visits} visits")
|
||||||
|
|
||||||
|
countries = get_countries(period, "today", 5)
|
||||||
|
if countries:
|
||||||
|
lines.append("\nLaender (heute):")
|
||||||
|
for c in countries[:5]:
|
||||||
|
label = c.get("label", "?")
|
||||||
|
visits = c.get("nb_visits", 0)
|
||||||
|
lines.append(f" {label}: {visits}")
|
||||||
|
|
||||||
|
return "\n".join(lines) if lines else "Keine Daten verfuegbar."
|
||||||
|
|
||||||
|
|
||||||
|
def format_trend(days: int = 30) -> str:
|
||||||
|
"""Besucherentwicklung ueber N Tage — fuer Trend-Fragen."""
|
||||||
|
trend = get_visitor_trend(days)
|
||||||
|
if isinstance(trend, dict) and "error" in trend:
|
||||||
|
return f"Matomo-Fehler: {trend['error']}"
|
||||||
|
|
||||||
|
lines = [f"Besucherentwicklung (letzte {days} Tage):"]
|
||||||
|
total = 0
|
||||||
|
day_count = 0
|
||||||
|
for date_str, data in sorted(trend.items()):
|
||||||
|
if isinstance(data, dict):
|
||||||
|
v = data.get("nb_uniq_visitors", 0)
|
||||||
|
actions = data.get("nb_actions", 0)
|
||||||
|
lines.append(f" {date_str}: {v} Besucher, {actions} Aufrufe")
|
||||||
|
total += v
|
||||||
|
day_count += 1
|
||||||
|
else:
|
||||||
|
lines.append(f" {date_str}: 0")
|
||||||
|
day_count += 1
|
||||||
|
|
||||||
|
if day_count > 0:
|
||||||
|
avg = total / day_count
|
||||||
|
lines.append(f"\nDurchschnitt: {avg:.0f} Besucher/Tag, Gesamt: {total}")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
@ -264,6 +264,28 @@ TOOLS = [
|
||||||
"parameters": {"type": "object", "properties": {}, "required": []},
|
"parameters": {"type": "object", "properties": {}, "required": []},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "get_matomo_analytics",
|
||||||
|
"description": "Matomo Website-Analytik: Besucher heute, Trend, Top-Seiten, Traffic-Quellen, Laender. Fuer alle Fragen zu Besucherzahlen, Zuschauern, Traffic, Seitenaufrufen, Bounce Rate, Herkunftslaendern.",
|
||||||
|
"parameters": {"type": "object", "properties": {}, "required": []},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "function",
|
||||||
|
"function": {
|
||||||
|
"name": "get_matomo_trend",
|
||||||
|
"description": "Besucherentwicklung ueber Zeit: Tageweise Besucherzahlen ueber N Tage. Nutze bei Fragen wie 'wie entwickeln sich die Zuschauer', 'Besuchertrend', 'wachsen wir'.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"days": {"type": "number", "description": "Anzahl Tage zurueckblicken (default: 30)", "default": 30}
|
||||||
|
},
|
||||||
|
"required": [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "function",
|
"type": "function",
|
||||||
"function": {
|
"function": {
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,9 @@ OPENROUTER_KEY="sk-or-v1-f5b2699f4a4708aff73ea0b8bb2653d0d913d57c56472942e510f82
|
||||||
OPENAI_API_KEY="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"
|
OPENAI_API_KEY="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"
|
||||||
MEMORY_API_TOKEN="Ai8eeQibV6Z1RWc7oNPim4PXB4vILU1nRW2-XgRcX2M"
|
MEMORY_API_TOKEN="Ai8eeQibV6Z1RWc7oNPim4PXB4vILU1nRW2-XgRcX2M"
|
||||||
MEMORY_API_URL="http://100.121.192.94:8400"
|
MEMORY_API_URL="http://100.121.192.94:8400"
|
||||||
|
MATOMO_TOKEN="7d3987d48dcd7fdf9776bd81a4da1778"
|
||||||
|
MATOMO_URL="http://100.113.244.101"
|
||||||
|
MATOMO_SITE_ID="1"
|
||||||
|
|
||||||
# --- HOMELAB MCP-SERVER (auf pve-hetzner Host) ---
|
# --- HOMELAB MCP-SERVER (auf pve-hetzner Host) ---
|
||||||
MCP_PATH="/root/homelab-mcp"
|
MCP_PATH="/root/homelab-mcp"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue