자동화된 멀티플랫폼 게임 할인 추적
중급
이것은Personal Productivity분야의자동화 워크플로우로, 11개의 노드를 포함합니다.주로 If, Cron, Gmail, Sqlite, Function 등의 노드를 사용하며. Deku Deals 및 Gmail 알림을 사용한 멀티 플랫폼 게임 할인 자동 추적
사전 요구사항
- •Google 계정 및 Gmail API 인증 정보
- •대상 API의 인증 정보가 필요할 수 있음
카테고리
워크플로우 미리보기
노드 연결 관계를 시각적으로 표시하며, 확대/축소 및 이동을 지원합니다
워크플로우 내보내기
다음 JSON 구성을 복사하여 n8n에 가져오면 이 워크플로우를 사용할 수 있습니다
{
"nodes": [
{
"name": "매일 확인 (오전 8시)",
"type": "n8n-nodes-base.cron",
"notes": {
"text": "### 1. Daily Check (8 AM)\n\nThis `Cron` node triggers the workflow automatically every **day at 8:00 AM** (based on your n8n server's local time zone). This allows for frequent checks for new deals.\n\n**To change the schedule:** Adjust the 'Hour' and 'Minute' fields to your preferred time and frequency.",
"position": "right"
},
"position": [
240,
300
],
"parameters": {
"mode": "everyDay",
"value": {
"hour": [
8
],
"minute": [
0
]
},
"options": {}
},
"typeVersion": 1,
"id": "-8--0"
},
{
"name": "Deku Deals 페이지 가져오기",
"type": "n8n-nodes-base.httpRequest",
"notes": {
"text": "### 2. Fetch Deku Deals Page\n\nThis `HTTP Request` node downloads the HTML content of Deku Deals' 'Most Popular' page.\n\n**Setup:**\n1. **URL:** The URL is pre-filled for Deku Deals' 'Most Popular' deals. You can change this to `/deals` or another specific section if you prefer.\n2. **Response Format:** Ensure this is set to `string` (for HTML content).\n\n**Considerations:**\n* Deku Deals is generally well-structured for scraping. If their layout changes, the `HTML Extract` node will need updates.",
"position": "right"
},
"position": [
480,
300
],
"parameters": {
"url": "https://www.dekudeals.com/most-popular",
"options": {},
"responseFormat": "string"
},
"typeVersion": 3,
"id": "Deku-Deals--1"
},
{
"name": "각 할인 카드 추출",
"type": "n8n-nodes-base.htmlExtract",
"notes": {
"text": "### 3. Extract Each Deal Card\n\nThis `HTML Extract` node first extracts each individual 'game card' HTML block. This makes it easier to process each deal independently in the next step.\n\n**Setup:**\n1. **HTML:** This field is already set to `{{ $node[\"Fetch Deku Deals Page\"].json.data }}`.\n2. **Selector:** `div.game-card` (This targets the main container for each deal).\n3. **Attribute:** `html` (to get the full HTML content of each card).\n\n**If the layout changes:** You might need to update `div.game-card` to the new wrapper for individual game deals.",
"position": "right"
},
"position": [
720,
300
],
"parameters": {
"html": "={{ $node[\"Fetch Deku Deals Page\"].json.data }}",
"extractOperations": [
{
"options": {},
"selector": "div.game-card",
"attribute": "html",
"operation": "extract",
"propertyName": "dealHtml"
}
]
},
"typeVersion": 1,
"id": "--2"
},
{
"name": "할인 상세 정보 파싱 (함수 코드)",
"type": "n8n-nodes-base.function",
"notes": {
"text": "### 4. Parse Deal Details (Function Code)\n\nThis `Function` node dives into the HTML of each deal card to extract the specific details (title, price, link, platform, etc.). It also creates a `dealUniqueId` for tracking.\n\n**Setup:**\n* This node uses JavaScript with `jsdom` (an n8n built-in library for parsing HTML) to select individual elements within each `dealHtml` item.\n* **Crucially, the selectors here (`div.name > a`, `div.price.current`, etc.) are also subject to website changes.** You might need to update them if the layout of `div.game-card` itself changes.\n\n**Output:** Each item will now have fields like `dealUniqueId`, `gameTitle`, `gameLink`, `platforms`, `currentPrice`, `originalPrice`, `discount`.",
"position": "right"
},
"position": [
960,
300
],
"parameters": {
"options": {},
"function": "const { JSDOM } = require('jsdom');\n\nconst deals = [];\n\nfor (const item of items) {\n const dom = new JSDOM(item.json.dealHtml);\n const doc = dom.window.document;\n\n const titleElement = doc.querySelector('div.name > a');\n const linkElement = doc.querySelector('div.name > a');\n const currentPriceElement = doc.querySelector('div.price-wrapper > div.price.current');\n const originalPriceElement = doc.querySelector('div.price-wrapper > div.price.original');\n const discountElement = doc.querySelector('div.price-wrapper > div.price.discount');\n const platformElements = doc.querySelectorAll('div.platforms > span.platform');\n\n const gameTitle = titleElement ? titleElement.textContent.trim() : 'N/A';\n const gameLink = linkElement ? `https://www.dekudeals.com${linkElement.getAttribute('href')}` : 'N/A';\n const currentPrice = currentPriceElement ? currentPriceElement.textContent.trim() : 'N/A';\n const originalPrice = originalPriceElement ? originalPriceElement.textContent.trim() : 'N/A';\n const discount = discountElement ? discountElement.textContent.trim() : 'N/A';\n const platforms = Array.from(platformElements).map(p => p.textContent.trim()).join(', ');\n\n // Create a unique ID for the deal for tracking in SQLite\n // Combination of title, platform, and current price should be fairly unique\n const dealUniqueId = `${gameTitle}|${platforms}|${currentPrice}`;\n\n deals.push({\n json: {\n dealUniqueId: dealUniqueId,\n gameTitle: gameTitle,\n gameLink: gameLink,\n platforms: platforms,\n currentPrice: currentPrice,\n originalPrice: originalPrice,\n discount: discount\n }\n });\n}\n\nreturn deals;"
},
"typeVersion": 1,
"id": "--3"
},
{
"name": "SQLite: 테이블 존재 확인",
"type": "n8n-nodes-base.sqlite",
"notes": {
"text": "### 5. SQLite: Ensure Table Exists\n\nThis `SQLite` node ensures that a local database table named `notified_deals` exists. This table will store the unique IDs of deals you've already been notified about.\n\n**Setup:**\n* **Database:** `dekudeals` (this creates a file `dekudeals.db` in your n8n data directory).\n* **Query:** `CREATE TABLE IF NOT EXISTS notified_deals (...)` as shown.\n\n**No further action needed**; this node will run automatically. On subsequent runs, it will simply confirm the table exists.",
"position": "right"
},
"position": [
1200,
220
],
"parameters": {
"query": "CREATE TABLE IF NOT EXISTS notified_deals (deal_id TEXT PRIMARY KEY, game_title TEXT, platforms TEXT, current_price TEXT, original_price TEXT, discount TEXT, deal_link TEXT, notified_date TEXT)",
"database": "dekudeals"
},
"typeVersion": 1,
"id": "SQLite--4"
},
{
"name": "SQLite: 알림 여부 확인",
"type": "n8n-nodes-base.sqlite",
"notes": {
"text": "### 6. SQLite: Check if Deal Already Notified\n\nThis `SQLite` node checks if each extracted deal (using its `dealUniqueId`) is already present in your `notified_deals` database.\n\n**Setup:**\n* **Database:** `dekudeals`\n* **Query:** `SELECT deal_id FROM notified_deals WHERE deal_id = '{{ $json.dealUniqueId }}'` (It looks for a match for the current deal's unique ID).\n\n**Output:** If the deal is found, this node will output an item. If not, it will output no item, which is crucial for the 'Item Lists' node below.",
"position": "right"
},
"position": [
1440,
300
],
"parameters": {
"query": "SELECT deal_id FROM notified_deals WHERE deal_id = '{{ $json.dealUniqueId }}'",
"database": "dekudeals"
},
"typeVersion": 1,
"id": "SQLite--5"
},
{
"name": "알림됨/신규 분할",
"type": "n8n-nodes-base.itemLists",
"notes": {
"text": "### 7. Split into Notified/New\n\nThis `Item Lists` node takes the results from the 'SQLite: Check if Notified' node and splits them into two paths:\n* **Original items without a matching ID in the database:** These are **NEW** deals (`Output 1`).\n* **Original items with a matching ID in the database:** These are **ALREADY NOTIFIED** deals (`Output 2`).\n\n**No configuration needed**; it automatically separates the items based on whether the `SQLite` query found a match or not.",
"position": "right"
},
"position": [
1680,
300
],
"parameters": {
"mode": "splitInBatches",
"property": "dealUniqueId"
},
"typeVersion": 1,
"id": "--6"
},
{
"name": "If (신규 할인 발견됨)",
"type": "n8n-nodes-base.if",
"notes": {
"text": "### 8. If (New Deals Found)\n\nThis `If` node checks if there are any *new* deals (i.e., items coming from the 'New' path of the 'Split into Notified/New' node).\n\n* **'True' branch:** If new deals are found, the workflow proceeds to insert them into the database and send a notification.\n* **'False' branch:** If no new deals are found, the workflow ends here (no notification needed).\n\n**No configuration needed**; it automatically checks if there are any items.",
"position": "right"
},
"position": [
1920,
220
],
"parameters": {
"conditions": [
{
"value1": "={{ $json.length }}",
"value2": "0",
"operation": "notEqual"
}
]
},
"typeVersion": 1,
"id": "If--7"
},
{
"name": "SQLite: 신규 할인 정보 삽입",
"type": "n8n-nodes-base.sqlite",
"notes": {
"text": "### 9. SQLite: Insert New Deals\n\nThis `SQLite` node inserts the details of the newly found deals into your `notified_deals` database. This ensures you won't be notified about them again on subsequent runs.\n\n**Setup:**\n* **Database:** `dekudeals`\n* **Query:** The `INSERT INTO` query is pre-filled, saving all the extracted deal details.\n\n**No further action needed**; it automatically stores the new deal information.",
"position": "right"
},
"position": [
2160,
140
],
"parameters": {
"query": "INSERT INTO notified_deals (deal_id, game_title, platforms, current_price, original_price, discount, deal_link, notified_date) VALUES ('{{ $json.dealUniqueId }}', '{{ $json.gameTitle }}', '{{ $json.platforms }}', '{{ $json.currentPrice }}', '{{ $json.originalPrice }}', '{{ $json.discount }}', '{{ $json.gameLink }}', '{{ new Date().toISOString() }}')",
"database": "dekudeals"
},
"typeVersion": 1,
"id": "SQLite--8"
},
{
"name": "알림 메시지 포맷팅",
"type": "n8n-nodes-base.function",
"notes": {
"text": "### 10. Format Notification Message\n\nThis `Function` node takes the new deal details and formats them into a clear, readable message for your notification (e.g., email or Telegram).\n\n**Customization:**\n* You can change the introductory text, add more emojis, or adjust the display format of each deal. It dynamically adds 'Was' and 'Discount' info only if available.\n\n**No configuration needed if your property names match the previous node's output.**",
"position": "right"
},
"position": [
2400,
140
],
"parameters": {
"options": {},
"function": "let message = \"🎮 **New Game Deals on Deku Deals!** 🎮\\n\\n\";\n\nfor (const item of items) {\n message += `**${item.json.gameTitle}** (${item.json.platforms})\\n` +\n ` Current Price: **${item.json.currentPrice}**` +\n (item.json.originalPrice !== 'N/A' ? ` (Was: ${item.json.originalPrice})` : '') +\n (item.json.discount !== 'N/A' ? ` | Discount: ${item.json.discount}` : '') +\n `\\n Claim/View Deal: ${item.json.gameLink}\\n\\n`;\n}\n\nmessage += \"Check out all deals: https://www.dekudeals.com/most-popular\";\n\nreturn [{ json: { notificationMessage: message } }];"
},
"typeVersion": 1,
"id": "--9"
},
{
"name": "이메일 알림 전송",
"type": "n8n-nodes-base.gmail",
"notes": {
"text": "### 11. Send Email Notification\n\nThis `Gmail` node sends the formatted notification message about the new game deals.\n\n**Setup:**\n1. **Gmail Credential:** Select your Gmail API credential.\n2. **From Email:** Enter your Gmail address (must match the authenticated account).\n3. **To Email:** **IMPORTANT: Change `YOUR_RECIPIENT_EMAIL@example.com` to your actual email address!**\n4. **Subject & Text:** These fields pull the formatted message from the previous node.\n\n**To switch to Telegram/Slack/Discord:** Simply replace this node with your preferred notification service's node, and map the `notificationMessage` to its text field. You'll need to set up credentials for that service.",
"position": "right"
},
"position": [
2640,
140
],
"parameters": {
"text": "={{ $json.notificationMessage }}",
"options": {},
"subject": "🎮 New Game Deals Alert! (Deku Deals)",
"toEmail": "YOUR_RECIPIENT_EMAIL@example.com",
"fromEmail": "YOUR_GMAIL_EMAIL@gmail.com"
},
"credentials": {
"gmailApi": {
"id": "YOUR_GMAIL_CREDENTIAL_ID",
"resolve": false
}
},
"typeVersion": 2,
"id": "--10"
}
],
"pinData": {},
"version": 1,
"connections": {
"-8--0": {
"main": [
[
{
"node": "Deku-Deals--1",
"type": "main"
}
]
]
},
"If--7": {
"main": [
[
{
"node": "SQLite--8",
"type": "main"
}
],
[]
]
},
"Deku-Deals--1": {
"main": [
[
{
"node": "--2",
"type": "main"
}
]
]
},
"--2": {
"main": [
[
{
"node": "--3",
"type": "main"
}
]
]
},
"--6": {
"main": [
[
{
"node": "If--7",
"type": "main"
}
],
[]
]
},
"SQLite--8": {
"main": [
[
{
"node": "--9",
"type": "main"
}
]
]
},
"SQLite--5": {
"main": [
[
{
"node": "--6",
"type": "main"
}
]
],
"output": [
{
"type": "item",
"toIndex": 0,
"fromIndex": 0,
"destination": [
{
"node": "알림됨/신규 분할",
"input": "input2"
}
]
}
]
},
"--9": {
"main": [
[
{
"node": "--10",
"type": "main"
}
]
]
},
"SQLite--4": {
"main": [
[
{
"node": "SQLite--5",
"type": "main"
}
]
]
},
"--3": {
"main": [
[
{
"node": "SQLite--5",
"type": "main"
}
]
]
}
}
}자주 묻는 질문
이 워크플로우를 어떻게 사용하나요?
위의 JSON 구성 코드를 복사하여 n8n 인스턴스에서 새 워크플로우를 생성하고 "JSON에서 가져오기"를 선택한 후, 구성을 붙여넣고 필요에 따라 인증 설정을 수정하세요.
이 워크플로우는 어떤 시나리오에 적합한가요?
중급 - 개인 생산성
유료인가요?
이 워크플로우는 완전히 무료이며 직접 가져와 사용할 수 있습니다. 다만, 워크플로우에서 사용하는 타사 서비스(예: OpenAI API)는 사용자 직접 비용을 지불해야 할 수 있습니다.
관련 워크플로우 추천
OpenAI와 Gmail을 사용하는 매일 긍정적인 뉴스 요약
OpenAI와 Gmail을 사용한 매일 긍정적인 뉴스 요약
If
Cron
Gmail
+
If
Cron
Gmail
9 노드Piotr Sobolewski
개인 생산성
자동 웹 크롤러: 세분화된 구인/제품 모니터링 및 Telegram 알림
자동화网页爬虫:细分职位/제품모니터링与Telegram警报
If
Cron
Function
+
If
Cron
Function
6 노드Piotr Sobolewski
시장 조사
기업 온라인 이미지 모니터링기
AI 감정 분석 및 다중 플랫폼 추적을 사용한 일일 기업 온라인 이미지 모니터링
Set
Cron
Gmail
+
Set
Cron
Gmail
17 노드Piotr Sobolewski
시장 조사
Gmail과 GPT 요약을 사용한 일일 이메일 요약 자동 발송
Gmail 및 GPT 요약을 사용한 일일 이메일 다이제스트 자동 전송, 매일 오후 발송
Cron
Gmail
Open Ai
+
Cron
Gmail
Open Ai
6 노드Piotr Sobolewski
개인 생산성
작업일지 기록
AI 작업시간표 생성기 - Gmail, 캘린더, GitHub을 Google 스프레드시트에 통합
If
Set
Code
+
If
Set
Code
31 노드Luka Zivkovic
개인 생산성
Excel에서 직업 지원 및 지원 상태 추적
LinkedIn, Indeed, Google Sheets 기반 자동 채용 지원 및 상태 추적
If
Set
Cron
+
If
Set
Cron
24 노드VipinW
개인 생산성
워크플로우 정보
난이도
중급
노드 수11
카테고리1
노드 유형8
저자
Piotr Sobolewski
@piotrsobolewskiAI PhD with 7 years experience as a game dev CEO, currently teaching, helping others and building something new.
외부 링크
n8n.io에서 보기 →
이 워크플로우 공유