summaryrefslogtreecommitdiff
path: root/app/models/integrations/chat_message/base_message.rb
blob: 501b214a7699c420f0031892a17b9e461acdb60b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# frozen_string_literal: true

module Integrations
  module ChatMessage
    class BaseMessage
      RELATIVE_LINK_REGEX = %r{!\[[^\]]*\]\((/uploads/[^\)]*)\)}.freeze

      attr_reader :markdown
      attr_reader :user_full_name
      attr_reader :user_name
      attr_reader :user_avatar
      attr_reader :project_name
      attr_reader :project_url

      def initialize(params)
        @markdown = params[:markdown] || false
        @project_name = params[:project_name] || params.dig(:project, :path_with_namespace)
        @project_url = params.dig(:project, :web_url) || params[:project_url]
        @user_full_name = params.dig(:user, :name) || params[:user_full_name]
        @user_name = params.dig(:user, :username) || params[:user_name]
        @user_avatar = params.dig(:user, :avatar_url) || params[:user_avatar]
      end

      def user_combined_name
        if user_full_name.present?
          "#{user_full_name} (#{user_name})"
        else
          user_name
        end
      end

      def summary
        return message if markdown

        format(message)
      end

      def pretext
        summary
      end

      def fallback
        format(message)
      end

      # NOTE: Make sure to call `#strip_markup` on any untrusted user input that's added to the
      # `title`, `subtitle`, `text`, `fallback`, or `author_name` fields.
      def attachments
        raise NotImplementedError
      end

      # NOTE: Make sure to call `#strip_markup` on any untrusted user input that's added to the
      # `title`, `subtitle`, `text`, `fallback`, or `author_name` fields.
      def activity
        raise NotImplementedError
      end

      private

      # NOTE: Make sure to call `#strip_markup` on any untrusted user input that's added to the string.
      def message
        raise NotImplementedError
      end

      def format(string)
        ::Slack::Messenger::Util::LinkFormatter.format(format_relative_links(string))
      end

      def format_relative_links(string)
        string.gsub(RELATIVE_LINK_REGEX, "#{project_url}\\1")
      end

      # Remove unsafe markup from user input, which can be used to hijack links in our own markup,
      # or insert new ones.
      #
      # This currently removes Markdown and Slack "mrkdwn" links (keeping the link label),
      # and all HTML markup (keeping the text nodes).
      # We can't just escape the markup characters, because each chat app handles this differently.
      #
      # See:
      # - https://api.slack.com/reference/surfaces/formatting#escaping
      # - https://gitlab.com/gitlab-org/slack-notifier#escaping
      def strip_markup(string)
        SlackMarkdownSanitizer.sanitize(string)
      end

      def attachment_color
        '#345'
      end

      def link(text, url)
        "[#{strip_markup(text)}](#{url})"
      end

      def pretty_duration(seconds)
        parse_string =
          if duration < 1.hour
            '%M:%S'
          else
            '%H:%M:%S'
          end

        Time.at(seconds).utc.strftime(parse_string)
      end
    end
  end
end