module DiscourseChat module Manager KEY_PREFIX = 'category_'.freeze def self.guardian Guardian.new(User.find_by(username: SiteSetting.chat_discourse_username)) end def self.get_store_key(cat_id = nil) "#{KEY_PREFIX}#{cat_id.present? ? cat_id : '*'}" end def self.get_all_rules rules = [] PluginStoreRow.where(plugin_name: DiscourseChat::PLUGIN_NAME) .where("key ~* :pattern", pattern: "^#{DiscourseChat::Manager::KEY_PREFIX}.*") .each do |row| PluginStore.cast_value(row.type_name, row.value).each_with_index do |rule, thisIndex| category_id = if row.key == DiscourseChat::Manager.get_store_key nil else row.key.gsub!(DiscourseChat::Manager::KEY_PREFIX, '') row.key end rules << { id: "#{(category_id || 'all')}_#{thisIndex}", provider: rule[:provider], channel: rule[:channel], filter: rule[:filter], category_id: category_id, tags: rule[:tags] } end end return rules end def self.get_rules_for_provider(provider) get_all_rules.select { |rule| rule[:provider] == provider } end def self.get_rules_for_category(cat_id = nil) PluginStore.get(DiscourseChat::PLUGIN_NAME, get_store_key(cat_id)) || [] end def self.trigger_notifications(post_id) Rails.logger.info("Triggering chat notifications for post #{post_id}") post = Post.find_by(id: post_id) # Abort if the chat_user doesn't have permission to see the post return if !guardian.can_see?(post) # Abort if the post is blank, or is non-regular (e.g. a "topic closed" notification) return if post.blank? || post.post_type != Post.types[:regular] topic = post.topic # Abort if a private message (possible TODO: Add support for notifying about group PMs) return if topic.blank? || topic.archetype == Archetype.private_message # Load all the rules that apply to this topic's category matching_rules = get_rules_for_category(topic.category_id) if topic.category # Also load the rules for the wildcard category matching_rules += get_rules_for_category(nil) end # If tagging is enabled, thow away rules that don't apply to this topic if SiteSetting.tagging_enabled topic_tags = topic.tags.present? ? topic.tags.pluck(:name) : [] matching_rules = matching_rules.select do |rule| next true if rule[:tags].nil? or rule[:tags].empty? # Filter has no tags specified any_tags_match = !((rule[:tags] & topic_tags).empty?) next any_tags_match # If any tags match, keep this filter, otherwise throw away end end # Sort by order of precedence (mute always wins; watch beats follow) precedence = { 'mute' => 0, 'watch' => 1, 'follow' => 2} sort_func = proc { |a, b| precedence[a[:filter]] <=> precedence[b[:filter]] } matching_rules = matching_rules.sort(&sort_func) # Take the first rule for each channel uniq_func = proc { |rule| rule.values_at(:provider, :channel) } matching_rules = matching_rules.uniq(&uniq_func) # If a matching rule is set to mute, we can discard it now matching_rules = matching_rules.select { |rule| rule[:filter] != "mute" } # If this is not the first post, discard all "follow" rules if not post.is_first_post? matching_rules = matching_rules.select { |rule| rule[:filter] != "follow" } end # All remaining rules now require a notification to be sent # If there are none left, abort return false if matching_rules.empty? # Loop through each rule, and trigger appropriate notifications matching_rules.each do |rule| Rails.logger.info("Sending notification to provider #{rule[:provider]}, channel #{rule[:channel]}") provider = ::DiscourseChat::Provider.get_by_name(rule[:provider]) if provider provider.trigger_notification(post, rule[:channel]) else puts "Can't find provider" # TODO: Handle when the provider does not exist end end end def self.create_rule(provider, channel, filter, category_id, tags) raise "Invalid filter" if !['mute','follow','watch'].include?(filter) data = get_rules_for_category(category_id) tags = Tag.where(name: tags).pluck(:name) tags = nil if tags.blank? data.push(provider: provider, channel: channel, filter: filter, tags: tags) PluginStore.set(DiscourseChat::PLUGIN_NAME, get_store_key(category_id), data) end end end