From a54c005674cea126307dd918cc75235a0b5a0bea Mon Sep 17 00:00:00 2001 From: eneller Date: Sat, 25 Oct 2025 00:48:28 +0200 Subject: [PATCH] refactor: cli for batch mode --- Makefile | 3 ++ README.md | 2 +- go.mod | 1 + go.sum | 2 ++ main.go | 102 ++++++++++++++++++++++++++++++------------------------ 5 files changed, 64 insertions(+), 46 deletions(-) diff --git a/Makefile b/Makefile index 00f35c7..4c6c509 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,9 @@ default: build build: $(CC) +run: + go run . + arm: env CGO_ENABLED=1 GOOS=linux GOARCH=arm64 $(CC) -o $(NAME)-arm64 diff --git a/README.md b/README.md index 6c0a8f0..8cba912 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ CLI Whatsapp Messages (and Polls) using go: Can be easily scheduled using `crontab` because of its non-interactive operation ``` NAME: - schneller-whatsapp - Run WhatsApp actions from your CLI. User JID has to end with '@s.whatsapp.net', Group ID with '@g.us' + schneller-whatsapp - Run WhatsApp actions from your CLI. User JID has to end with '@s.whatsapp.net', Group ID with '@g.us'. Defaults to listening on stdin for batch processing. USAGE: schneller-whatsapp [global options] [command [command options]] diff --git a/go.mod b/go.mod index 06e8ebe..36619ce 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/beeper/argo-go v1.1.2 // indirect github.com/elliotchance/orderedmap/v3 v3.1.0 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/mattn/go-colorable v0.1.14 // indirect diff --git a/go.sum b/go.sum index a96375e..62722a1 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,8 @@ github.com/elliotchance/orderedmap/v3 v3.1.0/go.mod h1:G+Hc2RwaZvJMcS4JpGCOyViCn github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= +github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= diff --git a/main.go b/main.go index 73e0fbe..4cf0033 100644 --- a/main.go +++ b/main.go @@ -2,12 +2,14 @@ package main import ( + "bufio" "context" "fmt" "log" "os" "time" + "github.com/google/shlex" _ "github.com/mattn/go-sqlite3" "github.com/mdp/qrterminal/v3" "github.com/urfave/cli/v3" @@ -78,6 +80,25 @@ func initClient() (*whatsmeow.Client, *sqlstore.Container, waLog.Logger) { return client, container, dbLog } +func sendMessage(message *waE2E.Message, jidStr string, client *whatsmeow.Client) { + if message != nil { + var JID types.JID + JID, err := types.ParseJID(jidStr) + if err != nil { + log.Fatal("Failed to parse JID: ", jidStr) + } else { + log.Println("Parsed JID correctly", JID) + } + _, err = client.SendMessage(context.Background(), JID, message) + // FIXME showing error even when successful + if err == nil { + log.Println("Sent Message successfully") + } else { + fmt.Println("Failed to Send Message", err) + + } + } +} func main() { var jidStr, header, text string var message *waE2E.Message @@ -85,7 +106,32 @@ func main() { var container *sqlstore.Container //var dbLog waLog.Logger cmd := &cli.Command{ - Usage: "Run WhatsApp actions from your CLI. User JID has to end with '@s.whatsapp.net', Group ID with '@g.us'", + Usage: "Run WhatsApp actions from your CLI. User JID has to end with '@s.whatsapp.net', Group ID with '@g.us'." + + "Defaults to listening on stdin for batch processing.", + Action: func(ctx context.Context, cmd *cli.Command) error { + fmt.Println("No command specified. Reading from stdin. Press Ctrl+D to exit or run with --help to get help.") + scanner := bufio.NewScanner(os.Stdin) + for scanner.Scan() { + if err := scanner.Err(); err != nil { + fmt.Fprintf(os.Stderr, "Error reading: %v\n", err) + continue + } + args, err := shlex.Split(scanner.Text()) + if err != nil { + fmt.Fprintf(os.Stderr, "Error parsing command: %v\n", err) + continue + } + fmt.Println(args) + subcmd := cmd.Command(args[0]) + if subcmd != nil { + subcmd.Run(ctx, args) + } else { + fmt.Fprintf(os.Stderr, "Unknown command: %s\n", args[0]) + } + } + fmt.Println("Stdin closed.") + return nil + }, Commands: []*cli.Command{ { Name: "message", @@ -95,15 +141,8 @@ func main() { &cli.StringArg{Name: "message", Destination: &text}, }, Action: func(ctx context.Context, cmd *cli.Command) error { - // parse JID - var JID types.JID - JID, err := types.ParseJID(jidStr) - if err != nil { - log.Fatal("Failed to parse JID: ", jidStr) - } else { - log.Println("Parsed JID correctly", JID) - } message = &waE2E.Message{Conversation: proto.String(text)} + sendMessage(message, jidStr, client) return nil }, }, @@ -126,45 +165,18 @@ func main() { Action: func(ctx context.Context, cmd *cli.Command) error { // parse JID message = client.BuildPollCreation(header, cmd.StringArgs("options"), 1) + sendMessage(message, jidStr, client) return nil }, }, }, - Before: func(ctx context.Context, cmd *cli.Command) (context.Context, error) { - client, container, _ = initClient() - return nil, nil - }, - After: func(ctx context.Context, cmd *cli.Command) error { - - if message != nil { - var JID types.JID - JID, err := types.ParseJID(jidStr) - if err != nil { - log.Fatal("Failed to parse JID: ", jidStr) - } else { - log.Println("Parsed JID correctly", JID) - } - _, err = client.SendMessage(context.Background(), JID, message) - // FIXME showing error even when successful - if err != nil { - log.Println("Sent Message successfully") - } else { - fmt.Println("Failed to Send Message", err) - - } - time.Sleep(5 * time.Second) - } - // Listen to Ctrl+C (you can also do something else that prevents the program from exiting) - // c := make(chan os.Signal, 1) - // signal.Notify(c, os.Interrupt, syscall.SIGTERM) - // <-c - - client.Disconnect() - container.Close() - return nil - }, - } - if err := cmd.Run(context.Background(), os.Args); err != nil { - log.Fatal(err) } + // before + client, container, _ = initClient() + // run + cmd.Run(context.Background(), os.Args) + // after + time.Sleep(5 * time.Second) + client.Disconnect() + container.Close() }