Skip to content

Commit 9b8aa09

Browse files
authored
DEV: adds an automation script for first accepted solution (#172)
1 parent 6767d8c commit 9b8aa09

File tree

5 files changed

+151
-0
lines changed

5 files changed

+151
-0
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# frozen_string_literal: true
2+
3+
class FirstAcceptedPostSolutionValidator
4+
def self.check(post, trust_level:)
5+
return false if post.archetype != Archetype.default
6+
return false if !post&.user&.human?
7+
return true if trust_level == 'any'
8+
9+
if TrustLevel.compare(post&.user&.trust_level, trust_level.to_i)
10+
return false
11+
end
12+
13+
if !UserAction.where(user_id: post&.user_id, action_type: UserAction::SOLVED).exists?
14+
return true
15+
end
16+
17+
false
18+
end
19+
end

config/locales/client.en.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,17 @@ en:
4242
solved_event:
4343
name: "Solved Event"
4444
details: "When a user marks a post as the accepted or unaccepted answer."
45+
46+
discourse_automation:
47+
triggerables:
48+
first_accepted_solution:
49+
max_trust_level:
50+
tl1: < TL1
51+
tl2: < TL2
52+
tl3: < TL3
53+
tl4: < TL4
54+
any: Any
55+
fields:
56+
maximum_trust_level:
57+
label: Trust Level
58+
description: Users under this Trust Level will trigger this automation

config/locales/server.en.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ en:
3333
name: "Tech Support"
3434
description: "10 Accepted answers"
3535

36+
discourse_automation:
37+
triggerables:
38+
first_accepted_solution:
39+
title: First accepted solution
40+
doc: Triggers when a user got a solution accepted for the first time.
41+
3642
education:
3743
topic_is_solved: |
3844
### This topic has been solved

plugin.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
SeedFu.fixture_paths << Rails.root.join("plugins", "discourse-solved", "db", "fixtures").to_s
2525

2626
[
27+
'../app/lib/first_accepted_post_solution_validator.rb',
2728
'../app/serializers/concerns/topic_answer_mixin.rb'
2829
].each { |path| load File.expand_path(path, __FILE__) }
2930

@@ -752,4 +753,45 @@ def user_ids
752753
prepend AddSolvedToTopicPostersSummary
753754
end
754755
end
756+
757+
if defined?(DiscourseAutomation)
758+
if respond_to?(:add_triggerable_to_scriptable)
759+
on(:accepted_solution) do |post|
760+
# testing directly automation is prone to issues
761+
# we prefer to abstract logic in service object and test this
762+
next if Rails.env.test?
763+
764+
name = 'first_accepted_solution'
765+
DiscourseAutomation::Automation.where(trigger: name, enabled: true).find_each do |automation|
766+
maximum_trust_level = automation.trigger_field('maximum_trust_level')&.dig('value')
767+
if FirstAcceptedPostSolutionValidator.check(post, trust_level: maximum_trust_level)
768+
automation.trigger!(
769+
'kind' => name,
770+
'accepted_post_id' => post.id,
771+
'usernames' => [post.user.username],
772+
'placeholders' => {
773+
'post_url' => Discourse.base_url + post.url
774+
}
775+
)
776+
end
777+
end
778+
end
779+
780+
TRUST_LEVELS = [
781+
{ id: 1, name: 'discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl1' },
782+
{ id: 2, name: 'discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl2' },
783+
{ id: 3, name: 'discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl3' },
784+
{ id: 4, name: 'discourse_automation.triggerables.first_accepted_solution.max_trust_level.tl4' },
785+
{ id: 'any', name: 'discourse_automation.triggerables.first_accepted_solution.max_trust_level.any' },
786+
]
787+
788+
add_triggerable_to_scriptable(:first_accepted_solution, :send_pms)
789+
790+
DiscourseAutomation::Triggerable.add(:first_accepted_solution) do
791+
placeholder :post_url
792+
793+
field :maximum_trust_level, component: :choices, extra: { content: TRUST_LEVELS }, required: true
794+
end
795+
end
796+
end
755797
end
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# frozen_string_literal: true
2+
3+
require 'rails_helper'
4+
5+
describe FirstAcceptedPostSolutionValidator do
6+
fab!(:user_tl1) { Fabricate(:user, trust_level: TrustLevel[1]) }
7+
8+
context 'user is under max trust level' do
9+
context 'has no post accepted yet' do
10+
it 'validates the post' do
11+
post_1 = create_post(user: user_tl1)
12+
expect(described_class.check(post_1, trust_level: TrustLevel[2])).to eq(true)
13+
end
14+
end
15+
16+
context 'has already had accepted posts' do
17+
before do
18+
accepted_post = create_post(user: user_tl1)
19+
DiscourseSolved.accept_answer!(accepted_post, Discourse.system_user)
20+
end
21+
22+
it 'doesn’t validate the post' do
23+
post_1 = create_post(user: user_tl1)
24+
expect(described_class.check(post_1, trust_level: TrustLevel[2])).to eq(false)
25+
end
26+
end
27+
end
28+
29+
context 'user is above or equal max trust level' do
30+
context 'has no post accepted yet' do
31+
it 'doesn’t validate the post' do
32+
post_1 = create_post(user: user_tl1)
33+
expect(described_class.check(post_1, trust_level: TrustLevel[1])).to eq(false)
34+
end
35+
end
36+
37+
context 'has already had accepted posts' do
38+
before do
39+
accepted_post = create_post(user: user_tl1)
40+
DiscourseSolved.accept_answer!(accepted_post, Discourse.system_user)
41+
end
42+
43+
it 'doesn’t validate the post' do
44+
post_1 = create_post(user: user_tl1)
45+
expect(described_class.check(post_1, trust_level: TrustLevel[1])).to eq(false)
46+
end
47+
end
48+
end
49+
50+
context 'using any trust level' do
51+
it 'validates the post' do
52+
post_1 = create_post(user: user_tl1)
53+
expect(described_class.check(post_1, trust_level: 'any')).to eq(true)
54+
end
55+
end
56+
57+
context 'user is system' do
58+
it 'doesn’t validate the post' do
59+
post_1 = create_post(user: Discourse.system_user)
60+
expect(described_class.check(post_1, trust_level: 'any')).to eq(false)
61+
end
62+
end
63+
64+
context 'post is a PM' do
65+
it 'doesn’t validate the post' do
66+
post_1 = create_post(user: user_tl1, target_usernames: [user_tl1.username], archetype: Archetype.private_message)
67+
expect(described_class.check(post_1, trust_level: 'any')).to eq(false)
68+
end
69+
end
70+
end

0 commit comments

Comments
 (0)