{
  "name": "A: GitLab MR \uc54c\ub9bc",
  "nodes": [
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "norm-1",
              "name": "event_type",
              "value": "={{ $json.body ? 'mr_open' : 'note_cmd' }}",
              "type": "string"
            },
            {
              "id": "norm-2",
              "name": "=mr_title",
              "value": "={{ $json.body ? $json.body.object_attributes.title : $json.title }}",
              "type": "string"
            },
            {
              "id": "norm-3",
              "name": "mr_url",
              "value": "={{ $json.body ? $json.body.object_attributes.url : $('User Mapping').item.json.body.object_attributes.url }}",
              "type": "string"
            },
            {
              "id": "norm-4",
              "name": "sender_username",
              "value": "={{ $json.body ? $json.body.user.username : $('User Mapping').item.json.body.user.username }}",
              "type": "string"
            },
            {
              "id": "norm-5",
              "name": "author_username",
              "value": "={{ $json.body ? $json.body.user.username : $json.author.username }}",
              "type": "string"
            },
            {
              "id": "norm-6",
              "name": "reviewers",
              "value": "={{ $json.body ? $json.body.reviewers : $json.reviewers }}",
              "type": "array"
            },
            {
              "id": "norm-7",
              "name": "assignees",
              "value": "={{ $json.body ? $json.body.assignees : $json.assignees }}",
              "type": "array"
            },
            {
              "id": "norm-8",
              "name": "note",
              "value": "={{ $json.body ? '' : ($('User Mapping').item.json.body.object_attributes.note || '') }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2336,
        784
      ],
      "id": "984bb6f7-a8a1-4665-bb77-453e489d4b02",
      "name": "Set: Normalize"
    },
    {
      "parameters": {
        "jsCode": "const data = $input.item.json;\nconst userMap = $('User Mapping').item.json.user_map || {};\nconst config = $('Config').item.json;\n\nfunction toMM(u) { return userMap[u] || u; }\n\nconst sender = toMM(data.sender_username || '');\nconst author = toMM(data.author_username || '');\nconst targets = (data.reviewers?.length > 0 ? data.reviewers : data.assignees) || [];\nconst targetStr = targets.length > 0\n  ? targets.map(r => `@${toMM(r.username)}`).join(' ')\n  : '(\ub9ac\ubdf0\uc5b4 \ubbf8\uc9c0\uc815)';\n\nconst c1 = config.cmd_review_done || '/\ub9ac\ubdf0\uc644\ub8cc';\nconst c2 = config.cmd_review_again || '/\ub9ac\ubdf0\ub2e4\uc2dc';\nconst deadline = config.review_deadline || '24\uc2dc\uac04 \uc774\ub0b4';\nconst note = (data.note || '').trim();\n\nlet message = '';\n\nif (data.event_type === 'mr_open') {\n  message = [\n    '----',\n    `##### :gitlab: @${sender}\ub2d8\uc774 \uc0c8\ub85c\uc6b4 MR\uc744 \ub4f1\ub85d\ud588\uc2b5\ub2c8\ub2e4!`,\n    '',\n    `- **\ub300\uc0c1 MR:** [${data.mr_title}](${data.mr_url})\\n`,\n    `- **\ub300\ud45c \ub9ac\ubdf0\uc5b4:** ${targetStr}\\n`,\n    '----\\n',\n    `:gunpang_timer: **${deadline}** \ud655\uc778\uc744 \ubd80\ud0c1\ub4dc\ub9bd\ub2c8\ub2e4.\\n`,\n    ':gunpang_paper_log: **MR \ud398\uc774\uc9c0 \ub313\uae00 \uba85\ub839\uc5b4 \uc548\ub0b4**',\n    `- **\\`${c1}\\`** : (\ub9ac\ubdf0\uc5b4\uc6a9) \ub9ac\ubdf0 \uc644\ub8cc \ud6c4, MR \uc791\uc131\uc790\uc5d0\uac8c \ud655\uc778\uc744 \uc694\uccad\ud569\ub2c8\ub2e4.`,\n    `- **\\`${c2}\\`** : (\uc791\uc131\uc790\uc6a9) \ub9ac\ubdf0 \ubc18\uc601 \uc644\ub8cc \ud6c4, \ub9ac\ubdf0\uc5b4\uc5d0\uac8c \ucd94\uac00 \uc9c8\ubb38/\uc7ac\uac80\ud1a0\ub97c \uc694\uccad\ud569\ub2c8\ub2e4.\\n`,\n    '----'\n  ].join('\\n');\n} else if (note.startsWith(c1)) {\n  message = [\n    '----',\n    `##### :gitlab: @${sender}\ub2d8\uc774 \ub9ac\ubdf0\ub97c \uc644\ub8cc\ud588\uc2b5\ub2c8\ub2e4! :comp:`,\n    `- **\ub300\uc0c1 MR:** [${data.mr_title}](${data.mr_url})\\n`,\n    `- **\uc694\uccad \ub300\uc0c1:** @${author}\\n`,\n    '----\\n',\n    `:gunpang_timer: **${deadline}** \ud655\uc778\uc744 \ubd80\ud0c1\ub4dc\ub9bd\ub2c8\ub2e4.\\n`,\n    '----'\n  ].join('\\n');\n} else if (note.startsWith(c2)) {\n  const afterCmd = note.slice(c2.length).trim();\n  const mentionMatch = afterCmd.match(/^@(\\S+)/);\n  const finalTargetStr = mentionMatch ? `@${toMM(mentionMatch[1])}` : targetStr;\n  message = [\n    '----',\n    `##### :gitlab: @${sender}\ub2d8\uc774 \ucd94\uac00 \ud655\uc778\uc744 \uc694\uccad\ud588\uc2b5\ub2c8\ub2e4! :please_call:`,\n    '',\n    `- **\ub300\uc0c1 MR:** [${data.mr_title}](${data.mr_url})\\n`,\n    `- **\uc694\uccad \ub300\uc0c1:** ${finalTargetStr}\\n`,\n    '----\\n',\n    `:gunpang_timer: **${deadline}** \ud655\uc778\uc744 \ubd80\ud0c1\ub4dc\ub9bd\ub2c8\ub2e4.\\n`,\n    '----'\n  ].join('\\n');\n} else {\n  return [];\n}\n\nreturn [{ json: { message } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2560,
        784
      ],
      "id": "f2568e5a-11a9-470a-8e1b-d54872d4e8b4",
      "name": "Build Message"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "gitlab-mr-notification",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        976,
        784
      ],
      "id": "267f4baf-930c-4428-9e38-4ffc957f053d",
      "name": "GitLab Webhook"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "cfg-a-1",
              "name": "mm_webhook_url",
              "value": "https://meeting.ssafy.com/hooks/YOUR_MATTERMOST_WEBHOOK_TOKEN",
              "type": "string"
            },
            {
              "id": "cfg-a-3",
              "name": "gitlab_base_url",
              "value": "https://lab.ssafy.com",
              "type": "string"
            },
            {
              "id": "cfg-a-5",
              "name": "review_deadline",
              "value": "24\uc2dc\uac04 \uc774\ub0b4",
              "type": "string"
            },
            {
              "id": "cfg-a-6",
              "name": "cmd_review_done",
              "value": "/\ub9ac\ubdf0\uc644\ub8cc",
              "type": "string"
            },
            {
              "id": "cfg-a-7",
              "name": "cmd_review_again",
              "value": "/\ub9ac\ubdf0\ub2e4\uc2dc",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1200,
        784
      ],
      "id": "dacb2e05-1731-47c0-ac4f-726fae3c3362",
      "name": "Config"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "um-map",
              "name": "user_map",
              "value": "={{ ({\n  'gitlab_username1': 'mm_username1',\n  'gitlab_username2': 'mm_username2'\n}) }}",
              "type": "object"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1440,
        784
      ],
      "id": "676a22a1-c869-4455-8d13-f862862dd1c9",
      "name": "User Mapping"
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "sw-mr",
                    "leftValue": "={{ $json.body.event_type }}",
                    "rightValue": "merge_request",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "MR"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "sw-note",
                    "leftValue": "={{ $json.body.event_type }}",
                    "rightValue": "note",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "note"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        1664,
        784
      ],
      "id": "30641cd3-2f7a-415a-8fd9-a7031612e337",
      "name": "Switch: Event Type"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "701a1525-1ff2-4054-b580-3cc74fcec3fb",
              "leftValue": "={{ $json.body.object_attributes.action }}",
              "rightValue": "=^(open|reopen)$",
              "operator": {
                "type": "string",
                "operation": "regex"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.3,
      "position": [
        1888,
        688
      ],
      "id": "49b7c0f9-f97d-46ba-9501-d27a2bf8c05c",
      "name": "Filter: MR Open"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "701a1525-1ff2-4054-b580-3cc74fcec3fb",
              "leftValue": "={{ $json.body.object_attributes.noteable_type }}",
              "rightValue": "MergeRequest",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            },
            {
              "id": "8e4e3836-cbf6-4630-8c85-5d53764c3f9d",
              "leftValue": "={{ $json.body.object_attributes.note.trim() || '' }}",
              "rightValue": "=^({{ $('Config').item.json.cmd_review_done }}|{{ $('Config').item.json.cmd_review_again }})",
              "operator": {
                "type": "string",
                "operation": "regex"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.3,
      "position": [
        1888,
        880
      ],
      "id": "f66aebe8-372b-4acc-8ca1-26d44832a101",
      "name": "Filter: Command Note"
    },
    {
      "parameters": {
        "url": "={{ $('Config').item.json.gitlab_base_url }}/api/v4/projects/{{ $json.body.project.id }}/merge_requests/{{ $json.body.merge_request.iid }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "gitlabApi",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2112,
        880
      ],
      "id": "17c58284-2198-4a3c-a885-e3d2949b9076",
      "name": "GitLab: Get MR Details",
      "credentials": {
        "gitlabApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $('Config').item.json.mm_webhook_url }}",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "=text",
              "value": "={{ $json.message }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2784,
        784
      ],
      "id": "0e4a574e-8f77-4bd6-ab4b-6c119ad8bac8",
      "name": "MM: Send Notification"
    },
    {
      "parameters": {
        "content": "## \ud83d\udccb A: GitLab MR \uc54c\ub9bc \u2014 \uc124\uc815 \uac00\uc774\ub4dc\n\n### 1\ufe0f\u20e3 Config \ub178\ub4dc \uc124\uc815\n- `mm_webhook_url` : Mattermost \ucc44\ub110\uc758 **Incoming Webhook URL**\n  - Mattermost \u2192 \ucc44\ub110 \uba54\ub274 \u2192 Integrations \u2192 Incoming Webhooks\uc5d0\uc11c \uc0dd\uc131\n- `gitlab_base_url` : GitLab \uc11c\ubc84 \uc8fc\uc18c (\uae30\ubcf8\uac12 `https://lab.ssafy.com` \uadf8\ub300\ub85c \uc0ac\uc6a9 \uac00\ub2a5)\n- `review_deadline` : \ub9ac\ubdf0 \uc694\uccad \ub9c8\uac10 \uc548\ub0b4 \ubb38\uad6c (\uc790\uc720\ub86d\uac8c \ubcc0\uacbd \uac00\ub2a5)\n- `cmd_review_done` / `cmd_review_again` : MR \ub313\uae00 \uba85\ub839\uc5b4 (\uae30\ubcf8\uac12 \uadf8\ub300\ub85c \uc0ac\uc6a9 \uad8c\uc7a5)\n\n### 2\ufe0f\u20e3 User Mapping \ub178\ub4dc \uc124\uc815\n- GitLab username \u2192 Mattermost username \ub9e4\ud551 \ud14c\uc774\ube14\n- \uc608\uc2dc: `{ 'gitlab_id1': 'mm_id1', 'gitlab_id2': 'mm_id2' }`\n- \ud300\uc6d0 \ubaa8\ub450 \ucd94\uac00\ud574\uc57c \uba58\uc158\uc774 \uc815\ud655\ud558\uac8c \uc791\ub3d9\ud569\ub2c8\ub2e4\n\n### 3\ufe0f\u20e3 GitLab Credential \ub4f1\ub85d\n- n8n \uc88c\uce21 \uba54\ub274 \u2192 Credentials \u2192 **GitLab API** \ucd94\uac00\n- GitLab\uc5d0\uc11c Personal Access Token \ubc1c\uae09 (`api` \uc2a4\ucf54\ud504 \ud544\uc694)\n- `GitLab: Get MR Details` \ub178\ub4dc\uc5d0\uc11c \ud574\ub2f9 Credential \uc120\ud0dd\n\n### 4\ufe0f\u20e3 GitLab Webhook \ub4f1\ub85d\n- GitLab \ud504\ub85c\uc81d\ud2b8 \u2192 Settings \u2192 Webhooks\n- URL: `https://YOUR_N8N_HOST/webhook/gitlab-mr-notification`\n- Trigger \uccb4\ud06c: **Merge request events**, **Comments**\n- Secret Token\uc740 \uc120\ud0dd\uc0ac\ud56d (\ubbf8\uc0ac\uc6a9 \uc2dc \ube44\uc6cc\ub450\uae30)",
        "height": 500,
        "width": 580,
        "color": 5
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        976,
        240
      ],
      "id": "setup-guide-a-mr-alert-001",
      "name": "Setup Guide"
    },
    {
      "parameters": {
        "content": "## \u2728 \uc774 \uc6cc\ud06c\ud50c\ub85c\uc6b0\uac00 \ud558\ub294 \uc77c\n\n**MR\uc774 \uc5f4\ub9ac\uac70\ub098 \ub2e4\uc2dc \uc5f4\ub9b4 \ub54c**\n\u2192 \uc9c0\uc815\ub41c MM \ucc44\ub110\uc5d0 \uc54c\ub9bc \uc804\uc1a1\n\u2192 \ub9ac\ubdf0\uc5b4(\ub610\ub294 \ub2f4\ub2f9\uc790) \uba58\uc158 \ud3ec\ud568\n\n**MR \ub313\uae00\uc5d0 `/\ub9ac\ubdf0\uc644\ub8cc` \uc785\ub825 \uc2dc**\n\u2192 MR \uc791\uc131\uc790\uc5d0\uac8c \"\ub9ac\ubdf0 \ub05d\ub0ac\uc73c\ub2c8 \ud655\uc778\ud574!\" MM \uc54c\ub9bc \uc804\uc1a1\n\n**MR \ub313\uae00\uc5d0 `/\ub9ac\ubdf0\ub2e4\uc2dc` \uc785\ub825 \uc2dc**\n\u2192 \uae30\ubcf8\uc801\uc73c\ub85c \ud574\ub2f9 MR\uc758 \ub9ac\ubdf0\uc5b4\uc5d0\uac8c \"\ub2e4\uc2dc \ubd10\uc918!\" MM \uc54c\ub9bc \uc804\uc1a1\n\u2192 `/\ub9ac\ubdf0\ub2e4\uc2dc @\uc544\uc774\ub514` \ub85c \uc785\ub825\ud558\uba74 \ud574\ub2f9 \uc544\uc774\ub514\uc5d0 \ub9e4\ud551\ub41c MM \uc720\uc800\ub97c \uc9c1\uc811 \ud638\ucd9c\n\n> GitLab username \u2192 MM username \ub9e4\ud551\uc740 **User Mapping** \ub178\ub4dc\uc5d0\uc11c \uc124\uc815",
        "height": 300,
        "width": 440,
        "color": 7
      },
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        976,
        -120
      ],
      "id": "feature-guide-a-mr-alert-001",
      "name": "Feature Guide"
    }
  ],
  "connections": {
    "Set: Normalize": {
      "main": [
        [
          {
            "node": "Build Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build Message": {
      "main": [
        [
          {
            "node": "MM: Send Notification",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitLab Webhook": {
      "main": [
        [
          {
            "node": "Config",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Config": {
      "main": [
        [
          {
            "node": "User Mapping",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "User Mapping": {
      "main": [
        [
          {
            "node": "Switch: Event Type",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Switch: Event Type": {
      "main": [
        [
          {
            "node": "Filter: MR Open",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Filter: Command Note",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter: MR Open": {
      "main": [
        [
          {
            "node": "Set: Normalize",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter: Command Note": {
      "main": [
        [
          {
            "node": "GitLab: Get MR Details",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "GitLab: Get MR Details": {
      "main": [
        [
          {
            "node": "Set: Normalize",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": true,
  "settings": {
    "executionOrder": "v1",
    "availableInMCP": false,
    "timeSavedMode": "fixed",
    "callerPolicy": "workflowsFromSameOwner",
    "errorWorkflow": "YOUR_ERROR_WORKFLOW_ID"
  },
  "versionId": "e5ee3408-70d9-4fbe-aa27-60934e5c2ef2",
  "id": "KggDOKrhqVE0HQQI3zjwY",
  "tags": []
}