#!/usr/bin/env ruby
# frozen_string_literal: true

# This is based on https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/bin/slack
#
# Sends Slack notification MSG to CI_SLACK_WEBHOOK_URL (which needs to be set).
# ICON_EMOJI needs to be set to an icon emoji name (without the `:` around it).
# CHANNEL is a channel name (without the `#`) or a channel ID such as
# C0123456789. An ID keeps working when the channel is renamed.
#
# Uses only the standard library, so it runs on a plain Ruby image.

require 'json'
require 'net/http'
require 'uri'

class SlackNotifier
  # Slack channel IDs are uppercase letters and digits starting with C or G.
  # Channel names are lowercase, so they never match and get the `#` prefix.
  CHANNEL_ID = /\A[CG][A-Z0-9]{8,}\z/

  def self.target(channel)
    CHANNEL_ID.match?(channel) ? channel : "##{channel}"
  end

  def initialize(args, webhook_url:, output: $stdout)
    @channel, @message, @icon_emoji, @username = args
    @webhook_url = webhook_url
    @output = output
  end

  # Returns zero even when Slack rejects the post, so a Slack problem does not
  # fail the job that reports through it.
  def execute
    if [@channel, @webhook_url, @message, @icon_emoji, @username].any? { |value| value.to_s.empty? }
      @output.puts "Missing argument(s) - Use: #{$PROGRAM_NAME} channel message icon_emoji username"
      @output.puts 'and set CI_SLACK_WEBHOOK_URL environment variable.'
      return 0
    end

    response = Net::HTTP.post_form(URI(@webhook_url), 'payload' => JSON.generate(payload))
    @output.print response.body

    0
  end

  private

  def payload
    {
      channel: self.class.target(@channel),
      username: @username,
      text: @message,
      icon_emoji: ":#{@icon_emoji}:"
    }
  end
end

exit SlackNotifier.new(ARGV, webhook_url: ENV['CI_SLACK_WEBHOOK_URL']).execute if $PROGRAM_NAME == __FILE__
