Hallo, ich hatte ein Problem mit den Benutzern, wenn sie an einer Veranstaltung teilnehmen oder nicht. Es erschienen zwei Avatare in der Veranstaltung anstatt nur einer. Nach einem Neuladen war das Problem zwar behoben, aber es verwirrte die Benutzer. Ich habe den Code auf meiner Seite angepasst; ich weiß nicht, ob dich das interessiert.
# frozen_string_literal: true
# name: discourse-calendar-rsvp-posts
# about: Erstelle kurze Themenantworten für RSVP-Veranstaltungen
# 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
# Hilfsfunktion zur Entscheidung, ob wir diese Veranstaltung ignorieren sollen
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
# Finde alle RSVP-Beiträge für eine Veranstaltung (Verlauf oder einfache Benachrichtigung)
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
# Finde speziell den Verlaufsbeitrag
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
# Finde und lösche alle Benachrichtigungsbeiträge (aber nicht den Verlaufsbeitrag)
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: "calendar-rsvp-posts cleanup").destroy
rescue StandardError => e
Rails.logger.warn("calendar-rsvp-posts: konnte Benachrichtigungsbeitrag #{post.id} nicht löschen: #{e}")
end
end
end
# Erstelle einen Verlaufszeileneintrag mit Zeitstempel
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
# Erstelle oder aktualisiere den Inhalt des Verlaufsbeitrags
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
# Füge neuen Eintrag zum bestehenden Verlauf hinzu
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
# Konvertiere einen einfachen Benachrichtigungsbeitrag ins Verlaufsformat
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
# Erstelle einen Benachrichtigungsbeitrag
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
# Zentrale Logikmethode zur Handhabung der Erstellung/Aktualisierung von Beiträgen
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: "calendar-rsvp-posts cleanup").destroy
rescue StandardError => e
Rails.logger.warn("calendar-rsvp-posts: konnte Beitrag #{post.id} nicht löschen: #{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
# ==========================================
# Hintergrund-Job-Definition
# ==========================================
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
# ==========================================
# Event-Handler
# ==========================================
# Handler für Erstellung/Aktualisierung der Teilnahme
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 || "jemand"
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
# Job in die Warteschlange stellen, anstatt die Anfrage zu blockieren
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: Handler-Fehler: #{e}")
Rails.logger.warn(e.backtrace.join("\n"))
end
end
on(:discourse_calendar_post_event_invitee_status_changed, &proc_handler)
# Handler für explizite Löschen von Teilnehmern (entfernte 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 || "jemand"
action_label = I18n.t('calendar_rsvp_posts.actions.removed')
# Job in die Warteschlange stellen, anstatt die Anfrage zu blockieren
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: after_destroy-Handler-Fehler: #{e}")
Rails.logger.warn(e.backtrace.join("\n"))
end
end
end
end
end
end