Building a Nextcloud Talk Bot with Local AI
What This Article Covers
- Building a bot for Nextcloud Talk using Ollama.
- Two approaches: webhook-based bots (official) and the webhook listener flow.
- Creating a bot, receiving messages, sending replies, completely on-premise.
- Extensions: file analysis, RAG on Nextcloud documents, notifications.
- When Nextcloud Talk is the right platform and when it isn’t.
Introduction
If you’re already running Nextcloud, you get Nextcloud Talk practically for free: a chat platform with video calls, channels, and file sharing, all on your own server. Bots have been officially supported since Talk 18+: as “bots” with webhook registration or as classic chat commands.
Combining Nextcloud Talk with Ollama gives you perhaps the most sovereign bot solution available: chat, files, and AI all on a single private server. For families, clubs, and small businesses with existing Nextcloud infrastructure, this adds minimal overhead.
Common Use Cases
- File assistant: “Summarize this PDF for me.” The bot accesses Nextcloud files directly, no upload needed.
- Family AI: Kids and parents chat with the assistant on the family server.
- Club chat: Questions about the club wiki answered via RAG.
- Proactive notifications: Nextcloud events (new file, calendar reminders) delivered as AI comments.
- Internal team bot: For organizations already using Nextcloud as their file hub.
Requirements
- Nextcloud 28+ with Nextcloud Talk installed
- Admin access (bot registration via
occcommand or admin UI) - Ollama with a model
- Python 3.10+, Flask or FastAPI for the webhook endpoint
Step 1: Register the Bot in Talk
Nextcloud Talk supports two bot types:
- Webhook bot: Talk calls your HTTP endpoint whenever a new message arrives (signature verified).
- Command: A
/commandin Talk triggers a script, simplest entry point.
Register a webhook bot with occ:
sudo -u www-data php occ talk:bot:install \
"AI Assistant" \
"secret-shared-secret" \
"https://your-domain.de/talk-bot/webhook"
This returns a bot ID and secret. The secret verifies that requests actually come from your Nextcloud instance.
.env:
NC_URL=https://cloud.your-domain.de
BOT_SECRET=secret-shared-secret
BOT_ID=123
OLLAMA_URL=http://localhost:11434
OLLAMA_MODEL=llama3.1:8b
Step 2: Webhook Endpoint (Python/Flask)
Talk sends a signed JSON POST to your endpoint each time a message arrives in a bot conversation:
import hashlib
import hmac
import os
import requests
import ollama
from flask import Flask, request, abort
from dotenv import load_dotenv
load_dotenv()
app = Flask(__name__)
NC_URL = os.environ["NC_URL"]
SECRET = os.environ["BOT_SECRET"].encode()
MODEL = os.environ.get("OLLAMA_MODEL", "llama3.1:8b")
ollama_client = ollama.Client(host=os.environ["OLLAMA_URL"])
SYSTEM = "You are a Nextcloud AI assistant. Keep answers concise and in English."
def verify_signature(req):
"""Talk signs requests with the shared secret."""
signature = req.headers.get("X-Nextcloud-Talk-Bot-Signature", "")
random_val = req.headers.get("X-Nextcloud-Talk-Random", "")
backend = req.headers.get("X-Nextcloud-Talk-Backend", "")
digest = hmac.new(SECRET, (random_val + req.get_data(as_text=True)).encode(),
hashlib.sha256).hexdigest()
return hmac.compare_digest(signature, digest)
def reply(token, text, reference_id=None):
"""Post a reply to the Talk conversation."""
requests.post(
f"{NC_URL}/ocs/v2.php/apps/spreed/api/v1/bot/{token}/message",
headers={"OCS-APIRequest": "true"},
json={"message": text[:32000], "referenceId": reference_id or ""},
auth=("bot", SECRET.decode()),
)
def ask_ollama(text):
resp = ollama_client.chat(model=MODEL, messages=[
{"role": "system", "content": SYSTEM},
{"role": "user", "content": text},
])
return resp["message"]["content"]
@app.route("/talk-bot/webhook", methods=["POST"])
def webhook():
if not verify_signature(request):
abort(401)
data = request.json or {}
message = (data.get("object", {}).get("content") or "")
actor = data.get("actor", {})
convo = data.get("target", {})
token = convo.get("id")
if not message or not token:
return {"ok": True}
# Only reply to mentions or in direct chats
if data.get("object", {}).get("type") == "chat_message":
if "@" in message or convo.get("type") == 1:
text = message.replace("@ai-assistant", "").strip()
answer = ask_ollama(text)
reply(token, answer, data["object"].get("id"))
return {"ok": True}
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5050)
Configure your reverse proxy (Nginx or Caddy) to route https://your-domain.de/talk-bot/ to 127.0.0.1:5050.
Extensions
Analyzing Nextcloud Files
The bot can access files via the Nextcloud WebDAV API using an app password or bot user account. When someone says “Summarize file X”, the bot retrieves it, extracts text, and sends it to Ollama:
def fetch_file(path):
r = requests.get(
f"{NC_URL}/remote.php/dav/files/bot/{path}",
auth=("bot", APP_PASSWORD))
return r.content
Convert PDFs to text using Stirling-PDF or pypdf, then send to the model.
RAG on Nextcloud Documents
Regularly index a Nextcloud folder (text extraction + embeddings + Qdrant): the bot answers questions from all team documents. See Local RAG.
Proactive Notifications
The bot can post without being asked. For example, when a cron job forwards Nextcloud events (new file, calendar reminder) to the bot.
Command Variant (Simpler Entry Point)
Instead of webhooks, register a Talk command /ai: Talk executes your script with parameters:
sudo -u www-data php occ talk:command:add ai "Ask the AI" \
--script "/opt/bot/ai.sh {ARGUMENTS}" --response 2
Good for simple commands and requires no web server, but lacks conversation context.
Deployment
services:
talk-bot:
build: ./bot
restart: always
env_file: .env
ports: ["127.0.0.1:5050:5050"]
depends_on: [ollama]
ollama:
image: ollama/ollama:latest
volumes: [ollama_data:/root/.ollama]
restart: always
volumes:
ollama_data:
Security
- Signature verification: Always verify, otherwise anyone with the URL can impersonate the bot.
- Shared secret: Treat it like a password, never commit it.
- HTTPS: Talk requires HTTPS endpoints. Use a reverse proxy with TLS.
- Bot permissions: The bot only reads conversations it’s added to.
Common Pitfalls
- Signature verification fails: Hash
X-Nextcloud-Talk-Random+ body in the correct order, using raw bytes, not re-parsed JSON. - Bot doesn’t see channel: The bot must be added to the conversation via the UI or
occ talk:bot:add-to-room. - Webhook unreachable: Talk calls from the Nextcloud server, so localhost endpoints only work if Nextcloud runs locally.
- Talk not installed: Bot features require the Talk app. Install it in Nextcloud’s app store.
Further Reading
- IRC-Coding.de: In-depth programming tutorials covering webhooks, Flask, and signature verification.
- Nextcloud Integration: Automate Nextcloud with n8n.
- Matrix Bots: A robust chat platform for communities.
- Mattermost Bot: Team chat alternative.
- Docker: Deployment fundamentals.
Key Takeaways:
- Nextcloud Talk + Ollama gives you the most sovereign bot solution possible: chat, file storage, and on-premise AI all in one.
- Register the bot via occ command and implement webhook signature verification.
- Standout feature: the bot accesses Nextcloud files directly, no manual uploads needed.
- Least effort for existing Nextcloud users; if you’re building a standalone chat platform, Mattermost or Matrix are stronger choices.
- Use commands for simple /slash commands, webhooks for full conversational bot logic.
FAQ
What are the minimum requirements?
Webhook bot or command?
Are Talk chats encrypted?
How does Talk perform with many users?
Can the bot read Nextcloud files?
What does it cost?
Talk or Matrix?
Sources and Further Reading
- Nextcloud Talk Bots: Official bot documentation.
- Nextcloud: Self-hosted cloud platform.
- Ollama: Local model server.
- IRC-Coding.de: Programming tutorials.


