2019-05-03 08:17:27 +10:00
# frozen_string_literal: true
2014-03-18 13:12:07 +11:00
class PostAlerter
2020-02-06 12:14:19 +00:00
USER_BATCH_SIZE = 100
2017-06-12 16:41:39 +09:00
def self . post_created ( post , opts = { } )
2018-05-15 09:51:32 +02:00
PostAlerter . new ( opts ) . after_save_post ( post , true )
2014-03-18 15:22:39 +11:00
post
end
2020-05-04 17:40:09 +10:00
def self . post_edited ( post , opts = { } )
PostAlerter . new ( opts ) . after_save_post ( post , false )
post
end
2021-09-20 21:18:38 +03:00
def self . create_notification_alert ( user : , post : , notification_type : , excerpt : nil , username : nil )
2021-10-11 20:55:18 +03:00
return if user . suspended?
2021-09-20 21:18:38 +03:00
if post_url = post . url
payload = {
2023-01-09 12:20:10 +00:00
notification_type : notification_type ,
post_number : post . post_number ,
topic_title : post . topic . title ,
topic_id : post . topic . id ,
excerpt :
2023-01-24 14:40:24 +10:00
excerpt ||
post . excerpt (
400 ,
text_entities : true ,
strip_links : true ,
remap_emoji : true ,
plain_hashtags : true ,
) ,
2023-01-09 12:20:10 +00:00
username : username || post . username ,
post_url : post_url ,
2021-09-20 21:18:38 +03:00
}
DiscourseEvent . trigger ( :pre_notification_alert , user , payload )
2021-11-22 13:38:49 +10:00
if user . allow_live_notifications?
MessageBus . publish ( " /notification-alert/ #{ user . id } " , payload , user_ids : [ user . id ] )
end
2021-09-20 21:18:38 +03:00
push_notification ( user , payload )
DiscourseEvent . trigger ( :post_notification_alert , user , payload )
end
end
def self . push_notification ( user , payload )
return if user . do_not_disturb?
2021-11-03 12:21:33 -05:00
DiscoursePluginRegistry . push_notification_filters . each do | filter |
return unless filter . call ( user , payload )
end
2021-09-20 21:18:38 +03:00
if user . push_subscriptions . exists?
2022-12-19 20:17:40 +00:00
if user . seen_since? ( SiteSetting . push_notification_time_window_mins . minutes . ago )
2023-03-28 13:22:54 -03:00
delay =
( SiteSetting . push_notification_time_window_mins - ( Time . now - user . last_seen_at ) / 60 )
Jobs . enqueue_in ( delay . minutes , :send_push_notification , user_id : user . id , payload : payload )
2022-12-19 20:17:40 +00:00
else
Jobs . enqueue ( :send_push_notification , user_id : user . id , payload : payload )
end
2021-09-20 21:18:38 +03:00
end
2023-01-09 12:20:10 +00:00
if SiteSetting . allow_user_api_key_scopes . split ( " | " ) . include? ( " push " ) &&
SiteSetting . allowed_user_api_push_urls . present?
clients =
user
. user_api_keys
. joins ( :scopes )
. where ( " user_api_key_scopes.name IN ('push', 'notifications') " )
. where ( " push_url IS NOT NULL AND push_url <> '' " )
. where ( " position(push_url IN ?) > 0 " , SiteSetting . allowed_user_api_push_urls )
. where ( " revoked_at IS NULL " )
. order ( client_id : :asc )
. pluck ( :client_id , :push_url )
2021-09-20 21:18:38 +03:00
if clients . length > 0
Jobs . enqueue ( :push_notification , clients : clients , payload : payload , user_id : user . id )
end
end
end
2017-06-12 16:41:39 +09:00
def initialize ( default_opts = { } )
@default_opts = default_opts
end
2015-12-18 16:32:53 +01:00
def not_allowed? ( user , post )
2023-01-09 12:20:10 +00:00
user . blank? || user . bot? || user . id == post . user_id
2015-12-18 16:32:53 +01:00
end
def all_allowed_users ( post )
@all_allowed_users || = post . topic . all_allowed_users . reject { | u | not_allowed? ( u , post ) }
end
2015-05-25 13:15:00 -04:00
def allowed_users ( post )
2015-12-18 16:32:53 +01:00
@allowed_users || = post . topic . allowed_users . reject { | u | not_allowed? ( u , post ) }
2015-05-25 13:15:00 -04:00
end
2015-12-18 16:32:53 +01:00
def allowed_group_users ( post )
@allowed_group_users || = post . topic . allowed_group_users . reject { | u | not_allowed? ( u , post ) }
end
2014-03-18 15:22:39 +11:00
2015-12-18 16:32:53 +01:00
def directly_targeted_users ( post )
allowed_users ( post ) - allowed_group_users ( post )
end
2015-12-14 23:17:09 +01:00
2016-01-27 21:38:14 +11:00
def indirectly_targeted_users ( post )
2015-12-18 16:32:53 +01:00
allowed_group_users ( post )
end
2014-03-18 13:12:07 +11:00
2018-04-12 16:19:44 +02:00
def only_allowed_users ( users , post )
2018-11-23 18:25:40 +01:00
return users unless post . topic . private_message?
users . select { | u | all_allowed_users ( post ) . include? ( u ) }
2018-04-12 16:19:44 +02:00
end
2017-09-19 15:51:10 +02:00
def notify_about_reply? ( post )
2018-12-04 17:54:27 +11:00
# small actions can be whispers in this case they will have an action code
# we never want to notify on this
post . post_type == Post . types [ :regular ] ||
( post . post_type == Post . types [ :whisper ] && post . action_code . nil? )
2017-09-19 15:51:10 +02:00
end
2015-12-18 16:32:53 +01:00
def after_save_post ( post , new_record = false )
2018-04-16 11:48:06 +02:00
notified = [ post . user , post . last_editor ] . uniq
2014-03-18 13:12:07 +11:00
2022-09-01 16:07:18 +03:00
DiscourseEvent . trigger ( :post_alerter_before_mentions , post , new_record , notified )
2015-12-18 16:32:53 +01:00
# mentions (users/groups)
2021-11-23 22:25:54 +02:00
mentioned_groups , mentioned_users , mentioned_here = extract_mentions ( post )
2014-03-18 13:12:07 +11:00
2021-11-23 22:25:54 +02:00
if mentioned_groups || mentioned_users || mentioned_here
2016-03-08 15:26:06 -05:00
mentioned_opts = { }
2018-04-12 16:19:44 +02:00
editor = post . last_editor
2016-03-08 15:26:06 -05:00
if post . last_editor_id != post . user_id
# Mention comes from an edit by someone else, so notification should say who added the mention.
2023-01-09 12:20:10 +00:00
mentioned_opts = {
user_id : editor . id ,
original_username : editor . username ,
display_username : editor . username ,
}
2016-03-08 15:26:06 -05:00
end
2014-03-18 13:12:07 +11:00
2016-03-08 15:26:06 -05:00
if mentioned_users
2018-11-23 18:25:40 +01:00
mentioned_users = only_allowed_users ( mentioned_users , post )
2022-12-09 12:23:11 +11:00
mentioned_users = mentioned_users - pm_watching_users ( post )
2018-03-08 11:40:48 +01:00
notified += notify_users ( mentioned_users - notified , :mentioned , post , mentioned_opts )
2016-03-08 15:26:06 -05:00
end
2021-03-17 08:20:41 +11:00
expand_group_mentions ( mentioned_groups , post ) do | group , users |
users = only_allowed_users ( users , post )
2023-06-16 14:48:07 -05:00
to_notify =
DiscoursePluginRegistry . apply_modifier (
:expand_group_mention_users ,
users - notified ,
group ,
)
2023-01-09 12:20:10 +00:00
notified +=
2023-06-16 14:48:07 -05:00
notify_users ( to_notify , :group_mentioned , post , mentioned_opts . merge ( group : group ) )
2021-03-17 08:20:41 +11:00
end
2021-11-23 22:25:54 +02:00
if mentioned_here
users = expand_here_mention ( post , exclude_ids : notified . map ( & :id ) )
users = only_allowed_users ( users , post )
notified += notify_users ( users - notified , :mentioned , post , mentioned_opts )
end
2015-11-30 17:03:47 +11:00
end
2014-03-18 13:12:07 +11:00
2022-09-01 16:07:18 +03:00
DiscourseEvent . trigger ( :post_alerter_before_replies , post , new_record , notified )
2015-12-18 16:32:53 +01:00
# replies
reply_to_user = post . reply_notification_target
2022-10-31 01:26:20 +03:00
if new_record && notify_about_reply? ( post )
if reply_to_user && ! notified . include? ( reply_to_user )
2022-10-25 11:53:35 +03:00
notified += notify_non_pm_users ( reply_to_user , :replied , post )
end
topic_author = post . topic . user
2023-01-09 12:20:10 +00:00
if topic_author && ! notified . include? ( topic_author ) &&
user_watching_topic? ( topic_author , post . topic )
2022-10-25 11:53:35 +03:00
notified += notify_non_pm_users ( topic_author , :replied , post )
end
2015-12-18 16:32:53 +01:00
end
2022-09-01 16:07:18 +03:00
DiscourseEvent . trigger ( :post_alerter_before_quotes , post , new_record , notified )
2015-12-18 16:32:53 +01:00
# quotes
quoted_users = extract_quoted_users ( post )
2018-03-08 11:40:48 +01:00
notified += notify_non_pm_users ( quoted_users - notified , :quoted , post )
2014-03-18 13:12:07 +11:00
2022-09-01 16:07:18 +03:00
DiscourseEvent . trigger ( :post_alerter_before_linked , post , new_record , notified )
2015-12-18 16:32:53 +01:00
# linked
linked_users = extract_linked_users ( post )
2018-03-08 11:40:48 +01:00
notified += notify_non_pm_users ( linked_users - notified , :linked , post )
2015-12-18 16:32:53 +01:00
2022-09-01 16:07:18 +03:00
DiscourseEvent . trigger ( :post_alerter_before_post , post , new_record , notified )
2023-07-04 15:08:29 +10:00
notified = notified + category_or_tag_muters ( post . topic )
2023-06-27 14:49:34 +10:00
2015-12-18 16:32:53 +01:00
if new_record
if post . topic . private_message?
2022-09-01 16:07:18 +03:00
# private messages
2023-06-08 17:41:44 +00:00
notified += notify_pm_users ( post , reply_to_user , quoted_users , notified , new_record )
2018-07-21 11:20:21 +02:00
elsif notify_about_reply? ( post )
2022-09-01 16:07:18 +03:00
# posts
2023-01-09 12:20:10 +00:00
notified +=
notify_post_users (
post ,
notified ,
new_record : new_record ,
include_category_watchers : false ,
include_tag_watchers : false ,
)
notified +=
notify_post_users (
post ,
notified ,
new_record : new_record ,
include_topic_watchers : false ,
notification_type : :watching_category_or_tag ,
)
2015-12-18 16:32:53 +01:00
end
2015-11-30 17:03:47 +11:00
end
2015-12-01 16:52:43 +11:00
sync_group_mentions ( post , mentioned_groups )
2016-07-06 15:56:40 -04:00
2022-09-01 16:07:18 +03:00
DiscourseEvent . trigger ( :post_alerter_before_first_post , post , new_record , notified )
2016-07-19 15:57:05 -04:00
if new_record && post . post_number == 1
2016-07-06 15:56:40 -04:00
topic = post . topic
if topic . present?
2018-05-17 16:09:21 +08:00
watchers = category_watchers ( topic ) + tag_watchers ( topic ) + group_watchers ( topic )
2022-08-29 16:11:11 +03:00
# Notify only users who can see the topic
watchers & = topic . all_allowed_users . pluck ( :id ) if post . topic . private_message?
2022-09-01 16:07:18 +03:00
notified += notify_first_post_watchers ( post , watchers , notified )
2016-07-06 15:56:40 -04:00
end
end
2021-09-20 21:18:38 +03:00
DiscourseEvent . trigger ( :post_alerter_after_save_post , post , new_record , notified )
2016-07-06 15:56:40 -04:00
end
2018-05-17 16:09:21 +08:00
def group_watchers ( topic )
GroupUser . where (
group_id : topic . allowed_groups . pluck ( :group_id ) ,
2023-01-09 12:20:10 +00:00
notification_level : GroupUser . notification_levels [ :watching_first_post ] ,
2018-05-17 16:09:21 +08:00
) . pluck ( :user_id )
end
def tag_watchers ( topic )
2021-12-01 10:26:56 +08:00
topic
. tag_users
. notification_level_visible ( [ TagUser . notification_levels [ :watching_first_post ] ] )
2023-01-09 12:20:10 +00:00
. distinct ( :user_id )
. pluck ( :user_id )
2018-05-17 16:09:21 +08:00
end
def category_watchers ( topic )
2023-01-09 12:20:10 +00:00
topic
. category_users
2018-05-17 16:09:21 +08:00
. where ( notification_level : CategoryUser . notification_levels [ :watching_first_post ] )
. pluck ( :user_id )
end
2023-06-27 14:49:34 +10:00
def category_or_tag_muters ( topic )
2023-07-03 19:58:53 +10:00
user_ids_sql = << ~ SQL
SELECT user_id FROM category_users WHERE category_id = #{topic.category_id.to_i} AND notification_level = #{CategoryUser.notification_levels[:muted]}
UNION
SELECT user_id FROM tag_users tu JOIN topic_tags tt ON tt . tag_id = tu . tag_id AND tt . topic_id = #{topic.id} AND tu.notification_level = #{TagUser.notification_levels[:muted]}
2023-07-06 11:27:23 +10:00
EXCEPT
SELECT user_id FROM topic_users tus WHERE tus . topic_id = #{topic.id} AND tus.notification_level = #{TopicUser.notification_levels[:watching]}
2023-07-03 19:58:53 +10:00
SQL
2023-07-04 15:08:29 +10:00
User
. where ( " id IN ( #{ user_ids_sql } ) " )
. joins ( " LEFT JOIN user_options ON user_options.user_id = users.id " )
. where (
" user_options.watched_precedence_over_muted IS false OR (user_options.watched_precedence_over_muted IS NULL AND #{ ! SiteSetting . watched_precedence_over_muted } ) " ,
)
2023-06-27 14:49:34 +10:00
end
2022-09-01 16:07:18 +03:00
def notify_first_post_watchers ( post , user_ids , notified = nil )
return [ ] if user_ids . blank?
2016-07-06 15:56:40 -04:00
user_ids . uniq!
2018-05-24 17:52:59 +02:00
warn_if_not_sidekiq
2022-08-10 18:55:29 +03:00
# Don't notify the OP and last editor
user_ids -= [ post . user_id , post . last_editor_id ]
2021-09-20 21:18:38 +03:00
users = User . where ( id : user_ids ) . includes ( :do_not_disturb_timings )
2022-09-01 16:07:18 +03:00
users = users . where . not ( id : notified . map ( & :id ) ) if notified . present?
2018-05-07 17:44:29 +08:00
2018-05-24 17:27:43 +02:00
DiscourseEvent . trigger ( :before_create_notifications_for_users , users , post )
2020-02-06 12:14:19 +00:00
each_user_in_batches ( users ) do | user |
2018-05-24 17:27:43 +02:00
create_notification ( user , Notification . types [ :watching_first_post ] , post )
2016-07-06 15:56:40 -04:00
end
2022-09-01 16:07:18 +03:00
users
2015-12-01 16:52:43 +11:00
end
def sync_group_mentions ( post , mentioned_groups )
GroupMention . where ( post_id : post . id ) . destroy_all
return if mentioned_groups . blank?
2021-03-29 12:43:24 -05:00
now = Time . zone . now
# insert_all instead of insert_all! since multiple post_alert jobs might be
# running concurrently
GroupMention . insert_all (
mentioned_groups . map do | group |
2023-01-09 12:20:10 +00:00
{ post_id : post . id , group_id : group . id , created_at : now , updated_at : now }
end ,
2021-03-29 12:43:24 -05:00
)
2014-03-18 13:12:07 +11:00
end
def unread_posts ( user , topic )
2023-01-09 12:20:10 +00:00
Post
. secured ( Guardian . new ( user ) )
. where (
" post_number > COALESCE((
2014-03-18 13:12:07 +11:00
SELECT last_read_post_number FROM topic_users tu
2023-01-09 12:20:10 +00:00
WHERE tu . user_id = ? AND tu . topic_id = ? ) , 0 ) " ,
user . id ,
topic . id ,
)
. where (
" reply_to_user_id = :user_id
2021-05-04 06:03:00 +03:00
OR exists ( SELECT 1 from topic_users tu
WHERE tu . user_id = :user_id AND
tu . topic_id = :topic_id AND
notification_level = :topic_level )
OR exists ( SELECT 1 from category_users cu
WHERE cu . user_id = :user_id AND
cu . category_id = :category_id AND
notification_level = :category_level )
OR exists ( SELECT 1 from tag_users tu
WHERE tu . user_id = :user_id AND
tu . tag_id IN ( SELECT tag_id FROM topic_tags WHERE topic_id = :topic_id ) AND
2023-01-09 12:20:10 +00:00
notification_level = :tag_level ) " ,
2021-05-04 06:03:00 +03:00
user_id : user . id ,
topic_id : topic . id ,
category_id : topic . category_id ,
topic_level : TopicUser . notification_levels [ :watching ] ,
category_level : CategoryUser . notification_levels [ :watching ] ,
2023-01-09 12:20:10 +00:00
tag_level : TagUser . notification_levels [ :watching ] ,
2021-05-04 06:03:00 +03:00
)
2017-07-28 10:20:09 +09:00
. where ( topic_id : topic . id )
2014-03-18 13:12:07 +11:00
end
def first_unread_post ( user , topic )
2023-01-09 12:20:10 +00:00
unread_posts ( user , topic ) . order ( " post_number " ) . first
2014-03-18 13:12:07 +11:00
end
def unread_count ( user , topic )
unread_posts ( user , topic ) . count
end
2018-05-25 11:59:29 +08:00
def destroy_notifications ( user , types , topic )
2014-03-18 13:12:07 +11:00
return if user . blank?
return unless Guardian . new ( user ) . can_see? ( topic )
2018-05-25 12:00:13 +08:00
User . transaction do
2023-01-09 12:20:10 +00:00
user . notifications . where ( notification_type : types , topic_id : topic . id ) . destroy_all
2018-05-25 12:00:13 +08:00
# Reload so notification counts sync up correctly
user . reload
end
2014-03-18 13:12:07 +11:00
end
2023-01-09 12:20:10 +00:00
NOTIFIABLE_TYPES =
% i [
mentioned
replied
quoted
posted
linked
private_message
group_mentioned
watching_first_post
event_reminder
event_invitation
] . map { | t | Notification . types [ t ] }
2015-05-04 13:49:32 +10:00
2016-01-27 21:38:14 +11:00
def group_stats ( topic )
2018-06-19 16:13:14 +10:00
sql = << ~ SQL
SELECT COUNT ( * ) FROM topics t
JOIN topic_allowed_groups g ON g . group_id = :group_id AND g . topic_id = t . id
LEFT JOIN group_archived_messages a ON a . topic_id = t . id AND a . group_id = g . group_id
WHERE a . id IS NULL AND t . deleted_at is NULL AND t . archetype = 'private_message'
SQL
2016-01-27 21:38:14 +11:00
topic . allowed_groups . map do | g |
{
group_id : g . id ,
2020-09-21 13:33:29 -04:00
group_name : g . name ,
2023-01-09 12:20:10 +00:00
inbox_count : DB . query_single ( sql , group_id : g . id ) . first . to_i ,
2016-01-27 21:38:14 +11:00
}
end
end
2022-03-11 11:57:47 +01:00
def notify_group_summary ( user , topic , acting_user_id : nil )
2016-01-27 21:38:14 +11:00
@group_stats || = { }
2022-03-11 11:57:47 +01:00
stats = ( @group_stats [ topic . id ] || = group_stats ( topic ) )
2016-01-27 21:38:14 +11:00
return unless stats
PERF: prevents eager pluck in post alerter (#21972)
Before (4 selects):
```
Topic Load (1.8ms) SELECT "topics"."id", "topics"."title", "topics"."last_posted_at", "topics"."created_at", "topics"."updated_at", "topics"."views", "topics"."posts_count", "topics"."user_id", "topics"."last_post_user_id", "topics"."reply_count", "topics"."featured_user1_id", "topics"."featured_user2_id", "topics"."featured_user3_id", "topics"."deleted_at", "topics"."highest_post_number", "topics"."like_count", "topics"."incoming_link_count", "topics"."category_id", "topics"."visible", "topics"."moderator_posts_count", "topics"."closed", "topics"."archived", "topics"."bumped_at", "topics"."has_summary", "topics"."archetype", "topics"."featured_user4_id", "topics"."notify_moderators_count", "topics"."spam_count", "topics"."pinned_at", "topics"."score", "topics"."percent_rank", "topics"."subtype", "topics"."slug", "topics"."deleted_by_id", "topics"."participant_count", "topics"."word_count", "topics"."excerpt", "topics"."pinned_globally", "topics"."pinned_until", "topics"."fancy_title", "topics"."highest_staff_post_number", "topics"."featured_link", "topics"."reviewable_score", "topics"."image_upload_id", "topics"."slow_mode_seconds", "topics"."bannered_until", "topics"."external_id" FROM "topics" WHERE "topics"."deleted_at" IS NULL ORDER BY "topics"."id" DESC LIMIT 1
User Load (1.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
(0.6ms) SELECT "groups"."id" FROM "groups" INNER JOIN "group_users" ON "groups"."id" = "group_users"."group_id" WHERE "group_users"."user_id" = 1
(0.4ms) SELECT "topic_allowed_groups"."group_id" FROM "topic_allowed_groups" WHERE "topic_allowed_groups"."topic_id" = 7 AND "topic_allowed_groups"."group_id" IN (1, 3, 10, 11) LIMIT 1
```
After (3 selects):
```
Topic Load (1.3ms) SELECT "topics"."id", "topics"."title", "topics"."last_posted_at", "topics"."created_at", "topics"."updated_at", "topics"."views", "topics"."posts_count", "topics"."user_id", "topics"."last_post_user_id", "topics"."reply_count", "topics"."featured_user1_id", "topics"."featured_user2_id", "topics"."featured_user3_id", "topics"."deleted_at", "topics"."highest_post_number", "topics"."like_count", "topics"."incoming_link_count", "topics"."category_id", "topics"."visible", "topics"."moderator_posts_count", "topics"."closed", "topics"."archived", "topics"."bumped_at", "topics"."has_summary", "topics"."archetype", "topics"."featured_user4_id", "topics"."notify_moderators_count", "topics"."spam_count", "topics"."pinned_at", "topics"."score", "topics"."percent_rank", "topics"."subtype", "topics"."slug", "topics"."deleted_by_id", "topics"."participant_count", "topics"."word_count", "topics"."excerpt", "topics"."pinned_globally", "topics"."pinned_until", "topics"."fancy_title", "topics"."highest_staff_post_number", "topics"."featured_link", "topics"."reviewable_score", "topics"."image_upload_id", "topics"."slow_mode_seconds", "topics"."bannered_until", "topics"."external_id" FROM "topics" WHERE "topics"."deleted_at" IS NULL ORDER BY "topics"."id" DESC LIMIT 1
User Load (0.5ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
(0.8ms) SELECT "topic_allowed_groups"."group_id" FROM "topic_allowed_groups" WHERE "topic_allowed_groups"."topic_id" = 7 AND "topic_allowed_groups"."group_id" IN (SELECT "groups"."id" FROM "groups" INNER JOIN "group_users" ON "groups"."id" = "group_users"."group_id" WHERE "group_users"."user_id" = 1) LIMIT 1
```
2023-06-07 12:46:48 +02:00
group_id = topic . topic_allowed_groups . where ( group_id : user . groups ) . pick ( :group_id )
2016-01-27 21:38:14 +11:00
2017-07-28 10:20:09 +09:00
stat = stats . find { | s | s [ :group_id ] == group_id }
2022-03-11 11:57:47 +01:00
return unless stat
2016-01-27 21:38:14 +11:00
2017-05-26 17:04:40 -04:00
DistributedMutex . synchronize ( " group_message_notify_ #{ user . id } " ) do
2022-03-11 11:57:47 +01:00
if stat [ :inbox_count ] > 0
Notification . consolidate_or_create! (
notification_type : Notification . types [ :group_message_summary ] ,
user_id : user . id ,
read : user . id === acting_user_id ? true : false ,
data : {
group_id : stat [ :group_id ] ,
group_name : stat [ :group_name ] ,
inbox_count : stat [ :inbox_count ] ,
2023-01-09 12:20:10 +00:00
username : user . username_lower ,
} . to_json ,
2022-03-11 11:57:47 +01:00
)
else
2023-01-09 12:20:10 +00:00
Notification
. where ( user_id : user . id , notification_type : Notification . types [ :group_message_summary ] )
2022-03-11 11:57:47 +01:00
. where ( " data::json ->> 'group_id' = ? " , stat [ :group_id ] . to_s )
. delete_all
end
2017-05-26 17:04:40 -04:00
end
2016-01-27 21:38:14 +11:00
# TODO decide if it makes sense to also publish a desktop notification
end
2020-02-03 11:27:19 +11:00
def should_notify_edit? ( notification , post , opts )
2020-05-04 17:55:00 +10:00
notification . created_at < 1 . day . ago ||
2023-01-09 12:20:10 +00:00
notification . data_hash [ " display_username " ] !=
( opts [ :display_username ] . presence || post . user . username )
2016-03-02 23:16:52 +11:00
end
def should_notify_like? ( user , notification )
2023-01-09 12:20:10 +00:00
if user . user_option . like_notification_frequency ==
UserOption . like_notification_frequency_type [ :always ]
return true
end
if user . user_option . like_notification_frequency ==
UserOption . like_notification_frequency_type [ :first_time_and_daily ] &&
notification . created_at < 1 . day . ago
return true
end
2018-05-15 09:51:32 +02:00
false
2016-03-02 23:16:52 +11:00
end
2020-02-03 11:27:19 +11:00
def should_notify_previous? ( user , post , notification , opts )
2016-03-07 21:56:33 +01:00
case notification . notification_type
2023-01-09 12:20:10 +00:00
when Notification . types [ :edited ]
should_notify_edit? ( notification , post , opts )
when Notification . types [ :liked ]
should_notify_like? ( user , notification )
else
false
2016-03-02 23:16:52 +11:00
end
end
2016-03-07 21:56:33 +01:00
COLLAPSED_NOTIFICATION_TYPES || = [
Notification . types [ :replied ] ,
Notification . types [ :posted ] ,
2018-12-24 12:44:50 +01:00
Notification . types [ :private_message ] ,
2022-12-14 10:22:26 +11:00
Notification . types [ :watching_category_or_tag ] ,
2016-03-07 21:56:33 +01:00
]
2017-06-12 16:41:39 +09:00
def create_notification ( user , type , post , opts = { } )
opts = @default_opts . merge ( opts )
2017-07-19 22:51:32 +02:00
DiscourseEvent . trigger ( :before_create_notification , user , type , post , opts )
2020-03-16 14:11:11 +05:30
return if user . blank? || user . bot? || post . blank?
2020-01-27 23:54:11 +05:30
return if ( topic = post . topic ) . blank?
2014-03-18 13:12:07 +11:00
2019-01-17 14:31:07 +08:00
is_liked = type == Notification . types [ :liked ]
2023-01-09 12:20:10 +00:00
if is_liked &&
user . user_option . like_notification_frequency ==
UserOption . like_notification_frequency_type [ :never ]
return
end
2016-03-06 09:12:59 +11:00
2023-02-14 16:45:06 +11:00
return if ! Guardian . new ( user ) . can_receive_post_notifications? ( post )
2014-03-18 13:12:07 +11:00
2020-01-27 23:54:11 +05:30
return if user . staged? && topic . category & . mailinglist_mirror?
2017-11-17 14:50:35 +01:00
2016-03-08 15:26:06 -05:00
notifier_id = opts [ :user_id ] || post . user_id # xxxxx look at revision history
2023-01-09 12:20:10 +00:00
if notifier_id &&
UserCommScreener . new (
acting_user_id : notifier_id ,
target_user_ids : user . id ,
) . ignoring_or_muting_actor? ( user . id )
return
end
2019-03-21 11:15:34 +00:00
2014-03-18 13:12:07 +11:00
# skip if muted on the topic
2023-01-09 12:20:10 +00:00
if TopicUser . where (
topic : topic ,
user : user ,
notification_level : TopicUser . notification_levels [ :muted ] ,
) . exists?
return
end
2014-03-18 13:12:07 +11:00
2015-12-14 23:17:09 +01:00
# skip if muted on the group
if group = opts [ :group ]
2023-01-09 12:20:10 +00:00
if GroupUser . where (
group_id : opts [ :group_id ] ,
user_id : user . id ,
notification_level : TopicUser . notification_levels [ :muted ] ,
) . exists?
return
end
2015-12-14 23:17:09 +01:00
end
2023-01-09 12:20:10 +00:00
existing_notifications =
user
. notifications
. order ( " notifications.id DESC " )
. where ( topic_id : post . topic_id , post_number : post . post_number )
. limit ( 10 )
2019-05-15 12:47:36 -04:00
# Don't notify the same user about the same type of notification on the same post
2023-01-09 12:20:10 +00:00
existing_notification_of_same_type =
existing_notifications . find { | n | n . notification_type == type }
2014-10-07 15:57:48 +11:00
2023-01-09 12:20:10 +00:00
if existing_notification_of_same_type &&
! should_notify_previous? ( user , post , existing_notification_of_same_type , opts )
2020-05-04 17:40:09 +10:00
return
end
2022-02-16 15:22:08 +10:00
# linked, quoted, mentioned, chat_quoted may be suppressed if you already have a reply notification
if [
2023-01-09 12:20:10 +00:00
Notification . types [ :quoted ] ,
Notification . types [ :linked ] ,
Notification . types [ :mentioned ] ,
Notification . types [ :chat_quoted ] ,
] . include? ( type )
if existing_notifications . find { | n | n . notification_type == Notification . types [ :replied ] }
return
end
2020-05-04 17:40:09 +10:00
end
2014-03-18 13:12:07 +11:00
collapsed = false
2016-03-07 21:56:33 +01:00
if COLLAPSED_NOTIFICATION_TYPES . include? ( type )
2020-01-27 23:54:11 +05:30
destroy_notifications ( user , COLLAPSED_NOTIFICATION_TYPES , topic )
2014-03-18 13:12:07 +11:00
collapsed = true
end
original_post = post
2018-12-24 12:44:50 +01:00
original_username = opts [ :display_username ] . presence || post . username
2014-03-18 13:12:07 +11:00
if collapsed
2020-01-27 23:54:11 +05:30
post = first_unread_post ( user , topic ) || post
count = unread_count ( user , topic )
2016-02-01 19:12:10 +01:00
if count > 1
I18n . with_locale ( user . effective_locale ) do
2023-01-09 12:20:10 +00:00
opts [ :display_username ] = I18n . t ( " embed.replies " , count : count )
2016-02-01 19:12:10 +01:00
end
2015-01-17 23:14:59 -08:00
end
2014-03-18 13:12:07 +11:00
end
2019-01-03 12:03:01 -05:00
UserActionManager . notification_created ( original_post , user , type , opts [ :acting_user_id ] )
2014-03-18 13:12:07 +11:00
2020-01-27 23:54:11 +05:30
topic_title = topic . title
2016-02-23 01:34:16 +01:00
# when sending a private message email, keep the original title
2020-01-27 23:54:11 +05:30
if topic . private_message? && modifications = post . revisions . map ( & :modifications )
2016-02-24 22:34:40 +08:00
if first_title_modification = modifications . find { | m | m . has_key? ( " title " ) }
2016-02-23 01:34:16 +01:00
topic_title = first_title_modification [ " title " ] [ 0 ]
end
end
2021-12-23 09:44:03 -03:00
notification_data = {
topic_title : topic_title ,
original_post_id : original_post . id ,
original_post_type : original_post . post_type ,
original_username : original_username ,
revision_number : opts [ :revision_number ] ,
display_username : opts [ :display_username ] || post . user . username ,
}
2015-11-30 17:03:47 +11:00
2023-01-09 12:20:10 +00:00
opts [ :custom_data ] . each { | k , v | notification_data [ k ] = v } if opts [ :custom_data ] & . is_a? ( Hash )
2021-12-27 11:25:37 -03:00
2015-11-30 17:03:47 +11:00
if group = opts [ :group ]
notification_data [ :group_id ] = group . id
notification_data [ :group_name ] = group . name
end
2020-07-10 12:05:55 +03:00
if opts [ :skip_send_email_to ] & . include? ( user . email )
skip_send_email = true
elsif original_post . via_email && ( incoming_email = original_post . incoming_email )
2021-01-15 10:54:46 +10:00
skip_send_email =
incoming_email . to_addresses_split . include? ( user . email ) ||
2023-01-09 12:20:10 +00:00
incoming_email . cc_addresses_split . include? ( user . email )
2017-11-10 16:10:25 +01:00
else
skip_send_email = opts [ :skip_send_email ]
end
2014-03-18 13:12:07 +11:00
# Create the notification
2023-06-20 14:07:02 -05:00
notification_data =
DiscoursePluginRegistry . apply_modifier ( :notification_data , notification_data )
2023-01-09 12:20:10 +00:00
created =
user . notifications . consolidate_or_create! (
notification_type : type ,
topic_id : post . topic_id ,
post_number : post . post_number ,
post_action_id : opts [ :post_action_id ] ,
data : notification_data . to_json ,
skip_send_email : skip_send_email ,
)
2018-01-22 17:11:52 +11:00
2021-10-11 20:55:18 +03:00
if created . id && existing_notifications . empty? && NOTIFIABLE_TYPES . include? ( type )
2023-01-09 12:20:10 +00:00
create_notification_alert (
user : user ,
post : original_post ,
notification_type : type ,
username : original_username ,
)
2018-01-22 17:11:52 +11:00
end
2018-05-24 17:27:43 +02:00
2018-01-22 17:11:52 +11:00
created . id ? created : nil
2014-03-18 13:12:07 +11:00
end
2018-12-04 17:54:27 +11:00
def create_notification_alert ( user : , post : , notification_type : , excerpt : nil , username : nil )
2021-09-20 21:18:38 +03:00
self . class . create_notification_alert (
user : user ,
post : post ,
notification_type : notification_type ,
excerpt : excerpt ,
2023-01-09 12:20:10 +00:00
username : username ,
2021-09-20 21:18:38 +03:00
)
2018-12-04 17:54:27 +11:00
end
2016-08-26 12:47:10 +10:00
def push_notification ( user , payload )
2021-09-20 21:18:38 +03:00
self . class . push_notification ( user , payload )
2016-08-26 12:47:10 +10:00
end
2015-11-30 17:03:47 +11:00
def expand_group_mentions ( groups , post )
return unless post . user && groups
2023-01-09 12:20:10 +00:00
Group
. mentionable ( post . user , include_public : false )
. where ( id : groups . map ( & :id ) )
. each do | group |
next if group . user_count > = SiteSetting . max_users_notified_per_group_mention
yield group , group . users
end
2015-11-30 17:03:47 +11:00
end
2021-11-23 22:25:54 +02:00
def expand_here_mention ( post , exclude_ids : nil )
posts = Post . where ( topic_id : post . topic_id )
posts = posts . where . not ( user_id : exclude_ids ) if exclude_ids . present?
if post . user . staff?
posts = posts . where ( post_type : [ Post . types [ :regular ] , Post . types [ :whisper ] ] )
else
posts = posts . where ( post_type : Post . types [ :regular ] )
end
2023-01-09 12:20:10 +00:00
User . real . where ( id : posts . select ( :user_id ) ) . limit ( SiteSetting . max_here_mentioned )
2021-11-23 22:25:54 +02:00
end
2014-03-18 13:12:07 +11:00
# TODO: Move to post-analyzer?
2015-11-30 17:03:47 +11:00
def extract_mentions ( post )
mentions = post . raw_mentions
2018-11-27 12:43:05 +01:00
return if mentions . blank?
2015-11-30 17:03:47 +11:00
2023-01-09 12:20:10 +00:00
groups = Group . where ( " LOWER(name) IN (?) " , mentions )
2018-11-27 12:43:05 +01:00
mentions -= groups . map ( & :name ) . map ( & :downcase )
groups = nil if groups . empty?
2015-11-30 17:03:47 +11:00
2018-11-27 12:43:05 +01:00
if mentions . present?
2023-01-09 12:20:10 +00:00
users =
User
. where ( username_lower : mentions )
. includes ( :do_not_disturb_timings )
. where . not ( id : post . user_id )
2018-11-27 12:43:05 +01:00
users = nil if users . empty?
2018-11-23 18:25:40 +01:00
end
2015-11-30 17:03:47 +11:00
2021-11-23 22:25:54 +02:00
# @here can be a user mention and then this feature is disabled
here = mentions . include? ( SiteSetting . here_mention ) && Guardian . new ( post . user ) . can_mention_here?
[ groups , users , here ]
2014-03-18 13:12:07 +11:00
end
# TODO: Move to post-analyzer?
# Returns a list of users who were quoted in the post
def extract_quoted_users ( post )
2023-01-09 12:20:10 +00:00
usernames =
if SiteSetting . display_name_on_posts && ! SiteSetting . prioritize_username_in_ux
post . raw . scan ( / username:([[:alnum:]]*)"(?= \ ]) / )
else
post . raw . scan ( / \ [quote= \ "([^,]+),.+ \ " \ ] / )
end . uniq . map { | q | q . first . strip . downcase }
2021-12-15 09:07:39 -03:00
User . where . not ( id : post . user_id ) . where ( username_lower : usernames )
2014-03-18 13:12:07 +11:00
end
def extract_linked_users ( post )
2023-01-09 12:20:10 +00:00
users =
post
. topic_links
. where ( reflection : false )
. map do | link |
linked_post = link . link_post
if ! linked_post && topic = link . link_topic
linked_post = topic . posts . find_by ( post_number : 1 )
end
( linked_post && post . user_id != linked_post . user_id && linked_post . user ) || nil
end
. compact
2020-07-03 14:52:49 +01:00
DiscourseEvent . trigger ( :after_extract_linked_users , users , post )
users
2014-03-18 13:12:07 +11:00
end
# Notify a bunch of users
2017-06-12 16:41:39 +09:00
def notify_non_pm_users ( users , type , post , opts = { } )
2018-03-08 11:40:48 +01:00
return [ ] if post . topic & . private_message?
2015-05-25 13:15:00 -04:00
2018-03-08 11:40:48 +01:00
notify_users ( users , type , post , opts )
end
2016-03-22 14:28:14 +11:00
2018-03-08 11:40:48 +01:00
def notify_users ( users , type , post , opts = { } )
2016-03-22 14:28:14 +11:00
users = [ users ] unless users . is_a? ( Array )
2020-01-16 15:17:16 -03:00
users . reject! ( & :staged? ) if post . topic & . private_message?
2015-05-25 13:15:00 -04:00
2018-05-24 17:52:59 +02:00
warn_if_not_sidekiq
2018-05-24 17:27:43 +02:00
DiscourseEvent . trigger ( :before_create_notifications_for_users , users , post )
2023-01-09 12:20:10 +00:00
users . each { | u | create_notification ( u , Notification . types [ type ] , post , opts ) }
2018-03-08 11:40:48 +01:00
users
2014-03-18 13:12:07 +11:00
end
2022-12-09 12:23:11 +11:00
def pm_watching_users ( post )
return [ ] if ! post . topic . private_message?
directly_targeted_users ( post ) . filter do | u |
notification_level = TopicUser . get ( post . topic , u ) & . notification_level
notification_level == TopicUser . notification_levels [ :watching ]
end
end
2023-06-08 17:41:44 +00:00
def notify_pm_users ( post , reply_to_user , quoted_users , notified , new_record = false )
2022-09-01 16:07:18 +03:00
return [ ] unless post . topic
2016-07-08 12:58:18 +10:00
2018-05-24 17:52:59 +02:00
warn_if_not_sidekiq
2021-06-28 08:55:13 +10:00
# To simplify things and to avoid IMAP double sync issues, and to cut down
# on emails sent via SMTP, any topic_allowed_users (except those who are
# not_allowed?) for a group that has SMTP enabled will have their notification
# email combined into one and sent via a single group SMTP email with CC addresses.
emails_to_skip_send = email_using_group_smtp_if_configured ( post )
# We create notifications for all directly_targeted_users and email those
# who do _not_ have their email addresses in the emails_to_skip_send array
# (which will include all topic allowed users' email addresses if group SMTP
# is enabled).
2018-05-24 17:27:43 +02:00
users = directly_targeted_users ( post ) . reject { | u | notified . include? ( u ) }
DiscourseEvent . trigger ( :before_create_notifications_for_users , users , post )
users . each do | user |
2022-12-09 12:23:11 +11:00
if reply_to_user == user || pm_watching_users ( post ) . include? ( user ) || user . staged?
2023-01-09 12:20:10 +00:00
create_notification (
user ,
Notification . types [ :private_message ] ,
post ,
skip_send_email_to : emails_to_skip_send ,
)
2018-05-15 09:51:32 +02:00
end
2018-05-24 17:27:43 +02:00
end
2018-05-15 09:51:32 +02:00
2021-06-28 08:55:13 +10:00
# Users that are part of all mentioned groups. Emails sent by this notification
# flow will not be sent via group SMTP if it is enabled.
2018-05-24 17:27:43 +02:00
users = indirectly_targeted_users ( post ) . reject { | u | notified . include? ( u ) }
DiscourseEvent . trigger ( :before_create_notifications_for_users , users , post )
users . each do | user |
case TopicUser . get ( post . topic , user ) & . notification_level
when TopicUser . notification_levels [ :watching ]
2021-12-15 09:07:39 -03:00
create_pm_notification ( user , post , emails_to_skip_send )
2018-05-24 17:27:43 +02:00
when TopicUser . notification_levels [ :tracking ]
2023-06-08 17:41:44 +00:00
# TopicUser is the canonical source of topic notification levels, except for
# new topics created within a group with default notification level set to
# `watching_first_post`. TopicUser notification level is set to `tracking`
# for these.
if is_replying? ( user , reply_to_user , quoted_users ) ||
( new_record && group_watched_first_post? ( user , post ) )
2021-12-15 09:07:39 -03:00
create_pm_notification ( user , post , emails_to_skip_send )
else
2022-03-11 11:57:47 +01:00
notify_group_summary ( user , post . topic )
2021-12-15 09:07:39 -03:00
end
when TopicUser . notification_levels [ :regular ]
if is_replying? ( user , reply_to_user , quoted_users )
create_pm_notification ( user , post , emails_to_skip_send )
end
2018-05-15 09:51:32 +02:00
end
end
end
2021-06-07 14:17:35 +10:00
def group_notifying_via_smtp ( post )
2021-11-17 09:24:17 +10:00
return if ! SiteSetting . enable_smtp || post . post_type != Post . types [ :regular ]
return if post . topic . allowed_groups . none?
2023-01-09 12:20:10 +00:00
return post . topic . first_smtp_enabled_group if post . topic . allowed_groups . count == 1
2021-11-16 10:21:49 +10:00
2021-11-17 09:24:17 +10:00
topic_incoming_email = post . topic . incoming_email . first
return if topic_incoming_email . blank?
group = Group . find_by_email ( topic_incoming_email . to_addresses )
2023-01-09 12:20:10 +00:00
return post . topic . first_smtp_enabled_group if ! group & . smtp_enabled
2021-11-16 10:21:49 +10:00
group
2021-06-07 14:17:35 +10:00
end
2021-06-28 08:55:13 +10:00
def email_using_group_smtp_if_configured ( post )
2021-01-20 10:53:08 +10:00
emails_to_skip_send = [ ]
group = group_notifying_via_smtp ( post )
return emails_to_skip_send if group . blank?
2021-06-28 08:55:13 +10:00
to_address = nil
cc_addresses = [ ]
# We need to use topic_allowed_users here instead of directly_targeted_users
# because we want to make sure the to_address goes to the OP of the topic.
2023-01-09 12:20:10 +00:00
topic_allowed_users_by_age =
post
. topic
. topic_allowed_users
. includes ( :user )
. order ( :created_at )
. reject { | tau | not_allowed? ( tau . user , post ) }
2021-06-28 08:55:13 +10:00
return emails_to_skip_send if topic_allowed_users_by_age . empty?
# This should usually be the OP of the topic, unless they are the one
# replying by email (they are excluded by not_allowed? then)
to_address = topic_allowed_users_by_age . first . user . email
cc_addresses = topic_allowed_users_by_age [ 1 .. - 1 ] . map { | tau | tau . user . email }
email_addresses = [ to_address , cc_addresses ] . flatten
# If any of these email addresses were cc address on the
# incoming email for the target post, do not send them emails (they
# already have been notified by the CC on the email)
if post . incoming_email . present?
cc_addresses = cc_addresses - post . incoming_email . cc_addresses_split
# If the to address is one of the recently added CC addresses, then we
# need to bail early, because otherwise we are sending a notification
# email to the user who was just added by CC. In this case the OP probably
# replied and CC'd some people, and they are the only other topic users.
return if post . incoming_email . cc_addresses_split . include? ( to_address )
2022-04-05 13:18:49 +10:00
# We don't want to create an email storm if someone emails the group and
# CC's 50 support addresses from various places, which all then respond
# with auto-responders saying they have received our email. Any auto-generated
# emails should not propagate notifications to anyone else, not even
# the regular topic user notifications.
return email_addresses . dup . uniq if post . incoming_email . is_auto_generated?
2021-06-28 08:55:13 +10:00
end
# Send a single email using group SMTP settings to cut down on the
# number of emails sent via SMTP, also to replicate how support systems
# and group inboxes generally work in other systems.
2021-07-05 10:09:16 +10:00
#
# We need to send this on a delay to allow for editing and finalising
# posts, the same way we do for private_message user emails/notifications.
Jobs . enqueue_in (
SiteSetting . personal_email_time_window_seconds ,
2021-06-28 08:55:13 +10:00
:group_smtp_email ,
group_id : group . id ,
post_id : post . id ,
email : to_address ,
2023-01-09 12:20:10 +00:00
cc_emails : cc_addresses ,
2021-06-28 08:55:13 +10:00
)
2021-01-20 10:53:08 +10:00
2021-06-03 14:47:32 +10:00
# Add the group's email_username into the array, because it is used for
2021-01-20 10:53:08 +10:00
# skip_send_email_to in the case of user private message notifications
# (we do not want the group to be sent any emails from here because it
# will make another email for IMAP to pick up in the group's mailbox)
emails_to_skip_send = email_addresses . dup if email_addresses . any?
emails_to_skip_send << group . email_username
2021-06-28 08:55:13 +10:00
emails_to_skip_send . uniq
2021-01-20 10:53:08 +10:00
end
2023-01-09 12:20:10 +00:00
def notify_post_users (
post ,
notified ,
group_ids : nil ,
include_topic_watchers : true ,
include_category_watchers : true ,
include_tag_watchers : true ,
new_record : false ,
notification_type : nil
)
2022-09-01 16:07:18 +03:00
return [ ] unless post . topic
2016-07-08 12:58:18 +10:00
2018-05-24 17:52:59 +02:00
warn_if_not_sidekiq
2019-05-03 08:17:27 +10:00
condition = + << ~ SQL
2021-06-11 03:55:50 +03:00
users . id IN (
2020-08-13 17:22:34 +10:00
SELECT id FROM users WHERE false
/ *topic* /
/ *category* /
/ *tags* /
)
SQL
2023-01-09 12:20:10 +00:00
condition . sub! " /*topic*/ " , << ~ SQL if include_topic_watchers
2020-08-13 17:22:34 +10:00
UNION
2018-05-15 09:51:32 +02:00
SELECT user_id
FROM topic_users
WHERE notification_level = :watching
AND topic_id = :topic_id
2020-08-13 17:22:34 +10:00
SQL
2016-07-08 12:58:18 +10:00
2023-01-09 12:20:10 +00:00
condition . sub! " /*category*/ " , << ~ SQL if include_category_watchers
2019-11-12 16:44:46 +11:00
UNION
2016-07-08 12:58:18 +10:00
2018-05-15 09:51:32 +02:00
SELECT cu . user_id
FROM category_users cu
LEFT JOIN topic_users tu ON tu . user_id = cu . user_id
AND tu . topic_id = :topic_id
WHERE cu . notification_level = :watching
AND cu . category_id = :category_id
2020-08-13 17:22:34 +10:00
AND ( tu . user_id IS NULL OR tu . notification_level = :watching )
2019-11-12 16:44:46 +11:00
SQL
2016-07-08 12:58:18 +10:00
2023-01-09 12:20:10 +00:00
tag_ids = post . topic . topic_tags . pluck ( " topic_tags.tag_id " )
2016-07-08 12:58:18 +10:00
2023-01-09 12:20:10 +00:00
condition . sub! " /*tags*/ " , << ~ SQL if include_tag_watchers && tag_ids . present?
2018-05-15 09:51:32 +02:00
UNION
SELECT tag_users . user_id
FROM tag_users
LEFT JOIN topic_users tu ON tu . user_id = tag_users . user_id
AND tu . topic_id = :topic_id
2021-12-01 10:26:56 +08:00
LEFT JOIN tag_group_memberships tgm ON tag_users . tag_id = tgm . tag_id
LEFT JOIN tag_group_permissions tgp ON tgm . tag_group_id = tgp . tag_group_id
LEFT JOIN group_users gu ON gu . user_id = tag_users . user_id
2022-04-06 11:43:57 +10:00
WHERE (
tgp . group_id IS NULL OR
tgp . group_id = gu . group_id OR
tgp . group_id = :everyone_group_id OR
gu . group_id = :staff_group_id
)
2021-12-01 10:26:56 +08:00
AND ( tag_users . notification_level = :watching
AND tag_users . tag_id IN ( :tag_ids )
AND ( tu . user_id IS NULL OR tu . notification_level = :watching ) )
2018-05-15 09:51:32 +02:00
SQL
2016-07-08 12:58:18 +10:00
2023-01-09 12:20:10 +00:00
notify =
User . where (
condition ,
watching : TopicUser . notification_levels [ :watching ] ,
topic_id : post . topic_id ,
category_id : post . topic . category_id ,
tag_ids : tag_ids ,
staff_group_id : Group :: AUTO_GROUPS [ :staff ] ,
everyone_group_id : Group :: AUTO_GROUPS [ :everyone ] ,
)
2015-11-30 17:03:47 +11:00
2021-06-11 03:55:50 +03:00
if group_ids . present?
notify = notify . joins ( :group_users ) . where ( " group_users.group_id IN (?) " , group_ids )
end
2023-01-09 12:20:10 +00:00
notify = notify . where ( staged : false ) . staff if post . topic . private_message?
2020-08-13 17:22:34 +10:00
2015-12-18 16:32:53 +01:00
exclude_user_ids = notified . map ( & :id )
2021-06-11 03:55:50 +03:00
notify = notify . where ( " users.id NOT IN (?) " , exclude_user_ids ) if exclude_user_ids . present?
2015-11-30 17:03:47 +11:00
2018-05-24 17:27:43 +02:00
DiscourseEvent . trigger ( :before_create_notifications_for_users , notify , post )
2020-01-21 08:41:13 +11:00
2023-01-09 12:20:10 +00:00
already_seen_user_ids =
Set . new (
TopicUser
. where ( topic_id : post . topic . id )
. where ( " last_read_post_number >= ? " , post . post_number )
. pluck ( :user_id ) ,
)
2020-01-21 08:41:13 +11:00
2020-02-06 12:14:19 +00:00
each_user_in_batches ( notify ) do | user |
2022-12-14 10:22:26 +11:00
calculated_type =
if ! new_record && already_seen_user_ids . include? ( user . id )
Notification . types [ :edited ]
elsif notification_type
Notification . types [ notification_type ]
else
Notification . types [ :posted ]
end
2020-05-07 07:52:21 +10:00
opts = { }
2023-01-09 12:20:10 +00:00
opts [ :display_username ] = post . last_editor . username if calculated_type ==
Notification . types [ :edited ]
2022-12-14 10:22:26 +11:00
create_notification ( user , calculated_type , post , opts )
2015-12-18 16:32:53 +01:00
end
2022-09-01 16:07:18 +03:00
notify
2014-03-18 13:12:07 +11:00
end
2015-11-30 17:03:47 +11:00
2018-05-24 17:52:59 +02:00
def warn_if_not_sidekiq
2023-01-09 12:20:10 +00:00
unless Sidekiq . server?
Rails . logger . warn (
" PostAlerter. #{ caller_locations ( 1 , 1 ) [ 0 ] . label } was called outside of sidekiq " ,
)
end
2018-05-24 17:52:59 +02:00
end
2020-02-06 12:14:19 +00:00
private
def each_user_in_batches ( users )
# This is race-condition-safe, unlike #find_in_batches
2023-01-09 12:20:10 +00:00
users
. pluck ( :id )
. each_slice ( USER_BATCH_SIZE ) do | user_ids_batch |
User . where ( id : user_ids_batch ) . includes ( :do_not_disturb_timings ) . each { | user | yield ( user ) }
end
2020-02-06 12:14:19 +00:00
end
2021-12-15 09:07:39 -03:00
def create_pm_notification ( user , post , emails_to_skip_send )
2023-01-09 12:20:10 +00:00
create_notification (
user ,
Notification . types [ :private_message ] ,
post ,
skip_send_email_to : emails_to_skip_send ,
)
2021-12-15 09:07:39 -03:00
end
def is_replying? ( user , reply_to_user , quoted_users )
reply_to_user == user || quoted_users . include? ( user )
end
2022-10-25 11:53:35 +03:00
def user_watching_topic? ( user , topic )
TopicUser . exists? (
user_id : user . id ,
topic_id : topic . id ,
2023-01-09 12:20:10 +00:00
notification_level : TopicUser . notification_levels [ :watching ] ,
2022-10-25 11:53:35 +03:00
)
end
2023-06-08 17:41:44 +00:00
def group_watched_first_post? ( user , post )
post . is_first_post? && group_watchers ( post . topic ) . include? ( user . id )
end
2014-03-18 13:12:07 +11:00
end