2025-06-22 17:44:23 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"slices"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
if len(os.Args) == 1 || slices.Contains(os.Args, "-help") {
|
|
|
|
printHelp()
|
|
|
|
os.Exit(0)
|
|
|
|
}
|
|
|
|
|
2025-06-22 19:16:17 +01:00
|
|
|
var fromUser string
|
|
|
|
var fromPassword string
|
|
|
|
var toUser string
|
|
|
|
var toPassword string
|
2025-06-22 17:44:23 +01:00
|
|
|
var pdsUrl string
|
2025-06-22 19:16:17 +01:00
|
|
|
var fromPdsUrl string
|
2025-06-22 17:44:23 +01:00
|
|
|
var dryrun bool = false
|
|
|
|
var verbose bool = false
|
|
|
|
|
|
|
|
var importProfile bool = false
|
|
|
|
var importFollows bool = false
|
2025-06-22 19:16:17 +01:00
|
|
|
var importPreferences bool = false
|
2025-06-22 17:44:23 +01:00
|
|
|
|
|
|
|
i := 1
|
|
|
|
for {
|
|
|
|
if i >= len(os.Args) { break }
|
|
|
|
if os.Args[i][0] != '-' {
|
|
|
|
fmt.Fprintf(os.Stderr, "unrecognised argument `%s`.\n", os.Args[i])
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
getValue := func(i int) (int, string) {
|
|
|
|
if i + 1 >= len(os.Args) {
|
|
|
|
fmt.Fprintf(os.Stderr, "-%s requires a value.\n", os.Args[i])
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
return i + 1, os.Args[i + 1]
|
|
|
|
}
|
|
|
|
|
|
|
|
switch os.Args[i][1:] {
|
|
|
|
case "from":
|
2025-06-22 19:16:17 +01:00
|
|
|
i, fromUser = getValue(i)
|
|
|
|
case "frompass":
|
|
|
|
i, fromPassword = getValue(i)
|
2025-06-22 17:44:23 +01:00
|
|
|
case "to":
|
2025-06-22 19:16:17 +01:00
|
|
|
i, toUser = getValue(i)
|
|
|
|
case "topass":
|
|
|
|
i, toPassword = getValue(i)
|
2025-06-22 17:44:23 +01:00
|
|
|
case "pds":
|
|
|
|
i, pdsUrl = getValue(i)
|
2025-06-22 19:16:17 +01:00
|
|
|
case "frompds":
|
|
|
|
i, fromPdsUrl = getValue(i)
|
2025-06-22 17:44:23 +01:00
|
|
|
case "dryrun":
|
|
|
|
dryrun = true
|
|
|
|
case "v":
|
|
|
|
verbose = true
|
|
|
|
case "profile":
|
|
|
|
importProfile = true
|
|
|
|
case "follows":
|
|
|
|
importFollows = true
|
2025-06-22 19:16:17 +01:00
|
|
|
case "preferences":
|
|
|
|
importPreferences = true
|
2025-06-22 17:44:23 +01:00
|
|
|
}
|
|
|
|
i += 1
|
|
|
|
}
|
|
|
|
|
2025-06-22 19:16:17 +01:00
|
|
|
if fromUser == "" {
|
2025-06-22 17:44:23 +01:00
|
|
|
fmt.Fprintf(os.Stderr, "missing required argument -from.\n")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2025-06-22 19:16:17 +01:00
|
|
|
if toUser == "" {
|
2025-06-22 17:44:23 +01:00
|
|
|
fmt.Fprintf(os.Stderr, "missing required argument -to.\n")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2025-06-22 19:16:17 +01:00
|
|
|
if toPassword == "" {
|
2025-06-22 17:44:23 +01:00
|
|
|
fmt.Fprintf(os.Stderr, "missing required argument -pass.\n")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
if pdsUrl == "" {
|
|
|
|
fmt.Fprintf(os.Stderr, "missing required argument -pds.\n")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2025-06-22 19:16:17 +01:00
|
|
|
if fromPdsUrl == "" { fromPdsUrl = pdsUrl }
|
2025-06-22 17:44:23 +01:00
|
|
|
|
2025-06-22 19:16:17 +01:00
|
|
|
if !(importProfile || importFollows || importPreferences) {
|
2025-06-22 17:44:23 +01:00
|
|
|
fmt.Fprintf(os.Stderr, "no action was specified.\n")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
2025-06-22 19:16:17 +01:00
|
|
|
var fromSession *AtprotoSession
|
|
|
|
if !(fromPassword == "" || fromPdsUrl == "") {
|
|
|
|
var err error
|
|
|
|
fromSession, err = CreateSession(fromPdsUrl, fromUser, fromPassword)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "failed to create session for source account: %v\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
toSession, err := CreateSession(pdsUrl, toUser, toPassword)
|
2025-06-22 17:44:23 +01:00
|
|
|
if err != nil {
|
2025-06-22 19:16:17 +01:00
|
|
|
fmt.Fprintf(os.Stderr, "failed to create session for destination account: %v\n", err)
|
2025-06-22 17:44:23 +01:00
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
|
|
|
if importProfile {
|
2025-06-22 19:16:17 +01:00
|
|
|
err := ImportProfile(toSession, pdsUrl, fromUser, dryrun, verbose)
|
2025-06-22 17:44:23 +01:00
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "failed to import profile: %v\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
2025-06-22 19:16:17 +01:00
|
|
|
if importPreferences {
|
|
|
|
if fromSession == nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "-frompass and -frompds must be provided for a preferences import.\n")
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
err := ImportPreferences(fromSession, toSession, pdsUrl, dryrun, verbose)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "failed to import preferences: %v\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
2025-06-22 17:44:23 +01:00
|
|
|
if importFollows {
|
2025-06-22 19:16:17 +01:00
|
|
|
err := ImportFollows(toSession, pdsUrl, fromUser, dryrun, verbose)
|
2025-06-22 17:44:23 +01:00
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(os.Stderr, "failed to import follows: %v\n", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func printHelp() {
|
|
|
|
fmt.Printf(
|
|
|
|
`usage: %s <arguments...> <imports...>
|
|
|
|
|
|
|
|
required arguments:
|
2025-06-22 19:16:17 +01:00
|
|
|
-from <identifier>: the source account handle or DID.
|
|
|
|
-frompass <password>: the source account password.
|
|
|
|
-to <identifier>: the destination account handle or DID.
|
|
|
|
-topass <password>: the destination account password.
|
|
|
|
-pds <pds-url>: the full https:// url of the destination account's PDS.
|
2025-06-22 17:44:23 +01:00
|
|
|
|
|
|
|
imports (at least one required):
|
|
|
|
-profile: imports the account profile, including avatar, banner, display name, and description.
|
|
|
|
-follows: imports the following list of the account.
|
|
|
|
|
|
|
|
optional arguments:
|
2025-06-22 19:16:17 +01:00
|
|
|
-frompds <pds-url>: the source account's PDS url (defaults to the value of -pds).
|
2025-06-22 17:44:23 +01:00
|
|
|
-dryrun: does not import follow records; good for sanity testing!
|
|
|
|
-v: verbose output
|
|
|
|
-help: shows this help message.
|
|
|
|
`,
|
|
|
|
os.Args[0],
|
|
|
|
)
|
|
|
|
}
|