diff --git a/plugins/discourse-narrative-bot/config/locales/server.en.yml b/plugins/discourse-narrative-bot/config/locales/server.en.yml index 6bd0bb2250e..5715ca4d6d3 100644 --- a/plugins/discourse-narrative-bot/config/locales/server.en.yml +++ b/plugins/discourse-narrative-bot/config/locales/server.en.yml @@ -201,12 +201,24 @@ en: like_not_found: |- Did you forget to like :heart: my [post?](%{url}) :crying_cat_face: not_found: |- - Looks like you didn’t upload an image so I’ve choosen a picture that I’m _sure_ you will enjoy. + Looks like you didn’t upload an image so I’ve chosen a picture that I’m _sure_ you will enjoy. `%{image_url}` Try uploading that one next, or pasting the link in on a line by itself! + likes: + instructions: |- + Here’s a picture of a unicorn: + + + + If you like it (and who wouldn’t!) go ahead and press the like :heart: button below this post to let me know. + reply: |- + Thanks for liking my post! + not_found: |- + Did you forget to like :heart: my [post?](%{url}) :crying_cat_face: + formatting: instructions: |- Can you make some words **bold** or _italic_ in your reply? diff --git a/plugins/discourse-narrative-bot/lib/discourse_narrative_bot/new_user_narrative.rb b/plugins/discourse-narrative-bot/lib/discourse_narrative_bot/new_user_narrative.rb index 8a9ed39cb49..86ca941815b 100644 --- a/plugins/discourse-narrative-bot/lib/discourse_narrative_bot/new_user_narrative.rb +++ b/plugins/discourse-narrative-bot/lib/discourse_narrative_bot/new_user_narrative.rb @@ -80,7 +80,22 @@ module DiscourseNarrativeBot } }, + # Note: tutorial_images and tutorial_likes are mutually exclusive. + # The prerequisites should ensure only one of them is called. tutorial_images: { + prerequisite: Proc.new { @user.has_trust_level?(SiteSetting.min_trust_to_post_embedded_media) }, + next_state: :tutorial_likes, + next_instructions: Proc.new { I18n.t("#{I18N_KEY}.likes.instructions", base_uri: Discourse.base_uri) }, + reply: { + action: :reply_to_image + }, + like: { + action: :track_images_like + } + }, + + tutorial_likes: { + prerequisite: Proc.new { !@user.has_trust_level?(SiteSetting.min_trust_to_post_embedded_media) }, next_state: :tutorial_flag, next_instructions: Proc.new { I18n.t("#{I18N_KEY}.flag.instructions", @@ -88,11 +103,12 @@ module DiscourseNarrativeBot about_url: url_helpers(:about_index_url), base_uri: Discourse.base_uri) }, - reply: { - action: :reply_to_image - }, like: { - action: :track_like + action: :reply_to_likes + }, + reply: { + next_state: :tutorial_likes, + action: :missing_likes_like } }, @@ -278,11 +294,11 @@ module DiscourseNarrativeBot end end - def track_like + def track_images_like post_topic_id = @post.topic_id return unless valid_topic?(post_topic_id) - post_liked = PostAction.find_by( + post_liked = PostAction.exists?( post_action_type_id: PostActionType.types[:like], post_id: @data[:last_post_id], user_id: @user.id @@ -362,6 +378,45 @@ module DiscourseNarrativeBot transition ? reply : false end + def missing_likes_like + return unless valid_topic?(@post.topic_id) + return if @post.user_id == self.discobot_user.id + + fake_delay + enqueue_timeout_job(@user) + + last_post = Post.find_by(id: @data[:last_post_id]) + reply_to(@post, I18n.t("#{I18N_KEY}.likes.not_found", i18n_post_args(url: last_post.url))) + false + end + + def reply_to_likes + post_topic_id = @post.topic_id + return unless valid_topic?(post_topic_id) + + post_liked = PostAction.exists?( + post_action_type_id: PostActionType.types[:like], + post_id: @data[:last_post_id], + user_id: @user.id + ) + + if post_liked + raw = <<~RAW + #{I18n.t("#{I18N_KEY}.likes.reply", i18n_post_args)} + + #{instance_eval(&@next_instructions)} + RAW + + fake_delay + + reply = reply_to(@post, raw) + enqueue_timeout_job(@user) + return reply + end + + false + end + def reply_to_formatting post_topic_id = @post.topic_id return unless valid_topic?(post_topic_id) diff --git a/plugins/discourse-narrative-bot/plugin.rb b/plugins/discourse-narrative-bot/plugin.rb index 9efcd7a88cc..b37e0a51e44 100644 --- a/plugins/discourse-narrative-bot/plugin.rb +++ b/plugins/discourse-narrative-bot/plugin.rb @@ -300,4 +300,17 @@ after_initialize do DiscourseNarrativeBot::BOT_USER_ID, "discobot@discourse.org" ) + + PostGuardian.class_eval do + alias_method :existing_can_create_post?, :can_create_post? + + def can_create_post?(parent) + return true if SiteSetting.discourse_narrative_bot_enabled && + parent.try(:subtype) == "system_message" && + parent.try(:user) == ::DiscourseNarrativeBot::Base.new.discobot_user + + existing_can_create_post?(parent) + end + end + end diff --git a/plugins/discourse-narrative-bot/spec/discourse_narrative_bot/new_user_narrative_spec.rb b/plugins/discourse-narrative-bot/spec/discourse_narrative_bot/new_user_narrative_spec.rb index 89d644c0335..f67f58c2366 100644 --- a/plugins/discourse-narrative-bot/spec/discourse_narrative_bot/new_user_narrative_spec.rb +++ b/plugins/discourse-narrative-bot/spec/discourse_narrative_bot/new_user_narrative_spec.rb @@ -542,6 +542,101 @@ describe DiscourseNarrativeBot::NewUserNarrative do end end + describe 'likes tutorial' do + let(:post_2) { Fabricate(:post, topic: topic) } + + before do + narrative.set_data(user, + state: :tutorial_likes, + topic_id: topic.id, + last_post_id: post_2.id, + track: described_class.to_s + ) + end + + describe 'when post is not in the right topic' do + it 'should not do anything' do + other_post + narrative.expects(:enqueue_timeout_job).with(user).never + + expect { narrative.input(:reply, user, post: other_post) }.to_not change { Post.count } + expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_likes) + end + end + + describe 'when user replies to the topic' do + describe 'when reply contains the skip trigger' do + it 'should create the right reply' do + post.update!(raw: skip_trigger) + described_class.any_instance.expects(:enqueue_timeout_job).with(user) + + DiscourseNarrativeBot::TrackSelector.new(:reply, user, post_id: post.id).select + + new_post = Post.last + + expect(new_post.raw).to eq(I18n.t( + 'discourse_narrative_bot.new_user_narrative.flag.instructions', + guidelines_url: Discourse.base_url + '/guidelines', + about_url: Discourse.base_url + '/about', + base_uri: '' + )) + + expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_flag) + end + + describe 'when allow_flagging_staff is false' do + it 'should go to the right state' do + SiteSetting.allow_flagging_staff = false + post.update!(raw: skip_trigger) + + DiscourseNarrativeBot::TrackSelector.new( + :reply, + user, + post_id: post.id + ).select + + expect(narrative.get_data(user)[:state].to_sym) + .to eq(:tutorial_search) + end + end + end + + it 'should create the right reply' do + narrative.expects(:enqueue_timeout_job).with(user).once + + narrative.input(:reply, user, post: post) + new_post = Post.last + + expect(new_post.raw).to eq(I18n.t( + 'discourse_narrative_bot.new_user_narrative.likes.not_found', + url: post_2.url, base_uri: '' + )) + + expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_likes) + end + end + + describe 'when the post is liked' do + it 'should create the right reply' do + PostActionCreator.like(user, post_2) + + expected_raw = <<~RAW + #{I18n.t("discourse_narrative_bot.new_user_narrative.likes.reply")} + + #{I18n.t( + 'discourse_narrative_bot.new_user_narrative.flag.instructions', + guidelines_url: "#{Discourse.base_url}/guidelines", + about_url: "#{Discourse.base_url}/about", + base_uri: '' + )} + RAW + + expect(Post.last.raw).to eq(expected_raw.chomp) + expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_flag) + end + end + end + describe 'fomatting tutorial' do before do narrative.set_data(user, state: :tutorial_formatting, topic_id: topic.id) @@ -662,6 +757,23 @@ describe DiscourseNarrativeBot::NewUserNarrative do end end + describe 'when min_trust_to_post_embedded_media is too high' do + before do + SiteSetting.min_trust_to_post_embedded_media = 4 + end + + it 'should skip the images tutorial step' do + post.update!( + raw: "[quote=\"#{post.user}, post:#{post.post_number}, topic:#{topic.id}\"]\n:monkey: :fries:\n[/quote]" + ) + + narrative.expects(:enqueue_timeout_job).with(user) + narrative.input(:reply, user, post: post) + + expect(narrative.get_data(user)[:state].to_sym).to eq(:tutorial_likes) + end + end + it 'should create the right reply' do post.update!( raw: "[quote=\"#{post.user}, post:#{post.post_number}, topic:#{topic.id}\"]\n:monkey: :fries:\n[/quote]"