This workflow follows the Agent → Lmchatdeepseek recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"name": "Moss - Telegram Assistant",
"nodes": [
{
"parameters": {
"updates": [
"message"
],
"additionalFields": {}
},
"id": "a276483d-6e0a-440e-aab3-dd16dc3d6315",
"name": "Telegram Trigger",
"type": "n8n-nodes-base.telegramTrigger",
"typeVersion": 1.1,
"position": [
0,
0
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"agent": "conversationalAgent",
"promptType": "define",
"text": "=={{ $json.text }}",
"options": {
"systemMessage": "==you are moss, a personal AI assistant, female\n\ncurrent date: {{ $now.format('yyyy-MM-dd') }}\n\n## tools\n\n### todoist\nuse todoist when the user mentions tasks, todos, or anything to track. pass a JSON string:\n- action: get_tasks | create_task | close_task | delete_task\n- filter: today | overdue (for get_tasks)\n- content: task name (for create_task)\n- due_string: e.g. \"tomorrow\" (for create_task)\n- task_id: required for close_task / delete_task\n\n### google_calendar\nuse google_calendar when the user asks about schedule, meetings, or events, or wants to add/delete them.\n- get_events: filter (today / tomorrow / this_week)\n- create_event: summary, start (ISO +08:00), end (optional), description, location\n- delete_event: event_id \u2014 ALWAYS call get_events first to find the id\n\n### google_drive\nuse google_drive when the user mentions files, documents, spreadsheets, or anything stored in Drive.\n- search_files: query (file name or keyword), max_results\n- read_file: file_id (get it from search_files first)\nOnly Google Docs/Sheets/Slides can be read as text. PDFs cannot.\n\n### gmail\nuse gmail when the user wants to read, send, search, or reply to emails.\n- send_email: to, subject, body, cc (optional)\n- search_emails: query (Gmail syntax), max_results\n- read_email: email_id \u2192 returns content + thread_id for replying\n- reply_email: thread_id, to, subject, body\nNEVER make up email content \u2014 always call the tool.\n\n## what you know about this user\n{{ $('load profile').item.json.profile || 'no profile yet' }}\n\n## behavior\n- direct and efficient. no filler phrases\n- have opinions, push back when it makes sense\n- reply in the language the user writes in\n- use structured output for complex answers\n- chain tool calls when needed (e.g. search then read, get events then delete)"
}
},
"id": "d8170eb9-b39e-417b-8deb-8491f875a0e5",
"name": "AI Agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 1.7,
"position": [
480,
0
]
},
{
"parameters": {
"chatId": "={{ $('Telegram Trigger').item.json.message.chat.id }}",
"text": "={{ $json.output }}",
"additionalFields": {
"appendAttribution": false,
"parse_mode": "Markdown"
}
},
"id": "d5ca14c4-f805-4928-bf95-2bcb4621da84",
"name": "Send Reply",
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
944,
0
],
"credentials": {
"telegramApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatDeepSeek",
"typeVersion": 1,
"position": [
144,
208
],
"id": "0419cd3e-f7c8-4785-9633-15e61570f059",
"name": "DeepSeek Chat Model",
"credentials": {
"deepSeekApi": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"jsCode": "const fs = require('fs');\n const chatId = $('Telegram Trigger').item.json.message.chat.id.toString();\n const text = $('Telegram Trigger').item.json.message.text || '';\n\n const profilePath = '/home/node/.n8n/moss_profiles.json';\n let profiles = {};\n try {\n profiles = JSON.parse(fs.readFileSync(profilePath, 'utf8'));\n } catch(e) {\n profiles = {};\n }\n\n const profile = profiles[chatId] || null;\n\n return [{\n json: {\n chatId,\n text,\n profile: profile || '',\n isNewUser: !profile\n }\n }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
208,
0
],
"id": "bf085567-0765-4132-909c-d35f9fbc1c76",
"name": "load profile"
},
{
"parameters": {
"jsCode": "const fs = require('fs');\n const chatId = $('load profile').item.json.chatId;\n const userMessage = $('load profile').item.json.text;\n const agentReply = $input.item.json.output || '';\n const existingProfile = $('load profile').item.json.profile || '';\n\n const profilePath = '/home/node/.n8n/moss_profiles.json';\n let profiles = {};\n try {\n profiles = JSON.parse(fs.readFileSync(profilePath, 'utf8'));\n } catch(e) {\n profiles = {};\n }\n\n const currentProfile = profiles[chatId] || existingProfile;\n const conversation = `user: ${userMessage}\\nmoss: ${agentReply}`;\n\n profiles[chatId] = currentProfile\n ? `${currentProfile}\\n\\n[recent] ${conversation}`.slice(-2000)\n : `[first conversation]\\n${conversation}`;\n\n fs.writeFileSync(profilePath, JSON.stringify(profiles, null, 2));\n\n return [{ json: { output: agentReply, chatId } }];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
832,
0
],
"id": "07427a40-4bd9-4a03-a602-1c1e330626c5",
"name": "update profile"
},
{
"parameters": {
"sessionIdType": "customKey",
"sessionKey": "={{ $('Telegram Trigger').item.json.message.chat.id }}"
},
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [
496,
208
],
"id": "d7a3d206-e730-4f34-bbe1-866c46d1c8ae",
"name": "Simple Memory"
},
{
"parameters": {
"description": "Manage the user's Todoist tasks. Actions: get_tasks (filter: today/overdue), create_task (content, due_string), close_task (task_id). Always get tasks first before closing.",
"jsCode": "const https = require('https');\n const TOKEN = 'YOUR_TODOIST_API_TOKEN';\n const BASE = 'api.todoist.com';\n\n function request(method, path, body) {\n return new Promise((resolve, reject) => {\n const data = body ? JSON.stringify(body) : null;\n const opts = {\n hostname: BASE,\n path: `/api/v1${path}`,\n method,\n headers: {\n 'Authorization': `Bearer ${TOKEN}`,\n 'Content-Type': 'application/json',\n ...(data ? { 'Content-Length': Buffer.byteLength(data) } : {})\n }\n };\n const req = https.request(opts, res => {\n let raw = '';\n res.on('data', c => raw += c);\n res.on('end', () => {\n try { resolve({ status: res.statusCode, body: raw ? JSON.parse(raw) : {} }); }\n catch(e) { resolve({ status: res.statusCode, body: raw }); }\n });\n });\n req.on('error', reject);\n if (data) req.write(data);\n req.end();\n });\n }\n\n let params = {};\n try { params = JSON.parse(query); } catch(e) {\n params = { action: 'get_tasks', filter: 'today' };\n }\n\n const { action = 'get_tasks', filter, content, due_string, priority, task_id } = params;\n\n let result;\n if (action === 'get_tasks') {\n const res = await request('GET', `/tasks?filter=${encodeURIComponent(filter || 'today')}`);\n const tasks = (res.body.results || []).map(t => ({ id: t.id, content: t.content, due: t.due?.string || null, priority: t.priority }));\n result = tasks.length ? tasks : 'no tasks found';\n\n } else if (action === 'create_task') {\n const body = { content };\n if (due_string) body.due_string = due_string;\n if (priority) body.priority = priority;\n const res = await request('POST', '/tasks', body);\n result = res.body.id ? { created: res.body.content, id: res.body.id, due: res.body.due?.string || null } : { error: 'create failed', detail: res.body };\n\n } else if (action === 'close_task') {\n const res = await request('POST', `/tasks/${task_id}/close`);\n result = res.status === 204 ? 'task marked complete' : { error: 'close failed', status: res.status };\n\n } else if (action === 'delete_task') {\n const res = await request('DELETE', `/tasks/${task_id}`);\n result = res.status === 204 ? 'task deleted' : { error: 'delete failed', status: res.status };\n\n } else {\n result = 'unknown action. use: get_tasks, create_task, close_task, delete_task';\n }\n\n return JSON.stringify(result);"
},
"type": "@n8n/n8n-nodes-langchain.toolCode",
"typeVersion": 1.3,
"position": [
896,
208
],
"id": "c2aa916a-b65b-4124-a7c0-4e74041e2feb",
"name": "todoist"
},
{
"parameters": {
"name": "google_calendar",
"description": "Manage the user's Google Calendar. Actions:\n- get_events: get events. filter: today | tomorrow | this_week\n- create_event: create an event. fields: summary, start (ISO datetime +08:00), end (optional), description, location\n- delete_event: delete an event. field: event_id (ALWAYS call get_events first to get the ID)\n\nALWAYS call this tool when asked about schedule or events \u2014 never make up data.\nFor delete: first get_events to find the right event and its id, then delete_event with that id.",
"workflowId": {
"__rl": true,
"value": "moss-cal-tool-001",
"mode": "id"
},
"workflowInputs": {
"mappingMode": "defineBelow",
"value": {},
"matchingColumns": [],
"schema": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
}
},
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"typeVersion": 2,
"position": [
1104,
208
],
"id": "node-cal-tool",
"name": "google calendar"
},
{
"parameters": {
"name": "google_drive",
"description": "Access the user's Google Drive. Actions:\n- search_files: find files by name. fields: query (search term), max_results (optional, default 10)\n- read_file: read text content of a Google Doc, Sheet, or Slides file. field: file_id (from search_files)\n\nUse search_files first to find the file ID, then read_file to get content.\nNote: only Google Workspace files (Docs/Sheets/Slides) support read_file \u2014 PDFs/images cannot be read as text.",
"workflowId": {
"__rl": true,
"value": "moss-drive-001",
"mode": "id"
},
"workflowInputs": {
"mappingMode": "defineBelow",
"value": {},
"matchingColumns": [],
"schema": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
}
},
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"typeVersion": 2.1,
"position": [
800,
300
],
"id": "node-drive-tool",
"name": "google_drive"
},
{
"parameters": {
"name": "gmail",
"description": "Access the user's Gmail. Actions:\n- send_email: send an email. fields: to, subject, body, cc (optional), bcc (optional)\n- search_emails: search inbox. fields: query (Gmail search syntax, e.g. 'from:boss@company.com is:unread'), max_results (optional)\n- read_email: read full content of an email. field: email_id (from search_emails). Also returns thread_id for replies.\n- reply_email: reply to a thread. fields: thread_id (from read_email), to, subject, body\n\nFor reading emails: search_emails first to get IDs, then read_email for content.\nALWAYS use this tool when the user asks about email \u2014 never make up email content.",
"workflowId": {
"__rl": true,
"value": "moss-gmail-001",
"mode": "id"
},
"workflowInputs": {
"mappingMode": "defineBelow",
"value": {},
"matchingColumns": [],
"schema": [],
"attemptToConvertTypes": false,
"convertFieldsToString": false
}
},
"type": "@n8n/n8n-nodes-langchain.toolWorkflow",
"typeVersion": 2.1,
"position": [
1000,
300
],
"id": "node-gmail-tool",
"name": "gmail"
}
],
"connections": {
"Telegram Trigger": {
"main": [
[
{
"node": "load profile",
"type": "main",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "update profile",
"type": "main",
"index": 0
}
]
]
},
"DeepSeek Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"load profile": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"update profile": {
"main": [
[
{
"node": "Send Reply",
"type": "main",
"index": 0
}
]
]
},
"Simple Memory": {
"ai_memory": [
[
{
"node": "AI Agent",
"type": "ai_memory",
"index": 0
}
]
]
},
"todoist": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"google calendar": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"google_drive": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
},
"gmail": {
"ai_tool": [
[
{
"node": "AI Agent",
"type": "ai_tool",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"executionOrder": "v1",
"binaryMode": "separate"
}
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
deepSeekApitelegramApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Moss - Telegram Assistant. Uses telegramTrigger, agent, telegram, lmChatDeepSeek. Event-driven trigger; 11 nodes.
Source: https://github.com/sethlsx/moss/blob/bd15b4f7f5068f0e2c54318448815e98681cfc28/workflows/moss_simple.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Agent Access Control Template. Uses memoryBufferWindow, lmChatOpenAi, telegramTrigger, airtable. Event-driven trigger; 36 nodes.
This workflow allows granular control over the access to tools connected to AI Agents (including Multi-Agent setups) using Role Based Access Control.
BoomerBobBot.TP. Uses agent, telegramTrigger, telegram, memoryBufferWindow. Event-driven trigger; 95 nodes.
A comprehensive n8n workflow demonstrating advanced AI agent orchestration, stateful conversation management, and multi-modal input processing for nutrition tracking applications.
> AI-powered nutrition assistant for Telegram — log meals, set goals, and get personalized daily reports with Google Sheets integration.