2019-02-03 02:59:51 +00:00
# frozen_string_literal: true
require 'csv'
class ImportService < BaseService
ROWS_PROCESSING_LIMIT = 20_000
def call ( import )
@import = import
@account = @import . account
case @import . type
when 'following'
import_follows!
2023-01-20 05:09:29 +00:00
when 'account_subscribings'
import_account_subscribings!
2019-02-03 02:59:51 +00:00
when 'blocking'
import_blocks!
when 'muting'
import_mutes!
when 'domain_blocking'
import_domain_blocks!
2020-11-19 16:48:13 +00:00
when 'bookmarks'
import_bookmarks!
2019-02-03 02:59:51 +00:00
end
end
private
def import_follows!
2019-04-03 16:17:43 +00:00
parse_import_data! ( [ 'Account address' ] )
2023-01-20 05:09:29 +00:00
import_relationships! ( 'follow' , 'unfollow' , @account . following . map { | account | { acct : account . acct } } , ROWS_PROCESSING_LIMIT , show_reblogs : { header : 'Show boosts' , default : true } , notify : { header : 'Notify on new posts' , default : false } , languages : { header : 'Languages' , default : nil } , delivery : { header : 'Delivery to home' , default : true } )
end
def import_account_subscribings!
parse_import_data! ( [ 'Account address' ] )
import_relationships! ( 'account_subscribe' , 'account_unsubscribe' , @account . active_subscribes . map { | subscribe | { acct : subscribe . target_account . acct , list_id : subscribe . list_id } } , ROWS_PROCESSING_LIMIT , lists : { header : 'List' , default : nil } , show_reblogs : { header : 'Show boosts' , default : true } , media_only : { header : 'Media only' , default : false } )
2019-02-03 02:59:51 +00:00
end
def import_blocks!
2019-04-03 16:17:43 +00:00
parse_import_data! ( [ 'Account address' ] )
2023-01-20 05:09:29 +00:00
import_relationships! ( 'block' , 'unblock' , @account . blocking . map { | account | { acct : account . acct } } , ROWS_PROCESSING_LIMIT )
2019-02-03 02:59:51 +00:00
end
def import_mutes!
2019-04-03 16:17:43 +00:00
parse_import_data! ( [ 'Account address' ] )
2023-01-20 05:09:29 +00:00
import_relationships! ( 'mute' , 'unmute' , @account . muting . map { | account | { acct : account . acct } } , ROWS_PROCESSING_LIMIT , notifications : { header : 'Hide notifications' , default : true } )
2019-02-03 02:59:51 +00:00
end
def import_domain_blocks!
2019-04-03 16:17:43 +00:00
parse_import_data! ( [ '#domain' ] )
items = @data . take ( ROWS_PROCESSING_LIMIT ) . map { | row | row [ '#domain' ] . strip }
2019-02-03 02:59:51 +00:00
if @import . overwrite?
2021-03-24 09:44:31 +00:00
presence_hash = items . index_with ( true )
2019-02-03 02:59:51 +00:00
@account . domain_blocks . find_each do | domain_block |
if presence_hash [ domain_block . domain ]
items . delete ( domain_block . domain )
else
@account . unblock_domain! ( domain_block . domain )
end
end
end
items . each do | domain |
@account . block_domain! ( domain )
end
AfterAccountDomainBlockWorker . push_bulk ( items ) do | domain |
[ @account . id , domain ]
end
end
2019-04-08 05:28:27 +00:00
def import_relationships! ( action , undo_action , overwrite_scope , limit , extra_fields = { } )
2020-03-30 18:32:34 +00:00
local_domain_suffix = " @ #{ Rails . configuration . x . local_domain } "
2023-01-20 05:09:29 +00:00
items = @data . take ( limit ) . each_with_object ( { } ) do | row , mapping |
key = row [ 'Account address' ] & . strip & . delete_suffix ( local_domain_suffix )
return if key . blank?
extra = extra_fields . each_with_object ( { } ) { | ( key , field_settings ) , extra | extra [ key ] = row [ field_settings [ :header ] ] & . strip || field_settings [ :default ] }
if extra [ :lists ] . nil?
extra . delete ( :lists )
else
extra [ :list_id ] = List . find_or_create_by! ( { account_id : @account . id , title : extra . delete ( :lists ) } ) . id
key = " #{ key } #{ extra [ :list_id ] } "
end
mapping [ key ] = extra
end
2019-02-03 02:59:51 +00:00
if @import . overwrite?
2023-01-20 05:09:29 +00:00
overwrite_scope . each do | scope |
acct = scope [ :acct ]
key = scope [ :list_id ] ? " #{ acct } #{ scope [ :list_id ] } " : acct
option = scope [ :list_id ] ? { list_id : scope [ :list_id ] } : { }
2019-02-03 02:59:51 +00:00
2023-01-20 05:09:29 +00:00
if items [ key ]
Import :: RelationshipWorker . perform_async ( @account . id , acct , action , items . delete ( key ) )
2019-02-03 02:59:51 +00:00
else
2023-01-20 05:09:29 +00:00
Import :: RelationshipWorker . perform_async ( @account . id , acct , undo_action , option )
2019-02-03 02:59:51 +00:00
end
end
end
2023-01-20 05:09:29 +00:00
items = items . map { | item | [ item [ 0 ] . split ( ' ' ) [ 0 ] , item [ 1 ] ] }
# Process one item representing the domain ahead of time.
preceding_items = items . uniq { | acct , _ | acct . split ( '@' ) [ 1 ] }
sorted_items = preceding_items + ( items - preceding_items )
2020-12-18 08:18:31 +00:00
2023-01-20 05:09:29 +00:00
Import :: RelationshipWorker . push_bulk ( sorted_items ) do | acct , extra |
2019-04-08 05:28:27 +00:00
[ @account . id , acct , action , extra ]
2019-02-03 02:59:51 +00:00
end
end
2020-11-19 16:48:13 +00:00
def import_bookmarks!
parse_import_data! ( [ '#uri' ] )
items = @data . take ( ROWS_PROCESSING_LIMIT ) . map { | row | row [ '#uri' ] . strip }
if @import . overwrite?
2021-03-24 09:44:31 +00:00
presence_hash = items . index_with ( true )
2020-11-19 16:48:13 +00:00
@account . bookmarks . find_each do | bookmark |
if presence_hash [ bookmark . status . uri ]
items . delete ( bookmark . status . uri )
else
bookmark . destroy!
end
end
end
2021-01-09 23:32:01 +00:00
statuses = items . filter_map do | uri |
2020-11-19 16:48:13 +00:00
status = ActivityPub :: TagManager . instance . uri_to_resource ( uri , Status )
next if status . nil? && ActivityPub :: TagManager . instance . local_uri? ( uri )
status || ActivityPub :: FetchRemoteStatusService . new . call ( uri )
2021-01-09 23:32:01 +00:00
end
2020-11-19 16:48:13 +00:00
account_ids = statuses . map ( & :account_id )
preloaded_relations = relations_map_for_account ( @account , account_ids )
statuses . keep_if { | status | StatusPolicy . new ( @account , status , preloaded_relations ) . show? }
statuses . each do | status |
@account . bookmarks . find_or_create_by! ( account : @account , status : status )
end
end
2019-04-03 16:17:43 +00:00
def parse_import_data! ( default_headers )
data = CSV . parse ( import_data , headers : true )
data = CSV . parse ( import_data , headers : default_headers ) unless data . headers & . first & . strip & . include? ( ' ' )
@data = data . reject ( & :blank? )
end
2019-02-03 02:59:51 +00:00
def import_data
Paperclip . io_adapters . for ( @import . data ) . read
end
2020-11-19 16:48:13 +00:00
def relations_map_for_account ( account , account_ids )
2021-07-03 09:48:27 +00:00
presenter = AccountRelationshipsPresenter . new ( account_ids , account )
2020-11-19 16:48:13 +00:00
{
blocking : { } ,
2021-07-03 09:48:27 +00:00
blocked_by : presenter . blocked_by ,
2020-11-19 16:48:13 +00:00
muting : { } ,
2021-07-03 09:48:27 +00:00
following : presenter . following ,
2020-11-19 16:48:13 +00:00
domain_blocking_by_domain : { } ,
}
end
2019-02-03 02:59:51 +00:00
end