Initial commit: Telegram Assistant workflows
This commit is contained in:
492
N8N_UPGRADE_LESSONS_2025-12-03.md
Normal file
492
N8N_UPGRADE_LESSONS_2025-12-03.md
Normal file
@@ -0,0 +1,492 @@
|
|||||||
|
# n8n Upgrade Lessons Learned - December 3, 2025
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
|
||||||
|
**CONCLUSION: DO NOT UPGRADE n8n beyond 1.19.4 until critical bugs are fixed**
|
||||||
|
|
||||||
|
Attempted to upgrade n8n from 1.19.4 to resolve Switch v1 incompatibility. Tested 4 versions (1.30.1, 1.90.3, 1.122.4, 1.123.0) - all failed with critical regressions. Spent 4+ hours debugging, created 14+ database backups, 4 test workflows. **Recommendation: Stay on 1.19.4.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Timeline
|
||||||
|
|
||||||
|
### 10:10 AM - Initial State
|
||||||
|
- n8n 1.19.4 working
|
||||||
|
- Telegram bot @mortimer_assi_bot returning 500 error on `/start` command
|
||||||
|
- Cause: Switch v1 syntax incompatible ("Could not find property option")
|
||||||
|
- Clean database backup created: `database.sqlite.backup_before_incremental_upgrade_20251203_101040`
|
||||||
|
|
||||||
|
### 10:30 AM - Upgrade Attempt #1: 1.30.1
|
||||||
|
- Upgraded via incremental path: 1.19.4 → 1.22.6 → 1.30.1
|
||||||
|
- Dockerfile changed to `FROM n8nio/n8n:1.30.1`
|
||||||
|
- Switch v3.2 syntax fixed manually
|
||||||
|
- **Problem**: Telegram credential lost during migration
|
||||||
|
|
||||||
|
### 11:00 AM - Testing 1.30.1
|
||||||
|
- Re-added Telegram credential (ID: Csk5cg4HtaSqP5jJ)
|
||||||
|
- Created "Mortimer Bot" workflow with Telegram Trigger v1
|
||||||
|
- **Problem**: Telegram Trigger polling doesn't start
|
||||||
|
- Evidence: No "Adding triggers and pollers" log message
|
||||||
|
- Zero executions despite workflow active=1
|
||||||
|
|
||||||
|
### 12:00 PM - Upgrade Attempt #2: 1.90.3
|
||||||
|
- Switched to 1.90.3 hoping for better stability
|
||||||
|
- **Problem**: Switch/IF nodes don't trigger downstream
|
||||||
|
- Workflows activate but never execute subsequent nodes
|
||||||
|
|
||||||
|
### 1:00 PM - Upgrade Attempt #3: 1.123.0
|
||||||
|
- Tried latest version 1.123.0
|
||||||
|
- **Problem**: Container crashed immediately (OOM kill)
|
||||||
|
- Database corruption suspected
|
||||||
|
- Rolled back to 1.30.1
|
||||||
|
|
||||||
|
### 2:00 PM - Webhook Approach
|
||||||
|
- Abandoned Telegram Trigger, switched to webhooks
|
||||||
|
- Created "Mortimer Working" workflow
|
||||||
|
- Set Telegram webhook: `https://flow.egonetix.de/webhook/fff67fbd-73f4-43b1-9860-a6bbf0b9e9e4`
|
||||||
|
- **Problem**: Webhook returns 404 "not registered"
|
||||||
|
|
||||||
|
### 2:30 PM - Debugging Webhook Issues
|
||||||
|
- Database shows active=1
|
||||||
|
- Logs show "=> Started"
|
||||||
|
- n8n API returns {"active": true}
|
||||||
|
- User toggled workflow off/on in UI
|
||||||
|
- **Still 404 errors**
|
||||||
|
|
||||||
|
### 3:00 PM - Investigation
|
||||||
|
- Tested 15+ different fixes (database toggles, restarts, API calls)
|
||||||
|
- Discovered webhooks only register after manual execution in editor
|
||||||
|
- Telegram Trigger polling completely broken in 1.30.1
|
||||||
|
- User frustrated: "i dont understand why these strange problems occure. that seems to be not normal"
|
||||||
|
|
||||||
|
### 3:30 PM - Version Research
|
||||||
|
- Checked GitHub releases: Latest stable = 1.122.4
|
||||||
|
- Checked Docker Hub: Latest = 1.123.1
|
||||||
|
- User already tried 1.122.4 → crashed system
|
||||||
|
- **Decision: Rollback to 1.19.4 required**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Issues Found by Version
|
||||||
|
|
||||||
|
### n8n 1.19.4 ✅ (WORKING)
|
||||||
|
- Telegram Trigger polling: ✅ Works
|
||||||
|
- Webhook registration: ✅ Works
|
||||||
|
- Switch v1 syntax: ✅ Works
|
||||||
|
- Stability: ✅ Excellent
|
||||||
|
- **Limitation**: Switch v1 syntax (but works correctly)
|
||||||
|
|
||||||
|
### n8n 1.30.1 ❌ (BROKEN)
|
||||||
|
- Telegram Trigger polling: ❌ **Never initializes**
|
||||||
|
- No "Adding triggers and pollers" log
|
||||||
|
- TelegramTrigger.node.js exists but doesn't run
|
||||||
|
- Credentials encrypted correctly
|
||||||
|
- Zero executions despite active=1
|
||||||
|
|
||||||
|
- Webhook registration: ❌ **Requires manual UI execution**
|
||||||
|
- Workflow shows "=> Started"
|
||||||
|
- Database active=1
|
||||||
|
- Returns 404 "not registered"
|
||||||
|
- Only works after opening in editor and executing
|
||||||
|
|
||||||
|
- Switch v3 breaking change: ⚠️ **Manual migration required**
|
||||||
|
- v1 syntax throws "Could not find property option"
|
||||||
|
- Must update to v3.2 format manually
|
||||||
|
- No automatic migration
|
||||||
|
|
||||||
|
### n8n 1.90.3 ❌ (BROKEN)
|
||||||
|
- Switch/IF nodes: ❌ **Don't trigger downstream**
|
||||||
|
- Workflows activate
|
||||||
|
- Nodes execute
|
||||||
|
- But subsequent nodes never run
|
||||||
|
- Different regression than 1.30.1
|
||||||
|
|
||||||
|
### n8n 1.122.4 / 1.123.0 ❌ (CRASHES)
|
||||||
|
- Container: ❌ **OOM kill on startup**
|
||||||
|
- Database: ❌ **Corruption suspected**
|
||||||
|
- Logs: ❌ **Container doesn't stay running**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### Switch v3.2 Migration
|
||||||
|
|
||||||
|
**Old v1 format (1.19.4):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"typeVersion": 1,
|
||||||
|
"options": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"value": {
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"leftValue": "={{ $json.message.text }}",
|
||||||
|
"rightValue": "/start",
|
||||||
|
"operator": "equal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**New v3.2 format (1.30.1+):**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"typeVersion": 3.2,
|
||||||
|
"rules": {
|
||||||
|
"values": [
|
||||||
|
{
|
||||||
|
"conditions": {
|
||||||
|
"conditions": [
|
||||||
|
{
|
||||||
|
"leftValue": "={{ $json.message.text }}",
|
||||||
|
"rightValue": "/start",
|
||||||
|
"operator": {
|
||||||
|
"type": "string",
|
||||||
|
"operation": "equals"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key Changes:**
|
||||||
|
1. `options.rules` → `rules.values`
|
||||||
|
2. `operator: "equal"` → `operator: {type: "string", operation: "equals"}`
|
||||||
|
3. Nested `conditions.conditions` array structure
|
||||||
|
|
||||||
|
### Telegram Trigger Polling Failure
|
||||||
|
|
||||||
|
**Expected behavior (1.19.4):**
|
||||||
|
```
|
||||||
|
Starting n8n...
|
||||||
|
Loading workflows...
|
||||||
|
- "Mortimer Bot" (ID: 70c37130...)
|
||||||
|
=> Started
|
||||||
|
=> Adding triggers and pollers
|
||||||
|
=> Telegram polling started for chat...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Actual behavior (1.30.1):**
|
||||||
|
```
|
||||||
|
Starting n8n...
|
||||||
|
Loading workflows...
|
||||||
|
- "Mortimer Bot" (ID: 70c37130...)
|
||||||
|
=> Started
|
||||||
|
[No polling initialization]
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verification:**
|
||||||
|
```bash
|
||||||
|
# Check if node exists
|
||||||
|
docker exec n8n find /usr -name "*Telegram*.node.js"
|
||||||
|
# Output: /usr/local/lib/node_modules/n8n/node_modules/n8n-nodes-base/dist/nodes/Telegram/TelegramTrigger.node.js
|
||||||
|
|
||||||
|
# Check credential
|
||||||
|
sqlite3 database.sqlite "SELECT id, name, type, LENGTH(data) FROM credentials_entity WHERE type = 'telegramApi'"
|
||||||
|
# Output: Csk5cg4HtaSqP5jJ|Telegram Bot|telegramApi|128
|
||||||
|
|
||||||
|
# Check webhook deleted (polling mode)
|
||||||
|
curl "https://api.telegram.org/bot<token>/getWebhookInfo"
|
||||||
|
# Output: {"url": "", "pending_update_count": 0} <- no webhook = polling mode
|
||||||
|
|
||||||
|
# Check executions
|
||||||
|
sqlite3 database.sqlite "SELECT COUNT(*) FROM execution_entity WHERE workflowId = '70c37130...'"
|
||||||
|
# Output: 0 <- never ran
|
||||||
|
```
|
||||||
|
|
||||||
|
**Root cause**: n8n 1.30.1 doesn't initialize polling loop for TelegramTrigger nodes. Bug in trigger registration system.
|
||||||
|
|
||||||
|
### Webhook Registration Failure
|
||||||
|
|
||||||
|
**Test workflow:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "Mortimer Working",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"type": "n8n-nodes-base.webhook",
|
||||||
|
"webhookId": "fff67fbd-73f4-43b1-9860-a6bbf0b9e9e4",
|
||||||
|
"parameters": {
|
||||||
|
"httpMethod": "POST",
|
||||||
|
"path": "fff67fbd-73f4-43b1-9860-a6bbf0b9e9e4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "n8n-nodes-base.telegram",
|
||||||
|
"parameters": {
|
||||||
|
"chatId": "={{ $json.body.message.chat.id }}",
|
||||||
|
"text": "Bot working!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"active": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Attempted fixes:**
|
||||||
|
1. Database: `UPDATE workflow_entity SET active = 0; UPDATE workflow_entity SET active = 1;`
|
||||||
|
2. Docker: `docker restart n8n` (5+ times)
|
||||||
|
3. API: `curl -X POST "${N8N_URL}/api/v1/workflows/aab68dba.../activate"`
|
||||||
|
4. UI toggle: User deactivated/reactivated (confirmed)
|
||||||
|
5. Manual execution: Open in editor → "Execute workflow" button
|
||||||
|
|
||||||
|
**Result**: Only #5 works (manual execution in editor)
|
||||||
|
|
||||||
|
**Test:**
|
||||||
|
```bash
|
||||||
|
curl -X POST "https://flow.egonetix.de/webhook/fff67fbd-73f4-43b1-9860-a6bbf0b9e9e4" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"body":{"message":{"text":"test","chat":{"id":579304651}}}}'
|
||||||
|
|
||||||
|
# Output: {"code":404,"message":"The requested webhook \"POST fff67fbd...\" is not registered"}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Root cause**: n8n 1.30.1 doesn't register production webhooks on workflow activation. Runtime webhook manager broken.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Workflows Created During Session
|
||||||
|
|
||||||
|
1. **Mortimer Bot** (70c37130-84e0-4349-9251-2d82e4db2d64)
|
||||||
|
- Telegram Trigger v1 → Switch v3 → Telegram Send
|
||||||
|
- Status: Deactivated (polling doesn't work)
|
||||||
|
- Executions: 0
|
||||||
|
|
||||||
|
2. **Mortimer Bot v2** (cc3069c6-3c99-424d-85c9-191d5baa3bf6)
|
||||||
|
- Telegram Trigger v1.1 with webhookId
|
||||||
|
- Status: Deactivated (polling doesn't work)
|
||||||
|
- Executions: 0
|
||||||
|
|
||||||
|
3. **Simple Telegram Router** (2ba483e8-ed62-4b88-b649-cd38550ab8aa)
|
||||||
|
- Webhook → Telegram Send
|
||||||
|
- Status: Deleted ("Workflow has no owner" error)
|
||||||
|
|
||||||
|
4. **Mortimer Working** (aab68dba-1ccd-451d-9685-479096f4be51)
|
||||||
|
- Webhook (fff67fbd-73f4-43b1-9860-a6bbf0b9e9e4) → Telegram Send
|
||||||
|
- Telegram webhook set to this URL
|
||||||
|
- Status: Active in database, 404 on requests
|
||||||
|
- Executions: 0
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Database Backups Created
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -lh /home/icke/n8n/database.sqlite.backup*
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key backups:**
|
||||||
|
- `database.sqlite.backup_before_incremental_upgrade_20251203_101040` - **CLEAN 1.19.4 STATE**
|
||||||
|
- `database.sqlite.backup_before_restore_20251203_141649` - Before rollback attempt
|
||||||
|
- 12+ additional backups during debugging
|
||||||
|
|
||||||
|
**Total size:** ~9.7MB each
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Credentials Status
|
||||||
|
|
||||||
|
**Telegram Bot (mortimer_assi_bot):**
|
||||||
|
- ID: Csk5cg4HtaSqP5jJ (re-added manually during session)
|
||||||
|
- Token: 8506559707:AAGn9dYm2PEuSGMbJ7jtiuIfGbl1ScaCxQk
|
||||||
|
- Status: ✅ Working
|
||||||
|
|
||||||
|
**OpenAI API:**
|
||||||
|
- ID: GPzxHwwXxeZg07o5 (openAiApi) - ✅ Working
|
||||||
|
- ID: MATuNdkZclq5ISbr (httpHeaderAuth) - ❌ Invalid key
|
||||||
|
|
||||||
|
**IMAP Email:**
|
||||||
|
- ID: BntHPR3YbFD5jAIM
|
||||||
|
- Status: ✅ Working
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recommendations
|
||||||
|
|
||||||
|
### Immediate Action
|
||||||
|
**Rollback to n8n 1.19.4:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Stop container
|
||||||
|
docker stop n8n
|
||||||
|
|
||||||
|
# Restore clean database
|
||||||
|
cp /home/icke/n8n/database.sqlite.backup_before_incremental_upgrade_20251203_101040 \
|
||||||
|
/home/icke/n8n/database.sqlite
|
||||||
|
|
||||||
|
# Edit Dockerfile
|
||||||
|
cd /home/icke/compose_files
|
||||||
|
# Change: FROM n8nio/n8n:1.30.1
|
||||||
|
# To: FROM n8nio/n8n:1.19.4
|
||||||
|
|
||||||
|
# Rebuild and restart
|
||||||
|
docker compose build n8n
|
||||||
|
docker compose up -d n8n
|
||||||
|
|
||||||
|
# Verify version
|
||||||
|
docker exec n8n n8n --version
|
||||||
|
# Expected: 1.19.4
|
||||||
|
|
||||||
|
# Test bot
|
||||||
|
# Send "/start" to @mortimer_assi_bot
|
||||||
|
# Should receive welcome message
|
||||||
|
```
|
||||||
|
|
||||||
|
### Long-term Strategy
|
||||||
|
|
||||||
|
1. **Stay on 1.19.4** until n8n fixes critical bugs
|
||||||
|
2. **Monitor GitHub issues** for fixes:
|
||||||
|
- Telegram Trigger polling not initializing
|
||||||
|
- Webhooks not registering on activation
|
||||||
|
- Switch v3 migration documentation
|
||||||
|
|
||||||
|
3. **Test future versions** on separate instance:
|
||||||
|
```bash
|
||||||
|
# Create test database
|
||||||
|
cp database.sqlite database.sqlite.test
|
||||||
|
|
||||||
|
# Run test container
|
||||||
|
docker run -d --name n8n-test \
|
||||||
|
-v /home/icke/n8n-test:/home/node/.n8n \
|
||||||
|
-e N8N_ENCRYPTION_KEY="B1W9cT+hha6ex4BTrhMtpRiW8kYkqcB0" \
|
||||||
|
n8nio/n8n:1.x.x
|
||||||
|
|
||||||
|
# Verify all features work
|
||||||
|
# - Telegram Trigger polling
|
||||||
|
# - Webhook registration
|
||||||
|
# - Switch nodes
|
||||||
|
# - All executions succeed
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Before any upgrade:**
|
||||||
|
- Export all workflows via UI
|
||||||
|
- Backup database with date
|
||||||
|
- Document all credentials
|
||||||
|
- Test rollback procedure
|
||||||
|
- Keep old version Docker image cached
|
||||||
|
|
||||||
|
### Never Do Again
|
||||||
|
|
||||||
|
❌ Don't upgrade n8n in production without testing
|
||||||
|
❌ Don't assume newer versions are stable
|
||||||
|
❌ Don't trust incremental upgrade paths
|
||||||
|
❌ Don't skip database backups
|
||||||
|
❌ Don't delete old Docker images until verified
|
||||||
|
❌ Don't modify production during work hours
|
||||||
|
❌ Don't assume API activation works like UI
|
||||||
|
❌ Don't trust container logs alone (check executions)
|
||||||
|
|
||||||
|
### Always Do
|
||||||
|
|
||||||
|
✅ Test upgrades on separate instance
|
||||||
|
✅ Create timestamped database backups
|
||||||
|
✅ Export all workflows before changes
|
||||||
|
✅ Verify credentials after migration
|
||||||
|
✅ Check execution_entity table for actual runs
|
||||||
|
✅ Monitor n8n GitHub issues before upgrading
|
||||||
|
✅ Keep documentation of working configurations
|
||||||
|
✅ Test all triggers/webhooks after changes
|
||||||
|
✅ Verify end-to-end functionality
|
||||||
|
✅ Have rollback plan ready
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Insights
|
||||||
|
|
||||||
|
1. **n8n version numbers don't indicate stability**
|
||||||
|
- Latest ≠ stable
|
||||||
|
- 1.122.4 crashed worse than 1.30.1
|
||||||
|
- No clear upgrade path documented
|
||||||
|
|
||||||
|
2. **Database migrations are one-way**
|
||||||
|
- Cannot downgrade without backup
|
||||||
|
- Schema changes break older versions
|
||||||
|
- Must backup before ANY upgrade
|
||||||
|
|
||||||
|
3. **Trigger systems are fragile**
|
||||||
|
- Telegram Trigger completely broken in 1.30.1
|
||||||
|
- Webhooks require manual registration
|
||||||
|
- No errors logged, silent failures
|
||||||
|
|
||||||
|
4. **Testing is mandatory**
|
||||||
|
- "Worked on template workflow" ≠ "works in my instance"
|
||||||
|
- UI shows active ≠ actually running
|
||||||
|
- Database active=1 ≠ webhooks registered
|
||||||
|
|
||||||
|
5. **Community templates use newer syntax**
|
||||||
|
- n8n.io templates show Switch v3.2
|
||||||
|
- This forced upgrade attempt
|
||||||
|
- But v3.2 works fine once migrated manually
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Questions for n8n Team
|
||||||
|
|
||||||
|
1. Why doesn't Telegram Trigger polling initialize in 1.30.1?
|
||||||
|
2. Why don't webhooks register on workflow activation?
|
||||||
|
3. Why do Switch/IF nodes not trigger in 1.90.3?
|
||||||
|
4. Why does 1.122.4 crash with OOM on startup?
|
||||||
|
5. Is there an official migration guide for Switch v1 → v3?
|
||||||
|
6. What is the recommended upgrade path from 1.19.4?
|
||||||
|
7. Are there automated tests for trigger systems?
|
||||||
|
8. Why don't logs show webhook registration failures?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## User Quotes
|
||||||
|
|
||||||
|
> "ok. stay on the current version" - User accepting to not upgrade initially
|
||||||
|
|
||||||
|
> "i dont get it. can i interact with mortimer now or not?" - Confusion about bot status
|
||||||
|
|
||||||
|
> "hm ok. the creds for the mortimer are gone. well, thank you....not" - Frustration with credential loss
|
||||||
|
|
||||||
|
> "man i dont care what you do. just make this thing work" - Extreme frustration at multiple failures
|
||||||
|
|
||||||
|
> "i dont understand why these strange problems occure. that seems to be not normal" - Questioning software quality
|
||||||
|
|
||||||
|
> "well what is the latest stable version?" - Final question after 4 hours debugging
|
||||||
|
|
||||||
|
**User patience level: Exhausted**
|
||||||
|
**User trust in n8n: Damaged**
|
||||||
|
**User trust in upgrade process: Zero**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Success Criteria for Next Attempt
|
||||||
|
|
||||||
|
Before attempting any future upgrade:
|
||||||
|
|
||||||
|
1. ✅ n8n GitHub issues show bugs fixed
|
||||||
|
2. ✅ Community reports successful upgrades
|
||||||
|
3. ✅ Release notes mention trigger/webhook fixes
|
||||||
|
4. ✅ Test instance runs for 48+ hours
|
||||||
|
5. ✅ All workflows execute successfully
|
||||||
|
6. ✅ Zero 404 webhook errors
|
||||||
|
7. ✅ Telegram Trigger shows polling logs
|
||||||
|
8. ✅ Switch nodes trigger downstream
|
||||||
|
9. ✅ User approves upgrade plan
|
||||||
|
10. ✅ Rollback tested and ready
|
||||||
|
|
||||||
|
**Until then: STAY ON 1.19.4**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Session Date:** December 3, 2025
|
||||||
|
**Session Duration:** ~4 hours
|
||||||
|
**Workflows Created:** 4
|
||||||
|
**Database Backups:** 14+
|
||||||
|
**n8n Versions Tested:** 4 (1.30.1, 1.90.3, 1.122.4, 1.123.0)
|
||||||
|
**Successful Versions:** 0
|
||||||
|
**Status:** Rollback to 1.19.4 recommended
|
||||||
|
**Next Steps:** Document learnings, continue another day
|
||||||
129
NATURAL_LANGUAGE_UPDATE.md
Normal file
129
NATURAL_LANGUAGE_UPDATE.md
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
# Natural Language Processing - Feature Added! 🎉
|
||||||
|
|
||||||
|
## ✅ What Changed
|
||||||
|
|
||||||
|
The Telegram bot now understands **natural language** - no need for commands!
|
||||||
|
|
||||||
|
### Before (Commands Only)
|
||||||
|
```
|
||||||
|
/deck add Review reports by Friday
|
||||||
|
/email search train ticket
|
||||||
|
/ask What's the weather?
|
||||||
|
```
|
||||||
|
|
||||||
|
### After (Natural Language)
|
||||||
|
```
|
||||||
|
add a deck called cleaning
|
||||||
|
tell me when my train leaves today
|
||||||
|
what's the weather like?
|
||||||
|
create a task to call mom tomorrow
|
||||||
|
find my invoice from last week
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🧠 How It Works
|
||||||
|
|
||||||
|
1. **Message received** → Bot checks if it starts with `/`
|
||||||
|
2. **If no `/`** → Sends to OpenAI for intent classification
|
||||||
|
3. **AI analyzes** and returns one of:
|
||||||
|
- `deck_add` - Create task in Nextcloud Deck
|
||||||
|
- `email_search` - Search emails via IMAP
|
||||||
|
- `general_chat` - Answer question with AI
|
||||||
|
4. **Converts to command format** and routes to appropriate workflow
|
||||||
|
5. **Processes normally** like a slash command
|
||||||
|
|
||||||
|
## 📝 Example Inputs
|
||||||
|
|
||||||
|
### Deck Tasks
|
||||||
|
```
|
||||||
|
add a deck called cleaning
|
||||||
|
create task review quarterly reports
|
||||||
|
remind me to call the dentist tomorrow
|
||||||
|
add card: finish the presentation by Friday
|
||||||
|
task for übermorgen: grocery shopping
|
||||||
|
```
|
||||||
|
|
||||||
|
### Email Search
|
||||||
|
```
|
||||||
|
tell me when my train leaves today
|
||||||
|
find my train ticket
|
||||||
|
search for invoice from November
|
||||||
|
show me emails about the project
|
||||||
|
where's my booking confirmation?
|
||||||
|
```
|
||||||
|
|
||||||
|
### General Chat
|
||||||
|
```
|
||||||
|
what's the weather like?
|
||||||
|
explain quantum computing
|
||||||
|
how do I convert Celsius to Fahrenheit?
|
||||||
|
tell me a joke
|
||||||
|
what's 15% of 250?
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Technical Details
|
||||||
|
|
||||||
|
**AI Prompt** classifies intent with examples:
|
||||||
|
- Extracts task name, due date for deck commands
|
||||||
|
- Extracts search query for email commands
|
||||||
|
- Passes full question for general chat
|
||||||
|
- Supports German dates (morgen, übermorgen)
|
||||||
|
- Returns structured JSON for routing
|
||||||
|
|
||||||
|
**OpenAI Model:** `gpt-4o-mini` (fast, cost-effective)
|
||||||
|
**Temperature:** 0.3 (focused, consistent)
|
||||||
|
**Max Tokens:** 150 (quick classification)
|
||||||
|
|
||||||
|
## 🚀 Usage
|
||||||
|
|
||||||
|
Just chat naturally with your bot:
|
||||||
|
|
||||||
|
✅ **"add a deck called cleaning"**
|
||||||
|
→ Creates Nextcloud Deck card with title "cleaning"
|
||||||
|
|
||||||
|
✅ **"tell me when my train leaves today"**
|
||||||
|
→ Searches emails for "train" and shows results
|
||||||
|
|
||||||
|
✅ **"what's the capital of Germany?"**
|
||||||
|
→ AI answers: "Berlin"
|
||||||
|
|
||||||
|
## ⚡ Next Steps
|
||||||
|
|
||||||
|
1. **Activate workflow** in n8n (already imported)
|
||||||
|
2. **Test in Telegram** - just send a message without `/`
|
||||||
|
3. **Monitor** first few uses to verify classification accuracy
|
||||||
|
|
||||||
|
## 📊 Classification Examples
|
||||||
|
|
||||||
|
```json
|
||||||
|
// Input: "add a deck called cleaning"
|
||||||
|
{
|
||||||
|
"intent": "deck_add",
|
||||||
|
"task": "cleaning",
|
||||||
|
"duedate": null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input: "find my train ticket"
|
||||||
|
{
|
||||||
|
"intent": "email_search",
|
||||||
|
"query": "train ticket"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Input: "what's the weather?"
|
||||||
|
{
|
||||||
|
"intent": "general_chat",
|
||||||
|
"question": "what's the weather?"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🐛 Fallback Behavior
|
||||||
|
|
||||||
|
If AI classification fails or is unclear:
|
||||||
|
- Defaults to `general_chat`
|
||||||
|
- Sends original message to OpenAI
|
||||||
|
- User still gets a helpful response
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Updated:** 2025-12-02
|
||||||
|
**Workflow:** telegram-receiver.json (re-imported)
|
||||||
|
**Status:** ✅ Ready to use - activate in n8n UI
|
||||||
277
README.md
Normal file
277
README.md
Normal file
@@ -0,0 +1,277 @@
|
|||||||
|
# Telegram AI Assistant for n8n
|
||||||
|
|
||||||
|
AI-powered Telegram bot that helps you manage tasks in Nextcloud Deck, search emails via IMAP, and chat with OpenAI - all controlled through Telegram commands.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
🤖 **AI-Powered Assistant**
|
||||||
|
- Natural language task creation with date extraction
|
||||||
|
- Smart email search and summarization
|
||||||
|
- General AI chat capabilities using GPT-4o-mini
|
||||||
|
|
||||||
|
📋 **Nextcloud Deck Integration**
|
||||||
|
- Add tasks directly from Telegram
|
||||||
|
- AI extracts task details, due dates, descriptions
|
||||||
|
- Supports German date formats (morgen, übermorgen)
|
||||||
|
|
||||||
|
📧 **IMAP Email Search**
|
||||||
|
- Search your inbox from Telegram
|
||||||
|
- View recent emails
|
||||||
|
- AI-formatted summaries
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The system consists of 4 n8n workflows:
|
||||||
|
|
||||||
|
1. **telegram-receiver.json** - Telegram webhook listener
|
||||||
|
- Receives all Telegram messages
|
||||||
|
- Parses commands vs natural language
|
||||||
|
- Routes to main router workflow
|
||||||
|
|
||||||
|
2. **telegram-router.json** - Command router
|
||||||
|
- Switches on command type (/deck, /email, /ask, /help)
|
||||||
|
- Handles help and start messages
|
||||||
|
- Delegates to specialized workflows
|
||||||
|
|
||||||
|
3. **telegram-deck.json** - Nextcloud Deck integration
|
||||||
|
- Processes `/deck` commands
|
||||||
|
- Uses OpenAI to extract task details
|
||||||
|
- Calls existing `/home/node/create_card_from_ai.sh` script
|
||||||
|
|
||||||
|
4. **telegram-email.json** - IMAP email search
|
||||||
|
- Processes `/email` commands
|
||||||
|
- Searches IMAP inbox with configured credentials
|
||||||
|
- Formats results for Telegram
|
||||||
|
|
||||||
|
5. **telegram-ai.json** - AI chat
|
||||||
|
- Processes `/ask` commands
|
||||||
|
- Calls OpenAI API for general questions
|
||||||
|
- Returns formatted Markdown responses
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- n8n instance running at `https://flow.egonetix.de/`
|
||||||
|
- Telegram bot token (already configured as credential `Csk5cg4HtaSqP5jJ`)
|
||||||
|
- OpenAI API key (already configured as `openai_api_key`)
|
||||||
|
- IMAP credentials (already configured as `BntHPR3YbFD5jAIM`)
|
||||||
|
- Nextcloud Deck accessible at `https://nextcloud.egonetix.de/`
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
1. **Import workflows into n8n:**
|
||||||
|
```bash
|
||||||
|
cd /home/icke/assistant
|
||||||
|
./import_workflows.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Configure Telegram webhook:**
|
||||||
|
- Get your bot token from BotFather
|
||||||
|
- Set webhook URL to: `https://flow.egonetix.de/webhook/8f3f59db-aaa5-4762-9416-94be04131fd2`
|
||||||
|
- Command:
|
||||||
|
```bash
|
||||||
|
curl -X POST "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/setWebhook" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"url":"https://flow.egonetix.de/webhook/8f3f59db-aaa5-4762-9416-94be04131fd2"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Activate all workflows in n8n UI:**
|
||||||
|
- Open n8n at https://flow.egonetix.de/
|
||||||
|
- Go to each workflow and click "Active"
|
||||||
|
- Verify webhook endpoints are listening
|
||||||
|
|
||||||
|
### Telegram Bot Commands
|
||||||
|
|
||||||
|
```
|
||||||
|
/start - Welcome message and feature overview
|
||||||
|
/help - Show all available commands
|
||||||
|
|
||||||
|
📋 Deck Commands:
|
||||||
|
/deck add <task> - Add task to Nextcloud Deck
|
||||||
|
Example: /deck add Review Q4 reports by Friday
|
||||||
|
/deck list - List tasks (coming soon)
|
||||||
|
|
||||||
|
📧 Email Commands:
|
||||||
|
/email search <query> - Search your inbox
|
||||||
|
Example: /email search invoice November
|
||||||
|
/email recent - Show 5 most recent emails
|
||||||
|
|
||||||
|
💬 AI Commands:
|
||||||
|
/ask <question> - Ask AI anything
|
||||||
|
Example: /ask What's the weather like today?
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### Credentials Used
|
||||||
|
|
||||||
|
- **Telegram API** (`Csk5cg4HtaSqP5jJ`)
|
||||||
|
- Bot token for sending/receiving messages
|
||||||
|
|
||||||
|
- **OpenAI API** (`openai_api_key`)
|
||||||
|
- Model: `gpt-4o-mini`
|
||||||
|
- Used for task extraction and AI chat
|
||||||
|
|
||||||
|
- **IMAP** (`BntHPR3YbFD5jAIM`)
|
||||||
|
- Server: `imap.egonetix.de:993`
|
||||||
|
- SSL enabled, read-only mode
|
||||||
|
|
||||||
|
### Workflow Communication
|
||||||
|
|
||||||
|
Workflows communicate via internal HTTP webhooks:
|
||||||
|
- Main receiver → Router: `http://localhost:8098/webhook/telegram-router`
|
||||||
|
- Router → Deck: `http://localhost:8098/webhook/telegram-deck`
|
||||||
|
- Router → Email: `http://localhost:8098/webhook/telegram-email`
|
||||||
|
- Router → AI: `http://localhost:8098/webhook/telegram-ai`
|
||||||
|
|
||||||
|
### Nextcloud Deck Integration
|
||||||
|
|
||||||
|
Uses existing bash script at `/home/node/create_card_from_ai.sh`:
|
||||||
|
```bash
|
||||||
|
/home/node/create_card_from_ai.sh "<title>" "<description>" "<duedate>"
|
||||||
|
```
|
||||||
|
|
||||||
|
The script handles:
|
||||||
|
- Authentication with Nextcloud
|
||||||
|
- Board/stack selection (hardcoded to board 1, stack 1)
|
||||||
|
- Card creation via Deck API
|
||||||
|
- Returns success/error status
|
||||||
|
|
||||||
|
### AI Task Extraction
|
||||||
|
|
||||||
|
OpenAI prompt for extracting task details:
|
||||||
|
```
|
||||||
|
Extract task information from user input. Return JSON with:
|
||||||
|
- title (required, concise task name)
|
||||||
|
- description (optional, details)
|
||||||
|
- duedate (optional, YYYY-MM-DD format)
|
||||||
|
|
||||||
|
For German dates like 'morgen', 'übermorgen', calculate from today.
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
⚠️ **Current Status:**
|
||||||
|
- n8n password is weak (`changeme`) - **CHANGE THIS**
|
||||||
|
- Nextcloud credentials hardcoded in bash scripts
|
||||||
|
- OpenAI API key exposed in multiple files
|
||||||
|
- No rate limiting on Telegram commands
|
||||||
|
|
||||||
|
🔒 **Recommendations:**
|
||||||
|
1. Change n8n admin password
|
||||||
|
2. Migrate credentials to n8n credential store
|
||||||
|
3. Rotate OpenAI API key after migration
|
||||||
|
4. Implement user authentication/whitelisting
|
||||||
|
5. Add rate limiting to prevent abuse
|
||||||
|
6. Enable n8n execution logging
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Webhook Not Receiving Messages
|
||||||
|
```bash
|
||||||
|
# Check Telegram webhook status
|
||||||
|
curl "https://api.telegram.org/bot<TOKEN>/getWebhookInfo"
|
||||||
|
|
||||||
|
# Verify n8n workflow is active
|
||||||
|
curl https://flow.egonetix.de/webhook/8f3f59db-aaa5-4762-9416-94be04131fd2
|
||||||
|
|
||||||
|
# Check n8n logs
|
||||||
|
docker logs n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deck Card Creation Fails
|
||||||
|
```bash
|
||||||
|
# Test bash script manually
|
||||||
|
docker exec n8n /home/node/create_card_from_ai.sh "Test Task" "Description" ""
|
||||||
|
|
||||||
|
# Verify Nextcloud credentials
|
||||||
|
curl -u robert.wiegand:November1985** \
|
||||||
|
https://nextcloud.egonetix.de/index.php/apps/deck/api/v1.0/boards
|
||||||
|
```
|
||||||
|
|
||||||
|
### OpenAI API Errors
|
||||||
|
- Check API key validity in n8n credentials
|
||||||
|
- Verify quota not exceeded
|
||||||
|
- Check model availability (gpt-4o-mini)
|
||||||
|
|
||||||
|
### IMAP Connection Issues
|
||||||
|
- Verify credentials in n8n: `BntHPR3YbFD5jAIM`
|
||||||
|
- Test IMAP connection: `openssl s_client -connect imap.egonetix.de:993`
|
||||||
|
- Check mailbox name (should be "INBOX")
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
### Testing Workflows Locally
|
||||||
|
|
||||||
|
Use n8n's webhook test feature or curl:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Test router
|
||||||
|
curl -X POST http://localhost:8098/webhook/telegram-router \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"command": "/help",
|
||||||
|
"args": "",
|
||||||
|
"chatId": "579304651",
|
||||||
|
"userId": 123456,
|
||||||
|
"username": "testuser"
|
||||||
|
}'
|
||||||
|
|
||||||
|
# Test deck workflow
|
||||||
|
curl -X POST http://localhost:8098/webhook/telegram-deck \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"command": "/deck",
|
||||||
|
"args": "add Test task for tomorrow",
|
||||||
|
"chatId": "579304651"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Extending Functionality
|
||||||
|
|
||||||
|
To add new commands:
|
||||||
|
|
||||||
|
1. Add case in **telegram-router.json** Switch node
|
||||||
|
2. Create new workflow with webhook endpoint
|
||||||
|
3. Update router to call new workflow
|
||||||
|
4. Update help text in `/help` command
|
||||||
|
5. Import updated workflows
|
||||||
|
|
||||||
|
### Monitoring
|
||||||
|
|
||||||
|
View execution history in n8n:
|
||||||
|
- Go to https://flow.egonetix.de/
|
||||||
|
- Click "Executions" tab
|
||||||
|
- Filter by workflow name
|
||||||
|
- Check for errors or slow executions
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
🚀 **Planned Features:**
|
||||||
|
- [ ] Deck task listing and completion
|
||||||
|
- [ ] Calendar integration for appointments
|
||||||
|
- [ ] Multi-turn conversations with context
|
||||||
|
- [ ] Voice message transcription (Whisper)
|
||||||
|
- [ ] Document upload to Nextcloud
|
||||||
|
- [ ] Scheduled reminders
|
||||||
|
- [ ] User authentication/whitelisting
|
||||||
|
- [ ] Analytics dashboard
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
- Built on existing credit card workflow patterns
|
||||||
|
- Uses proven integration with Nextcloud Deck API
|
||||||
|
- Leverages existing IMAP and OpenAI credentials
|
||||||
|
- Telegram bot token from existing infrastructure
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Internal tool for personal use on srvdocker02.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Author:** AI Assistant
|
||||||
|
**Created:** 2025-12-02
|
||||||
|
**n8n Version:** 1.19.4
|
||||||
|
**Infrastructure:** srvdocker02
|
||||||
147
SETUP_COMPLETE.md
Normal file
147
SETUP_COMPLETE.md
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
# Telegram AI Assistant - Setup Complete! 🎉
|
||||||
|
|
||||||
|
## ✅ What's Been Created
|
||||||
|
|
||||||
|
All 5 workflows have been successfully imported into n8n:
|
||||||
|
|
||||||
|
1. **Telegram Assistant - Receiver** - Webhook listener for Telegram messages
|
||||||
|
2. **Telegram Assistant - Router** - Command routing and dispatch
|
||||||
|
3. **Telegram Assistant - Deck Integration** - Nextcloud Deck task management
|
||||||
|
4. **Telegram Assistant - Email Search** - IMAP email search
|
||||||
|
5. **Telegram Assistant - AI Chat** - OpenAI-powered chat
|
||||||
|
|
||||||
|
## 📍 Next Steps
|
||||||
|
|
||||||
|
### 1. Activate Workflows in n8n
|
||||||
|
|
||||||
|
Open https://flow.egonetix.de/ and activate each workflow:
|
||||||
|
- Click on each workflow
|
||||||
|
- Toggle the "Active" switch at the top
|
||||||
|
- Verify webhook endpoints are listening
|
||||||
|
|
||||||
|
### 2. Configure Telegram Webhook
|
||||||
|
|
||||||
|
You need to get your Telegram bot token and set the webhook URL:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Replace <YOUR_BOT_TOKEN> with your actual bot token
|
||||||
|
curl -X POST "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/setWebhook" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"url":"https://flow.egonetix.de/webhook/8f3f59db-aaa5-4762-9416-94be04131fd2"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
To get your bot token:
|
||||||
|
- Open Telegram and search for @BotFather
|
||||||
|
- Send `/mybots`
|
||||||
|
- Select your bot
|
||||||
|
- Click "API Token"
|
||||||
|
|
||||||
|
### 3. Test Your Bot
|
||||||
|
|
||||||
|
Send these commands to your Telegram bot:
|
||||||
|
|
||||||
|
```
|
||||||
|
/start - Welcome message
|
||||||
|
/help - Show all commands
|
||||||
|
/deck add Review project proposal by Friday
|
||||||
|
/email search invoice
|
||||||
|
/ask What's the weather like today?
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Credentials Used
|
||||||
|
|
||||||
|
The workflows use these existing n8n credentials:
|
||||||
|
- **Telegram API**: `Csk5cg4HtaSqP5jJ`
|
||||||
|
- **OpenAI API**: `openai_api_key`
|
||||||
|
- **IMAP**: `BntHPR3YbFD5jAIM`
|
||||||
|
|
||||||
|
If any credentials are missing, add them in n8n Settings → Credentials.
|
||||||
|
|
||||||
|
## 📋 Available Commands
|
||||||
|
|
||||||
|
### Deck Commands
|
||||||
|
- `/deck add <task>` - Add task to Nextcloud Deck
|
||||||
|
- Example: `/deck add Review Q4 reports by tomorrow`
|
||||||
|
- AI extracts: title, description, due date (supports German dates)
|
||||||
|
|
||||||
|
### Email Commands
|
||||||
|
- `/email search <query>` - Search your inbox
|
||||||
|
- Example: `/email search invoice November`
|
||||||
|
- `/email recent` - Show 5 most recent emails
|
||||||
|
|
||||||
|
### AI Commands
|
||||||
|
- `/ask <question>` - Ask AI anything
|
||||||
|
- Example: `/ask Explain quantum computing`
|
||||||
|
|
||||||
|
### Other
|
||||||
|
- `/start` - Welcome message
|
||||||
|
- `/help` - Show this help
|
||||||
|
|
||||||
|
## 🐛 Troubleshooting
|
||||||
|
|
||||||
|
### Webhook Not Working
|
||||||
|
```bash
|
||||||
|
# Check webhook status
|
||||||
|
curl "https://api.telegram.org/bot<TOKEN>/getWebhookInfo"
|
||||||
|
|
||||||
|
# Verify n8n workflow is active
|
||||||
|
curl https://flow.egonetix.de/webhook/8f3f59db-aaa5-4762-9416-94be04131fd2
|
||||||
|
```
|
||||||
|
|
||||||
|
### Workflows Not Executing
|
||||||
|
- Check n8n logs: `docker logs n8n`
|
||||||
|
- Verify all workflows are "Active" in n8n UI
|
||||||
|
- Check webhook endpoints are green/listening
|
||||||
|
|
||||||
|
### Deck Card Creation Fails
|
||||||
|
```bash
|
||||||
|
# Test bash script manually
|
||||||
|
docker exec n8n /home/node/create_card_from_ai.sh "Test Task" "Description" ""
|
||||||
|
```
|
||||||
|
|
||||||
|
### OpenAI API Errors
|
||||||
|
- Verify API key in n8n credentials
|
||||||
|
- Check OpenAI quota/billing
|
||||||
|
|
||||||
|
## 📁 Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
/home/icke/assistant/
|
||||||
|
├── README.md # Main documentation
|
||||||
|
├── SETUP_COMPLETE.md # This file
|
||||||
|
├── import_workflows.sh # Import script (already run)
|
||||||
|
└── workflows/
|
||||||
|
├── telegram-receiver.json # ✅ Imported
|
||||||
|
├── telegram-router.json # ✅ Imported
|
||||||
|
├── telegram-deck.json # ✅ Imported
|
||||||
|
├── telegram-email.json # ✅ Imported
|
||||||
|
└── telegram-ai.json # ✅ Imported
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔐 Security Reminders
|
||||||
|
|
||||||
|
⚠️ **Important:**
|
||||||
|
- Change n8n password from `changeme`
|
||||||
|
- Telegram bot token is sensitive - keep it secret
|
||||||
|
- Consider adding user whitelisting (only your Telegram ID)
|
||||||
|
- Enable rate limiting if bot becomes public
|
||||||
|
|
||||||
|
## 🚀 Future Enhancements
|
||||||
|
|
||||||
|
Ideas for expansion:
|
||||||
|
- Deck task listing and completion
|
||||||
|
- Calendar integration
|
||||||
|
- Voice message transcription
|
||||||
|
- File uploads to Nextcloud
|
||||||
|
- Scheduled reminders
|
||||||
|
- Multi-turn conversations with context
|
||||||
|
|
||||||
|
## 📖 Full Documentation
|
||||||
|
|
||||||
|
See [README.md](README.md) for complete technical details, architecture overview, and development guide.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Status:** ✅ All workflows imported and ready to activate
|
||||||
|
**Date:** 2025-12-02
|
||||||
|
**Location:** /home/icke/assistant/
|
||||||
101
import_workflows.sh
Executable file
101
import_workflows.sh
Executable file
@@ -0,0 +1,101 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
###############################################################################
|
||||||
|
# n8n Workflow Import Script
|
||||||
|
#
|
||||||
|
# Purpose: Import all Telegram Assistant workflows into n8n via API
|
||||||
|
# Usage: ./import_workflows.sh
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
set +e
|
||||||
|
|
||||||
|
echo "🚀 Importing Telegram Assistant workflows into n8n"
|
||||||
|
echo "=================================================="
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
N8N_URL="https://flow.egonetix.de"
|
||||||
|
N8N_API_KEY="n8n_api_42f1838c1e2de90cadcb669f78083de92697a85322c0b6009ad2e55760db992ab0bf61515a3cf0e1"
|
||||||
|
WORKFLOW_DIR="/home/icke/assistant/workflows"
|
||||||
|
|
||||||
|
# Workflows to import (in order)
|
||||||
|
WORKFLOWS=(
|
||||||
|
"telegram-receiver.json"
|
||||||
|
"telegram-router.json"
|
||||||
|
"telegram-deck.json"
|
||||||
|
"telegram-email.json"
|
||||||
|
"telegram-ai.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Function to import a single workflow
|
||||||
|
import_workflow() {
|
||||||
|
local workflow_file="$1"
|
||||||
|
local workflow_path="$WORKFLOW_DIR/$workflow_file"
|
||||||
|
|
||||||
|
if [ ! -f "$workflow_path" ]; then
|
||||||
|
echo "❌ Workflow file not found: $workflow_path"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "📤 Importing: $workflow_file"
|
||||||
|
|
||||||
|
# Import workflow via n8n API
|
||||||
|
response=$(curl -s -w "\n%{http_code}" -X POST "$N8N_URL/api/v1/workflows" \
|
||||||
|
-H "X-N8N-API-KEY: $N8N_API_KEY" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d @"$workflow_path")
|
||||||
|
|
||||||
|
# Extract HTTP status code (last line)
|
||||||
|
http_code=$(echo "$response" | tail -n1)
|
||||||
|
body=$(echo "$response" | head -n-1)
|
||||||
|
|
||||||
|
if [ "$http_code" = "200" ] || [ "$http_code" = "201" ]; then
|
||||||
|
# Extract workflow ID and name from response
|
||||||
|
workflow_id=$(echo "$body" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||||
|
workflow_name=$(echo "$body" | grep -o '"name":"[^"]*"' | head -1 | cut -d'"' -f4)
|
||||||
|
echo "✅ Successfully imported: $workflow_name (ID: $workflow_id)"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "❌ Failed to import: $workflow_file"
|
||||||
|
echo " HTTP Status: $http_code"
|
||||||
|
echo " Response: $body"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Import all workflows
|
||||||
|
success_count=0
|
||||||
|
fail_count=0
|
||||||
|
|
||||||
|
for workflow in "${WORKFLOWS[@]}"; do
|
||||||
|
if import_workflow "$workflow"; then
|
||||||
|
((success_count++))
|
||||||
|
else
|
||||||
|
((fail_count++))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Summary
|
||||||
|
echo ""
|
||||||
|
echo "=================================================="
|
||||||
|
echo "📊 Import Summary"
|
||||||
|
echo "=================================================="
|
||||||
|
echo "✅ Successfully imported: $success_count workflows"
|
||||||
|
echo "❌ Failed: $fail_count workflows"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ $fail_count -eq 0 ]; then
|
||||||
|
echo "🎉 All workflows imported successfully!"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo "1. Open n8n: $N8N_URL"
|
||||||
|
echo "2. Activate all workflows (toggle switch in UI)"
|
||||||
|
echo "3. Configure Telegram webhook:"
|
||||||
|
echo " curl -X POST \"https://api.telegram.org/bot<YOUR_BOT_TOKEN>/setWebhook\" \\"
|
||||||
|
echo " -H \"Content-Type: application/json\" \\"
|
||||||
|
echo " -d '{\"url\":\"$N8N_URL/webhook/8f3f59db-aaa5-4762-9416-94be04131fd2\"}'"
|
||||||
|
echo ""
|
||||||
|
echo "4. Test with: /start command in Telegram"
|
||||||
|
else
|
||||||
|
echo "⚠️ Some workflows failed to import. Check errors above."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
359
workflows/deck-current.json
Normal file
359
workflows/deck-current.json
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
{
|
||||||
|
"name": "Telegram Assistant - Deck Integration",
|
||||||
|
"active": false,
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"httpMethod": "POST",
|
||||||
|
"path": "telegram-deck",
|
||||||
|
"responseMode": "responseNode",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "webhook-deck-1",
|
||||||
|
"name": "Webhook",
|
||||||
|
"type": "n8n-nodes-base.webhook",
|
||||||
|
"typeVersion": 1.1,
|
||||||
|
"position": [
|
||||||
|
240,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"webhookId": "telegram-deck"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "runOnceForEachItem",
|
||||||
|
"jsCode": "// Parse deck command\nconst args = $input.item.json.args || '';\nconst chatId = $input.item.json.chatId;\n\n// Extract subcommand\nconst parts = args.trim().split(/\\s+/);\nconst subCommand = parts[0]?.toLowerCase() || 'help';\nconst taskText = parts.slice(1).join(' ');\n\nreturn {\n chatId: chatId,\n subCommand: subCommand,\n taskText: taskText,\n originalArgs: args\n};"
|
||||||
|
},
|
||||||
|
"id": "parse-deck-command-1",
|
||||||
|
"name": "Parse Deck Command",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
460,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"dataType": "string",
|
||||||
|
"value1": "={{ $json.subCommand }}",
|
||||||
|
"rules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"value2": "add",
|
||||||
|
"output": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"value2": "list",
|
||||||
|
"output": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"fallbackOutput": 2
|
||||||
|
},
|
||||||
|
"id": "switch-deck-action-1",
|
||||||
|
"name": "Route Deck Action",
|
||||||
|
"type": "n8n-nodes-base.switch",
|
||||||
|
"typeVersion": 3,
|
||||||
|
"position": [
|
||||||
|
680,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"value1": "={{ $json.taskText }}",
|
||||||
|
"operation": "isNotEmpty"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "check-task-text-1",
|
||||||
|
"name": "Check Task Text",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
900,
|
||||||
|
200
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"url": "https://api.openai.com/v1/chat/completions",
|
||||||
|
"authentication": "predefinedCredentialType",
|
||||||
|
"nodeCredentialType": "openAiApi",
|
||||||
|
"sendHeaders": true,
|
||||||
|
"headerParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "Authorization",
|
||||||
|
"value": "=Bearer {{ $credentials.openai_api_key }}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={\n \"model\": \"gpt-4o-mini\",\n \"messages\": [\n {\n \"role\": \"system\",\n \"content\": \"Extract task information from user input. Return JSON with: title (required, concise task name), description (optional, details), duedate (optional, YYYY-MM-DD format). For German dates like 'morgen', 'übermorgen', calculate from today ({{ $now.toFormat('yyyy-MM-dd') }}). Be concise.\"\n },\n {\n \"role\": \"user\",\n \"content\": \"{{ $json.taskText }}\"\n }\n ],\n \"temperature\": 0.3,\n \"max_tokens\": 150\n}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "ai-extract-task-1",
|
||||||
|
"name": "AI Extract Task Details",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.1,
|
||||||
|
"position": [
|
||||||
|
1120,
|
||||||
|
100
|
||||||
|
],
|
||||||
|
"credentials": {
|
||||||
|
"httpHeaderAuth": {
|
||||||
|
"id": "openai_api_key",
|
||||||
|
"name": "OpenAI API Key"
|
||||||
|
},
|
||||||
|
"openAiApi": {
|
||||||
|
"id": "GPzxHwwXxeZg07o5",
|
||||||
|
"name": "OpenAi account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "runOnceForEachItem",
|
||||||
|
"jsCode": "// Parse AI response\nconst aiResponse = $input.item.json.choices[0].message.content;\nlet taskData;\n\ntry {\n // Try to parse as JSON\n taskData = JSON.parse(aiResponse);\n} catch (e) {\n // Fallback to original text\n taskData = {\n title: $input.item.json.taskText.substring(0, 100),\n description: $input.item.json.taskText,\n duedate: null\n };\n}\n\nreturn {\n chatId: $input.item.json.chatId,\n title: taskData.title || 'Untitled Task',\n description: taskData.description || '',\n duedate: taskData.duedate || null\n};"
|
||||||
|
},
|
||||||
|
"id": "format-task-data-1",
|
||||||
|
"name": "Format Task Data",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
1340,
|
||||||
|
100
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"command": "=/home/node/create_card_from_ai.sh \"{{ $json.title }}\" \"{{ $json.description }}\" \"{{ $json.duedate || '' }}\""
|
||||||
|
},
|
||||||
|
"id": "create-deck-card-1",
|
||||||
|
"name": "Create Deck Card",
|
||||||
|
"type": "n8n-nodes-base.executeCommand",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
1560,
|
||||||
|
100
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "runOnceForEachItem",
|
||||||
|
"jsCode": "// Check if card creation was successful\nconst output = $input.item.json.stdout || '';\nconst error = $input.item.json.stderr || '';\n\nlet success = false;\nlet cardUrl = '';\n\nif (output.includes('Card created') || !error) {\n success = true;\n // Extract card URL if present in output\n const urlMatch = output.match(/https?:\\/\\/[^\\s]+/);\n cardUrl = urlMatch ? urlMatch[0] : '';\n}\n\nconst responseText = success \n ? `✅ Task added to Nextcloud Deck!\\n\\n📋 *${$input.item.json.title}*${$input.item.json.duedate ? '\\n📅 Due: ' + $input.item.json.duedate : ''}${cardUrl ? '\\n🔗 ' + cardUrl : ''}`\n : `❌ Failed to create task.\\n\\nError: ${error}`;\n\nreturn {\n chatId: $input.item.json.chatId,\n text: responseText,\n parseMode: 'Markdown'\n};"
|
||||||
|
},
|
||||||
|
"id": "format-success-response-1",
|
||||||
|
"name": "Format Success Response",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
1780,
|
||||||
|
100
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify($json) }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "respond-result-1",
|
||||||
|
"name": "Respond Result",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
2000,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify({\n chatId: $json.chatId,\n text: \"❌ Please specify a task to add.\\n\\nExample: /deck add Review Q4 reports by Friday\"\n}) }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "respond-no-task-1",
|
||||||
|
"name": "Respond No Task",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
1120,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify({\n chatId: $json.chatId,\n text: \"📋 *Deck Commands*\\n\\n/deck add <task> - Add a task\\n/deck list - List tasks (coming soon)\\n\\nExample:\\n/deck add Review project proposal by tomorrow\"\n}) }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "respond-deck-help-1",
|
||||||
|
"name": "Respond Deck Help",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
900,
|
||||||
|
580
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify({\n chatId: $json.chatId,\n text: \"📋 List functionality coming soon!\\n\\nFor now, check your Nextcloud Deck directly at:\\nhttps://nextcloud.egonetix.de/apps/deck\"\n}) }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "respond-list-placeholder-1",
|
||||||
|
"name": "Respond List (Coming Soon)",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
900,
|
||||||
|
400
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"Webhook": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Parse Deck Command",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Parse Deck Command": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Route Deck Action",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Route Deck Action": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Check Task Text",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Check Task Text": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "AI Extract Task Details",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Respond No Task",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"AI Extract Task Details": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Format Task Data",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Format Task Data": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Create Deck Card",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Create Deck Card": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Format Success Response",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Format Success Response": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Respond Result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Respond No Task": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Respond Result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Respond Deck Help": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Respond Result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Respond List (Coming Soon)": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Respond Result",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {},
|
||||||
|
"staticData": null,
|
||||||
|
"meta": null,
|
||||||
|
"pinData": {},
|
||||||
|
"triggerCount": 0,
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
1
workflows/deck-fixed.json
Normal file
1
workflows/deck-fixed.json
Normal file
File diff suppressed because one or more lines are too long
112
workflows/telegram-ai.json
Normal file
112
workflows/telegram-ai.json
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
{
|
||||||
|
"name": "Telegram Assistant - AI Chat",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"httpMethod": "POST",
|
||||||
|
"path": "4e0e30c5-2360-49e5-a37c-e2558f811341",
|
||||||
|
"responseMode": "responseNode",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "webhook-ai-1",
|
||||||
|
"name": "Webhook",
|
||||||
|
"type": "n8n-nodes-base.webhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
240,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"webhookId": "4e0e30c5-2360-49e5-a37c-e2558f811341"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://api.openai.com/v1/chat/completions",
|
||||||
|
"authentication": "predefinedCredentialType",
|
||||||
|
"nodeCredentialType": "openAiApi",
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={\n \"model\": \"gpt-4o-mini\",\n \"messages\": [\n {\n \"role\": \"user\",\n \"content\": \"{{ $json.body.args }}\"\n }\n ],\n \"temperature\": 0.7,\n \"max_tokens\": 500\n}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "call-openai-1",
|
||||||
|
"name": "Call OpenAI",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 3,
|
||||||
|
"position": [
|
||||||
|
460,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"credentials": {
|
||||||
|
"openAiApi": {
|
||||||
|
"id": "openai_api_key",
|
||||||
|
"name": "OpenAI API Key"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "runOnceForEachItem",
|
||||||
|
"jsCode": "const aiResponse = $input.item.json.choices[0].message.content;\nconst chatId = $input.item.json.body.chatId;\n\nreturn {\n chatId: chatId,\n text: aiResponse,\n parseMode: 'Markdown'\n};"
|
||||||
|
},
|
||||||
|
"id": "format-response-1",
|
||||||
|
"name": "Format Response",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
680,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify($json) }}"
|
||||||
|
},
|
||||||
|
"id": "respond-1",
|
||||||
|
"name": "Respond",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
900,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"Webhook": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Call OpenAI",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Call OpenAI": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Format Response",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Format Response": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Respond",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {}
|
||||||
|
}
|
||||||
179
workflows/telegram-deck.json
Normal file
179
workflows/telegram-deck.json
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
{
|
||||||
|
"name": "Telegram Assistant - Deck Integration",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"httpMethod": "POST",
|
||||||
|
"path": "telegram-deck",
|
||||||
|
"responseMode": "responseNode",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "webhook-deck-1",
|
||||||
|
"name": "Webhook",
|
||||||
|
"type": "n8n-nodes-base.webhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [240, 300],
|
||||||
|
"webhookId": "telegram-deck"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "runOnceForEachItem",
|
||||||
|
"jsCode": "const args = $input.item.json.args || '';\nconst chatId = $input.item.json.chatId;\nconst parts = args.trim().split(/\\s+/);\nconst subCommand = parts[0]?.toLowerCase() || 'help';\nconst taskText = parts.slice(1).join(' ');\nreturn {chatId, subCommand, taskText, originalArgs: args};"
|
||||||
|
},
|
||||||
|
"id": "parse-deck-command-1",
|
||||||
|
"name": "Parse Deck Command",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [460, 300]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "rules",
|
||||||
|
"rules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"operation": "equal",
|
||||||
|
"value1": "={{ $json.subCommand }}",
|
||||||
|
"value2": "add"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operation": "equal",
|
||||||
|
"value1": "={{ $json.subCommand }}",
|
||||||
|
"value2": "list"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"fallbackOutput": "extra"
|
||||||
|
},
|
||||||
|
"id": "switch-deck-action-1",
|
||||||
|
"name": "Route Deck Action",
|
||||||
|
"type": "n8n-nodes-base.switch",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [680, 300]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"string": [{"value1": "={{ $json.taskText }}", "operation": "isNotEmpty"}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "check-task-text-1",
|
||||||
|
"name": "Check Task Text",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [900, 200]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"url": "https://api.openai.com/v1/chat/completions",
|
||||||
|
"authentication": "genericCredentialType",
|
||||||
|
"genericAuthType": "httpHeaderAuth",
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={\"model\": \"gpt-4o-mini\", \"messages\": [{\"role\": \"system\", \"content\": \"Extract task information from user input. Return JSON with: title (required, concise task name), description (optional, details), duedate (optional, YYYY-MM-DD format). For German dates like 'morgen', 'übermorgen', calculate from today ({{ $now.toFormat('yyyy-MM-dd') }}). Be concise.\"}, {\"role\": \"user\", \"content\": \"{{ $json.taskText }}\"}], \"temperature\": 0.3, \"max_tokens\": 150}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "ai-extract-task-1",
|
||||||
|
"name": "AI Extract Task Details",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 3,
|
||||||
|
"position": [1120, 100],
|
||||||
|
"credentials": {
|
||||||
|
"httpHeaderAuth": {
|
||||||
|
"id": "openai_api_key",
|
||||||
|
"name": "OpenAI API Key"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "runOnceForEachItem",
|
||||||
|
"jsCode": "const aiResponse = $input.item.json.choices[0].message.content;\nlet taskData;\ntry {taskData = JSON.parse(aiResponse);} catch (e) {taskData = {title: $input.item.json.taskText.substring(0, 100), description: $input.item.json.taskText, duedate: null};}\nreturn {chatId: $input.item.json.chatId, title: taskData.title || 'Untitled Task', description: taskData.description || '', duedate: taskData.duedate || null};"
|
||||||
|
},
|
||||||
|
"id": "format-task-data-1",
|
||||||
|
"name": "Format Task Data",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [1340, 100]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"command": "=/home/node/create_card_from_ai.sh \"{{ $json.title }}\" \"{{ $json.description }}\" \"{{ $json.duedate || '' }}\""
|
||||||
|
},
|
||||||
|
"id": "create-deck-card-1",
|
||||||
|
"name": "Create Deck Card",
|
||||||
|
"type": "n8n-nodes-base.executeCommand",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1560, 100]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "runOnceForEachItem",
|
||||||
|
"jsCode": "const output = $input.item.json.stdout || '';\nconst error = $input.item.json.stderr || '';\nlet success = !error || output.includes('Card created');\nconst urlMatch = output.match(/https?:\\/\\/[^\\s]+/);\nconst cardUrl = urlMatch ? urlMatch[0] : '';\nconst responseText = success ? `✅ Task added to Nextcloud Deck!\\n\\n📋 *${$input.item.json.title}*${$input.item.json.duedate ? '\\n📅 Due: ' + $input.item.json.duedate : ''}${cardUrl ? '\\n🔗 ' + cardUrl : ''}` : `❌ Failed to create task.\\n\\nError: ${error}`;\nreturn {chatId: $input.item.json.chatId, text: responseText, parseMode: 'Markdown'};"
|
||||||
|
},
|
||||||
|
"id": "format-success-response-1",
|
||||||
|
"name": "Format Success Response",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [1780, 100]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify($json) }}"
|
||||||
|
},
|
||||||
|
"id": "respond-result-1",
|
||||||
|
"name": "Respond Result",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [2000, 300]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify({chatId: $json.chatId, text: \"❌ Please specify a task to add.\\n\\nExample: /deck add Review Q4 reports by Friday\"}) }}"
|
||||||
|
},
|
||||||
|
"id": "respond-no-task-1",
|
||||||
|
"name": "Respond No Task",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1120, 300]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify({chatId: $json.chatId, text: \"📋 *Deck Commands*\\n\\n/deck add <task>\\nOr: 'add a deck called cleaning'\\n\\nExample:\\n/deck add Review project proposal by tomorrow\", parseMode: 'Markdown'}) }}"
|
||||||
|
},
|
||||||
|
"id": "respond-deck-help-1",
|
||||||
|
"name": "Respond Deck Help",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [900, 500]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify({chatId: $json.chatId, text: \"📋 List functionality coming soon!\\n\\nFor now, check your Nextcloud Deck at:\\nhttps://nextcloud.egonetix.de/apps/deck\"}) }}"
|
||||||
|
},
|
||||||
|
"id": "respond-list-placeholder-1",
|
||||||
|
"name": "Respond List",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [900, 400]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"Webhook": {"main": [[{"node": "Parse Deck Command", "type": "main", "index": 0}]]},
|
||||||
|
"Parse Deck Command": {"main": [[{"node": "Route Deck Action", "type": "main", "index": 0}]]},
|
||||||
|
"Route Deck Action": {"main": [[{"node": "Check Task Text", "type": "main", "index": 0}], [{"node": "Respond List", "type": "main", "index": 0}], [{"node": "Respond Deck Help", "type": "main", "index": 0}]]},
|
||||||
|
"Check Task Text": {"main": [[{"node": "AI Extract Task Details", "type": "main", "index": 0}], [{"node": "Respond No Task", "type": "main", "index": 0}]]},
|
||||||
|
"AI Extract Task Details": {"main": [[{"node": "Format Task Data", "type": "main", "index": 0}]]},
|
||||||
|
"Format Task Data": {"main": [[{"node": "Create Deck Card", "type": "main", "index": 0}]]},
|
||||||
|
"Create Deck Card": {"main": [[{"node": "Format Success Response", "type": "main", "index": 0}]]},
|
||||||
|
"Format Success Response": {"main": [[{"node": "Respond Result", "type": "main", "index": 0}]]},
|
||||||
|
"Respond No Task": {"main": [[{"node": "Respond Result", "type": "main", "index": 0}]]},
|
||||||
|
"Respond Deck Help": {"main": [[{"node": "Respond Result", "type": "main", "index": 0}]]},
|
||||||
|
"Respond List": {"main": [[{"node": "Respond Result", "type": "main", "index": 0}]]}
|
||||||
|
},
|
||||||
|
"settings": {}
|
||||||
|
}
|
||||||
157
workflows/telegram-email.json
Normal file
157
workflows/telegram-email.json
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
{
|
||||||
|
"name": "Telegram Assistant - Email Search",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"httpMethod": "POST",
|
||||||
|
"path": "telegram-email",
|
||||||
|
"responseMode": "responseNode",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "webhook-email-1",
|
||||||
|
"name": "Webhook",
|
||||||
|
"type": "n8n-nodes-base.webhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [240, 300],
|
||||||
|
"webhookId": "telegram-email"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "runOnceForEachItem",
|
||||||
|
"jsCode": "const args = $input.item.json.args || '';\nconst chatId = $input.item.json.chatId;\nconst parts = args.trim().split(/\\s+/);\nconst subCommand = parts[0]?.toLowerCase() || 'help';\nconst searchQuery = parts.slice(1).join(' ');\nreturn {chatId, subCommand, searchQuery, originalArgs: args};"
|
||||||
|
},
|
||||||
|
"id": "parse-email-command-1",
|
||||||
|
"name": "Parse Email Command",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [460, 300]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "rules",
|
||||||
|
"rules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"operation": "equal",
|
||||||
|
"value1": "={{ $json.subCommand }}",
|
||||||
|
"value2": "search"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operation": "equal",
|
||||||
|
"value1": "={{ $json.subCommand }}",
|
||||||
|
"value2": "recent"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"fallbackOutput": "extra"
|
||||||
|
},
|
||||||
|
"id": "switch-email-action-1",
|
||||||
|
"name": "Route Email Action",
|
||||||
|
"type": "n8n-nodes-base.switch",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [680, 300]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"string": [{"value1": "={{ $json.searchQuery }}", "operation": "isNotEmpty"}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "check-search-query-1",
|
||||||
|
"name": "Check Search Query",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [900, 200]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mailbox": "INBOX",
|
||||||
|
"options": {"limit": 5, "search": "={{ $json.searchQuery }}"}
|
||||||
|
},
|
||||||
|
"id": "imap-search-1",
|
||||||
|
"name": "IMAP Search",
|
||||||
|
"type": "n8n-nodes-base.emailReadImap",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [1120, 100],
|
||||||
|
"credentials": {
|
||||||
|
"imap": {
|
||||||
|
"id": "BntHPR3YbFD5jAIM",
|
||||||
|
"name": "IMAP account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "runOnceForAllItems",
|
||||||
|
"jsCode": "const chatId = $input.first().json.chatId;\nconst emails = $input.all();\nif (emails.length === 0) {return {chatId, text: \"📭 No emails found matching your search.\", parseMode: 'Markdown'};}\nlet responseText = `📧 *Found ${emails.length} email${emails.length > 1 ? 's' : ''}:*\\n\\n`;\nemails.forEach((email, index) => {\n const subject = email.json.subject || '(No subject)';\n const from = email.json.from?.text || 'Unknown';\n const date = email.json.date ? new Date(email.json.date).toLocaleDateString('de-DE') : 'Unknown';\n responseText += `${index + 1}. *${subject}*\\n From: ${from}\\n Date: ${date}\\n\\n`;\n});\nif (emails.length === 5) {responseText += \"_Showing first 5 results. Refine your search for more specific results._\";}\nreturn {chatId, text: responseText, parseMode: 'Markdown'};"
|
||||||
|
},
|
||||||
|
"id": "format-email-results-1",
|
||||||
|
"name": "Format Email Results",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [1340, 100]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify($json) }}"
|
||||||
|
},
|
||||||
|
"id": "respond-result-1",
|
||||||
|
"name": "Respond Result",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1560, 300]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify({chatId: $json.chatId, text: \"❌ Please specify what to search for.\\n\\nExample: /email search invoice November\"}) }}"
|
||||||
|
},
|
||||||
|
"id": "respond-no-query-1",
|
||||||
|
"name": "Respond No Query",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1120, 300]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mailbox": "INBOX",
|
||||||
|
"options": {"limit": 5}
|
||||||
|
},
|
||||||
|
"id": "imap-recent-1",
|
||||||
|
"name": "IMAP Recent",
|
||||||
|
"type": "n8n-nodes-base.emailReadImap",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [900, 400],
|
||||||
|
"credentials": {
|
||||||
|
"imap": {
|
||||||
|
"id": "BntHPR3YbFD5jAIM",
|
||||||
|
"name": "IMAP account"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify({chatId: $json.chatId, text: \"📧 *Email Commands*\\n\\n/email search <query>\\nOr: 'find my train ticket'\\n\\nExamples:\\n/email search invoice from last week\\n/email recent\", parseMode: 'Markdown'}) }}"
|
||||||
|
},
|
||||||
|
"id": "respond-email-help-1",
|
||||||
|
"name": "Respond Email Help",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [900, 500]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"Webhook": {"main": [[{"node": "Parse Email Command", "type": "main", "index": 0}]]},
|
||||||
|
"Parse Email Command": {"main": [[{"node": "Route Email Action", "type": "main", "index": 0}]]},
|
||||||
|
"Route Email Action": {"main": [[{"node": "Check Search Query", "type": "main", "index": 0}], [{"node": "IMAP Recent", "type": "main", "index": 0}], [{"node": "Respond Email Help", "type": "main", "index": 0}]]},
|
||||||
|
"Check Search Query": {"main": [[{"node": "IMAP Search", "type": "main", "index": 0}], [{"node": "Respond No Query", "type": "main", "index": 0}]]},
|
||||||
|
"IMAP Search": {"main": [[{"node": "Format Email Results", "type": "main", "index": 0}]]},
|
||||||
|
"Format Email Results": {"main": [[{"node": "Respond Result", "type": "main", "index": 0}]]},
|
||||||
|
"Respond No Query": {"main": [[{"node": "Respond Result", "type": "main", "index": 0}]]},
|
||||||
|
"IMAP Recent": {"main": [[{"node": "Format Email Results", "type": "main", "index": 0}]]},
|
||||||
|
"Respond Email Help": {"main": [[{"node": "Respond Result", "type": "main", "index": 0}]]}
|
||||||
|
},
|
||||||
|
"settings": {}
|
||||||
|
}
|
||||||
275
workflows/telegram-receiver.json
Normal file
275
workflows/telegram-receiver.json
Normal file
@@ -0,0 +1,275 @@
|
|||||||
|
{
|
||||||
|
"name": "Telegram Assistant - Receiver",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"httpMethod": "POST",
|
||||||
|
"path": "8f3f59db-aaa5-4762-9416-94be04131fd2",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "telegram-trigger-1",
|
||||||
|
"name": "Webhook",
|
||||||
|
"type": "n8n-nodes-base.webhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
240,
|
||||||
|
300
|
||||||
|
],
|
||||||
|
"webhookId": "8f3f59db-aaa5-4762-9416-94be04131fd2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"conditions": {
|
||||||
|
"string": [
|
||||||
|
{
|
||||||
|
"value1": "={{$json.message?.text || ''}}",
|
||||||
|
"operation": "startsWith",
|
||||||
|
"value2": "/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "check-command-1",
|
||||||
|
"name": "Check if Command",
|
||||||
|
"type": "n8n-nodes-base.if",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
460,
|
||||||
|
300
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"chatId": "={{ $json.message.chat.id }}",
|
||||||
|
"text": "=Command received: {{ $json.message.text }}\n\nProcessing...",
|
||||||
|
"additionalFields": {}
|
||||||
|
},
|
||||||
|
"id": "send-ack-1",
|
||||||
|
"name": "Send Acknowledgment",
|
||||||
|
"type": "n8n-nodes-base.telegram",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [
|
||||||
|
680,
|
||||||
|
200
|
||||||
|
],
|
||||||
|
"credentials": {
|
||||||
|
"telegramApi": {
|
||||||
|
"id": "6Rim8odDFpQoHxCM",
|
||||||
|
"name": "mortimer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "runOnceForEachItem",
|
||||||
|
"jsCode": "// Extract command and parameters\nconst text = $input.item.json.message?.text || '';\nconst chatId = $input.item.json.message?.chat?.id;\nconst messageId = $input.item.json.message?.message_id;\nconst userId = $input.item.json.message?.from?.id;\nconst username = $input.item.json.message?.from?.username || 'unknown';\n\n// Parse command\nconst parts = text.trim().split(/\\s+/);\nconst command = parts[0].toLowerCase();\nconst args = parts.slice(1).join(' ');\n\nreturn {\n command: command,\n args: args,\n chatId: chatId,\n messageId: messageId,\n userId: userId,\n username: username,\n originalText: text\n};"
|
||||||
|
},
|
||||||
|
"id": "parse-command-1",
|
||||||
|
"name": "Parse Command",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
900,
|
||||||
|
200
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "runOnceForEachItem",
|
||||||
|
"jsCode": "// Extract natural language message\nconst text = $input.item.json.message?.text || '';\nconst chatId = $input.item.json.message?.chat?.id;\nconst messageId = $input.item.json.message?.message_id;\nconst userId = $input.item.json.message?.from?.id;\nconst username = $input.item.json.message?.from?.username || 'unknown';\n\nreturn {\n chatId: chatId,\n messageId: messageId,\n userId: userId,\n username: username,\n originalText: text\n};"
|
||||||
|
},
|
||||||
|
"id": "parse-natural-language-1",
|
||||||
|
"name": "Parse Natural Language",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
680,
|
||||||
|
400
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "https://api.openai.com/v1/chat/completions",
|
||||||
|
"authentication": "predefinedCredentialType",
|
||||||
|
"nodeCredentialType": "openAiApi",
|
||||||
|
"sendHeaders": true,
|
||||||
|
"headerParameters": {
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "Authorization",
|
||||||
|
"value": "=Bearer {{ $credentials.openai_api_key }}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={\n \"model\": \"gpt-4o-mini\",\n \"messages\": [\n {\n \"role\": \"system\",\n \"content\": \"Analyze user intent and classify into one of these categories. Return ONLY valid JSON with no additional text:\\n\\n1. 'deck_add' - User wants to create a task/card in Nextcloud Deck\\n Examples: 'add a deck called cleaning', 'create task review reports', 'remind me to call mom tomorrow'\\n Extract: {intent: 'deck_add', task: 'task description', duedate: 'YYYY-MM-DD or null'}\\n\\n2. 'email_search' - User wants to search emails\\n Examples: 'find my train ticket', 'search for invoice', 'show emails from last week'\\n Extract: {intent: 'email_search', query: 'search terms'}\\n\\n3. 'general_chat' - General questions, conversation\\n Examples: 'what's the weather', 'explain quantum physics', 'tell me a joke'\\n Extract: {intent: 'general_chat', question: 'full question'}\\n\\nFor dates like 'today', 'tomorrow', 'next week', calculate from today: {{ $now.toFormat('yyyy-MM-dd') }}.\\nFor German: 'morgen' = tomorrow, 'übermorgen' = day after tomorrow.\\n\\nReturn JSON format: {\\\"intent\\\": \\\"category\\\", ...extracted_data}\"\n },\n {\n \"role\": \"user\",\n \"content\": \"{{ $json.originalText }}\"\n }\n ],\n \"temperature\": 0.3,\n \"max_tokens\": 150\n}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "ai-classify-intent-1",
|
||||||
|
"name": "AI Classify Intent",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 3,
|
||||||
|
"position": [
|
||||||
|
900,
|
||||||
|
400
|
||||||
|
],
|
||||||
|
"credentials": {
|
||||||
|
"httpHeaderAuth": {
|
||||||
|
"id": "openai_api_key",
|
||||||
|
"name": "OpenAI API Key"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "runOnceForEachItem",
|
||||||
|
"jsCode": "// Parse AI classification result\nconst aiResponse = $input.item.json.choices[0].message.content;\nconst chatId = $input.item.json.chatId;\nconst userId = $input.item.json.userId;\nconst username = $input.item.json.username;\nconst originalText = $input.item.json.originalText;\n\nlet classification;\ntry {\n classification = JSON.parse(aiResponse);\n} catch (e) {\n // Fallback to general chat if parsing fails\n classification = {\n intent: 'general_chat',\n question: originalText\n };\n}\n\n// Map intent to command format for router\nlet command, args;\n\nswitch (classification.intent) {\n case 'deck_add':\n command = '/deck';\n args = 'add ' + (classification.task || originalText);\n break;\n case 'email_search':\n command = '/email';\n args = 'search ' + (classification.query || originalText);\n break;\n case 'general_chat':\n default:\n command = '/ask';\n args = classification.question || originalText;\n break;\n}\n\nreturn {\n command: command,\n args: args,\n chatId: chatId,\n userId: userId,\n username: username,\n originalText: originalText,\n classifiedIntent: classification.intent\n};"
|
||||||
|
},
|
||||||
|
"id": "format-as-command-1",
|
||||||
|
"name": "Format as Command",
|
||||||
|
"type": "n8n-nodes-base.code",
|
||||||
|
"typeVersion": 2,
|
||||||
|
"position": [
|
||||||
|
1120,
|
||||||
|
400
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "=https://flow.egonetix.de/webhook/telegram-router-direct",
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={{ JSON.stringify($json) }}",
|
||||||
|
"options": {
|
||||||
|
"response": {
|
||||||
|
"response": {
|
||||||
|
"neverError": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "call-router-natural-1",
|
||||||
|
"name": "Call Router (Natural Language)",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 3,
|
||||||
|
"position": [
|
||||||
|
1340,
|
||||||
|
400
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"method": "POST",
|
||||||
|
"url": "=https://flow.egonetix.de/webhook/telegram-router-direct",
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={{ JSON.stringify($json) }}",
|
||||||
|
"options": {
|
||||||
|
"response": {
|
||||||
|
"response": {
|
||||||
|
"neverError": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "call-router-1",
|
||||||
|
"name": "Call Router Workflow",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 3,
|
||||||
|
"position": [
|
||||||
|
1120,
|
||||||
|
200
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"Webhook": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Check if Command",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Check if Command": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Send Acknowledgment",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Parse Natural Language",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Parse Natural Language": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "AI Classify Intent",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"AI Classify Intent": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Format as Command",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Format as Command": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Call Router (Natural Language)",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Send Acknowledgment": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Parse Command",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"Parse Command": {
|
||||||
|
"main": [
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"node": "Call Router Workflow",
|
||||||
|
"type": "main",
|
||||||
|
"index": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {}
|
||||||
|
}
|
||||||
175
workflows/telegram-router.json
Normal file
175
workflows/telegram-router.json
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
{
|
||||||
|
"name": "Telegram Assistant - Router",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"httpMethod": "POST",
|
||||||
|
"path": "79209f6f-28f3-4782-8366-2517e47adc0e",
|
||||||
|
"responseMode": "responseNode",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "webhook-router-1",
|
||||||
|
"name": "Webhook",
|
||||||
|
"type": "n8n-nodes-base.webhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [240, 300],
|
||||||
|
"webhookId": "79209f6f-28f3-4782-8366-2517e47adc0e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"mode": "rules",
|
||||||
|
"rules": {
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"operation": "equal",
|
||||||
|
"value1": "={{ $json.command }}",
|
||||||
|
"value2": "/start"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operation": "equal",
|
||||||
|
"value1": "={{ $json.command }}",
|
||||||
|
"value2": "/help"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operation": "equal",
|
||||||
|
"value1": "={{ $json.command }}",
|
||||||
|
"value2": "/deck"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operation": "equal",
|
||||||
|
"value1": "={{ $json.command }}",
|
||||||
|
"value2": "/email"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"operation": "equal",
|
||||||
|
"value1": "={{ $json.command }}",
|
||||||
|
"value2": "/ask"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"fallbackOutput": "extra"
|
||||||
|
},
|
||||||
|
"id": "switch-command-1",
|
||||||
|
"name": "Route Command",
|
||||||
|
"type": "n8n-nodes-base.switch",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [460, 300]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify({chatId: $json.chatId, text: \"👋 Welcome to your Telegram Assistant!\\n\\nI can help you with:\\n\\n📋 /deck - Manage Nextcloud Deck tasks\\n📧 /email - Search your emails\\n💬 /ask - Chat with AI\\n❓ /help - Show this message\\n\\nOr just chat naturally - I understand!\", parseMode: 'Markdown'}) }}"
|
||||||
|
},
|
||||||
|
"id": "respond-start-1",
|
||||||
|
"name": "Respond Start",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [680, 100]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify({chatId: $json.chatId, text: \"🤖 *Telegram Assistant Help*\\n\\n📋 *Deck:*\\n /deck add <task>\\n Or: 'add a deck called cleaning'\\n\\n📧 *Email:*\\n /email search <query>\\n Or: 'find my train ticket'\\n\\n💬 *AI:*\\n /ask <question>\\n Or: 'what's the weather?'\", parseMode: 'Markdown'}) }}"
|
||||||
|
},
|
||||||
|
"id": "respond-help-1",
|
||||||
|
"name": "Respond Help",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [680, 200]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"url": "=https://flow.egonetix.de/webhook/c1294640-f15e-411e-b38b-8b69b8419bce",
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={{ JSON.stringify($json) }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "call-deck-1",
|
||||||
|
"name": "Call Deck Workflow",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.1,
|
||||||
|
"position": [680, 300]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"url": "=https://flow.egonetix.de/webhook/telegram-email",
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={{ JSON.stringify($json) }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "call-email-1",
|
||||||
|
"name": "Call Email Workflow",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.1,
|
||||||
|
"position": [680, 400]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"url": "=https://flow.egonetix.de/webhook/4e0e30c5-2360-49e5-a37c-e2558f811341",
|
||||||
|
"sendBody": true,
|
||||||
|
"specifyBody": "json",
|
||||||
|
"jsonBody": "={{ JSON.stringify($json) }}",
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
"id": "call-ai-1",
|
||||||
|
"name": "Call AI Workflow",
|
||||||
|
"type": "n8n-nodes-base.httpRequest",
|
||||||
|
"typeVersion": 4.1,
|
||||||
|
"position": [680, 500]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify({chatId: $json.chatId, text: \"❌ Unknown command: \" + $json.command + \"\\n\\nType /help to see available commands.\"}) }}"
|
||||||
|
},
|
||||||
|
"id": "respond-unknown-1",
|
||||||
|
"name": "Respond Unknown",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [680, 600]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"chatId": "={{ $json.chatId }}",
|
||||||
|
"text": "={{ $json.text }}",
|
||||||
|
"additionalFields": {"parse_mode": "={{ $json.parseMode || 'HTML' }}"}
|
||||||
|
},
|
||||||
|
"id": "send-telegram-1",
|
||||||
|
"name": "Send Telegram Response",
|
||||||
|
"type": "n8n-nodes-base.telegram",
|
||||||
|
"typeVersion": 1.1,
|
||||||
|
"position": [900, 300],
|
||||||
|
"credentials": {
|
||||||
|
"telegramApi": {
|
||||||
|
"id": "6Rim8odDFpQoHxCM",
|
||||||
|
"name": "mortimer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"parameters": {
|
||||||
|
"respondWith": "json",
|
||||||
|
"responseBody": "={{ JSON.stringify({ success: true }) }}"
|
||||||
|
},
|
||||||
|
"id": "respond-ok-1",
|
||||||
|
"name": "Respond OK",
|
||||||
|
"type": "n8n-nodes-base.respondToWebhook",
|
||||||
|
"typeVersion": 1,
|
||||||
|
"position": [1120, 300]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"connections": {
|
||||||
|
"Webhook": {"main": [[{"node": "Route Command", "type": "main", "index": 0}]]},
|
||||||
|
"Route Command": {"main": [[{"node": "Respond Start", "type": "main", "index": 0}], [{"node": "Respond Help", "type": "main", "index": 0}], [{"node": "Call Deck Workflow", "type": "main", "index": 0}], [{"node": "Call Email Workflow", "type": "main", "index": 0}], [{"node": "Call AI Workflow", "type": "main", "index": 0}], [{"node": "Respond Unknown", "type": "main", "index": 0}]]},
|
||||||
|
"Respond Start": {"main": [[{"node": "Send Telegram Response", "type": "main", "index": 0}]]},
|
||||||
|
"Respond Help": {"main": [[{"node": "Send Telegram Response", "type": "main", "index": 0}]]},
|
||||||
|
"Call Deck Workflow": {"main": [[{"node": "Send Telegram Response", "type": "main", "index": 0}]]},
|
||||||
|
"Call Email Workflow": {"main": [[{"node": "Send Telegram Response", "type": "main", "index": 0}]]},
|
||||||
|
"Call AI Workflow": {"main": [[{"node": "Send Telegram Response", "type": "main", "index": 0}]]},
|
||||||
|
"Respond Unknown": {"main": [[{"node": "Send Telegram Response", "type": "main", "index": 0}]]},
|
||||||
|
"Send Telegram Response": {"main": [[{"node": "Respond OK", "type": "main", "index": 0}]]}
|
||||||
|
},
|
||||||
|
"settings": {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user