mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 09:42:07 +08:00
FEATURE: Add 'Create topic' automation script (#26552)
This commit adds a new automation script for creating topics. It's very similar to the existing 'create a post' automation, except that it posts new topics in a specific category and with optional tags. Internal topic: t/125829.
This commit is contained in:
parent
0085365459
commit
84b4e4bddf
|
@ -18,6 +18,6 @@ export default class PlaceholdersList extends Component {
|
||||||
|
|
||||||
@action
|
@action
|
||||||
copyPlaceholder(placeholder) {
|
copyPlaceholder(placeholder) {
|
||||||
this.args.onCopy(`${this.args.currentValue}{{${placeholder}}}`);
|
this.args.onCopy(`${this.args.currentValue || ""}{{${placeholder}}}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -244,6 +244,19 @@ en:
|
||||||
label: Topic ID
|
label: Topic ID
|
||||||
post:
|
post:
|
||||||
label: Post content
|
label: Post content
|
||||||
|
topic:
|
||||||
|
fields:
|
||||||
|
creator:
|
||||||
|
label: Creator
|
||||||
|
updated_user_context: The updated user
|
||||||
|
body:
|
||||||
|
label: Topic body
|
||||||
|
title:
|
||||||
|
label: Topic title
|
||||||
|
category:
|
||||||
|
label: Topic category
|
||||||
|
tags:
|
||||||
|
label: Topic tags
|
||||||
group_category_notification_default:
|
group_category_notification_default:
|
||||||
fields:
|
fields:
|
||||||
group:
|
group:
|
||||||
|
|
|
@ -63,6 +63,9 @@ en:
|
||||||
post:
|
post:
|
||||||
title: Create a post
|
title: Create a post
|
||||||
description: Create a post on a specified topic
|
description: Create a post on a specified topic
|
||||||
|
topic:
|
||||||
|
title: Create a topic
|
||||||
|
description: Create a topic as a specific user
|
||||||
flag_post_on_words:
|
flag_post_on_words:
|
||||||
title: Flag post on words
|
title: Flag post on words
|
||||||
description: Flags a post if it contains specified words
|
description: Flags a post if it contains specified words
|
||||||
|
|
|
@ -16,6 +16,7 @@ module DiscourseAutomation
|
||||||
POST = "post"
|
POST = "post"
|
||||||
SEND_PMS = "send_pms"
|
SEND_PMS = "send_pms"
|
||||||
SUSPEND_USER_BY_EMAIL = "suspend_user_by_email"
|
SUSPEND_USER_BY_EMAIL = "suspend_user_by_email"
|
||||||
|
TOPIC = "topic"
|
||||||
TOPIC_REQUIRED_WORDS = "topic_required_words"
|
TOPIC_REQUIRED_WORDS = "topic_required_words"
|
||||||
USER_GLOBAL_NOTICE = "user_global_notice"
|
USER_GLOBAL_NOTICE = "user_global_notice"
|
||||||
USER_GROUP_MEMBERSHIP_THROUGH_BADGE = "user_group_membership_through_badge"
|
USER_GROUP_MEMBERSHIP_THROUGH_BADGE = "user_group_membership_through_badge"
|
||||||
|
|
76
plugins/automation/lib/discourse_automation/scripts/topic.rb
Normal file
76
plugins/automation/lib/discourse_automation/scripts/topic.rb
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
DiscourseAutomation::Scriptable.add(DiscourseAutomation::Scripts::TOPIC) do
|
||||||
|
version 1
|
||||||
|
|
||||||
|
field :creator, component: :user
|
||||||
|
field :creator, component: :user, triggerable: :user_updated, accepted_contexts: [:updated_user]
|
||||||
|
|
||||||
|
field :body, component: :post, required: true, accepts_placeholders: true
|
||||||
|
field :title, component: :text, required: true, accepts_placeholders: true
|
||||||
|
field :category, component: :category, required: true
|
||||||
|
field :tags, component: :tags
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
placeholders = { creator_username: creator_username }.merge(context["placeholders"] || {})
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
topic_raw = fields.dig("body", "value")
|
||||||
|
topic_raw = utils.apply_placeholders(topic_raw, placeholders)
|
||||||
|
|
||||||
|
title = fields.dig("title", "value")
|
||||||
|
title = utils.apply_placeholders(title, placeholders)
|
||||||
|
|
||||||
|
creator = User.find_by(username: creator_username)
|
||||||
|
if !creator
|
||||||
|
Rails.logger.warn "[discourse-automation] creator with username: `#{creator_username}` was not found"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
category_id = fields.dig("category", "value")
|
||||||
|
category = Category.find_by(id: category_id)
|
||||||
|
if !category
|
||||||
|
Rails.logger.warn "[discourse-automation] category of id: `#{category_id}` was not found"
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
tags = fields.dig("tags", "value") || []
|
||||||
|
new_post =
|
||||||
|
PostCreator.new(
|
||||||
|
creator,
|
||||||
|
raw: topic_raw,
|
||||||
|
title: title,
|
||||||
|
category: category.id,
|
||||||
|
tags: tags,
|
||||||
|
).create!
|
||||||
|
|
||||||
|
if context["kind"] == DiscourseAutomation::Triggers::USER_UPDATED && new_post.persisted?
|
||||||
|
user.user_custom_fields.create(name: automation.name, value: "true")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -59,6 +59,7 @@ after_initialize do
|
||||||
lib/discourse_automation/scripts/group_category_notification_default
|
lib/discourse_automation/scripts/group_category_notification_default
|
||||||
lib/discourse_automation/scripts/pin_topic
|
lib/discourse_automation/scripts/pin_topic
|
||||||
lib/discourse_automation/scripts/post
|
lib/discourse_automation/scripts/post
|
||||||
|
lib/discourse_automation/scripts/topic
|
||||||
lib/discourse_automation/scripts/send_pms
|
lib/discourse_automation/scripts/send_pms
|
||||||
lib/discourse_automation/scripts/suspend_user_by_email
|
lib/discourse_automation/scripts/suspend_user_by_email
|
||||||
lib/discourse_automation/scripts/topic_required_words
|
lib/discourse_automation/scripts/topic_required_words
|
||||||
|
|
171
plugins/automation/spec/scripts/topic_spec.rb
Normal file
171
plugins/automation/spec/scripts/topic_spec.rb
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require_relative "../discourse_automation_helper"
|
||||||
|
|
||||||
|
describe "Topic" do
|
||||||
|
let!(:raw) { "this is me testing a new topic by automation" }
|
||||||
|
let!(:title) { "This is a new topic created by automation" }
|
||||||
|
fab!(:category) { Fabricate(:category) }
|
||||||
|
fab!(:tag1) { Fabricate(:tag) }
|
||||||
|
fab!(:tag2) { Fabricate(:tag) }
|
||||||
|
|
||||||
|
before { SiteSetting.discourse_automation_enabled = true }
|
||||||
|
|
||||||
|
context "when using point_in_time trigger" do
|
||||||
|
fab!(:automation) do
|
||||||
|
Fabricate(
|
||||||
|
:automation,
|
||||||
|
script: DiscourseAutomation::Scripts::TOPIC,
|
||||||
|
trigger: DiscourseAutomation::Triggers::POINT_IN_TIME,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
automation.upsert_field!(
|
||||||
|
"execute_at",
|
||||||
|
"date_time",
|
||||||
|
{ value: 3.hours.from_now },
|
||||||
|
target: "trigger",
|
||||||
|
)
|
||||||
|
automation.upsert_field!("title", "text", { value: title }, target: "script")
|
||||||
|
automation.upsert_field!("body", "post", { value: raw }, target: "script")
|
||||||
|
automation.upsert_field!(
|
||||||
|
"category",
|
||||||
|
"category",
|
||||||
|
{ value: category.id.to_s },
|
||||||
|
target: "script",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates expected topic" do
|
||||||
|
freeze_time 6.hours.from_now do
|
||||||
|
expect {
|
||||||
|
Jobs::DiscourseAutomationTracker.new.execute
|
||||||
|
|
||||||
|
topic = Topic.last
|
||||||
|
expect(topic.category.id).to eq(category.id)
|
||||||
|
expect(topic.title).to eq(title)
|
||||||
|
expect(topic.posts.first.raw).to eq(raw)
|
||||||
|
}.to change { Topic.count }.by(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when using recurring trigger" do
|
||||||
|
fab!(:automation) do
|
||||||
|
Fabricate(
|
||||||
|
:automation,
|
||||||
|
script: DiscourseAutomation::Scripts::TOPIC,
|
||||||
|
trigger: DiscourseAutomation::Triggers::RECURRING,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
automation.upsert_field!("title", "text", { value: title }, target: "script")
|
||||||
|
automation.upsert_field!("body", "post", { value: raw }, target: "script")
|
||||||
|
automation.upsert_field!(
|
||||||
|
"category",
|
||||||
|
"category",
|
||||||
|
{ value: category.id.to_s },
|
||||||
|
target: "script",
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates expected topic" do
|
||||||
|
expect {
|
||||||
|
automation.trigger!
|
||||||
|
|
||||||
|
topic = Topic.last
|
||||||
|
expect(topic.category.id).to eq(category.id)
|
||||||
|
expect(topic.title).to eq(title)
|
||||||
|
expect(topic.posts.first.raw).to eq(raw)
|
||||||
|
}.to change { Topic.count }.by(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when using user_updated trigger" do
|
||||||
|
fab!(:user_field_1) { Fabricate(:user_field, name: "custom field 1") }
|
||||||
|
fab!(:user_field_2) { Fabricate(:user_field, name: "custom field 2") }
|
||||||
|
|
||||||
|
fab!(:user) do
|
||||||
|
user = Fabricate(:user, trust_level: TrustLevel[0])
|
||||||
|
user.set_user_field(user_field_1.id, "Answer custom 1")
|
||||||
|
user.set_user_field(user_field_2.id, "Answer custom 2")
|
||||||
|
user.user_profile.location = "Japan"
|
||||||
|
user.user_profile.save
|
||||||
|
user.save
|
||||||
|
user
|
||||||
|
end
|
||||||
|
|
||||||
|
fab!(:automation) do
|
||||||
|
automation =
|
||||||
|
Fabricate(
|
||||||
|
:automation,
|
||||||
|
script: DiscourseAutomation::Scripts::TOPIC,
|
||||||
|
trigger: DiscourseAutomation::Triggers::USER_UPDATED,
|
||||||
|
)
|
||||||
|
automation.upsert_field!(
|
||||||
|
"custom_fields",
|
||||||
|
"custom_fields",
|
||||||
|
{ value: ["custom field 1", "custom field 2"] },
|
||||||
|
target: "trigger",
|
||||||
|
)
|
||||||
|
automation.upsert_field!(
|
||||||
|
"user_profile",
|
||||||
|
"user_profile",
|
||||||
|
{ value: ["location"] },
|
||||||
|
target: "trigger",
|
||||||
|
)
|
||||||
|
automation
|
||||||
|
end
|
||||||
|
let!(:user_raw_post) do
|
||||||
|
"This is a raw test post for user custom field 1: {{custom_field_1}}, custom field 2: {{custom_field_2}} and location: {{location}}"
|
||||||
|
end
|
||||||
|
let!(:placeholder_applied_user_raw_post) do
|
||||||
|
"This is a raw test post for user custom field 1: #{user.custom_fields["user_field_#{user_field_1.id}"]}, custom field 2: #{user.custom_fields["user_field_#{user_field_2.id}"]} and location: #{user.user_profile.location}"
|
||||||
|
end
|
||||||
|
|
||||||
|
before do
|
||||||
|
automation.upsert_field!(
|
||||||
|
"title",
|
||||||
|
"text",
|
||||||
|
{ value: "{{custom_field_1}} {{location}} this is a title" },
|
||||||
|
target: "script",
|
||||||
|
)
|
||||||
|
automation.upsert_field!("body", "post", { value: user_raw_post }, target: "script")
|
||||||
|
automation.upsert_field!(
|
||||||
|
"category",
|
||||||
|
"category",
|
||||||
|
{ value: category.id.to_s },
|
||||||
|
target: "script",
|
||||||
|
)
|
||||||
|
automation.upsert_field!("tags", "tags", { value: %w[feedback automation] }, target: "script")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "creates a topic correctly" do
|
||||||
|
expect {
|
||||||
|
UserUpdater.new(user, user).update(location: "Japan")
|
||||||
|
|
||||||
|
topic = Topic.last
|
||||||
|
expect(topic.category.id).to eq(category.id)
|
||||||
|
expect(topic.title).to eq(
|
||||||
|
"#{user.custom_fields["user_field_#{user_field_1.id}"]} #{user.user_profile.location} this is a title",
|
||||||
|
)
|
||||||
|
expect(topic.posts.first.raw).to eq(placeholder_applied_user_raw_post)
|
||||||
|
expect(topic.tags.pluck(:name)).to contain_exactly("feedback", "automation")
|
||||||
|
}.to change { Topic.count }.by(1)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when creator is one of accepted context" do
|
||||||
|
before do
|
||||||
|
automation.upsert_field!("creator", "user", { value: "updated_user" }, target: "script")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sets the creator to the topic creator" do
|
||||||
|
expect { UserUpdater.new(user, user).update(location: "Japan") }.to change {
|
||||||
|
Topic.where(user_id: user.id).count
|
||||||
|
}.by(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user