From 5622de4a785e6b7c9a4946af3efaf8b4c2bc5755 Mon Sep 17 00:00:00 2001 From: Fire Demon Date: Sun, 23 Aug 2020 09:38:27 -0500 Subject: [Feature] Support Misskey-compatible boosts with attached content notes --- app/lib/activitypub/activity/create.rb | 49 +++++++++++++++++++++++++++-- app/lib/activitypub/adapter.rb | 14 +++++---- app/lib/activitypub/case_transform.rb | 4 ++- app/lib/command_tag/command/status_tools.rb | 14 +++++++++ app/lib/command_tag/processor.rb | 15 +++++++++ app/lib/formatter.rb | 18 +++++------ 6 files changed, 95 insertions(+), 19 deletions(-) (limited to 'app/lib') diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 69480ccf6..9ab60107b 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -85,7 +85,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity @params = {} unless @status.nil? - process_status_update_params + reblog_uri.blank? ? process_status_update_params : process_reblog_update_params process_tags process_audience @@ -95,7 +95,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity return @status end - process_status_params + reblog_uri.blank? ? process_status_params : process_reblog_params process_tags process_audience @@ -128,6 +128,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity language: detected_language, spoiler_text: converted_object_type? ? '' : (text_from_summary || ''), title: text_from_title, + reblog: reblogged_status, created_at: @object['published'], override_timestamps: @options[:override_timestamps], reply: @object['inReplyTo'].present?, @@ -155,6 +156,40 @@ class ActivityPub::Activity::Create < ActivityPub::Activity end end + def process_reblog_params + @params = begin + { + uri: object_uri, + url: object_url || object_uri, + account: @account, + text: text_from_content || '', + language: detected_language, + spoiler_text: converted_object_type? ? '' : (text_from_summary || ''), + title: text_from_title, + reblog: reblogged_status, + created_at: @object['published'], + override_timestamps: @options[:override_timestamps], + reply: @object['inReplyTo'].present?, + sensitive: @object['sensitive'] || false, + visibility: visibility_from_audience, + thread: replied_to_status, + } + end + end + + def process_reblog_update_params + @params = begin + { + text: text_from_content || '', + language: detected_language, + spoiler_text: converted_object_type? ? '' : (text_from_summary || ''), + title: text_from_title, + sensitive: @object['sensitive'] || false, + visibility: visibility_from_audience, + } + end + end + def process_audience (as_array(audience_to) + as_array(audience_cc)).uniq.each do |audience| next if audience == ActivityPub::TagManager::COLLECTIONS[:public] @@ -459,6 +494,16 @@ class ActivityPub::Activity::Create < ActivityPub::Activity value_or_id(@object['inReplyTo']) end + def reblogged_status + FetchRemoteStatusService.new.call(reblog_uri) if reblog_uri.present? + end + + def reblog_uri + return @reblog_uri if defined?(@reblog_uri) + + @reblog_uri = @object['reblog'].presence || @object['_misskey_quote'].presence + end + def text_from_content return Formatter.instance.linkify([[text_from_name, text_from_summary.presence].compact.join("\n\n"), object_url || object_uri].join(' ')) if converted_object_type? diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb index 107b93b44..33fa47d63 100644 --- a/app/lib/activitypub/adapter.rb +++ b/app/lib/activitypub/adapter.rb @@ -8,15 +8,17 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base CONTEXT_EXTENSION_MAP = { direct_message: { 'litepub': 'http://litepub.social/ns#', 'directMessage': 'litepub:directMessage' }, - edited: { 'mp' => 'http://the.monsterpit.net/ns#', 'edited' => 'mp:edited' }, - require_dereference: { 'mp' => 'http://the.monsterpit.net/ns#', 'requireDereference' => 'mp:requireDereference' }, - show_replies: { 'mp' => 'http://the.monsterpit.net/ns#', 'showReplies' => 'mp:showReplies' }, - show_unlisted: { 'mp' => 'http://the.monsterpit.net/ns#', 'showUnlisted' => 'mp:showUnlisted' }, - private: { 'mp' => 'http://the.monsterpit.net/ns#', 'private' => 'mp:private' }, - require_auth: { 'mp' => 'http://the.monsterpit.net/ns#', 'requireAuth' => 'mp:requireAuth' }, + edited: { 'mp' => 'https://the.monsterpit.net/ns#', 'edited' => 'mp:edited' }, + require_dereference: { 'mp' => 'https://the.monsterpit.net/ns#', 'requireDereference' => 'mp:requireDereference' }, + show_replies: { 'mp' => 'https://the.monsterpit.net/ns#', 'showReplies' => 'mp:showReplies' }, + show_unlisted: { 'mp' => 'https://the.monsterpit.net/ns#', 'showUnlisted' => 'mp:showUnlisted' }, + private: { 'mp' => 'https://the.monsterpit.net/ns#', 'private' => 'mp:private' }, + require_auth: { 'mp' => 'https://the.monsterpit.net/ns#', 'requireAuth' => 'mp:requireAuth' }, metadata: { 'mp' => 'https://the.monsterpit.net/ns#', 'metadata' => { '@id' => 'mp:metadata', '@type' => '@id' } }, server_metadata: { 'mp' => 'https://the.monsterpit.net/ns#', 'serverMetadata' => { '@id' => 'mp:serverMetadata', '@type' => '@id' } }, root: { 'mp' => 'https://the.monsterpit.net/ns#', 'root' => { '@id' => 'mp:root', '@type' => '@id' } }, + reblog: { 'mp' => 'https://the.monsterpit.net/ns#', 'reblog' => { '@id' => 'mp:reblog', '@type' => '@id' }, + 'misskey' => 'https://misskey.io/ns#', '_misskey_quote' => { '@id' => 'misskey:_misskey_quote', '@type' => '@id' } }, manually_approves_followers: { 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers' }, sensitive: { 'sensitive' => 'as:sensitive' }, hashtag: { 'Hashtag' => 'as:Hashtag' }, diff --git a/app/lib/activitypub/case_transform.rb b/app/lib/activitypub/case_transform.rb index 7f716f862..7f31fabda 100644 --- a/app/lib/activitypub/case_transform.rb +++ b/app/lib/activitypub/case_transform.rb @@ -14,8 +14,10 @@ module ActivityPub::CaseTransform when String camel_lower_cache[value] ||= if value.start_with?('_:') '_:' + value.gsub(/\A_:/, '').underscore.camelize(:lower) - else + elsif value != '_misskey_quote' value.underscore.camelize(:lower) + else + value end else value end diff --git a/app/lib/command_tag/command/status_tools.rb b/app/lib/command_tag/command/status_tools.rb index 1cdb90e4a..5cc11dde2 100644 --- a/app/lib/command_tag/command/status_tools.rb +++ b/app/lib/command_tag/command/status_tools.rb @@ -1,5 +1,19 @@ # frozen_string_literal: true module CommandTag::Command::StatusTools + def handle_boost_once_at_start(args) + return unless @parent.present? && StatusPolicy.new(@account, @parent).reblog? + + status = ReblogService.new.call( + @account, @parent, + visibility: @status.visibility, + spoiler_text: args.join(' ').presence || @status.spoiler_text + ) + end + + alias handle_reblog_at_start handle_boost_once_at_start + alias handle_rb_at_start handle_boost_once_at_start + alias handle_rt_at_start handle_boost_once_at_start + def handle_article_before_save(args) return unless author_of_status? && args.present? diff --git a/app/lib/command_tag/processor.rb b/app/lib/command_tag/processor.rb index 2c33b5f83..9edcb58ba 100644 --- a/app/lib/command_tag/processor.rb +++ b/app/lib/command_tag/processor.rb @@ -12,6 +12,12 @@ require_relative 'commands' +class CommandTag::Break < Mastodon::Error + def initialize(msg = 'A handler stopped execution.') + super + end +end + class CommandTag::Processor include Redisable include ImgProxyHelper @@ -83,6 +89,8 @@ class CommandTag::Processor execute_statements(:at_end) all_handlers!(:shutdown) + rescue CommandTag::Break + nil rescue StandardError => e @status.update(published: false) @status.destroy @@ -247,6 +255,13 @@ class CommandTag::Processor @status.destroy end + def replace_status!(new_status) + return if new_status.blank? + + destroy_status! + @status = new_status + end + def normalize(text) text.to_s.strip.downcase end diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb index 5559ddb73..6673f4b4b 100644 --- a/app/lib/formatter.rb +++ b/app/lib/formatter.rb @@ -32,15 +32,8 @@ class Formatter include ActionView::Helpers::TextHelper def format(status, **options) - if status.reblog? - prepend_reblog = status.reblog.account.acct - status = status.proper - else - prepend_reblog = false - end - summary = nil - raw_content = status.text + raw_content = status.proper.text summary_mode = false if status.title.present? @@ -56,8 +49,13 @@ class Formatter return '' if raw_content.blank? return format_remote_content(raw_content, status.emojis, summary: summary, **options) unless status.local? - html = raw_content - html = "RT @#{prepend_reblog} #{html}" if prepend_reblog + if status.reblog? + html = "šŸ” @#{status.reblog.account.acct}\nšŸ”— #{ActivityPub::TagManager.instance.url_for(status.reblog)}" + html += "\nā„¹ļø #{status.reblog.spoiler_text}" if status.reblog.spoiler_text.present? + else + html = raw_content + end + html = "šŸ“„ #{html}" if summary_mode return html if options[:plaintext] -- cgit