diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/controllers/api/v1/statuses/publishing_controller.rb | 9 | ||||
-rw-r--r-- | app/lib/command_tag/commands.rb | 8 | ||||
-rw-r--r-- | app/lib/command_tag/commands/hello_world.rb | 7 | ||||
-rw-r--r-- | app/lib/command_tag/commands/status_tools.rb | 18 | ||||
-rw-r--r-- | app/lib/command_tag/processor.rb | 79 | ||||
-rw-r--r-- | app/services/post_status_service.rb | 7 | ||||
-rw-r--r-- | app/services/process_command_tags_service.rb | 10 | ||||
-rw-r--r-- | app/services/publish_status_service.rb | 22 | ||||
-rw-r--r-- | app/services/update_status_service.rb | 4 |
9 files changed, 154 insertions, 10 deletions
diff --git a/app/controllers/api/v1/statuses/publishing_controller.rb b/app/controllers/api/v1/statuses/publishing_controller.rb index 5124b1009..97c052e22 100644 --- a/app/controllers/api/v1/statuses/publishing_controller.rb +++ b/app/controllers/api/v1/statuses/publishing_controller.rb @@ -8,14 +8,7 @@ class Api::V1::Statuses::PublishingController < Api::BaseController before_action :set_status def create - @status.update!(published: true) - - LinkCrawlWorker.perform_in(rand(1..30).seconds, @status.id) unless @status.spoiler_text? - DistributionWorker.perform_async(@status.id) - ActivityPub::DistributionWorker.perform_async(@status.id) if @status.local? && !@status.local_only? - - mentions = @status.active_mentions.includes(:account).where(id: @new_mention_ids, accounts: { domain: nil }) - mentions.each { |mention| LocalNotificationWorker.perform_async(mention.account.id, mention.id, mention.class.name) } + PublishStatusService.new.call(@status) render json: @status, serializer: (@status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer), diff --git a/app/lib/command_tag/commands.rb b/app/lib/command_tag/commands.rb new file mode 100644 index 000000000..0248e6e99 --- /dev/null +++ b/app/lib/command_tag/commands.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true +module CommandTag::Commands + def self.included(base) + CommandTag::Commands.constants.map(&CommandTag::Commands.method(:const_get)).grep(Module) do |mod| + base.include(mod) + end + end +end diff --git a/app/lib/command_tag/commands/hello_world.rb b/app/lib/command_tag/commands/hello_world.rb new file mode 100644 index 000000000..4cf17685e --- /dev/null +++ b/app/lib/command_tag/commands/hello_world.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module CommandTag::Commands::HelloWorld + def handle_hello_world_at_start + @text << "\nHello, world!" + end +end diff --git a/app/lib/command_tag/commands/status_tools.rb b/app/lib/command_tag/commands/status_tools.rb new file mode 100644 index 000000000..3c6d7137e --- /dev/null +++ b/app/lib/command_tag/commands/status_tools.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +module CommandTag::Commands::StatusTools + def handle_publish_once_at_end + return unless author_of_parent? && !@parent.published? + + PublishStatusService.new.call(@parent) + end + + def handle_edit_once_before_save + return unless author_of_parent? + + params = @parent.slice(*UpdateStatusService::ALLOWED_ATTRIBUTES).with_indifferent_access.compact + params[:text] = @text + UpdateStatusService.new.call(@parent, params) + destroy_status! + end +end diff --git a/app/lib/command_tag/processor.rb b/app/lib/command_tag/processor.rb new file mode 100644 index 000000000..b122b9e8d --- /dev/null +++ b/app/lib/command_tag/processor.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +class CommandTag::Processor + include CommandTag::Commands + + STATEMENT_RE = /^\s*#!\s*([^\n]+ (?:start|begin|do)$.*)\n\s*#!\s*(?:end|stop|done)\s*$|^\s*#!\s*(.*?)\s*$/im.freeze + STATEMENT_STRIP_RE = /^\s*#!\s*(?:[^\n]+ (?:start|begin|do)$.*)\n\s*#!\s*(?:end|stop|done)\s*$\n?|^\s*#!\s*(?:.*?)\s*$\n?/im.freeze + + def initialize(account, status) + @account = account + @status = status + @parent = status.thread + @run_once = Set[] + @vars = {} + @text = status.text.gsub("\r\n", "\n").gsub("\n\r", "\n").gsub("\r", "\n") + + return unless @account.present? && @account.local? && @status.present? + end + + def process! + @statements = parse_statements + @text = @text.gsub(STATEMENT_STRIP_RE, '').split("\n") + + %w(at_start once_at_start).each { |suffix| execute_statements(suffix) } + @text = @text.join("\n").rstrip + %w(before_save once_before_save).each { |suffix| execute_statements(suffix) } + + if @text.blank? + %w(when_blank once_when_blank).each { |suffix| execute_statements(suffix) } + + unless (@status.published? && !@status.edited.zero?) || @text.present? + %w(before_destroy once_before_destroy).each { |suffix| execute_statements(suffix) } + @status.destroy + %w(after_destroy once_after_destroy).each { |suffix| execute_statements(suffix) } + end + elsif @status.destroyed? + %w(after_destroy once_after_destroy).each { |suffix| execute_statements(suffix) } + elsif @status.update(text: @text) + %w(after_save once_after_save).each { |suffix| execute_statements(suffix) } + else + %w(after_save_fail once_after_save_fail).each { |suffix| execute_statements(suffix) } + end + + %w(at_end once_at_end).each { |suffix| execute_statements(suffix) } + end + + private + + def parse_statements + @text.scan(STATEMENT_RE).flatten.map do |statement| + next if statement.blank? || statement[0]&.strip.blank? + + statement = statement.scan(/^(.*) (?:start|begin|do)$(.*)|'([^']*)'|"([^"]*)"|(\S+)/im).flatten.compact + statement[0] = statement[0].strip.tr('- ', '_').downcase + statement + end.compact + end + + def execute_statements(suffix) + @statements.each do |statement| + name = "handle_#{statement[0]}_#{suffix}" + is_run_once = suffix.start_with?('once_') + public_send(name, *statement[1..-1]) if respond_to?(name) && !(is_run_once && @run_once.include?(name)) + @run_once << name if is_run_once + end + end + + def author_of_status? + @account.id == @status.account_id + end + + def author_of_parent? + @account.id == @parent&.account_id + end + + def destroy_status! + @status.destroy unless @status.destroyed? + end +end diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index 5ddc1aeeb..132e91907 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -88,6 +88,7 @@ class PostStatusService < BaseService @status = @account.statuses.create!(status_attributes) end + process_command_tags_service.call(@account, @status) process_hashtags_service.call(@status, nil, @tag_names) process_mentions_service.call(@status, mentions: @mentions, deliver: @options[:publish]) end @@ -148,6 +149,10 @@ class PostStatusService < BaseService ProcessHashtagsService.new end + def process_command_tags_service + ProcessCommandTagsService.new + end + def scheduled? @scheduled_at.present? end @@ -178,8 +183,10 @@ class PostStatusService < BaseService def bump_potential_friendship! return if !@status.reply? || @account.id == @status.in_reply_to_account_id + ActivityTracker.increment('activity:interactions') return if @account.following?(@status.in_reply_to_account_id) + PotentialFriendshipTracker.record(@account.id, @status.in_reply_to_account_id, :reply) end diff --git a/app/services/process_command_tags_service.rb b/app/services/process_command_tags_service.rb new file mode 100644 index 000000000..2d8180673 --- /dev/null +++ b/app/services/process_command_tags_service.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +class ProcessCommandTagsService < BaseService + def call(account, status, raise_if_no_output: true) + CommandTag::Processor.new(account, status).process! + raise Mastodon::LengthValidationError, 'Processed command tags yielded no output.' if raise_if_no_output && status.destroyed? + + status + end +end diff --git a/app/services/publish_status_service.rb b/app/services/publish_status_service.rb new file mode 100644 index 000000000..737186a17 --- /dev/null +++ b/app/services/publish_status_service.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true +class PublishStatusService < BaseService + def call(status) + return if status.published? + + status.update!(published: true) + + ProcessMentionsService.new.call(status) + + LinkCrawlWorker.perform_in(rand(1..30).seconds, status.id) unless status.spoiler_text? + DistributionWorker.perform_async(status.id) + ActivityPub::DistributionWorker.perform_async(status.id) if status.local? && !status.local_only? + + return if !status.reply? || status.account.id == status.in_reply_to_account_id + + ActivityTracker.increment('activity:interactions') + + return if status.account.following?(status.in_reply_to_account_id) + + PotentialFriendshipTracker.record(status.account.id, status.in_reply_to_account_id, :reply) + end +end diff --git a/app/services/update_status_service.rb b/app/services/update_status_service.rb index 440b99ce7..795e43d01 100644 --- a/app/services/update_status_service.rb +++ b/app/services/update_status_service.rb @@ -13,7 +13,6 @@ class UpdateStatusService < BaseService media_attachments media_attachment_ids application - rate_limit ).freeze # Updates the content of an existing status. @@ -21,7 +20,7 @@ class UpdateStatusService < BaseService # @param [Hash] params The attributes of the new status. # @param [Enumerable] mentions Additional mentions added to the status. # @param [Enumerable] tags New tags for the status to belong to (implicit tags are preserved). - def call(status, params, mentions, tags) + def call(status, params, mentions = nil, tags = nil) raise ActiveRecord::RecordNotFound if status.blank? || status.discarded? || status.destroyed? return status if params.blank? @@ -47,6 +46,7 @@ class UpdateStatusService < BaseService ApplicationRecord.transaction do @status.update!(@params) + ProcessCommandTagsService.new.call(@account, @status) if @account.local? detach_deleted_tags attach_updated_tags end |