/* Copyright © 2025 maxtheweb */ package cmd import ( "fmt" "os" "os/exec" "path/filepath" "github.com/spf13/cobra" ) // loginCmd represents the login command var loginCmd = &cobra.Command{ Use: "login", Short: "Set up your X API credentials easily", Long: `Opens your editor to configure your X API credentials. The configuration will be saved to ~/.config/xapi/config.toml Get your credentials from: https://developer.x.com/en/portal/dashboard You'll need: - OAuth 1.0a credentials (Consumer Key/Secret, Access Token/Secret) - OAuth 2.0 credentials (Client ID/Secret - optional, for future features) Usage: xapi login # Edit credentials in your editor`, Run: func(cmd *cobra.Command, args []string) { runLogin() }, } func init() { rootCmd.AddCommand(loginCmd) } // findEditor searches for an available text editor func findEditor() string { // First, check if EDITOR is set if editor := os.Getenv("EDITOR"); editor != "" { // Verify it exists if _, err := exec.LookPath(editor); err == nil { return editor } } // Check for VISUAL (some systems use this) if visual := os.Getenv("VISUAL"); visual != "" { if _, err := exec.LookPath(visual); err == nil { return visual } } // Try common editors in order of preference editors := []string{ "nvim", // Neovim (power users) "vim", // Vim (widely available) "vi", // Vi (almost always there) "nano", // Nano (beginner-friendly) "emacs", // Emacs (for the enlightened) "micro", // Micro (modern terminal editor) "code", // VS Code (if they have it in terminal) "subl", // Sublime Text "gedit", // GNOME editor "kate", // KDE editor } for _, editor := range editors { if _, err := exec.LookPath(editor); err == nil { return editor } } return "" // No editor found } func runLogin() { // Get config directory homeDir, err := os.UserHomeDir() if err != nil { fmt.Fprintf(os.Stderr, "Error getting home directory: %v\n", err) os.Exit(1) } configDir := filepath.Join(homeDir, ".config", "xapi") configFile := filepath.Join(configDir, "config.toml") // Create config directory if it doesn't exist if err := os.MkdirAll(configDir, 0755); err != nil { fmt.Fprintf(os.Stderr, "Error creating config directory: %v\n", err) os.Exit(1) } // Check if config file exists, if not create template if _, err := os.Stat(configFile); os.IsNotExist(err) { template := `# X API Configuration # Get your credentials from: https://developer.x.com/en/portal/dashboard # # SECURITY NOTE: Never share this file or commit it to version control! # OAuth 1.0a Credentials (required for posting and searching) [oauth] consumer_key = "" consumer_secret = "" access_token = "" access_token_secret = "" # OAuth 2.0 Credentials (optional, for future features) [oauth2] client_id = "" client_secret = "" ` if err := os.WriteFile(configFile, []byte(template), 0600); err != nil { fmt.Fprintf(os.Stderr, "Error creating config file: %v\n", err) os.Exit(1) } } // Get editor using smart detection editor := findEditor() if editor == "" { fmt.Fprintf(os.Stderr, "Error: No text editor found!\n\n") fmt.Println("Please install one of: nvim, vim, vi, nano, emacs") fmt.Println("Or set your preferred editor: export EDITOR=nvim") os.Exit(1) } // Open editor fmt.Printf("Opening %s with %s...\n", configFile, editor) cmd := exec.Command(editor, configFile) cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { fmt.Fprintf(os.Stderr, "Error opening editor: %v\n", err) os.Exit(1) } fmt.Println("\nCredentials saved!") fmt.Println("\nNext step:") fmt.Println(" xapi status # Test your authentication") fmt.Println("\nThen you can use:") fmt.Println(" xapi search # Search tweets") fmt.Println(" xapi create # Create posts") }