diff --git a/workflows/Monitor & Respond to Google Maps Reviews using AI, Apify, Slack, and Sheets-11053/monitor_respond_to_google_maps_reviews_using_ai_apify_slack_and_sheets.json b/workflows/Monitor & Respond to Google Maps Reviews using AI, Apify, Slack, and Sheets-11053/monitor_respond_to_google_maps_reviews_using_ai_apify_slack_and_sheets.json new file mode 100644 index 000000000..e5c71df7d --- /dev/null +++ b/workflows/Monitor & Respond to Google Maps Reviews using AI, Apify, Slack, and Sheets-11053/monitor_respond_to_google_maps_reviews_using_ai_apify_slack_and_sheets.json @@ -0,0 +1 @@ +{"id":"5yXquF2kybZBhbRV","meta":{"instanceId":"d66f4a8292e2201b622a3d783e2857e4d90c675c5e7bbd26eacc6af060964522"},"name":"Monitor Google Maps reviews from Apify to Slack and Google Sheets","tags":[],"nodes":[{"id":"f2753220-ab01-4a39-86c6-d9e7f4de32a0","name":"Every 24 Hours","type":"n8n-nodes-base.scheduleTrigger","position":[-496,48],"parameters":{"rule":{"interval":[{"field":"hours"}]}},"typeVersion":1.1},{"id":"55818ecf-0a34-4a9f-bc6c-c399879c5f58","name":"CONFIG (Edit Here)","type":"n8n-nodes-base.set","notes":"Settings: Enter your Store URL and Sheet ID here","position":[-272,48],"parameters":{"values":{"string":[{"name":"MAPS_URL","value":"https://www.google.com/maps/place/YOUR_STORE_ID"},{"name":"SHOP_NAME","value":"My Store Name"},{"name":"MANAGER_NAME","value":"Manager"},{"name":"SHEET_ID","value":"YOUR_GOOGLE_SHEET_ID"},{"name":"SHEET_NAME","value":"Reviews"}]},"options":{"dotNotation":true}},"notesInFlow":true,"typeVersion":2},{"id":"b40cd37c-e43c-4a81-9e2f-a9208ed41d23","name":"Get Existing IDs","type":"n8n-nodes-base.googleSheets","notes":"For duplicate check","position":[-48,144],"parameters":{"options":{},"sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"","cachedResultName":"Reviews"},"documentId":{"__rl":true,"mode":"id","value":"={{ $json.SHEET_ID }}"}},"notesInFlow":true,"typeVersion":4.1},{"id":"19b27e60-9c7f-4c27-ab7e-09945d8e5725","name":"Save to Google Sheets","type":"n8n-nodes-base.googleSheets","position":[976,48],"parameters":{"columns":{"value":{"text":"={{ $json.text }}","stars":"={{ $json.stars }}","output":"={{ $json.output }}","ai_reply":"={{ $json.ai_reply }}","reviewId":"={{ $json.reviewerId }}","reviewUrl":"={{ $json.reviewerUrl }}","ai_summary":"={{ $json.ai_summary }}","publishedAt":"={{ $json.publishAt }}","reviewerName":"={{ $json.name }}","publishedAt date":"={{ $json.publishedAtDate }}"},"schema":[{"id":"publishedAt date","type":"string","display":true,"removed":false,"required":false,"displayName":"publishedAt date","defaultMatch":false,"canBeUsedToMatch":true},{"id":"reviewId","type":"string","display":true,"required":false,"displayName":"reviewId","defaultMatch":false,"canBeUsedToMatch":true},{"id":"publishedAt","type":"string","display":true,"required":false,"displayName":"publishedAt","defaultMatch":false,"canBeUsedToMatch":true},{"id":"reviewerName","type":"string","display":true,"required":false,"displayName":"reviewerName","defaultMatch":false,"canBeUsedToMatch":true},{"id":"stars","type":"string","display":true,"required":false,"displayName":"stars","defaultMatch":false,"canBeUsedToMatch":true},{"id":"text","type":"string","display":true,"required":false,"displayName":"text","defaultMatch":false,"canBeUsedToMatch":true},{"id":"ai_summary","type":"string","display":true,"required":false,"displayName":"ai_summary","defaultMatch":false,"canBeUsedToMatch":true},{"id":"ai_reply","type":"string","display":true,"required":false,"displayName":"ai_reply","defaultMatch":false,"canBeUsedToMatch":true},{"id":"reviewUrl","type":"string","display":true,"required":false,"displayName":"reviewUrl","defaultMatch":false,"canBeUsedToMatch":true},{"id":"status","type":"string","display":true,"removed":true,"required":false,"displayName":"status","defaultMatch":false,"canBeUsedToMatch":true},{"id":"output","type":"string","display":true,"removed":false,"required":false,"displayName":"output","defaultMatch":false,"canBeUsedToMatch":true}],"mappingMode":"defineBelow","matchingColumns":[],"attemptToConvertTypes":false,"convertFieldsToString":false},"options":{},"operation":"append","sheetName":{"__rl":true,"mode":"list","value":"gid=0","cachedResultUrl":"","cachedResultName":"Reviews"},"documentId":{"__rl":true,"mode":"id","value":"={{ $('CONFIG (Edit Here)').item.json.SHEET_ID }}"}},"typeVersion":4.1},{"id":"48614959-cb01-4602-8c75-54366dfbd987","name":"If Rating < 4","type":"n8n-nodes-base.if","position":[1200,48],"parameters":{"conditions":{"number":[{"value1":"={{ $json.reviewId }}","value2":4}]}},"typeVersion":1},{"id":"f104e08a-2bd0-4445-984b-2be8a4df0ff6","name":"Slack (Alert)","type":"n8n-nodes-base.slack","position":[1424,-48],"parameters":{"text":"🚨 Negative review detected","select":"channel","channelId":{"__rl":true,"mode":"list","value":"YOUR_CHANNEL_ID","cachedResultName":"negative"},"otherOptions":{"includeLinkToWorkflow":true},"authentication":"oAuth2"},"typeVersion":2.1},{"id":"7abc738b-c7c2-405b-b2a3-e2b9e17c4a4c","name":"Run an Actor and get dataset","type":"@apify/n8n-nodes-apify.apify","position":[-48,-48],"parameters":{"actorId":{"__rl":true,"mode":"list","value":"Xb8osYTtOjlsgI6k9","cachedResultUrl":"https://console.apify.com/actors/Xb8osYTtOjlsgI6k9/input","cachedResultName":"Google Maps Reviews Scraper (compass/Google-Maps-Reviews-Scraper)"},"operation":"Run actor and get dataset","customBody":"={\n \"startUrls\": [\n {\n \"url\": \"{{ $('CONFIG (Edit Here)').item.json.MAPS_URL }}\"\n }\n ],\n \"maxReviews\": 10,\n \"language\": \"en\",\n \"reviewsSort\": \"newest\"\n}","actorSource":"store"},"typeVersion":1},{"id":"3bd96b91-da52-490c-921f-1edba358ea5c","name":"AI Agent","type":"@n8n/n8n-nodes-langchain.agent","position":[416,48],"parameters":{"text":"=Review Text: {{ $json.text }}\nStars: {{ $json.stars }}\nReviewer Name: {{ $json.reviewerName }}","options":{"systemMessage":"You are an excellent customer support manager for a physical store.\nBased on the \"Google Maps Review\" and \"Star Rating (1-5)\" provided by the user, create the following two items:\n\n### 1. Summary (For Slack Notification)\nSummarize the main point of the review in under 30 characters so the store manager can grasp it immediately via notification.\n\n### 2. Reply Draft (For Spreadsheet)\nCreate an appropriate reply message from the store.\nStrictly follow these rules:\n- 4-5 Stars: Thank them for the high rating and warmly say you look forward to their next visit.\n- 1-3 Stars: Sincerely apologize for the inconvenience, thank them for the valuable feedback, and express a polite attitude towards improvement.\n- Tone: Polite (formal) but sincere and warm.\n- No signature needed.\n\n### Output Format\nDo not include any preamble or greetings. Output only the following format:\n\n[Summary]\n(Summary text here)\n\n[Reply Draft]\n(Reply draft here)"},"promptType":"define"},"typeVersion":3},{"id":"a268a424-af7a-43c8-b646-628bfe03d152","name":"OpenRouter Chat Model","type":"@n8n/n8n-nodes-langchain.lmChatOpenRouter","position":[464,272],"parameters":{"options":{}},"typeVersion":1},{"id":"7a6610c6-cdb9-4de1-af03-dd5c08b3df18","name":"Filter Duplicates","type":"n8n-nodes-base.merge","notes":"Pass only new reviews","position":[176,48],"parameters":{"mode":"combine","options":{},"joinMode":"keepNonMatches","mergeByFields":{"values":[{"field1":"reviewerId","field2":"reviewId"}]}},"notesInFlow":true,"typeVersion":2.1},{"id":"3b954670-8d94-4db0-b637-16bf447fdb4a","name":"Slack (Alert)1","type":"n8n-nodes-base.slack","position":[1424,144],"parameters":{"text":"🚨 Positive review received!","select":"channel","channelId":{"__rl":true,"mode":"list","value":"YOUR_CHANNEL_ID","cachedResultName":"positive"},"otherOptions":{"includeLinkToWorkflow":true},"authentication":"oAuth2"},"typeVersion":2.1},{"id":"d481159e-c7fd-40f0-9695-aecdb3068015","name":"Code in JavaScript","type":"n8n-nodes-base.code","position":[752,48],"parameters":{"jsCode":"// Get all original data from the 'Filter Duplicates' node\nconst originalItems = $('Filter Duplicates').all();\n\n// Get all results from the 'AI Agent' node\nconst aiItems = $input.all();\n\n// Combine data in order and return\nreturn aiItems.map((item, index) => {\n const aiText = item.json.output || item.json.text || \"\";\n\n // Split the AI text into \"Summary\" and \"Reply Draft\" based on the English markers\n const summaryMatch = aiText.match(/\\[Summary\\]\\s*([\\s\\S]*?)\\s*(?=\\[Reply Draft\\]|$)/);\n const replyMatch = aiText.match(/\\[Reply Draft\\]\\s*([\\s\\S]*)/);\n\n const summary = summaryMatch ? summaryMatch[1].trim() : \"\";\n const reply = replyMatch ? replyMatch[1].trim() : \"\";\n\n // Retrieve original data based on index\n // Assuming AI processing order matches the original data order\n const originalData = originalItems[index] ? originalItems[index].json : {};\n\n return {\n json: {\n ...originalData, // Inherit reviewId, stars, text, etc.\n ai_summary: summary, // Split summary\n ai_reply: reply, // Split reply draft\n output: aiText // Full AI output\n }\n };\n});"},"typeVersion":2},{"id":"9a9ca427-4026-4661-916d-9e4b0aa10195","name":"Sticky Note","type":"n8n-nodes-base.stickyNote","position":[-1168,-192],"parameters":{"width":512,"height":624,"content":"## How it works\n1. **Schedule:** Runs every 24 hours (customizable) to fetch the latest data.\n2. **Scrape:** Uses **Apify** to retrieve the latest reviews from a specific Google Maps URL.\n3. **Filter:** Checks the **Google Sheet** database to identify only new reviews and avoid duplicates.\n4. **AI Analysis:** An **AI Agent** (via OpenRouter/OpenAI) analyzes the review text to:\n - Generate a short summary.\n - Draft a polite, context-aware reply based on the star rating (e.g., apologies for low stars, gratitude for high stars).\n5. **Alert:** Sends a **Slack** notification.\n - **Low Rating (<4 stars):** Alerts a specific channel (e.g., #customer-support) with a warning.\n - **High Rating:** Alerts a general channel (e.g., #wins) to celebrate.\n6. **Save:** Appends the review details, AI summary, and draft reply to the Google Sheet.\n\n## Requirements\n- **n8n:** Cloud or self-hosted (v1.0+).\n- **Apify Account:** To run the *Google Maps Reviews Scraper*.\n- **Google Cloud Platform:** Enabled Google Sheets API.\n- **Slack Workspace:** A webhook URL or OAuth connection.\n- **OpenRouter (or OpenAI) API Key:** For the LLM generation.\n\n"},"typeVersion":1},{"id":"5bebe09f-3c19-4a4c-98cc-0ba8593b72eb","name":"Sticky Note1","type":"n8n-nodes-base.stickyNote","position":[-464,-272],"parameters":{"color":7,"width":1952,"height":592,"content":"## How to set up\n1. **Google Sheets:** Create a new sheet with the following headers in the first row:\n `reviewId`, `publishedAt`, `reviewerName`, `stars`, `text`, `ai_summary`, `ai_reply`, `reviewUrl`, `output`, `publishedAt date`.\n2. **Configure Credentials:** Set up your accounts for Google Sheets, Apify, Slack, and OpenRouter within n8n.\n3. **Edit the \"CONFIG\" Node:**\n - `MAPS_URL`: Paste the full Google Maps link to your store.\n - `SHEET_ID`: Paste the ID found in your Google Sheet URL.\n - `SHOP_NAME`: Your store's name.\n4. **Slack Nodes:** Select the appropriate channels for positive and negative alerts."},"typeVersion":1},{"id":"ce00761e-fd4f-4cb4-8743-c6d8ba6c488a","name":"Sticky Note2","type":"n8n-nodes-base.stickyNote","position":[800,416],"parameters":{"color":7,"width":640,"height":208,"content":"## How to customize\n- **Change the AI Persona:** Open the **AI Agent** node and modify the \"System Message\" to match your brand's tone of voice (e.g., casual, formal, or witty).\n- **Adjust Alert Thresholds:** Edit the **If Rating < 4** node to change the criteria for what constitutes a \"negative\" review (e.g., strictly < 3 stars).\n- **Multi-Store Support:** You can loop this workflow over a list of URLs to manage multiple locations in a single execution."},"typeVersion":1}],"active":false,"pinData":{},"settings":{"executionOrder":"v1"},"versionId":"ec0852e5-41e9-4ed5-995e-cda9b88ad7cc","connections":{"AI Agent":{"main":[[{"node":"Code in JavaScript","type":"main","index":0}]]},"If Rating < 4":{"main":[[{"node":"Slack (Alert)","type":"main","index":0}],[{"node":"Slack (Alert)1","type":"main","index":0}]]},"Every 24 Hours":{"main":[[{"node":"CONFIG (Edit Here)","type":"main","index":0}]]},"Get Existing IDs":{"main":[[{"node":"Filter Duplicates","type":"main","index":1}]]},"Filter Duplicates":{"main":[[{"node":"AI Agent","type":"main","index":0}]]},"CONFIG (Edit Here)":{"main":[[{"node":"Get Existing IDs","type":"main","index":0},{"node":"Run an Actor and get dataset","type":"main","index":0}]]},"Code in JavaScript":{"main":[[{"node":"Save to Google Sheets","type":"main","index":0}]]},"OpenRouter Chat Model":{"ai_languageModel":[[{"node":"AI Agent","type":"ai_languageModel","index":0}]]},"Save to Google Sheets":{"main":[[{"node":"If Rating < 4","type":"main","index":0}]]},"Run an Actor and get dataset":{"main":[[{"node":"Filter Duplicates","type":"main","index":0}]]}}} \ No newline at end of file