forked from treehouse/mastodon
Make follow requests federate
parent
d551e43a9b
commit
149887a0ff
|
@ -18,12 +18,12 @@ class Api::V1::FollowRequestsController < ApiController
|
||||||
end
|
end
|
||||||
|
|
||||||
def authorize
|
def authorize
|
||||||
FollowRequest.find_by!(account_id: params[:id], target_account: current_account).authorize!
|
AuthorizeFollowService.new.call(Account.find(params[:id]), current_account)
|
||||||
render_empty
|
render_empty
|
||||||
end
|
end
|
||||||
|
|
||||||
def reject
|
def reject
|
||||||
FollowRequest.find_by!(account_id: params[:id], target_account: current_account).reject!
|
RejectFollowService.new.call(Account.find(params[:id]), current_account)
|
||||||
render_empty
|
render_empty
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
module ObfuscateFilename
|
module ObfuscateFilename
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
|
|
@ -143,6 +143,10 @@ module AtomBuilderHelper
|
||||||
xml.link(:rel => 'mentioned', :href => TagManager::COLLECTIONS[:public], 'ostatus:object-type' => TagManager::TYPES[:collection])
|
xml.link(:rel => 'mentioned', :href => TagManager::COLLECTIONS[:public], 'ostatus:object-type' => TagManager::TYPES[:collection])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def privacy_scope(xml, level)
|
||||||
|
xml['mastodon'].scope(level)
|
||||||
|
end
|
||||||
|
|
||||||
def include_author(xml, account)
|
def include_author(xml, account)
|
||||||
object_type xml, :person
|
object_type xml, :person
|
||||||
uri xml, TagManager.instance.uri_for(account)
|
uri xml, TagManager.instance.uri_for(account)
|
||||||
|
@ -152,6 +156,7 @@ module AtomBuilderHelper
|
||||||
link_alternate xml, TagManager.instance.url_for(account)
|
link_alternate xml, TagManager.instance.url_for(account)
|
||||||
link_avatar xml, account
|
link_avatar xml, account
|
||||||
portable_contact xml, account
|
portable_contact xml, account
|
||||||
|
privacy_scope xml, account.locked? ? :private : :public
|
||||||
end
|
end
|
||||||
|
|
||||||
def rich_content(xml, activity)
|
def rich_content(xml, activity)
|
||||||
|
@ -216,6 +221,7 @@ module AtomBuilderHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
category(xml, 'nsfw') if stream_entry.target.sensitive?
|
category(xml, 'nsfw') if stream_entry.target.sensitive?
|
||||||
|
privacy_scope(xml, stream_entry.target.visibility)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -237,6 +243,7 @@ module AtomBuilderHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
category(xml, 'nsfw') if stream_entry.activity.sensitive?
|
category(xml, 'nsfw') if stream_entry.activity.sensitive?
|
||||||
|
privacy_scope(xml, stream_entry.activity.visibility)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -249,6 +256,7 @@ module AtomBuilderHelper
|
||||||
'xmlns:poco' => TagManager::POCO_XMLNS,
|
'xmlns:poco' => TagManager::POCO_XMLNS,
|
||||||
'xmlns:media' => TagManager::MEDIA_XMLNS,
|
'xmlns:media' => TagManager::MEDIA_XMLNS,
|
||||||
'xmlns:ostatus' => TagManager::OS_XMLNS,
|
'xmlns:ostatus' => TagManager::OS_XMLNS,
|
||||||
|
'xmlns:mastodon' => TagManager::MTDN_XMLNS,
|
||||||
}, &block)
|
}, &block)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,9 @@ class TagManager
|
||||||
unfavorite: 'http://activitystrea.ms/schema/1.0/unfavorite',
|
unfavorite: 'http://activitystrea.ms/schema/1.0/unfavorite',
|
||||||
delete: 'http://activitystrea.ms/schema/1.0/delete',
|
delete: 'http://activitystrea.ms/schema/1.0/delete',
|
||||||
follow: 'http://activitystrea.ms/schema/1.0/follow',
|
follow: 'http://activitystrea.ms/schema/1.0/follow',
|
||||||
|
request_friend: 'http://activitystrea.ms/schema/1.0/request-friend',
|
||||||
|
authorize: 'http://activitystrea.ms/schema/1.0/authorize',
|
||||||
|
reject: 'http://activitystrea.ms/schema/1.0/reject',
|
||||||
unfollow: 'http://ostatus.org/schema/1.0/unfollow',
|
unfollow: 'http://ostatus.org/schema/1.0/unfollow',
|
||||||
block: 'http://mastodon.social/schema/1.0/block',
|
block: 'http://mastodon.social/schema/1.0/block',
|
||||||
unblock: 'http://mastodon.social/schema/1.0/unblock',
|
unblock: 'http://mastodon.social/schema/1.0/unblock',
|
||||||
|
@ -38,6 +41,7 @@ class TagManager
|
||||||
POCO_XMLNS = 'http://portablecontacts.net/spec/1.0'
|
POCO_XMLNS = 'http://portablecontacts.net/spec/1.0'
|
||||||
DFRN_XMLNS = 'http://purl.org/macgirvin/dfrn/1.0'
|
DFRN_XMLNS = 'http://purl.org/macgirvin/dfrn/1.0'
|
||||||
OS_XMLNS = 'http://ostatus.org/schema/1.0'
|
OS_XMLNS = 'http://ostatus.org/schema/1.0'
|
||||||
|
MTDN_XMLNS = 'http://mastodon.social/schema/1.0'
|
||||||
|
|
||||||
def unique_tag(date, id, type)
|
def unique_tag(date, id, type)
|
||||||
"tag:#{Rails.configuration.x.local_domain},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}"
|
"tag:#{Rails.configuration.x.local_domain},#{date.strftime('%Y-%m-%d')}:objectId=#{id}:objectType=#{type}"
|
||||||
|
|
|
@ -12,11 +12,11 @@ class Favourite < ApplicationRecord
|
||||||
validates :status_id, uniqueness: { scope: :account_id }
|
validates :status_id, uniqueness: { scope: :account_id }
|
||||||
|
|
||||||
def verb
|
def verb
|
||||||
:favorite
|
destroyed? ? :unfavorite : :favorite
|
||||||
end
|
end
|
||||||
|
|
||||||
def title
|
def title
|
||||||
"#{account.acct} favourited a status by #{status.account.acct}"
|
destroyed? ? "#{account.acct} no longer favourites a status by #{status.account.acct}" : "#{account.acct} favourited a status by #{status.account.acct}"
|
||||||
end
|
end
|
||||||
|
|
||||||
delegate :object_type, to: :target
|
delegate :object_type, to: :target
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class FollowRequest < ApplicationRecord
|
class FollowRequest < ApplicationRecord
|
||||||
include Paginable
|
include Paginable
|
||||||
|
include Streamable
|
||||||
|
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
belongs_to :target_account, class_name: 'Account'
|
belongs_to :target_account, class_name: 'Account'
|
||||||
|
@ -12,12 +13,47 @@ class FollowRequest < ApplicationRecord
|
||||||
validates :account_id, uniqueness: { scope: :target_account_id }
|
validates :account_id, uniqueness: { scope: :target_account_id }
|
||||||
|
|
||||||
def authorize!
|
def authorize!
|
||||||
|
@verb = :authorize
|
||||||
|
|
||||||
account.follow!(target_account)
|
account.follow!(target_account)
|
||||||
MergeWorker.perform_async(target_account.id, account.id)
|
MergeWorker.perform_async(target_account.id, account.id)
|
||||||
|
|
||||||
destroy!
|
destroy!
|
||||||
end
|
end
|
||||||
|
|
||||||
def reject!
|
def reject!
|
||||||
|
@verb = :reject
|
||||||
destroy!
|
destroy!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def verb
|
||||||
|
destroyed? ? (@verb || :delete) : :request_friend
|
||||||
|
end
|
||||||
|
|
||||||
|
def target
|
||||||
|
target_account
|
||||||
|
end
|
||||||
|
|
||||||
|
def object_type
|
||||||
|
:person
|
||||||
|
end
|
||||||
|
|
||||||
|
def hidden?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
def title
|
||||||
|
if destroyed?
|
||||||
|
case @verb
|
||||||
|
when :authorize
|
||||||
|
"#{target_account.acct} authorized #{account.acct}'s request to follow"
|
||||||
|
when :reject
|
||||||
|
"#{target_account.acct} rejected #{account.acct}'s request to follow"
|
||||||
|
else
|
||||||
|
"#{account.acct} withdrew the request to follow #{target_account.acct}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
"#{account.acct} requested to follow #{target_account.acct}"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -30,7 +30,7 @@ class StreamEntry < ApplicationRecord
|
||||||
end
|
end
|
||||||
|
|
||||||
def targeted?
|
def targeted?
|
||||||
[:follow, :unfollow, :block, :unblock, :share, :favorite].include? verb
|
[:follow, :request_friend, :authorize, :unfollow, :block, :unblock, :share, :favorite].include? verb
|
||||||
end
|
end
|
||||||
|
|
||||||
def target
|
def target
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AuthorizeFollowService < BaseService
|
||||||
|
include StreamEntryRenderer
|
||||||
|
|
||||||
|
def call(source_account, target_account)
|
||||||
|
follow_request = FollowRequest.find_by!(account: source_account, target_account: target_account)
|
||||||
|
follow_request.authorize!
|
||||||
|
NotificationWorker.perform_async(stream_entry_to_xml(follow_request.stream_entry), target_account.id, source_account.id) unless source_account.local?
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class BlockService < BaseService
|
class BlockService < BaseService
|
||||||
|
include StreamEntryRenderer
|
||||||
|
|
||||||
def call(account, target_account)
|
def call(account, target_account)
|
||||||
return if account.id == target_account.id
|
return if account.id == target_account.id
|
||||||
|
|
||||||
|
@ -10,6 +12,6 @@ class BlockService < BaseService
|
||||||
block = account.block!(target_account)
|
block = account.block!(target_account)
|
||||||
|
|
||||||
BlockWorker.perform_async(account.id, target_account.id)
|
BlockWorker.perform_async(account.id, target_account.id)
|
||||||
NotificationWorker.perform_async(block.stream_entry.id, target_account.id) unless target_account.local?
|
NotificationWorker.perform_async(stream_entry_to_xml(block.stream_entry), account.id, target_account.id) unless target_account.local?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module StreamEntryRenderer
|
||||||
|
def stream_entry_to_xml(stream_entry)
|
||||||
|
renderer = StreamEntriesController.renderer.new(method: 'get', http_host: Rails.configuration.x.local_domain, https: Rails.configuration.x.use_https)
|
||||||
|
renderer.render(:show, assigns: { stream_entry: stream_entry }, formats: [:atom])
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class FavouriteService < BaseService
|
class FavouriteService < BaseService
|
||||||
|
include StreamEntryRenderer
|
||||||
|
|
||||||
# Favourite a status and notify remote user
|
# Favourite a status and notify remote user
|
||||||
# @param [Account] account
|
# @param [Account] account
|
||||||
# @param [Status] status
|
# @param [Status] status
|
||||||
|
@ -15,7 +17,7 @@ class FavouriteService < BaseService
|
||||||
if status.local?
|
if status.local?
|
||||||
NotifyService.new.call(favourite.status.account, favourite)
|
NotifyService.new.call(favourite.status.account, favourite)
|
||||||
else
|
else
|
||||||
NotificationWorker.perform_async(favourite.stream_entry.id, status.account_id)
|
NotificationWorker.perform_async(stream_entry_to_xml(favourite.stream_entry), account.id, status.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
favourite
|
favourite
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class FollowService < BaseService
|
class FollowService < BaseService
|
||||||
|
include StreamEntryRenderer
|
||||||
|
|
||||||
# Follow a remote user, notify remote user about the follow
|
# Follow a remote user, notify remote user about the follow
|
||||||
# @param [Account] source_account From which to follow
|
# @param [Account] source_account From which to follow
|
||||||
# @param [String] uri User URI to follow in the form of username@domain
|
# @param [String] uri User URI to follow in the form of username@domain
|
||||||
|
@ -20,10 +22,13 @@ class FollowService < BaseService
|
||||||
private
|
private
|
||||||
|
|
||||||
def request_follow(source_account, target_account)
|
def request_follow(source_account, target_account)
|
||||||
return unless target_account.local?
|
|
||||||
|
|
||||||
follow_request = FollowRequest.create!(account: source_account, target_account: target_account)
|
follow_request = FollowRequest.create!(account: source_account, target_account: target_account)
|
||||||
|
|
||||||
|
if target_account.local?
|
||||||
NotifyService.new.call(target_account, follow_request)
|
NotifyService.new.call(target_account, follow_request)
|
||||||
|
else
|
||||||
|
NotificationWorker.perform_async(stream_entry_to_xml(follow_request.stream_entry), source_account.id, target_account.id)
|
||||||
|
end
|
||||||
|
|
||||||
follow_request
|
follow_request
|
||||||
end
|
end
|
||||||
|
@ -35,7 +40,7 @@ class FollowService < BaseService
|
||||||
NotifyService.new.call(target_account, follow)
|
NotifyService.new.call(target_account, follow)
|
||||||
else
|
else
|
||||||
subscribe_service.call(target_account)
|
subscribe_service.call(target_account)
|
||||||
NotificationWorker.perform_async(follow.stream_entry.id, target_account.id)
|
NotificationWorker.perform_async(stream_entry_to_xml(follow.stream_entry), source_account.id, target_account.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
MergeWorker.perform_async(target_account.id, source_account.id)
|
MergeWorker.perform_async(target_account.id, source_account.id)
|
||||||
|
|
|
@ -29,6 +29,10 @@ class ProcessInteractionService < BaseService
|
||||||
case verb(xml)
|
case verb(xml)
|
||||||
when :follow
|
when :follow
|
||||||
follow!(account, target_account) unless target_account.locked? || target_account.blocking?(account)
|
follow!(account, target_account) unless target_account.locked? || target_account.blocking?(account)
|
||||||
|
when :request_friend
|
||||||
|
follow_request!(account, target_account) unless !target_account.locked? || target_account.blocking?(account)
|
||||||
|
when :authorize
|
||||||
|
authorize_follow_request!(account, target_account)
|
||||||
when :unfollow
|
when :unfollow
|
||||||
unfollow!(account, target_account)
|
unfollow!(account, target_account)
|
||||||
when :favorite
|
when :favorite
|
||||||
|
@ -72,6 +76,16 @@ class ProcessInteractionService < BaseService
|
||||||
NotifyService.new.call(target_account, follow)
|
NotifyService.new.call(target_account, follow)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def follow_request(account, target_account)
|
||||||
|
follow_request = FollowRequest.create!(account: account, target_account: target_account)
|
||||||
|
NotifyService.new.call(target_account, follow_request)
|
||||||
|
end
|
||||||
|
|
||||||
|
def authorize_target_account!(account, target_account)
|
||||||
|
follow_request = FollowRequest.find_by(account: target_account, target_account: account)
|
||||||
|
follow_request&.authorize!
|
||||||
|
end
|
||||||
|
|
||||||
def unfollow!(account, target_account)
|
def unfollow!(account, target_account)
|
||||||
account.unfollow!(target_account)
|
account.unfollow!(target_account)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ProcessMentionsService < BaseService
|
class ProcessMentionsService < BaseService
|
||||||
|
include StreamEntryRenderer
|
||||||
|
|
||||||
# Scan status for mentions and fetch remote mentioned users, create
|
# Scan status for mentions and fetch remote mentioned users, create
|
||||||
# local mention pointers, send Salmon notifications to mentioned
|
# local mention pointers, send Salmon notifications to mentioned
|
||||||
# remote users
|
# remote users
|
||||||
|
@ -33,7 +35,7 @@ class ProcessMentionsService < BaseService
|
||||||
if mentioned_account.local?
|
if mentioned_account.local?
|
||||||
NotifyService.new.call(mentioned_account, mention)
|
NotifyService.new.call(mentioned_account, mention)
|
||||||
else
|
else
|
||||||
NotificationWorker.perform_async(status.stream_entry.id, mentioned_account.id)
|
NotificationWorker.perform_async(stream_entry_to_xml(status.stream_entry), status.account_id, mentioned_account.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ReblogService < BaseService
|
class ReblogService < BaseService
|
||||||
|
include StreamEntryRenderer
|
||||||
|
|
||||||
# Reblog a status and notify its remote author
|
# Reblog a status and notify its remote author
|
||||||
# @param [Account] account Account to reblog from
|
# @param [Account] account Account to reblog from
|
||||||
# @param [Status] reblogged_status Status to be reblogged
|
# @param [Status] reblogged_status Status to be reblogged
|
||||||
|
@ -18,15 +20,9 @@ class ReblogService < BaseService
|
||||||
if reblogged_status.local?
|
if reblogged_status.local?
|
||||||
NotifyService.new.call(reblog.reblog.account, reblog)
|
NotifyService.new.call(reblog.reblog.account, reblog)
|
||||||
else
|
else
|
||||||
NotificationWorker.perform_async(reblog.stream_entry.id, reblog.reblog.account_id)
|
NotificationWorker.perform_async(stream_entry_to_xml(reblog.stream_entry), account.id, reblog.reblog.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
reblog
|
reblog
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def send_interaction_service
|
|
||||||
@send_interaction_service ||= SendInteractionService.new
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class RejectFollowService < BaseService
|
||||||
|
include StreamEntryRenderer
|
||||||
|
|
||||||
|
def call(source_account, target_account)
|
||||||
|
follow_request = FollowRequest.find_by!(account: source_account, target_account: target_account)
|
||||||
|
follow_request.reject!
|
||||||
|
NotificationWorker.perform_async(stream_entry_to_xml(follow_request.stream_entry), target_account.id, source_account.id) unless source_account.local?
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class RemoveStatusService < BaseService
|
class RemoveStatusService < BaseService
|
||||||
|
include StreamEntryRenderer
|
||||||
|
|
||||||
def call(status)
|
def call(status)
|
||||||
remove_from_self(status) if status.account.local?
|
remove_from_self(status) if status.account.local?
|
||||||
remove_from_followers(status)
|
remove_from_followers(status)
|
||||||
|
@ -43,7 +45,7 @@ class RemoveStatusService < BaseService
|
||||||
|
|
||||||
def send_delete_salmon(account, status)
|
def send_delete_salmon(account, status)
|
||||||
return unless status.local?
|
return unless status.local?
|
||||||
NotificationWorker.perform_async(status.stream_entry.id, account.id)
|
NotificationWorker.perform_async(stream_entry_to_xml(status.stream_entry), status.account_id, account.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def remove_reblogs(status)
|
def remove_reblogs(status)
|
||||||
|
|
|
@ -2,27 +2,16 @@
|
||||||
|
|
||||||
class SendInteractionService < BaseService
|
class SendInteractionService < BaseService
|
||||||
# Send an Atom representation of an interaction to a remote Salmon endpoint
|
# Send an Atom representation of an interaction to a remote Salmon endpoint
|
||||||
# @param [StreamEntry] stream_entry
|
# @param [String] Entry XML
|
||||||
|
# @param [Account] source_account
|
||||||
# @param [Account] target_account
|
# @param [Account] target_account
|
||||||
def call(stream_entry, target_account)
|
def call(xml, source_account, target_account)
|
||||||
envelope = salmon.pack(entry_xml(stream_entry), stream_entry.account.keypair)
|
envelope = salmon.pack(xml, source_account.keypair)
|
||||||
salmon.post(target_account.salmon_url, envelope)
|
salmon.post(target_account.salmon_url, envelope)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def entry_xml(stream_entry)
|
|
||||||
Nokogiri::XML::Builder.new do |xml|
|
|
||||||
entry(xml, true) do
|
|
||||||
author(xml) do
|
|
||||||
include_author xml, stream_entry.account
|
|
||||||
end
|
|
||||||
|
|
||||||
include_entry xml, stream_entry
|
|
||||||
end
|
|
||||||
end.to_xml
|
|
||||||
end
|
|
||||||
|
|
||||||
def salmon
|
def salmon
|
||||||
@salmon ||= OStatus2::Salmon.new
|
@salmon ||= OStatus2::Salmon.new
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class UnblockService < BaseService
|
class UnblockService < BaseService
|
||||||
|
include StreamEntryRenderer
|
||||||
|
|
||||||
def call(account, target_account)
|
def call(account, target_account)
|
||||||
return unless account.blocking?(target_account)
|
return unless account.blocking?(target_account)
|
||||||
|
|
||||||
unblock = account.unblock!(target_account)
|
unblock = account.unblock!(target_account)
|
||||||
NotificationWorker.perform_async(unblock.stream_entry.id, target_account.id) unless target_account.local?
|
NotificationWorker.perform_async(stream_entry_to_xml(unblock.stream_entry), account.id, target_account.id) unless target_account.local?
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class UnfavouriteService < BaseService
|
class UnfavouriteService < BaseService
|
||||||
|
include StreamEntryRenderer
|
||||||
|
|
||||||
def call(account, status)
|
def call(account, status)
|
||||||
favourite = Favourite.find_by!(account: account, status: status)
|
favourite = Favourite.find_by!(account: account, status: status)
|
||||||
favourite.destroy!
|
favourite.destroy!
|
||||||
|
|
||||||
unless status.local?
|
unless status.local?
|
||||||
NotificationWorker.perform_async(favourite.stream_entry.id, status.account_id)
|
NotificationWorker.perform_async(stream_entry_to_xml(favourite.stream_entry), account.id, status.account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
favourite
|
favourite
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class UnfollowService < BaseService
|
class UnfollowService < BaseService
|
||||||
|
include StreamEntryRenderer
|
||||||
|
|
||||||
# Unfollow and notify the remote user
|
# Unfollow and notify the remote user
|
||||||
# @param [Account] source_account Where to unfollow from
|
# @param [Account] source_account Where to unfollow from
|
||||||
# @param [Account] target_account Which to unfollow
|
# @param [Account] target_account Which to unfollow
|
||||||
def call(source_account, target_account)
|
def call(source_account, target_account)
|
||||||
follow = source_account.unfollow!(target_account)
|
follow = source_account.unfollow!(target_account)
|
||||||
NotificationWorker.perform_async(follow.stream_entry.id, target_account.id) unless target_account.local?
|
NotificationWorker.perform_async(stream_entry_to_xml(follow.stream_entry), source_account.id, target_account.id) unless target_account.local?
|
||||||
UnmergeWorker.perform_async(target_account.id, source_account.id)
|
UnmergeWorker.perform_async(target_account.id, source_account.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,6 +10,7 @@ class UpdateRemoteProfileService < BaseService
|
||||||
unless author_xml.nil?
|
unless author_xml.nil?
|
||||||
account.display_name = author_xml.at_xpath('./poco:displayName', poco: TagManager::POCO_XMLNS).content unless author_xml.at_xpath('./poco:displayName', poco: TagManager::POCO_XMLNS).nil?
|
account.display_name = author_xml.at_xpath('./poco:displayName', poco: TagManager::POCO_XMLNS).content unless author_xml.at_xpath('./poco:displayName', poco: TagManager::POCO_XMLNS).nil?
|
||||||
account.note = author_xml.at_xpath('./poco:note', poco: TagManager::POCO_XMLNS).content unless author_xml.at_xpath('./poco:note', poco: TagManager::POCO_XMLNS).nil?
|
account.note = author_xml.at_xpath('./poco:note', poco: TagManager::POCO_XMLNS).content unless author_xml.at_xpath('./poco:note', poco: TagManager::POCO_XMLNS).nil?
|
||||||
|
account.locked = author_xml.at_xpath('./mastodon:scope', mastodon: TagManager::MTDN_XMLNS)&.content == 'private'
|
||||||
|
|
||||||
unless account.suspended? || DomainBlock.find_by(domain: account.domain)&.reject_media?
|
unless account.suspended? || DomainBlock.find_by(domain: account.domain)&.reject_media?
|
||||||
account.avatar_remote_url = author_xml.at_xpath('./xmlns:link[@rel="avatar"]', xmlns: TagManager::XMLNS)['href'] unless author_xml.at_xpath('./xmlns:link[@rel="avatar"]', xmlns: TagManager::XMLNS).nil? || author_xml.at_xpath('./xmlns:link[@rel="avatar"]', xmlns: TagManager::XMLNS)['href'].blank?
|
account.avatar_remote_url = author_xml.at_xpath('./xmlns:link[@rel="avatar"]', xmlns: TagManager::XMLNS)['href'] unless author_xml.at_xpath('./xmlns:link[@rel="avatar"]', xmlns: TagManager::XMLNS).nil? || author_xml.at_xpath('./xmlns:link[@rel="avatar"]', xmlns: TagManager::XMLNS)['href'].blank?
|
||||||
|
|
|
@ -5,7 +5,7 @@ class NotificationWorker
|
||||||
|
|
||||||
sidekiq_options retry: 5
|
sidekiq_options retry: 5
|
||||||
|
|
||||||
def perform(stream_entry_id, target_account_id)
|
def perform(xml, source_account_id, target_account_id)
|
||||||
SendInteractionService.new.call(StreamEntry.find(stream_entry_id), Account.find(target_account_id))
|
SendInteractionService.new.call(xml, Account.find(source_account_id), Account.find(target_account_id))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class PushNotificationWorker
|
|
||||||
include Sidekiq::Worker
|
|
||||||
|
|
||||||
def perform(notification_id)
|
|
||||||
SendPushNotificationService.new.call(Notification.find(notification_id))
|
|
||||||
rescue ActiveRecord::RecordNotFound
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -13,7 +13,7 @@ RSpec.describe AtomBuilderHelper, type: :helper do
|
||||||
|
|
||||||
describe '#feed' do
|
describe '#feed' do
|
||||||
it 'creates a feed' do
|
it 'creates a feed' do
|
||||||
expect(used_in_builder { |xml| helper.feed(xml) }).to match '<feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0"/>'
|
expect(used_in_builder { |xml| helper.feed(xml) }).to match '<feed xmlns="http://www.w3.org/2005/Atom" xmlns:thr="http://purl.org/syndication/thread/1.0" xmlns:activity="http://activitystrea.ms/spec/1.0/" xmlns:poco="http://portablecontacts.net/spec/1.0" xmlns:media="http://purl.org/syndication/atommedia" xmlns:ostatus="http://ostatus.org/schema/1.0" xmlns:mastodon="http://mastodon.social/schema/1.0"/>'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue