From b2187301fdf70efc42df7356b176c9c53129359c Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 6 Mar 2019 18:38:49 +1100 Subject: [PATCH] FEATURE: allow users to easily track/watch/mute topics via email If you reply to an email with the word "mute" a topic will be muted If you reply to an email with the word "track" a topic will be tracked If you reply to an email with the word "watch" a topic will be watched These ninja command can help advanced mailing list ex-users, saves a trip to the website --- lib/email/receiver.rb | 18 ++++++++++++ spec/components/email/receiver_spec.rb | 38 +++++++++++++++++++++----- spec/fixtures/emails/mute.eml | 10 +++++++ spec/fixtures/emails/track.eml | 10 +++++++ spec/fixtures/emails/watch.eml | 10 +++++++ 5 files changed, 79 insertions(+), 7 deletions(-) create mode 100644 spec/fixtures/emails/mute.eml create mode 100644 spec/fixtures/emails/track.eml create mode 100644 spec/fixtures/emails/watch.eml diff --git a/lib/email/receiver.rb b/lib/email/receiver.rb index 736626194d7..c7f706d05c2 100644 --- a/lib/email/receiver.rb +++ b/lib/email/receiver.rb @@ -899,6 +899,22 @@ module Email create_post_with_attachments(options) end + def notification_level_for(body) + # since we are stripping save all this work on long replies + return nil if body.length > 40 + + body = body.strip.downcase + case body + when "mute" + NotificationLevels.topic_levels[:muted] + when "track" + NotificationLevels.topic_levels[:tracking] + when "watch" + NotificationLevels.topic_levels[:watching] + else nil + end + end + def create_reply(options = {}) raise TopicNotFoundError if options[:topic].nil? || options[:topic].trashed? raise BouncedEmailError if options[:bounce] && options[:topic].archetype != Archetype.private_message @@ -908,6 +924,8 @@ module Email if post_action_type = post_action_for(options[:raw]) create_post_action(options[:user], options[:post], post_action_type) + elsif notification_level = notification_level_for(options[:raw]) + TopicUser.change(options[:user].id, options[:post].topic_id, notification_level: notification_level) else raise TopicClosedError if options[:topic].closed? options[:topic_id] = options[:topic].id diff --git a/spec/components/email/receiver_spec.rb b/spec/components/email/receiver_spec.rb index f243065e490..a87629e00f6 100644 --- a/spec/components/email/receiver_spec.rb +++ b/spec/components/email/receiver_spec.rb @@ -251,6 +251,10 @@ describe Email::Receiver do ) end + let :topic_user do + TopicUser.find_by(topic_id: topic.id, user_id: user.id) + end + it "uses MD5 of 'mail_string' there is no message_id" do mail_string = email(:missing_message_id) expect { Email::Receiver.new(mail_string).process! }.to change { IncomingEmail.count } @@ -285,14 +289,34 @@ describe Email::Receiver do expect { process(:reply_user_matching) }.to raise_error(Email::Receiver::TopicNotFoundError) end - it "raises a TopicClosedError when the topic was closed" do - topic.update_columns(closed: true) - expect { process(:reply_user_matching) }.to raise_error(Email::Receiver::TopicClosedError) - end + context "a closed topic" do - it "does not raise TopicClosedError when performing a like action" do - topic.update_columns(closed: true) - expect { process(:like) }.to change(PostAction, :count) + before do + topic.update_columns(closed: true) + end + + it "raises a TopicClosedError when the topic was closed" do + expect { process(:reply_user_matching) }.to raise_error(Email::Receiver::TopicClosedError) + end + + it "Can watch topics via the watch command" do + # TODO support other locales as well, the tricky thing is that these string live in + # client.yml not on server yml so it is a bit tricky to find + + topic.update_columns(closed: true) + process(:watch) + expect(topic_user.notification_level).to eq(NotificationLevels.topic_levels[:watching]) + end + + it "Can mute topics via the mute command" do + process(:mute) + expect(topic_user.notification_level).to eq(NotificationLevels.topic_levels[:muted]) + end + + it "can track a topic via the track command" do + process(:track) + expect(topic_user.notification_level).to eq(NotificationLevels.topic_levels[:tracking]) + end end it "raises an InvalidPost when there was an error while creating the post" do diff --git a/spec/fixtures/emails/mute.eml b/spec/fixtures/emails/mute.eml new file mode 100644 index 00000000000..62dea407517 --- /dev/null +++ b/spec/fixtures/emails/mute.eml @@ -0,0 +1,10 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <13@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +mute diff --git a/spec/fixtures/emails/track.eml b/spec/fixtures/emails/track.eml new file mode 100644 index 00000000000..a7d7ad10dfb --- /dev/null +++ b/spec/fixtures/emails/track.eml @@ -0,0 +1,10 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <13@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +track diff --git a/spec/fixtures/emails/watch.eml b/spec/fixtures/emails/watch.eml new file mode 100644 index 00000000000..446ff77b0d7 --- /dev/null +++ b/spec/fixtures/emails/watch.eml @@ -0,0 +1,10 @@ +Return-Path: +From: Foo Bar +To: reply+4f97315cc828096c9cb34c6f1a0d6fe8@bar.com +Date: Fri, 15 Jan 2016 00:12:43 +0100 +Message-ID: <13@foo.bar.mail> +Mime-Version: 1.0 +Content-Type: text/plain +Content-Transfer-Encoding: 7bit + +watch