0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-08 17:53:55 +08:00
discourse/plugins/automation/lib/discourse_automation/scripts/post.rb
Régis Hanol 6a79b59ac8
FEATURE: Track automation errors and improve logging (#36938)
When automations fail silently (e.g., PM sending blocked due to
validation errors), admins have no visibility into what's going wrong.
This leaves them guessing why their automations aren't working as
expected.

This change addresses the problem in three ways:

1. Error tracking in automation stats

The Stat model now tracks `total_errors` alongside existing run metrics.
When an automation raises an exception during execution, it's caught and
counted. This gives admins visibility into which automations are
experiencing problems.

2. Centralized logging module

Created `DiscourseAutomation::Logger` to replace scattered
`Rails.logger.warn("[discourse-automation] ...")` calls throughout the
codebase. This eliminates the fragile manual prefix pattern and ensures
consistent log formatting across all automation code. All existing
logging calls have been migrated to use this module.

3. Error count display in admin UI

The automation list now shows recent error counts next to run
statistics. Clicking the error count links directly to `/logs` filtered
for discourse-automation entries, making it easy to investigate
failures. The link uses `data-auto-route="true"` to ensure proper
navigation outside Ember's client-side router.

<img width="1397" height="1041" alt="2026-01-01 @ 23 03 34"
src="https://github.com/user-attachments/assets/6a6733f1-999c-44de-ab04-78dd764a1a43"
/>

Ref - https://meta.discourse.org/t/389922
2026-01-19 18:50:13 +01:00

78 lines
2.7 KiB
Ruby
Vendored

# frozen_string_literal: true
DiscourseAutomation::Scriptable.add(DiscourseAutomation::Scripts::POST) do
version 1
placeholder :creator_username
field :creator, component: :user
field :creator, component: :user, triggerable: :user_updated, accepted_contexts: [:updated_user]
field :topic, component: :text, required: true
field :post, component: :post, required: true, accepts_placeholders: true
placeholder :creator_username
placeholder :updated_user_username, triggerable: :user_updated
placeholder :updated_user_name, triggerable: :user_updated
triggerables %i[recurring point_in_time user_updated]
script do |context, fields, automation|
creator_username = fields.dig("creator", "value")
creator_username = context["user"]&.username if creator_username == "updated_user"
creator_username ||= Discourse.system_user.username
topic_id = fields.dig("topic", "value")
post_raw = fields.dig("post", "value")
placeholders = { creator_username: creator_username }.merge(context["placeholders"] || {})
creator = User.find_by(username: creator_username)
topic = Topic.find_by(id: topic_id)
if !topic
DiscourseAutomation::Logger.warn("topic with id: `#{topic_id}` was not found")
next
end
if topic.closed? || topic.archived?
DiscourseAutomation::Logger.warn("topic with id: `#{topic_id}` is closed or archived")
next
end
if context["kind"] == DiscourseAutomation::Triggers::USER_UPDATED
user = context["user"]
user_data = context["user_data"]
user_profile_data = user_data[:profile_data] || {}
user_custom_fields = {}
user_data[:custom_fields]&.each do |k, v|
user_custom_fields[k.gsub(/\s+/, "_").underscore] = v
end
user = User.find(context["user"].id)
placeholders["username"] = user.username
placeholders["name"] = user.name
placeholders["updated_user_username"] = user.username
placeholders["updated_user_name"] = user.name
placeholders = placeholders.merge(user_profile_data, user_custom_fields)
end
post_raw = utils.apply_placeholders(post_raw, placeholders)
if !creator
DiscourseAutomation::Logger.warn("creator with username: `#{creator_username}` was not found")
next
end
post_creator = PostCreator.new(creator, topic_id:, raw: post_raw)
new_post = post_creator.create
if new_post.blank? || post_creator.errors.present?
DiscourseAutomation::Logger.error(
"Failed to create post in topic #{topic_id}: #{post_creator.errors.full_messages.join(", ")}",
)
next
end
if context["kind"] == DiscourseAutomation::Triggers::USER_UPDATED && new_post.persisted?
user.user_custom_fields.create(name: automation.name, value: "true")
end
end
end