From 15936674ea6d2fa2147191b31346979e580bbee4 Mon Sep 17 00:00:00 2001 From: abansal88 Date: Sun, 29 Mar 2020 20:32:20 -0700 Subject: [PATCH] adding support for sending media messages --- README.md | 8 +- executors/message_manager.go | 65 ------------- executors/send_message_manager.go | 152 ++++++++++++++++++++++++++++++ main.go | 16 +++- utils/keys.go | 6 ++ utils/models.go | 125 ++++++++++++++++++++---- 6 files changed, 285 insertions(+), 87 deletions(-) delete mode 100644 executors/message_manager.go create mode 100644 executors/send_message_manager.go diff --git a/README.md b/README.md index df66943..fe597b9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ -# go-whatsapp-client +# go-whatsapp-cli This repo builds over Rhymen/go-whatsapp to provide a cli for whatsapp. +# Downloading +go get github.com/abansal4032/go-whatsapp-cli + # Building The Makefile consists of rules to help you build the project. @@ -22,6 +25,7 @@ Make sure you make the changes in the Makefile if you are using go 1.13. The cur Current commands installed:\ **1. whatsapp-cli login**: Logs you in\ **2. whatsapp-cli logout**: Logs you out\ -**3. whatsapp-cli sendText --to --text **: Sends the text_message to the phone_number +**3. whatsapp-cli sendText --to --text **: Sends the text_message to the phone_number\ +**4. whatsapp-cli sendMedia --to --text --filepath **: Sends the file contents to the uphone_number. Currently supports sending audio, video and images.\ **NOTE** : More coming soon... \ No newline at end of file diff --git a/executors/message_manager.go b/executors/message_manager.go deleted file mode 100644 index e7978b7..0000000 --- a/executors/message_manager.go +++ /dev/null @@ -1,65 +0,0 @@ -package executors - -import ( - "fmt" - "github.com/abansal4032/go-whatsapp-cli/utils" - "time" - - "github.com/Rhymen/go-whatsapp" -) - -// SendMessage sends the provided message to the recipient. -func SendMessage(args interface{}, msgType string) error { - msg, err := composeMessage(args, msgType) - if err != nil { - return fmt.Errorf("error while composing message: %v\n", err) - } - return sendMessage(msg) -} - -func composeMessage(msgMetadata interface{}, msgType string) (interface{}, error) { - switch msgType { - case utils.TEXTMESSAGEKEY: - sendTextArgs, ok := msgMetadata.(*utils.SendTextArgs) - if !ok { - return nil, fmt.Errorf("cannot read args for text message") - } - text := sendTextArgs.GetContent() + utils.MESSAGEFOOTER - receiver := sendTextArgs.GetReceiver() - msg := whatsapp.TextMessage{ - Info: whatsapp.MessageInfo{ - RemoteJid: receiver + utils.CONTACTSUFFIX, - }, - Text: text, - } - return msg, nil - case utils.IMAGEMESSAGEKEY: - case utils.VIDEOMESSAGEKEY: - default: - return nil, fmt.Errorf("unknown message type : %v\n", msgType) - } - // Gotta keep the compiler happy - return nil, fmt.Errorf("unknown message type : %v\n", msgType) -} - -func sendMessage(msg interface{}) error { - wac, err := whatsapp.NewConn(5 * time.Second) - if err != nil { - return fmt.Errorf("error creating connection: %v\n", err) - } - - err = LoginWithConnection(wac) - if err != nil { - return fmt.Errorf("error logging in: %v\n", err) - } - - <-time.After(3 * time.Second) - - msgID, err := wac.Send(msg) - if err != nil { - return fmt.Errorf("error sending message: %v", err) - } - - fmt.Printf("successfully sent, messaageID : %v\n", msgID) - return nil -} diff --git a/executors/send_message_manager.go b/executors/send_message_manager.go new file mode 100644 index 0000000..1625a1f --- /dev/null +++ b/executors/send_message_manager.go @@ -0,0 +1,152 @@ +package executors + +import ( + "fmt" + "github.com/abansal4032/go-whatsapp-cli/utils" + "os" + "strings" + "time" + + "github.com/Rhymen/go-whatsapp" +) + +// SendMessage sends the provided message to the recipient. +func SendMessage(args interface{}, msgType string) error { + msg, err := composeMessage(args, msgType) + if err != nil { + return fmt.Errorf("error while composing message: %v\n", err) + } + return sendMessage(msg) +} + +func composeMessage(msgMetadata interface{}, msgType string) (interface{}, error) { + switch msgType { + case utils.TEXTMESSAGEKEY: + return textMessage(msgMetadata) + case utils.IMAGEMESSAGEKEY: + return imageMessage(msgMetadata) + case utils.VIDEOMESSAGEKEY: + return videoMessage(msgMetadata) + case utils.AUDIOMESSAGEKEY: + return audioMessage(msgMetadata) + case utils.MEDIAMESSAGEKEY: + return mediaMessage(msgMetadata) + default: + return nil, fmt.Errorf("unknown message type : %v\n feel free to raise an issue on the github repo with the desired message type to send", msgType) + } +} + +func textMessage(msgMetadata interface{}) (interface{}, error) { + sendTextArgs, ok := msgMetadata.(*utils.SendTextArgs) + if !ok { + return nil, fmt.Errorf("cannot read args for text message") + } + text := sendTextArgs.Content() + utils.MESSAGEFOOTER + receiver := sendTextArgs.Receiver() + msg := whatsapp.TextMessage{ + Info: whatsapp.MessageInfo{ + RemoteJid: receiver + utils.CONTACTSUFFIX, + }, + Text: text, + } + return msg, nil +} + +func videoMessage(msgMetadata interface{}) (interface{}, error) { + sendMediaArgs, ok := msgMetadata.(*utils.SendMediaArgs) + if !ok { + return nil, fmt.Errorf("cannot read args for media message") + } + text := sendMediaArgs.Content() + utils.MESSAGEFOOTER + receiver := sendMediaArgs.Receiver() + contentType := sendMediaArgs.ContentType() + media, _ := os.Open(sendMediaArgs.Filepath()) + msg := whatsapp.VideoMessage{ + Info: whatsapp.MessageInfo{ + RemoteJid: receiver + utils.CONTACTSUFFIX, + }, + Type: contentType, + Caption: text, + Content: media, + } + return msg, nil +} + +func imageMessage(msgMetadata interface{}) (interface{}, error) { + sendMediaArgs, ok := msgMetadata.(*utils.SendMediaArgs) + if !ok { + return nil, fmt.Errorf("cannot read args for media message") + } + text := sendMediaArgs.Content() + utils.MESSAGEFOOTER + receiver := sendMediaArgs.Receiver() + contentType := sendMediaArgs.ContentType() + media, _ := os.Open(sendMediaArgs.Filepath()) + msg := whatsapp.ImageMessage{ + Info: whatsapp.MessageInfo{ + RemoteJid: receiver + utils.CONTACTSUFFIX, + }, + Type: contentType, + Caption: text, + Content: media, + } + return msg, nil +} + +func audioMessage(msgMetadata interface{}) (interface{}, error) { + sendMediaArgs, ok := msgMetadata.(*utils.SendMediaArgs) + if !ok { + return nil, fmt.Errorf("cannot read args for media message") + } + receiver := sendMediaArgs.Receiver() + contentType := sendMediaArgs.ContentType() + media, _ := os.Open(sendMediaArgs.Filepath()) + msg := whatsapp.AudioMessage{ + Info: whatsapp.MessageInfo{ + RemoteJid: receiver + utils.CONTACTSUFFIX, + }, + Type: contentType, + Content: media, + } + return msg, nil +} + +func mediaMessage(msgMetadata interface{}) (interface{}, error) { + sendMediaArgs, ok := msgMetadata.(*utils.SendMediaArgs) + if !ok { + return nil, fmt.Errorf("cannot read args for media message") + } + contentType := sendMediaArgs.ContentType() + t := strings.Split(contentType, "/")[0] + switch t { + case "image": + return imageMessage(msgMetadata) + case "video": + return videoMessage(msgMetadata) + case "audio": + return audioMessage(msgMetadata) + default: + return nil, fmt.Errorf("unknown content type : %v\n feel free to raise an issue with the content type on the github repo", contentType) + } +} + +func sendMessage(msg interface{}) error { + wac, err := whatsapp.NewConn(5 * time.Second) + if err != nil { + return fmt.Errorf("error creating connection: %v\n", err) + } + + err = LoginWithConnection(wac) + if err != nil { + return fmt.Errorf("error logging in: %v\n", err) + } + + <-time.After(3 * time.Second) + + msgID, err := wac.Send(msg) + if err != nil { + return fmt.Errorf("error sending message: %v", err) + } + + fmt.Printf("successfully sent, messaageID : %v\n", msgID) + return nil +} diff --git a/main.go b/main.go index 716c862..5fe653f 100644 --- a/main.go +++ b/main.go @@ -22,7 +22,6 @@ func errorHandler(err error) { } func main() { - sendTextCmd := flag.NewFlagSet("sendText", flag.ExitOnError) var err error defer func() { errorHandler(err) @@ -41,8 +40,9 @@ func main() { } case "sendText": + sendTextCmd := flag.NewFlagSet("sendText", flag.ExitOnError) var args *utils.SendTextArgs - args, err = utils.ParseSendArgs(sendTextCmd, os.Args[2:]) + args, err = utils.ParseSendTextArgs(sendTextCmd, os.Args[2:]) if err != nil { commandUsage(err, sendTextCmd) return @@ -51,6 +51,18 @@ func main() { return } + case "sendMedia": + sendMediaCmd := flag.NewFlagSet("sendMedia", flag.ExitOnError) + var args *utils.SendMediaArgs + args, err = utils.ParseSendMediaArgs(sendMediaCmd, os.Args[2:]) + if err != nil { + commandUsage(err, sendMediaCmd) + return + } + if err = executors.SendMessage(args, utils.MEDIAMESSAGEKEY); err != nil { + return + } + default: // TODO : add a list of all permitted actions err = fmt.Errorf("wrong command provided. please see below for the list of permitted actions") diff --git a/utils/keys.go b/utils/keys.go index 1466262..70a68ab 100644 --- a/utils/keys.go +++ b/utils/keys.go @@ -9,6 +9,12 @@ const IMAGEMESSAGEKEY = "ImageMessage" // VIDEOMESSAGEKEY identifies a message of video type. const VIDEOMESSAGEKEY = "VideoMessage" +// AUDIOMESSAGEKEY identifies a message of audio type. +const AUDIOMESSAGEKEY = "AudioMessage" + +// MEDIAMESSAGEKEY identifies a message of media type. +const MEDIAMESSAGEKEY = "MediaMessage" + // MESSAGEFOOTER is the identifier added to every message to distinguish the messages sent using CLI. const MESSAGEFOOTER = "\n sent using github.com/abansal4032/go-whatsapp-client" diff --git a/utils/models.go b/utils/models.go index 09cd0d6..997210b 100644 --- a/utils/models.go +++ b/utils/models.go @@ -3,41 +3,130 @@ package utils import ( "flag" "fmt" + "net/http" + "os" ) -// SendTextArgs holds the information for sending a message. -type SendTextArgs struct { +type commonArgs struct { to string text string } -// ParseSendArgs parses the arguments for sending a message. -func ParseSendArgs(f *flag.FlagSet, args []string) (*SendTextArgs, error) { +// SendTextArgs holds the information for sending a text message. +type SendTextArgs struct { + commonArgs +} + +// SendMediaArgs holds the information for sending a media message(file). +type SendMediaArgs struct { + commonArgs + filepath string + contentType string +} + +// ParseSendTextArgs parses the arguments for sending a text message. +func ParseSendTextArgs(f *flag.FlagSet, args []string) (*SendTextArgs, error) { to := f.String("to", "", "the recipient of the message.") text := f.String("text", "", "the content of the message.") if err := f.Parse(args); err != nil { return nil, err } - // checks for mandatory fields - if *to == "" { - return nil, fmt.Errorf("to field empty") + sendArgs := &SendTextArgs{ + commonArgs{ + to: *to, + text: *text, + }, } - if *text == "" { - return nil, fmt.Errorf("text field empty") + if err := sendArgs.Validate(); err != nil { + return nil, err } - sendArgs := &SendTextArgs{ - to: *to, - text: *text, + return sendArgs, nil +} + +// Validate validates the body. +func (s *SendTextArgs) Validate() error { + return s.validate() +} + +// ParseSendMediaArgs parses the arguments for sending a media message(file). +func ParseSendMediaArgs(f *flag.FlagSet, args []string) (*SendMediaArgs, error) { + to := f.String("to", "", "the recipient of the message.") + text := f.String("text", "", "the content of the message.") + filepath := f.String("filepath", "", "the absolute of the file to be sent.") + if err := f.Parse(args); err != nil { + return nil, err + } + sendArgs := &SendMediaArgs{ + commonArgs: commonArgs{ + to: *to, + text: *text, + }, + filepath: *filepath, + } + sendArgs.contentType = getFileContentType(*filepath) + if err := sendArgs.Validate(); err != nil { + return nil, err } return sendArgs, nil } -// GetReceiver gets the receiver of the message. -func (s *SendTextArgs) GetReceiver() string { - return s.to +// Validate validates the body. +func (s *SendMediaArgs) Validate() error { + // TODO : Do not need text for audioMessage + if err := s.validate(); err != nil { + return err + } + if s.filepath == "" || !exists(s.filepath) { + return fmt.Errorf("error validating args : the filepath %v does not exist", s.filepath) + } + if s.contentType == "" { + return fmt.Errorf("error validating args : could not get the content type of file") + } + return nil +} + +// Filepath returns the filepath specified by the user. +func (s *SendMediaArgs) Filepath() string { + return s.filepath +} + +// ContentType returns the mime-type of the file content. +func (s *SendMediaArgs) ContentType() string { + return s.contentType } -// GetContent gets the content of the message. -func (s *SendTextArgs) GetContent() string { - return s.text +func exists(filepath string) bool { + if _, err := os.Stat(filepath); err != nil { + // TODO : should check for IsNotExist for error here + return false + } + return true +} + +// Receiver gets the receiver of the message. +func (c *commonArgs) Receiver() string { + return c.to +} + +// Content gets the content of the message. +func (c *commonArgs) Content() string { + return c.text +} + +func (c *commonArgs) validate() error { + if c.to == "" { + return fmt.Errorf("error validating args : empty receiver") + } + return nil +} + +func getFileContentType(filepath string) string { + f, _ := os.Open(filepath) + buffer := make([]byte, 512) + _, err := f.Read(buffer) + if err != nil { + return "" + } + contentType := http.DetectContentType(buffer) + return contentType }