Skip to content
Snippets Groups Projects
Unverified Commit f8443868 authored by Eugen Rochko's avatar Eugen Rochko Committed by GitHub
Browse files

Add `tootctl accounts merge` (#15201)


* Add `tootctl accounts merge`

* Update lib/mastodon/accounts_cli.rb

Co-authored-by: default avatarYamagishi Kazutoshi <ykzts@desire.sh>

Co-authored-by: default avatarYamagishi Kazutoshi <ykzts@desire.sh>
parent a2da0262
No related branches found
No related tags found
No related merge requests found
......@@ -67,6 +67,7 @@ class Account < ApplicationRecord
include Paginable
include AccountCounters
include DomainNormalizable
include AccountMerging
TRUST_LEVELS = {
untrusted: 0,
......
# frozen_string_literal: true
module AccountMerging
extend ActiveSupport::Concern
def merge_with!(other_account)
# Since it's the same remote resource, the remote resource likely
# already believes we are following/blocking, so it's safe to
# re-attribute the relationships too. However, during the presence
# of the index bug users could have *also* followed the reference
# account already, therefore mass update will not work and we need
# to check for (and skip past) uniqueness errors
owned_classes = [
Status, StatusPin, MediaAttachment, Poll, Report, Tombstone, Favourite,
Follow, FollowRequest, Block, Mute, AccountIdentityProof,
AccountModerationNote, AccountPin, AccountStat, ListAccount,
PollVote, Mention
]
owned_classes.each do |klass|
klass.where(account_id: other_account.id).find_each do |record|
begin
record.update_attribute(:account_id, id)
rescue ActiveRecord::RecordNotUnique
next
end
end
end
target_classes = [Follow, FollowRequest, Block, Mute, AccountModerationNote, AccountPin]
target_classes.each do |klass|
klass.where(target_account_id: other_account.id).find_each do |record|
begin
record.update_attribute(:target_account_id, id)
rescue ActiveRecord::RecordNotUnique
next
end
end
end
end
end
......@@ -196,6 +196,46 @@ module Mastodon
say('OK', :green)
end
option :force, type: :boolean, aliases: [:f], description: 'Override public key check'
desc 'merge FROM TO', 'Merge two remote accounts into one'
long_desc <<-LONG_DESC
Merge two remote accounts specified by their username@domain
into one, whereby the TO account is the one being merged into
and kept, while the FROM one is removed. It is primarily meant
to fix duplicates caused by other servers changing their domain.
The command by default only works if both accounts have the same
public key to prevent mistakes. To override this, use the --force.
LONG_DESC
def merge(from_acct, to_acct)
username, domain = from_acct.split('@')
from_account = Account.find_remote(username, domain)
if from_account.nil? || from_account.local?
say("No such account (#{from_acct})", :red)
exit(1)
end
username, domain = to_acct.split('@')
to_account = Account.find_remote(username, domain)
if to_account.nil? || to_account.local?
say("No such account (#{to_acct})", :red)
exit(1)
end
if from_account.public_key != to_account.public_key && !options[:force]
say("Accounts don't have the same public key, might not be duplicates!", :red)
say('Override with --force', :red)
exit(1)
end
to_account.merge_with!(from_account)
from_account.destroy
say('OK', :green)
end
desc 'backup USERNAME', 'Request a backup for a user'
long_desc <<-LONG_DESC
Request a new backup for an account with a given USERNAME.
......@@ -335,7 +375,8 @@ module Mastodon
option :verbose, type: :boolean, aliases: [:v]
desc 'unfollow ACCT', 'Make all local accounts unfollow account specified by ACCT'
def unfollow(acct)
target_account = Account.find_remote(*acct.split('@'))
username, domain = acct.split('@')
target_account = Account.find_remote(username, domain)
if target_account.nil?
say('No such account', :red)
......
......@@ -476,48 +476,13 @@ module Mastodon
if other_account.public_key == reference_account.public_key
# The accounts definitely point to the same resource, so
# it's safe to re-attribute content and relationships
merge_accounts!(reference_account, other_account)
reference_account.merge_with!(other_account)
end
other_account.destroy
end
end
def merge_accounts!(main_account, duplicate_account)
# Since it's the same remote resource, the remote resource likely
# already believes we are following/blocking, so it's safe to
# re-attribute the relationships too. However, during the presence
# of the index bug users could have *also* followed the reference
# account already, therefore mass update will not work and we need
# to check for (and skip past) uniqueness errors
owned_classes = [
Status, StatusPin, MediaAttachment, Poll, Report, Tombstone, Favourite,
Follow, FollowRequest, Block, Mute, AccountIdentityProof,
AccountModerationNote, AccountPin, AccountStat, ListAccount,
PollVote, Mention
]
owned_classes.each do |klass|
klass.where(account_id: duplicate_account.id).find_each do |record|
begin
record.update_attribute(:account_id, main_account.id)
rescue ActiveRecord::RecordNotUnique
next
end
end
end
target_classes = [Follow, FollowRequest, Block, Mute, AccountModerationNote, AccountPin]
target_classes.each do |klass|
klass.where(target_account_id: duplicate_account.id).find_each do |record|
begin
record.update_attribute(:target_account_id, main_account.id)
rescue ActiveRecord::RecordNotUnique
next
end
end
end
end
def merge_conversations!(main_conv, duplicate_conv)
owned_classes = [ConversationMute, AccountConversation]
owned_classes.each do |klass|
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment