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