# frozen_string_literal: true module DiscourseAutomation module EventHandlers def self.handle_post_created_edited(post, action) return if post.post_type != Post.types[:regular] || post.user_id < 0 topic = post.topic return if topic.blank? name = DiscourseAutomation::Triggers::POST_CREATED_EDITED DiscourseAutomation::Automation .where(trigger: name, enabled: true) .find_each do |automation| original_post_only = automation.trigger_field("original_post_only") if original_post_only["value"] next if topic.posts_count > 1 end first_post_only = automation.trigger_field("first_post_only") if first_post_only["value"] next if post.user.user_stat.post_count != 1 end first_topic_only = automation.trigger_field("first_topic_only") if first_topic_only["value"] next if post.post_number != 1 next if post.user.user_stat.topic_count != 1 end skip_via_email = automation.trigger_field("skip_via_email") if skip_via_email["value"] next if post.via_email? end valid_trust_levels = automation.trigger_field("valid_trust_levels") if valid_trust_levels["value"] next if valid_trust_levels["value"].exclude?(post.user.trust_level) end restricted_category = automation.trigger_field("restricted_category") if restricted_category["value"] category_ids = if topic.category_id.blank? [] else [topic.category_id, topic.category.parent_category_id] end next if !category_ids.include?(restricted_category["value"]) end restricted_tags = automation.trigger_field("restricted_tags") if restricted_tags["value"] next if (restricted_tags["value"] & topic.tags.map(&:name)).empty? end restricted_group_ids = automation.trigger_field("restricted_groups")["value"] if restricted_group_ids.present? next if !topic.private_message? target_group_ids = topic.allowed_groups.pluck(:id) next if (restricted_group_ids & target_group_ids).empty? ignore_group_members = automation.trigger_field("ignore_group_members")["value"] next if ignore_group_members && post.user.in_any_groups?(restricted_group_ids) end ignore_automated = automation.trigger_field("ignore_automated") next if ignore_automated["value"] && post.incoming_email&.is_auto_generated? action_type = automation.trigger_field("action_type") selected_action = action_type["value"]&.to_sym if selected_action next if selected_action == :created && action != :create next if selected_action == :edited && action != :edit end automation.trigger!( "kind" => name, "action" => action, "post" => post, "placeholders" => { "topic_url" => topic.relative_url, "topic_title" => topic.title, }, ) end end def self.handle_user_updated(user, new_user: false) return if user.id < 0 name = DiscourseAutomation::Triggers::USER_UPDATED DiscourseAutomation::Automation .where(trigger: name, enabled: true) .find_each do |automation| once_per_user = automation.trigger_field("once_per_user")["value"] if once_per_user && user.custom_fields[ DiscourseAutomation::AUTOMATION_IDS_CUSTOM_FIELD ].presence&.include?(automation.id) next end new_users_only = automation.trigger_field("new_users_only")["value"] new_user_custom_field = automation.new_user_custom_field_name new_user ||= user.custom_fields[new_user_custom_field].present? next if new_users_only && !new_user required_custom_fields = automation.trigger_field("custom_fields") user_data = {} user_custom_fields_data = DB.query <<-SQL SELECT uf.name AS field_name, ucf.value AS field_value FROM user_fields uf JOIN user_custom_fields ucf ON CONCAT('user_field_', uf.id) = ucf.name WHERE ucf.user_id = #{user.id}; SQL user_custom_fields_data = user_custom_fields_data.each_with_object({}) do |obj, hash| field_name = obj.field_name field_value = obj.field_value hash[field_name] = field_value end if required_custom_fields["value"] if required_custom_fields["value"].any? { |field| user_custom_fields_data[field].blank? } if new_users_only user.custom_fields[new_user_custom_field] = "1" user.save_custom_fields end next end user_data[:custom_fields] = user_custom_fields_data end required_user_profile_fields = automation.trigger_field("user_profile") user_profile_data = UserProfile.find(user.id).attributes if required_user_profile_fields["value"] if required_user_profile_fields["value"].any? { |field| user_profile_data[field].blank? } if new_users_only user.custom_fields[new_user_custom_field] = "1" user.save_custom_fields end next end user_data[:profile_data] = user_profile_data end if new_users_only && once_per_user user.custom_fields.delete(new_user_custom_field) user.save_custom_fields end automation.add_id_to_custom_field(user, DiscourseAutomation::AUTOMATION_IDS_CUSTOM_FIELD) automation.trigger!("kind" => name, "user" => user, "user_data" => user_data) end end def self.handle_category_created_edited(category, action) name = DiscourseAutomation::Triggers::CATEGORY_CREATED_EDITED DiscourseAutomation::Automation .where(trigger: name, enabled: true) .find_each do |automation| restricted_category = automation.trigger_field("restricted_category") if restricted_category["value"].present? next if restricted_category["value"] != category.parent_category_id end automation.trigger!("kind" => name, "action" => action, "category" => category) end end def self.handle_pm_created(topic) return if topic.user_id < 0 user = topic.user target_usernames = topic.allowed_users.pluck(:username) - [user.username] target_group_ids = topic.allowed_groups.pluck(:id) return if (target_usernames.length + target_group_ids.length) > 1 name = DiscourseAutomation::Triggers::PM_CREATED DiscourseAutomation::Automation .where(trigger: name, enabled: true) .find_each do |automation| restricted_username = automation.trigger_field("restricted_user")["value"] next if restricted_username.present? && restricted_username != target_usernames.first restricted_group_id = automation.trigger_field("restricted_group")["value"] next if restricted_group_id.present? && restricted_group_id != target_group_ids.first ignore_staff = automation.trigger_field("ignore_staff") next if ignore_staff["value"] && user.staff? ignore_group_members = automation.trigger_field("ignore_group_members") next if ignore_group_members["value"] && user.in_any_groups?([restricted_group_id]) ignore_automated = automation.trigger_field("ignore_automated") next if ignore_automated["value"] && topic.first_post.incoming_email&.is_auto_generated? valid_trust_levels = automation.trigger_field("valid_trust_levels") if valid_trust_levels["value"] next if !valid_trust_levels["value"].include?(user.trust_level) end automation.trigger!("kind" => name, "post" => topic.first_post) end end def self.handle_topic_tags_changed(topic, old_tag_names, new_tag_names, user) name = DiscourseAutomation::Triggers::TOPIC_TAGS_CHANGED DiscourseAutomation::Automation .where(trigger: name, enabled: true) .find_each do |automation| if topic.private_message? next if !automation.trigger_field("trigger_with_pms")["value"] end watching_categories = automation.trigger_field("watching_categories") if watching_categories["value"] next if !watching_categories["value"].include?(topic.category_id) end removed_tags = old_tag_names - new_tag_names added_tags = new_tag_names - old_tag_names watching_tags = automation.trigger_field("watching_tags") if watching_tags["value"] should_skip = false watching_tags["value"].each do |tag| should_skip = true if !removed_tags.empty? && !removed_tags.include?(tag) should_skip = true if !added_tags.empty? && !added_tags.include?(tag) end next if should_skip end automation.trigger!( "kind" => name, "topic" => topic, "removed_tags" => removed_tags, "added_tags" => added_tags, "user" => user, "placeholders" => { "topic_url" => topic.relative_url, "topic_title" => topic.title, }, ) end end def self.handle_after_post_cook(post, cooked) return cooked if post.post_type != Post.types[:regular] || post.post_number > 1 name = DiscourseAutomation::Triggers::AFTER_POST_COOK DiscourseAutomation::Automation .where(trigger: name, enabled: true) .find_each do |automation| valid_trust_levels = automation.trigger_field("valid_trust_levels") if valid_trust_levels["value"] next if valid_trust_levels["value"].exclude?(post.user.trust_level) end restricted_category = automation.trigger_field("restricted_category") if restricted_category["value"] category_ids = [post.topic&.category&.parent_category&.id, post.topic&.category&.id] next if !category_ids.compact.include?(restricted_category["value"]) end restricted_tags = automation.trigger_field("restricted_tags") if tag_names = restricted_tags["value"] found = false next if !post.topic post.topic.tags.each do |tag| found ||= tag_names.include?(tag.name) break if found end next if !found end if new_cooked = automation.trigger!("kind" => name, "post" => post, "cooked" => cooked) cooked = new_cooked end end cooked end def self.handle_user_promoted(user_id, new_trust_level, old_trust_level) trigger = DiscourseAutomation::Triggers::USER_PROMOTED user = User.find_by(id: user_id) return if user.blank? # don't want to do anything if the user is demoted. this should probably # be a separate event in core return if new_trust_level < old_trust_level DiscourseAutomation::Automation .where(trigger: trigger, enabled: true) .find_each do |automation| trust_level_code_all = DiscourseAutomation::USER_PROMOTED_TRUST_LEVEL_CHOICES.first[:id] restricted_group_id = automation.trigger_field("restricted_group")["value"] trust_level_transition = automation.trigger_field("trust_level_transition")["value"] trust_level_transition = trust_level_transition || trust_level_code_all if restricted_group_id.present? && !GroupUser.exists?(user_id: user_id, group_id: restricted_group_id) next end transition_code = "TL#{old_trust_level}#{new_trust_level}" if trust_level_transition == trust_level_code_all || trust_level_transition == transition_code automation.trigger!( "kind" => trigger, "usernames" => [user.username], "placeholders" => { "trust_level_transition" => I18n.t( "discourse_automation.triggerables.user_promoted.transition_placeholder", from_level_name: TrustLevel.name(old_trust_level), to_level_name: TrustLevel.name(new_trust_level), ), }, ) end end end def self.handle_stalled_topic(post) return if post.topic.blank? return if post.user_id != post.topic.user_id DiscourseAutomation::Automation .where(trigger: DiscourseAutomation::Triggers::STALLED_TOPIC) .where(enabled: true) .find_each do |automation| fields = automation.serialized_fields categories = fields.dig("categories", "value") next if categories && !categories.include?(post.topic.category_id) tags = fields.dig("tags", "value") next if tags&.any? && (tags & post.topic.tags.map(&:name)).empty? DiscourseAutomation::UserGlobalNotice .where(identifier: automation.id) .where(user_id: post.user_id) .destroy_all end end end end