Post di RSVP del calendario

Ciao, avevo un problema riguardante gli utenti quando partecipano o meno a un evento: nell’evento apparivano due avatar invece di uno, e il problema si risolveva aggiornando la pagina, ma creava confusione tra gli utenti. Ho modificato il codice dal mio lato, non so se possa interessarti.

# frozen_string_literal: true
# name: discourse-calendar-rsvp-posts
# about: Crea risposte brevi ai topic per gli eventi RSVP
# version: 0.4
# authors: Mario Santana

after_initialize do
 module ::CalendarRsvpPosts
   PLUGIN_NAME = "discourse-calendar-rsvp-posts"
   
   def self.history_marker
     I18n.t('calendar_rsvp_posts.markers.history')
   end
   
   def self.notification_marker
     I18n.t('calendar_rsvp_posts.markers.notification')
   end

   # Helper per decidere se ignorare questo evento
   def self.should_post_for_event?(event)
     return false if event.nil?
     return true if SiteSetting.calendar_rsvp_posts_allow_past_events
     return false if event.starts_at.nil?
     event.starts_at >= Time.current
   end

   # Trova eventuali post RSVP per un evento (storico o semplice notifica)
   def self.find_rsvp_posts(event)
     return [] if event.nil? || event.post.nil? || event.post.topic.nil?
     
     event.post.topic.posts
       .where(user_id: Discourse.system_user.id)
       .where("raw LIKE ? OR raw LIKE ?", 
              "%#{history_marker}%",
              "%#{notification_marker}%")
       .order(created_at: :asc)
   end

   # Trova specificamente il post dello storico
   def self.find_history_post(event)
     return nil if event.nil? || event.post.nil? || event.post.topic.nil?
     
     event.post.topic.posts
       .where(user_id: Discourse.system_user.id)
       .where("raw LIKE ?", "%#{history_marker}%")
       .order(created_at: :asc)
       .first
   end

   # Trova ed elimina tutti i post di notifica (ma non il post dello storico)
   def self.delete_notification_posts(event)
     return if event.nil? || event.post.nil? || event.post.topic.nil?
     
     notification_posts = event.post.topic.posts
       .where(user_id: Discourse.system_user.id)
       .where("raw LIKE ?", "%#{notification_marker}%")
     
     notification_posts.each do |post|
       begin
         PostDestroyer.new(Discourse.system_user, post, context: "pulizia calendar-rsvp-posts").destroy
       rescue StandardError => e
         Rails.logger.warn("calendar-rsvp-posts: impossibile eliminare il post di notifica #{post.id}: #{e}")
       end
     end
   end

   # Costruisce una riga di voce dello storico con timestamp
   def self.build_history_entry(username, action_label, extra_text = nil)
     timestamp = Time.current.strftime("%Y-%m-%d %H:%M UTC")
     entry = "- **#{timestamp}** - #{username} #{action_label}"
     entry += " (#{extra_text})" if extra_text.present?
     entry
   end

   # Costruisce o aggiorna il contenuto del post dello storico
   def self.build_history_raw(event, new_entry)
     event_title = (event.name.presence || event.post.topic.title).to_s
     parts = []
     parts << history_marker
     parts << "### #{I18n.t('calendar_rsvp_posts.history.header', event_title: event_title)}"
     parts << ""
     parts << new_entry
     parts.join("\n")
   end

   # Aggiunge una nuova voce allo storico esistente
   def self.append_to_history(existing_raw, new_entry)
     lines = existing_raw.split("\n")
     header_end_idx = lines.index { |line| line.start_with?("### RSVP History") }
     
     if header_end_idx
       insert_idx = header_end_idx + 2
       lines.insert(insert_idx, new_entry)
     else
       lines << new_entry
     end
     
     lines.join("\n")
   end

   # Converte un semplice post di notifica nel formato storico
   def self.convert_to_history(simple_post, event)
     raw = simple_post.raw
     
     going_label = I18n.t('calendar_rsvp_posts.actions.going').gsub('(', '\\(').gsub(')', '\\)')
     interested_label = I18n.t('calendar_rsvp_posts.actions.interested').gsub('(', '\\(').gsub(')', '\\)')
     not_going_label = I18n.t('calendar_rsvp_posts.actions.not_going').gsub('(', '\\(').gsub(')', '\\)')
     removed_label = I18n.t('calendar_rsvp_posts.actions.removed').gsub('(', '\\(').gsub(')', '\\)')
     
     pattern = /\*\*([^\*]+)\s+(#{Regexp.escape(going_label)}|#{Regexp.escape(interested_label)}|#{Regexp.escape(not_going_label)}|#{Regexp.escape(removed_label)})\*\*/
     match = raw.match(pattern)
     
     if match
       username = match[1]
       action = match[2]
       
       extra_match = raw.match(/\.\s+([^.]+)\.$/)
       extra_text = extra_match ? extra_match[1] : nil
       
       timestamp = simple_post.created_at.strftime("%Y-%m-%d %H:%M UTC")
       first_entry = "- **#{timestamp}** - #{username} #{action}"
       first_entry += " (#{extra_text})" if extra_text.present?
       
       event_title = (event.name.presence || event.post.topic.title).to_s
       parts = []
       parts << history_marker
       parts << "### #{I18n.t('calendar_rsvp_posts.history.header', event_title: event_title)}"
       parts << ""
       parts << first_entry
       parts.join("\n")
     else
       event_title = (event.name.presence || event.post.topic.title).to_s
       history_marker + "\n### #{I18n.t('calendar_rsvp_posts.history.header', event_title: event_title)}\n\n" + raw
     end
   end

   # Costruisce un post di notifica
   def self.build_notification_raw(username, action_label, event, extra_text = nil)
     event_title = (event.name.presence || event.post.topic.title).to_s
     extra_text_formatted = extra_text.present? ? "#{extra_text} " : ""
     
     notification_raw = notification_marker + "\n"
     notification_raw += I18n.t('calendar_rsvp_posts.notification.template', 
                               event_title: event_title,
                               extra: extra_text_formatted,
                               username: username,
                               action: action_label)
     notification_raw
   end

   # Metodo logico centrale per gestire la creazione/aggiornamento dei post
   def self.publish_rsvp_update(event, username, action_label, extra_text = nil)
     if SiteSetting.calendar_rsvp_posts_enable_history
       history_post = find_history_post(event)
       all_rsvp_posts = find_rsvp_posts(event)
       new_entry = build_history_entry(username, action_label, extra_text)

       if all_rsvp_posts.empty?
         notification_raw = build_notification_raw(username, action_label, event, extra_text)
         PostCreator.create!(
           Discourse.system_user,
           topic_id: event.post.topic_id,
           raw: notification_raw,
           skip_validations: true
         )
       elsif history_post.nil?
         first_post = all_rsvp_posts.first
         history_raw = convert_to_history(first_post, event)
         history_raw = append_to_history(history_raw, new_entry)
         
         PostRevisor.new(first_post, event.post.topic).revise!(
           Discourse.system_user,
           raw: history_raw,
           skip_validations: true,
           skip_revision: false
         )

         notification_raw = build_notification_raw(username, action_label, event, extra_text)
         PostCreator.create!(
           Discourse.system_user,
           topic_id: event.post.topic_id,
           raw: notification_raw,
           skip_validations: true
         )
       else
         updated_raw = append_to_history(history_post.raw, new_entry)
         PostRevisor.new(history_post, event.post.topic).revise!(
           Discourse.system_user,
           raw: updated_raw,
           skip_validations: true,
           skip_revision: false
         )

         delete_notification_posts(event)

         notification_raw = build_notification_raw(username, action_label, event, extra_text)
         PostCreator.create!(
           Discourse.system_user,
           topic_id: event.post.topic_id,
           raw: notification_raw,
           skip_validations: true
         )
       end
     else
       all_rsvp_posts = find_rsvp_posts(event)
       
       all_rsvp_posts.each do |post|
         begin
           PostDestroyer.new(Discourse.system_user, post, context: "pulizia calendar-rsvp-posts").destroy
         rescue StandardError => e
           Rails.logger.warn("calendar-rsvp-posts: impossibile eliminare il post #{post.id}: #{e}")
         end
       end

       notification_raw = build_notification_raw(username, action_label, event, extra_text)
       PostCreator.create!(
         Discourse.system_user,
         topic_id: event.post.topic_id,
         raw: notification_raw,
         skip_validations: true
       )
     end
   end
 end

 # ==========================================
 # Definizione del Job in Background
 # ==========================================
 module ::Jobs
   class ProcessCalendarRsvpPost < ::Jobs::Base
     def execute(args)
       event_id = args[:event_id]
       username = args[:username]
       action_label = args[:action_label]
       extra_text = args[:extra_text]

       event = DiscoursePostEvent::Event.find_by(id: event_id)
       return unless event

       ::CalendarRsvpPosts.publish_rsvp_update(event, username, action_label, extra_text)
     end
   end
 end

 # ==========================================
 # Gestori degli Eventi
 # ==========================================

 # Gestore per creazione/aggiornamento della partecipazione
 proc_handler = proc do |invitee|
   begin
     event = invitee&.event
     next if event.nil?
     next unless CalendarRsvpPosts.should_post_for_event?(event)

     going_val = DiscoursePostEvent::Invitee.statuses[:going]
     interested_val = DiscoursePostEvent::Invitee.statuses[:interested]
     not_going_val = DiscoursePostEvent::Invitee.statuses[:not_going]

     new_status = invitee.status
     prev_status =
       if invitee.respond_to?(:previous_changes) && invitee.previous_changes["status"]
         invitee.previous_changes["status"][0]
       else
         nil
       end

     next if prev_status && prev_status == new_status

     username = invitee.user&.username || "qualcuno"
     action_label = nil

     if new_status == going_val && SiteSetting.calendar_rsvp_posts_on_new_going
       action_label = I18n.t('calendar_rsvp_posts.actions.going')
     elsif new_status == interested_val && SiteSetting.calendar_rsvp_posts_on_new_interested
       action_label = I18n.t('calendar_rsvp_posts.actions.interested')
     elsif new_status == not_going_val && SiteSetting.calendar_rsvp_posts_on_new_not_going
       action_label = I18n.t('calendar_rsvp_posts.actions.not_going')
     end

     next if action_label.nil?

     extra_text = nil
     if event.max_attendees.present?
       current_going = event.going_count
       prev_going =
         if prev_status == going_val && new_status != going_val
           current_going + 1
         elsif prev_status != going_val && new_status == going_val
           current_going - 1
         else
           current_going
         end

       was_full = prev_going >= event.max_attendees
       is_full = current_going >= event.max_attendees

       if was_full && !is_full
         extra_text = I18n.t('calendar_rsvp_posts.capacity.spots_available')
       elsif !was_full && is_full
         extra_text = I18n.t('calendar_rsvp_posts.capacity.now_full')
       end
     end

     # Metti in coda il job invece di bloccare la richiesta
     Jobs.enqueue(:process_calendar_rsvp_post, 
       event_id: event.id, 
       username: username, 
       action_label: action_label, 
       extra_text: extra_text
     )

   rescue StandardError => e
     Rails.logger.warn("calendar-rsvp-posts: errore del gestore: #{e}")
     Rails.logger.warn(e.backtrace.join("\n"))
   end
 end

 on(:discourse_calendar_post_event_invitee_status_changed, &proc_handler)

 # Gestore per eliminazioni esplicite degli invitati (rimozione RSVP)
 if defined?(DiscoursePostEvent::Invitee)
   DiscoursePostEvent::Invitee.class_eval do
     after_destroy do
       event = self.event
       
       should_process = event.present? &&
                        SiteSetting.calendar_rsvp_posts_on_removed_rsvp &&
                        (SiteSetting.calendar_rsvp_posts_allow_past_events || event.starts_at.nil? || event.starts_at >= Time.current)
       
       if should_process
         begin
           username = self.user&.username || "qualcuno"
           action_label = I18n.t('calendar_rsvp_posts.actions.removed')

           # Metti in coda il job invece di bloccare la richiesta
           Jobs.enqueue(:process_calendar_rsvp_post, 
             event_id: event.id, 
             username: username, 
             action_label: action_label,
             extra_text: nil
           )
           
         rescue StandardError => e
           Rails.logger.warn("calendar-rsvp-posts: errore del gestore after_destroy: #{e}")
           Rails.logger.warn(e.backtrace.join("\n"))
         end
       end
     end
   end
 end
end