238 lines
6.6 KiB
Go
238 lines
6.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"text/tabwriter"
|
|
"time"
|
|
|
|
"git.warky.dev/wdevs/whatshooked/pkg/models"
|
|
"git.warky.dev/wdevs/whatshooked/pkg/storage"
|
|
resolvespec_common "github.com/bitechdev/ResolveSpec/pkg/spectypes"
|
|
"github.com/google/uuid"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
accountUser string
|
|
accountPhoneNumber string
|
|
accountType string
|
|
accountSessionPath string
|
|
accountDisplayName string
|
|
)
|
|
|
|
var accountsCmd = &cobra.Command{
|
|
Use: "accounts",
|
|
Short: "Manage WhatsApp accounts",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
client := NewClient(cliConfig)
|
|
listAccounts(client)
|
|
},
|
|
}
|
|
|
|
var accountsListCmd = &cobra.Command{
|
|
Use: "list",
|
|
Short: "List all accounts",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
client := NewClient(cliConfig)
|
|
listAccounts(client)
|
|
},
|
|
}
|
|
|
|
var accountsAddCmd = &cobra.Command{
|
|
Use: "add",
|
|
Short: "Add a new WhatsApp account",
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
client := NewClient(cliConfig)
|
|
addAccount(client)
|
|
},
|
|
}
|
|
|
|
var accountsRemoveCmd = &cobra.Command{
|
|
Use: "remove <id>",
|
|
Short: "Remove a WhatsApp account by ID",
|
|
Args: cobra.ExactArgs(1),
|
|
Run: func(cmd *cobra.Command, args []string) {
|
|
client := NewClient(cliConfig)
|
|
removeAccount(client, args[0])
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
accountsAddCmd.Flags().StringVarP(&accountPhoneNumber, "phone", "p", "", "Phone number (with country code)")
|
|
accountsAddCmd.Flags().StringVarP(&accountType, "type", "t", "whatsmeow", "Account type (whatsmeow/business-api)")
|
|
accountsAddCmd.Flags().StringVarP(&accountSessionPath, "session-path", "s", "", "Session path (auto-generated if omitted)")
|
|
accountsAddCmd.Flags().StringVarP(&accountDisplayName, "display-name", "d", "", "Display name")
|
|
accountsAddCmd.Flags().StringVarP(&accountUser, "user", "u", "", "Owner username for DB mode (default: first admin)")
|
|
|
|
accountsCmd.AddCommand(accountsListCmd)
|
|
accountsCmd.AddCommand(accountsAddCmd)
|
|
accountsCmd.AddCommand(accountsRemoveCmd)
|
|
}
|
|
|
|
func listAccounts(client *Client) {
|
|
if serverAvailable(client) {
|
|
listAccountsHTTP(client)
|
|
} else {
|
|
fmt.Println("[server unavailable, reading from database]")
|
|
if !tryInitDB() {
|
|
fmt.Println("Error: server unreachable and no database config found. Use --server-config to specify config path.")
|
|
return
|
|
}
|
|
listAccountsDB()
|
|
}
|
|
}
|
|
|
|
func listAccountsHTTP(client *Client) {
|
|
resp, err := client.Get("/api/accounts")
|
|
checkError(err)
|
|
defer resp.Body.Close()
|
|
|
|
var accounts []map[string]interface{}
|
|
checkError(decodeJSON(resp, &accounts))
|
|
|
|
if len(accounts) == 0 {
|
|
fmt.Println("No accounts configured")
|
|
return
|
|
}
|
|
|
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
|
fmt.Fprintln(w, "ID\tPHONE\tTYPE\tSTATUS\tACTIVE")
|
|
for _, acc := range accounts {
|
|
fmt.Fprintf(w, "%v\t%v\t%v\t%v\t%v\n",
|
|
acc["id"], acc["phone_number"], acc["account_type"], acc["status"], acc["active"])
|
|
}
|
|
w.Flush()
|
|
}
|
|
|
|
func listAccountsDB() {
|
|
var accounts []models.ModelPublicWhatsappAccount
|
|
err := storage.DB.NewSelect().Model(&accounts).OrderExpr("created_at ASC").Scan(context.Background())
|
|
checkError(err)
|
|
|
|
if len(accounts) == 0 {
|
|
fmt.Println("No accounts configured")
|
|
return
|
|
}
|
|
|
|
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
|
|
fmt.Fprintln(w, "ID\tPHONE\tTYPE\tSTATUS\tACTIVE")
|
|
for _, acc := range accounts {
|
|
active := "yes"
|
|
if !acc.Active {
|
|
active = "no"
|
|
}
|
|
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\n",
|
|
acc.ID.String(),
|
|
acc.PhoneNumber.String(),
|
|
acc.AccountType.String(),
|
|
acc.Status.String(),
|
|
active,
|
|
)
|
|
}
|
|
w.Flush()
|
|
}
|
|
|
|
func addAccount(client *Client) {
|
|
phone := accountPhoneNumber
|
|
if phone == "" {
|
|
phone = promptRequired("Phone Number (with country code)")
|
|
}
|
|
|
|
acctType := accountType
|
|
if acctType == "" {
|
|
acctType = promptLine("Account type (whatsmeow/business-api)", "whatsmeow")
|
|
}
|
|
|
|
displayName := accountDisplayName
|
|
if displayName == "" {
|
|
displayName = promptLine("Display name (optional)", "")
|
|
}
|
|
|
|
if serverAvailable(client) {
|
|
addAccountHTTP(client, phone, acctType, displayName)
|
|
} else {
|
|
fmt.Println("[server unavailable, writing to database]")
|
|
if !tryInitDB() {
|
|
fmt.Println("Error: server unreachable and no database config found. Use --server-config to specify config path.")
|
|
return
|
|
}
|
|
addAccountDB(phone, acctType, displayName)
|
|
}
|
|
}
|
|
|
|
func addAccountHTTP(client *Client, phone, acctType, displayName string) {
|
|
payload := map[string]interface{}{
|
|
"phone_number": phone,
|
|
"account_type": acctType,
|
|
"display_name": displayName,
|
|
}
|
|
resp, err := client.Post("/api/accounts/add", payload)
|
|
checkError(err)
|
|
defer resp.Body.Close()
|
|
fmt.Println("Account added successfully")
|
|
fmt.Println("Check server logs for QR code to pair the device")
|
|
}
|
|
|
|
func addAccountDB(phone, acctType, displayName string) {
|
|
userID := dbOwnerUserID(accountUser)
|
|
if userID == "" {
|
|
fmt.Println("Error: no users found in database. Create a user first with: users add")
|
|
return
|
|
}
|
|
|
|
id := uuid.New().String()
|
|
sessionPath := accountSessionPath
|
|
if sessionPath == "" {
|
|
sessionPath = fmt.Sprintf("./sessions/%s", id)
|
|
}
|
|
|
|
now := time.Now()
|
|
var cfgJSON string
|
|
if acctType == "business-api" {
|
|
b, _ := json.Marshal(map[string]string{})
|
|
cfgJSON = string(b)
|
|
}
|
|
|
|
account := &models.ModelPublicWhatsappAccount{
|
|
ID: resolvespec_common.NewSqlString(id),
|
|
PhoneNumber: resolvespec_common.NewSqlString(phone),
|
|
AccountType: resolvespec_common.NewSqlString(acctType),
|
|
DisplayName: resolvespec_common.NewSqlString(displayName),
|
|
SessionPath: resolvespec_common.NewSqlString(sessionPath),
|
|
Config: resolvespec_common.NewSqlString(cfgJSON),
|
|
Status: resolvespec_common.NewSqlString("disconnected"),
|
|
UserID: resolvespec_common.NewSqlString(userID),
|
|
Active: true,
|
|
CreatedAt: resolvespec_common.NewSqlTimeStamp(now),
|
|
UpdatedAt: resolvespec_common.NewSqlTimeStamp(now),
|
|
}
|
|
|
|
repo := storage.NewWhatsAppAccountRepository(storage.DB)
|
|
checkError(repo.Create(context.Background(), account))
|
|
|
|
fmt.Printf("Account '%s' added (ID: %s)\n", phone, id)
|
|
fmt.Println("Start the server to connect and pair the device")
|
|
}
|
|
|
|
func removeAccount(client *Client, id string) {
|
|
if serverAvailable(client) {
|
|
resp, err := client.Post("/api/accounts/remove", map[string]string{"id": id})
|
|
checkError(err)
|
|
defer resp.Body.Close()
|
|
fmt.Println("Account removed successfully")
|
|
} else {
|
|
fmt.Println("[server unavailable, removing from database]")
|
|
if !tryInitDB() {
|
|
fmt.Println("Error: server unreachable and no database config found. Use --server-config to specify config path.")
|
|
return
|
|
}
|
|
repo := storage.NewWhatsAppAccountRepository(storage.DB)
|
|
checkError(repo.Delete(context.Background(), id))
|
|
fmt.Printf("Account '%s' removed\n", id)
|
|
}
|
|
}
|
|
|