Send mentions to Slack directly

🚧

Pre-requisites

Users should have a custom field SlackID

📘

How to apply this rule?

🚧

Warning: sample URLs!

Please make sure to replace https://hooks.slack.com/services/xxxxx/yyyyyy/zzzzzzzzztGRAKwHLfm4kw with your Slack Webhook URL and https://youraccount.tpondemand.com/ with your Targetprocess URL

[
  {
    "type": "source:targetprocess:EntityChanged",
    "entityTypes": [
      "comment"
    ],
    "modifications": {
      "created": true,
      "deleted": false,
      "updated": false
    }
  },
  {
    "or": [
      {
        "and": [
          {
            "target": {
              "type": "field",
              "name": "Description",
              "target": {
                "type": "pipelineBlockOutput"
              }
            },
            "value": {
              "type": "constant",
              "value": "@user:"
            },
            "operator": {
              "type": "contains"
            }
          }
        ]
      },
      {
        "and": [
          {
            "target": {
              "type": "field",
              "name": "Description",
              "target": {
                "type": "pipelineBlockOutput"
              }
            },
            "value": {
              "type": "constant",
              "value": "@user:"
            },
            "operator": {
              "type": "contains"
            }
          }
        ]
      }
    ],
    "type": "filter:Relational"
  },
  {
    "type": "action:JavaScript",
    "script": "//WHEN Comment with @mention is created\n//THEN Send Private Slack message to mentioned users\n\nconst generalID = args.Current.General.Id;\nconst generalName = args.Current.General.Name;\nconst authorName = args.Author.FullName;\n\nconst MARKDOWN_MARK = \"<!--markdown-->\";\n\nconst isMarkdown = desc => desc.startsWith(MARKDOWN_MARK);\nconst preprocessMarkdown = desc => desc.replace(MARKDOWN_MARK, \"\");\nconst preprocessHtml = desc => decodeHtmlText(removeHtmlTags(desc));\nconst removeHtmlTags = desc => desc.replace(/<\\/?[^>]+(>|$)/g, \"\");\nconst decodeHtmlText = desc => desc.replace(/&#(\\d+);/g, (_, code) => String.fromCharCode(code));\n\nconst rawDescription = args.Current.Description;\nconst description = isMarkdown(rawDescription) ?\n  preprocessMarkdown(rawDescription) :\n  preprocessHtml(rawDescription);\n\nfunction findLogins(desc) {\n  const result = [];\n  const regex = /@user:(.+?)[[]/g;\n\n  let match;\n  while ((match = regex.exec(desc)) !== null) {\n    const login = match[1];\n    if (!result.includes(login)) {\n      result.push(login);\n    }\n  }\n\n  return result;\n}\n\nconst logins = findLogins(description);\n\nconst api = context.getService(\"targetprocess/api/v2\");\n\nconst generalType = await api.queryAsync(\"General\", {\n  select: \"entityType.name\",\n  where: `id = ${generalID}`\n});\n\nconst quote = x => `\"${x}\"`;\nconst users = await api.queryAsync(\"Users\", {\n  select: \"{ login, slackId, name }\",\n  where: `login in [${logins.map(quote)}]`\n});\n\n// What should we do if user with target login wasn't found?\nconst getSlackIdByLogin = login => users.find(x => x.login === login).slackId;\nconst getUserNameByLogin = login => users.find(x => x.login === login).name;\nconst message = description\n  .replace(/@user:(.+?)\\[.*?\\]/g, (_, login) => {\n    const slackId = getSlackIdByLogin(login);\n    return slackId ? `<@${slackId}>` : getUserNameByLogin(login);\n  });\n\n\nconst slackIds = users.filter(function (user) {\n  return user.slackId;\n});\n\nreturn slackIds.map(x => ({\n  command: \"SendHttpRequest\",\n  payload: {\n    url: \"https://hooks.slack.com/services/xxxxx/yyyyyy/zzzzzzzzztGRAKwHLfm4kw\", //change this value\n    body: {\n      \"text\": `${authorName} mentioned you in ${generalType[0]} *#${generalID}* <https://youraccount.tpondemand.com/entity/${generalID}|${generalName}>:\\n \"${message}\"`,\n      \"channel\": x.slackId,\n      \"icon_emoji\": \":muscle:\",\n      \"username\": \"TP\"\n    }\n  }\n}));"
  }
]