diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index 2f8dfaaf..36871fc6 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -79,6 +79,8 @@ def client def post_slack_message puzzle = Puzzle.by_date(Aoc.begin_time.change(day: params[:day])) + return if puzzle.thread_ts.nil? + username = "<#{helpers.profile_url(current_user.uid)}|#{current_user.username}>" text = "#{username} submitted a new #{solution_markdown} for part #{params[:challenge]} in :#{@snippet.language}-hd:" client.chat_postMessage(channel: ENV.fetch("SLACK_CHANNEL", "#aoc-dev"), text:, thread_ts: puzzle.thread_ts) diff --git a/app/errors/slack_error.rb b/app/errors/slack_error.rb new file mode 100644 index 00000000..859af45d --- /dev/null +++ b/app/errors/slack_error.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +class SlackError < StandardError; end diff --git a/app/jobs/generate_slack_thread.rb b/app/jobs/generate_slack_thread.rb index 48e133e3..bf859b98 100644 --- a/app/jobs/generate_slack_thread.rb +++ b/app/jobs/generate_slack_thread.rb @@ -5,43 +5,56 @@ class GenerateSlackThread < ApplicationJob queue_as :default + retry_on SlackError do |_, error| + client.chat_postMessage(channel: "#aoc-dev", text: error) + end + def perform(date) @puzzle = Puzzle.find_or_create_by(date:) + return if @puzzle.slack_url.present? - if @puzzle.title ||= title_scraped - post_message(channel: ENV.fetch("SLACK_CHANNEL", "#aoc-dev"), text: @puzzle.title) - @puzzle.slack_url = save_permalink - @puzzle.thread_ts = @message["message"]["ts"] - @puzzle.save + if @puzzle.persisted? + @puzzle.title = scraped_title || "`SPOILER` <#{@puzzle.url}|Day #{date.day}>" + @puzzle.thread_ts = message["message"]["ts"] + @puzzle.slack_url = permalink + @puzzle.save! else - post_message(channel: "#aoc-dev", text: "Title not found for day ##{@puzzle.date.day}") - @puzzle.destroy + client.chat_postMessage(channel: "#aoc-dev", text: @puzzle.errors.full_messages.join(", ")) end end private + def channel + ENV.fetch("SLACK_CHANNEL", "#aoc-dev") + end + def client @client ||= Slack::Web::Client.new end - def post_message(channel:, text:) - # https://api.slack.com/methods/chat.postMessage - @message = client.chat_postMessage(channel:, text:) + def message + @message ||= if @puzzle.thread_ts.present? + { "message" => { "ts" => @puzzle.thread_ts } } + else + response = client.chat_postMessage(channel:, text: @puzzle.title) + raise SlackError.new, "Failed to post message for day ##{@puzzle.date.day}" unless response["ok"] + + response + end end - def save_permalink - # https://api.slack.com/methods/chat.getPermalink - slack_thread = client.chat_getPermalink( - channel: @message["channel"], - message_ts: @message["message"]["ts"] - ) + def permalink + @permalink ||= begin + response = client.chat_getPermalink(channel:, message_ts: message["message"]["ts"])[:permalink] + raise SlackError.new, "Failed to get permalink for day ##{@puzzle.date.day}" unless response["ok"] - slack_thread[:permalink] || Aoc.slack_channel + response + end end - def title_scraped - @title_scraped ||= begin + def scraped_title + @scraped_title ||= begin html = URI.parse(@puzzle.url).open doc = Nokogiri::HTML(html) titles = doc.css("h2").map(&:text) @@ -49,6 +62,12 @@ def title_scraped "`SPOILER` <#{@puzzle.url}|#{raw_title.gsub('---', '').strip}>" if raw_title.present? rescue OpenURI::HTTPError + day = @puzzle.date.day + client.chat_postMessage( + channel: "#aoc-dev", + text: "Title not found for day ##{day}, run `bundle exec rake 'update_puzzle_thread[#{day},#{channel}]'`" + ) + nil end end diff --git a/app/views/days/show.html.erb b/app/views/days/show.html.erb index 4f5a83a5..4b23911c 100644 --- a/app/views/days/show.html.erb +++ b/app/views/days/show.html.erb @@ -11,7 +11,7 @@ · <%= link_to "puzzle", Aoc.url(@day), target: :_blank, rel: :noopener, class: "link-explicit link-external" %> - <% if @puzzle.present? %> + <% if @puzzle.slack_url.present? %> · <%= link_to "slack thread", @puzzle.slack_url, target: :_blank, rel: :noopener, class: "link-explicit link-slack" %> <% end %> diff --git a/lib/tasks/update_puzzle_thread.rake b/lib/tasks/update_puzzle_thread.rake new file mode 100644 index 00000000..30f83e57 --- /dev/null +++ b/lib/tasks/update_puzzle_thread.rake @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +desc "update_puzzle_thread" +task :update_puzzle_thread, %i[day channel] => :environment do |_, args| + next p "day param missing" if args[:day].nil? + next p "channel missing or incorrect" unless args[:channel].in? %w[aoc aoc-dev] + + puzzle = Puzzle.by_date(Aoc.begin_time.change(day: args[:day])) + next p "Puzzle not found" if puzzle.nil? + + html = URI.parse(@puzzle.url).open + doc = Nokogiri::HTML(html) + titles = doc.css("h2").map(&:text) + raw_title = titles.find { |title| title.match?(/--- Day \d+:/) } + next p "Title not found" if raw_title.nil? + + client = Slack::Web::Client.new + text = "`SPOILER` <#{@puzzle.url}|#{raw_title.gsub('---', '').strip}>" + response = client.chat_update(channel: args[:channel], ts: puzzle.thread_ts, text:) + next p "Failed to update message" unless response["ok"] + + puzzle.update!(title: text) +end