8
n8n 中文网amn8n.com

PulsePoint紧急告警转iMessage与AI摘要

中级

这是一个Personal Productivity, AI Summarization领域的自动化工作流,包含 11 个节点。主要使用 If, Code, HttpRequest, ManualTrigger, Agent 等节点。 PulsePoint紧急告警转iMessage与AI摘要

前置要求
  • 可能需要目标 API 的认证凭证
  • OpenAI API Key
工作流预览
可视化展示节点连接关系,支持缩放和平移
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
  "meta": {
    "instanceId": "9fe2e2e308ee9fa575b11a458a16465194c029a4f53a09a925eb0b5fa7b5761a",
    "templateCredsSetupCompleted": true
  },
  "nodes": [
    {
      "id": "07757d09-129a-48f0-9a11-965ac480c490",
      "name": "便签",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1020,
        -340
      ],
      "parameters": {
        "width": 880,
        "height": 1000,
        "content": "## 使用 n8n HTTP 请求节点发送 Blooio.com 消息"
      },
      "typeVersion": 1
    },
    {
      "id": "75f418c3-3f51-4be0-812b-57077fefbcb8",
      "name": "发送消息",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        1080,
        460
      ],
      "parameters": {
        "url": "https://api.blooio.com/send-message",
        "method": "POST",
        "options": {},
        "sendBody": true,
        "sendHeaders": true,
        "authentication": "genericCredentialType",
        "bodyParameters": {
          "parameters": [
            {
              "name": "identifier",
              "value": "=+11111111111"
            },
            {
              "name": "message",
              "value": "={{ $json.output }}"
            }
          ]
        },
        "genericAuthType": "httpBearerAuth",
        "headerParameters": {
          "parameters": [
            {
              "name": "accept",
              "value": "application/json"
            }
          ]
        }
      },
      "credentials": {
        "httpBearerAuth": {
          "id": "WxjvtozS2uLOEBkw",
          "name": "Blooio Bearer Auth account"
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "d9285e13-a035-4ba1-a9ff-3c512715e192",
      "name": "当点击\"执行工作流\"时",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        40,
        40
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "f90fe9a1-d266-479a-b2f4-bbb047afc46c",
      "name": "计划触发器",
      "type": "n8n-nodes-base.scheduleTrigger",
      "position": [
        40,
        240
      ],
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 60
            }
          ]
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "0fc5a46b-17a4-42e8-941c-fd5c1df53600",
      "name": "AI Agent",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "position": [
        700,
        460
      ],
      "parameters": {
        "text": "={{$input}}",
        "options": {
          "systemMessage": "Your only purpose is to brief the user on what is happening around them. Be concise. Use emojis and tell them what's happening around them. Give them a full report like\nWhat is happening, where, the time, units and the status of the units"
        },
        "promptType": "define"
      },
      "typeVersion": 2
    },
    {
      "id": "dab24453-cb4b-499e-917c-428a2e579ee1",
      "name": "全部合并",
      "type": "n8n-nodes-base.code",
      "position": [
        340,
        480
      ],
      "parameters": {
        "jsCode": "const allIncidents = items.map(item => item.json);\n\nreturn [\n  {\n    json: {\n      incidents: allIncidents\n    }\n  }\n];"
      },
      "typeVersion": 2
    },
    {
      "id": "fe7343f7-1b42-48f4-9a80-121e1596f3e2",
      "name": "条件判断",
      "type": "n8n-nodes-base.if",
      "position": [
        520,
        480
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "version": 2,
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "ae6c4ed0-033e-4fba-8472-77293d616a00",
              "operator": {
                "type": "array",
                "operation": "lengthGt",
                "rightType": "number"
              },
              "leftValue": "={{ $json.incidents }}",
              "rightValue": 0
            }
          ]
        }
      },
      "typeVersion": 2.2
    },
    {
      "id": "4bcdb173-a75b-4b6a-8d79-30115b853e94",
      "name": "OpenAI 聊天模型",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
      "position": [
        720,
        640
      ],
      "parameters": {
        "model": {
          "__rl": true,
          "mode": "list",
          "value": "o4-mini",
          "cachedResultName": "o4-mini"
        },
        "options": {}
      },
      "credentials": {
        "openAiApi": {
          "id": "JrqIdyKAvwxNgaEM",
          "name": "Tay - SLUG"
        }
      },
      "typeVersion": 1.2
    },
    {
      "id": "9c308697-6b1b-4475-a1d0-fdad0b562bdd",
      "name": "获取警报",
      "type": "n8n-nodes-base.code",
      "position": [
        300,
        240
      ],
      "parameters": {
        "jsCode": "const crypto = require('crypto');\nconst fetch = require('node-fetch');\n\n// Step 1: Fetch encrypted PulsePoint incident data\nconst url = \"https://api.pulsepoint.org/v1/webapp?resource=incidents&agencyid=19100\";\nconst response = await fetch(url);\nconst data = await response.json();\n\n// Step 2: Decrypt response\nconst ct = Buffer.from(data.ct, \"base64\");\nconst iv = Buffer.from(data.iv, \"hex\");\nconst salt = Buffer.from(data.s, \"hex\");\n\nconst e = \"CommonIncidents\";\nconst password =\n  e[13] +\n  e[1] +\n  e[2] +\n  \"brady\" +\n  \"5\" +\n  \"r\" +\n  e.toLowerCase()[6] +\n  e[5] +\n  \"gs\";\n\n// Key derivation (Python-style)\nlet key = Buffer.alloc(0);\nlet block = null;\nwhile (key.length < 32) {\n  const hasher = crypto.createHash(\"md5\");\n  if (block) hasher.update(block);\n  hasher.update(Buffer.from(password, \"utf8\"));\n  hasher.update(salt);\n  block = hasher.digest();\n  key = Buffer.concat([key, block], key.length + block.length);\n}\nkey = key.slice(0, 32);\n\n// Decrypt\nconst decipher = crypto.createDecipheriv(\"aes-256-cbc\", key, iv);\nlet decrypted = decipher.update(ct);\ndecrypted += decipher.final(\"utf8\");\n\nconst cleanedOutput = decrypted\n  .slice(1, decrypted.lastIndexOf('\"'))\n  .replace(/\\\\\"/g, '\"');\nconst finalData = JSON.parse(cleanedOutput);\nconst active = finalData.incidents.active;\n\n// Step 3: Use static data to track seen incident IDs\nconst workflowStaticData = $getWorkflowStaticData('global');\n\nif (!workflowStaticData.hasOwnProperty('seenIncidentIDs')) {\n  workflowStaticData.seenIncidentIDs = [];\n}\n\nconst seenIDs = workflowStaticData.seenIncidentIDs;\nconst newIncidents = active.filter(i => !seenIDs.includes(i.ID));\nconst newIDs = newIncidents.map(i => i.ID);\n\n// Update static data and prune to last 100 entries\nworkflowStaticData.seenIncidentIDs = [...new Set([...seenIDs, ...newIDs])].slice(-100);\n\n// Incident call type mapping\nconst incidentCodeMapping = {\n  ME: \"Medical Emergency\",\n  IFT: \"Interfacility Transfer\",\n  CA: \"Canceled Assignment\",\n  ER: \"Emergency Response\",\n  CMA: \"Community Medical Assistance\",\n  VEG: \"Vegetation Fire\",\n  TC: \"Traffic Collision\",\n  OI: \"Other Incidents\",\n  AA: \"Auto Aid\",\n  MU: \"Mutual Aid\",\n  ST: \"Strike Team\",\n  AE: \"Aircraft Emergency\",\n  AES: \"Aircraft Emergency Standby\",\n  AC: \"Aircraft Crash\",\n  LZ: \"Landing Zone\",\n  FULL: \"Full Assignment\",\n  AF: \"Appliance Fire\",\n  CHIM: \"Chimney Fire\",\n  CB: \"Controlled Burn / Prescribed Fire\",\n  ELF: \"Electrical Fire\",\n  FIRE: \"Fire\",\n  GAS: \"Gas Main\",\n  HC: \"Hazardous Condition\",\n  MCI: \"Multi Casualty Incident\",\n  FLW: \"Flood Warning\",\n  TOW: \"Tornado Warning\",\n  TSW: \"Tsunami Warning\",\n  EQ: \"Earthquake\",\n  RL: \"Residential Lockout\",\n  VL: \"Vehicle Lockout\",\n  CL: \"Commercial Lockout\",\n  SF: \"Structural Fire\",\n  OD: \"Overdose\",\n  HM: \"Hazardous Materials Incident\",\n  MA: \"Medical Alert\",\n  ES: \"Emergency Services\",\n  AR: \"Animal Rescue\",\n  ELR: \"Elevator Rescue\",\n  USAR: \"Urban Search and Rescue\",\n  VS: \"Vessel Sinking\",\n  TCE: \"Expanded Traffic Collision\",\n  TCT: \"Traffic Collision Involving Train\",\n  RTE: \"Railroad/Train Emergency\",\n  IF: \"Illegal Fire\",\n  MF: \"Marine Fire\",\n  OF: \"Outside Fire\",\n  PF: \"Pole Fire\",\n  GF: \"Garbage Fire\",\n};\n\n// Unit dispatch status mapping (example)\nconst statusMapping = {\n  ER: \"En Route\",\n  OS: \"On Scene\",\n  AR: \"At Hospital / Released\",\n  TR: \"Transporting\",\n  TA: \"Taking Assignment\",\n  CA: \"Canceled\",\n  CL: \"Cleared\",\n};\n\nreturn newIncidents.map((incident) => {\n  const callTypeCode = incident.PulsePointIncidentCallType;\n  const callType = incidentCodeMapping[callTypeCode] || callTypeCode;\n\n  const units = incident.Unit.map((unit) => ({\n    UnitID: unit.UnitID,\n    Status: statusMapping[unit.PulsePointDispatchStatus] || unit.PulsePointDispatchStatus,\n    ClearedAt: unit.UnitClearedDateTime || null,\n  }));\n\n  return {\n    json: {\n      ID: incident.ID,\n      Type: callType,\n      Address: incident.FullDisplayAddress,\n      Time: incident.CallReceivedDateTime,\n      Location: {\n        Lat: incident.Latitude,\n        Lng: incident.Longitude,\n      },\n      Units: units,\n    },\n  };\n});\n\n"
      },
      "typeVersion": 2
    },
    {
      "id": "8adc042a-87a7-4586-9afc-957a7ca4b578",
      "name": "便签1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -320,
        -40
      ],
      "parameters": {
        "color": 3,
        "width": 500,
        "height": 1100,
        "content": "> 🚨 **快速预览:**"
      },
      "typeVersion": 1
    },
    {
      "id": "26a5f634-3f48-477a-b063-e03ec9eb0b0c",
      "name": "便签2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        240,
        -20
      ],
      "parameters": {
        "width": 640,
        "height": 220,
        "content": "> 🟢 **入门指南:**"
      },
      "typeVersion": 1
    }
  ],
  "pinData": {},
  "connections": {
    "If": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Send Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge all": {
      "main": [
        [
          {
            "node": "If",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get alerts": {
      "main": [
        [
          {
            "node": "Merge all",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get alerts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenAI Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "When clicking ‘Execute workflow’": {
      "main": [
        [
          {
            "node": "Get alerts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
常见问题

如何使用这个工作流?

复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。

这个工作流适合什么场景?

中级 - 个人效率, AI 摘要总结

需要付费吗?

本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。

工作流信息
难度等级
中级
节点数量11
分类2
节点类型8
难度说明

适合有一定经验的用户,包含 6-15 个节点的中等复杂度工作流

外部链接
在 n8n.io 查看

分享此工作流