class Webhook < ApplicationRecord include Triggerable SLACK_WEBHOOK_URL_REGEX = %r{//hooks\.slack\.com/services/T[^\/]+/B[^\/]+/[^\/]+\Z}i CAMPFIRE_WEBHOOK_URL_REGEX = %r{/rooms/\d+/\d+-[^\/]+/messages\Z}i BASECAMP_CAMPFIRE_WEBHOOK_URL_REGEX = %r{/\d+/integrations/[^\/]+/buckets/\d+/chats/\d+/lines\Z}i PERMITTED_SCHEMES = %w[ http https ].freeze PERMITTED_ACTIONS = %w[ card_assigned card_closed card_collection_changed card_due_date_added card_due_date_changed card_due_date_removed card_published card_reopened card_staged card_title_changed card_unassigned card_unstaged comment_created ].freeze has_secure_token :signing_secret has_many :deliveries, dependent: :delete_all has_one :delinquency_tracker, dependent: :delete belongs_to :collection serialize :subscribed_actions, type: Array, coder: JSON scope :ordered, -> { order(name: :asc, id: :desc) } scope :active, -> { where(active: true) } after_create :create_delinquency_tracker! normalizes :subscribed_actions, with: ->(value) { Array.wrap(value).map(&:to_s).uniq & PERMITTED_ACTIONS } validates :name, presence: true validate :validate_url def activate update_columns active: true end def deactivate update_columns active: false end def renderer @renderer ||= ApplicationController.renderer.new end def for_basecamp? url.match? BASECAMP_CAMPFIRE_WEBHOOK_URL_REGEX end def for_campfire? url.match? CAMPFIRE_WEBHOOK_URL_REGEX end def for_slack? url.match? SLACK_WEBHOOK_URL_REGEX end private def validate_url uri = URI.parse(url.presence) if PERMITTED_SCHEMES.exclude?(uri.scheme) errors.add :url, "must use #{PERMITTED_SCHEMES.to_choice_sentence}" end rescue URI::InvalidURIError errors.add :url, "not a URL" end end