Skip to content

Instantly share code, notes, and snippets.

@HunterXProgrammer
Last active February 15, 2023 10:26
Show Gist options
  • Save HunterXProgrammer/0278cc5508e04a3cc88a1ee1fc84893f to your computer and use it in GitHub Desktop.
Save HunterXProgrammer/0278cc5508e04a3cc88a1ee1fc84893f to your computer and use it in GitHub Desktop.
Adds support for sending videos/pdf/documents and voice messages via whatsmeow mdtest command in Termux/Tasker. Check this for info - https://www.reddit.com/r/tasker/comments/10wiahq/howto_send_imagesvideospdfdocuments_in_whatsapp/
case "senddoc":
if len(args) < 3 {
log.Errorf("Usage: senddoc <jid> <document path> <title> [mime-type]")
return
}
recipient, ok := parseJID(args[0])
if !ok {
return
}
data, err := os.ReadFile(args[1])
if err != nil {
log.Errorf("Failed to read %s: %v", args[0], err)
return
}
uploaded, err := cli.Upload(context.Background(), data, whatsmeow.MediaDocument)
if err != nil {
log.Errorf("Failed to upload file: %v", err)
return
}
if len(args) < 4 {
msg := &waProto.Message{DocumentMessage: &waProto.DocumentMessage{
Title: proto.String(args[2]),
Url: proto.String(uploaded.URL),
DirectPath: proto.String(uploaded.DirectPath),
MediaKey: uploaded.MediaKey,
Mimetype: proto.String(http.DetectContentType(data)),
FileEncSha256: uploaded.FileEncSHA256,
FileSha256: uploaded.FileSHA256,
FileLength: proto.Uint64(uint64(len(data))),
}}
resp, err := cli.SendMessage(context.Background(), recipient, msg)
if err != nil {
log.Errorf("Error sending document message: %v", err)
} else {
log.Infof("Document message sent (server timestamp: %s)", resp.Timestamp)
}
} else {
msg := &waProto.Message{DocumentMessage: &waProto.DocumentMessage{
Title: proto.String(args[2]),
Url: proto.String(uploaded.URL),
DirectPath: proto.String(uploaded.DirectPath),
MediaKey: uploaded.MediaKey,
Mimetype: proto.String(args[3]),
FileEncSha256: uploaded.FileEncSHA256,
FileSha256: uploaded.FileSHA256,
FileLength: proto.Uint64(uint64(len(data))),
}}
resp, err := cli.SendMessage(context.Background(), recipient, msg)
if err != nil {
log.Errorf("Error sending document message: %v", err)
} else {
log.Infof("Document message sent (server timestamp: %s)", resp.Timestamp)
}
}
case "sendvid":
if len(args) < 2 {
log.Errorf("Usage: sendvid <jid> <video path>")
return
}
recipient, ok := parseJID(args[0])
if !ok {
return
}
data, err := os.ReadFile(args[1])
if err != nil {
log.Errorf("Failed to read %s: %v", args[0], err)
return
}
uploaded, err := cli.Upload(context.Background(), data, whatsmeow.MediaVideo)
if err != nil {
log.Errorf("Failed to upload file: %v", err)
return
}
jpegImageFile, jpegErr := os.Open("/storage/emulated/0/Tasker/convert_video/thumbnail.jpg")
if jpegErr != nil{
log.Errorf("Failed to find preview thumbnail file generated by Tasker")
log.Infof("The video message will still be sent, but preview won't be available")
msg := &waProto.Message{VideoMessage: &waProto.VideoMessage{
Url: proto.String(uploaded.URL),
DirectPath: proto.String(uploaded.DirectPath),
MediaKey: uploaded.MediaKey,
Mimetype: proto.String(http.DetectContentType(data)),
FileEncSha256: uploaded.FileEncSHA256,
FileSha256: uploaded.FileSHA256,
FileLength: proto.Uint64(uint64(len(data))),
}}
resp, err := cli.SendMessage(context.Background(), recipient, msg)
if err != nil {
log.Errorf("Error sending video message: %v", err)
} else {
log.Infof("Video message sent (server timestamp: %s)", resp.Timestamp)
}
} else {
defer jpegImageFile.Close()
jpegFileinfo, _ := jpegImageFile.Stat()
var jpegSize int64 = jpegFileinfo.Size()
jpegBytes := make([]byte, jpegSize)
jpegBuffer := bufio.NewReader(jpegImageFile)
_, jpegErr = jpegBuffer.Read(jpegBytes)
thumbnailResp, err := cli.Upload(context.Background(), jpegBytes, whatsmeow.MediaImage)
if err != nil {
log.Errorf("Failed to upload preview thumbnail file: %v", err)
return
}
msg := &waProto.Message{VideoMessage: &waProto.VideoMessage{
Url: proto.String(uploaded.URL),
DirectPath: proto.String(uploaded.DirectPath),
ThumbnailDirectPath: &thumbnailResp.DirectPath,
ThumbnailSha256: thumbnailResp.FileSHA256,
ThumbnailEncSha256: thumbnailResp.FileEncSHA256,
JpegThumbnail: jpegBytes,
MediaKey: uploaded.MediaKey,
Mimetype: proto.String(http.DetectContentType(data)),
FileEncSha256: uploaded.FileEncSHA256,
FileSha256: uploaded.FileSHA256,
FileLength: proto.Uint64(uint64(len(data))),
}}
resp, err := cli.SendMessage(context.Background(), recipient, msg)
if err != nil {
log.Errorf("Error sending video message: %v", err)
} else {
log.Infof("Video message sent (server timestamp: %s)", resp.Timestamp)
}
}
case "sendaudio":
if len(args) < 2 {
log.Errorf("Usage: sendaudio <jid> <audio path>")
return
}
recipient, ok := parseJID(args[0])
if !ok {
return
}
data, err := os.ReadFile(args[1])
if err != nil {
log.Errorf("Failed to read %s: %v", args[0], err)
return
}
uploaded, err := cli.Upload(context.Background(), data, whatsmeow.MediaAudio)
if err != nil {
log.Errorf("Failed to upload file: %v", err)
return
}
msg := &waProto.Message{AudioMessage: &waProto.AudioMessage{
Url: proto.String(uploaded.URL),
DirectPath: proto.String(uploaded.DirectPath),
MediaKey: uploaded.MediaKey,
Mimetype: proto.String("audio/ogg; codecs=opus"),
FileEncSha256: uploaded.FileEncSHA256,
FileSha256: uploaded.FileSHA256,
FileLength: proto.Uint64(uint64(len(data))),
}}
resp, err := cli.SendMessage(context.Background(), recipient, msg)
if err != nil {
log.Errorf("Error sending audio message: %v", err)
} else {
log.Infof("Audio message sent (server timestamp: %s)", resp.Timestamp)
}
case "sendimg":
if len(args) < 2 {
log.Errorf("Usage: sendimg <jid> <image path> [caption]")
return
}
recipient, ok := parseJID(args[0])
if !ok {
return
}
data, err := os.ReadFile(args[1])
if err != nil {
log.Errorf("Failed to read %s: %v", args[0], err)
return
}
uploaded, err := cli.Upload(context.Background(), data, whatsmeow.MediaImage)
if err != nil {
log.Errorf("Failed to upload file: %v", err)
return
}
jpegImageFile, jpegErr := os.Open("/storage/emulated/0/Tasker/convert_image/thumbnail.jpg")
if jpegErr != nil{
log.Errorf("Failed to find preview thumbnail file generated by Tasker")
log.Infof("The image message will still be sent, but preview won't be available")
msg := &waProto.Message{ImageMessage: &waProto.ImageMessage{
Caption: proto.String(strings.Join(args[2:], " ")),
Url: proto.String(uploaded.URL),
DirectPath: proto.String(uploaded.DirectPath),
MediaKey: uploaded.MediaKey,
Mimetype: proto.String(http.DetectContentType(data)),
FileEncSha256: uploaded.FileEncSHA256,
FileSha256: uploaded.FileSHA256,
FileLength: proto.Uint64(uint64(len(data))),
}}
resp, err := cli.SendMessage(context.Background(), recipient, msg)
if err != nil {
log.Errorf("Error sending image message: %v", err)
} else {
log.Infof("Image message sent (server timestamp: %s)", resp.Timestamp)
}
} else {
defer jpegImageFile.Close()
jpegFileinfo, _ := jpegImageFile.Stat()
var jpegSize int64 = jpegFileinfo.Size()
jpegBytes := make([]byte, jpegSize)
jpegBuffer := bufio.NewReader(jpegImageFile)
_, jpegErr = jpegBuffer.Read(jpegBytes)
thumbnailResp, err := cli.Upload(context.Background(), jpegBytes, whatsmeow.MediaImage)
if err != nil {
log.Errorf("Failed to upload preview thumbnail file: %v", err)
return
}
msg := &waProto.Message{ImageMessage: &waProto.ImageMessage{
Caption: proto.String(strings.Join(args[2:], " ")),
Url: proto.String(uploaded.URL),
DirectPath: proto.String(uploaded.DirectPath),
ThumbnailDirectPath: &thumbnailResp.DirectPath,
ThumbnailSha256: thumbnailResp.FileSHA256,
ThumbnailEncSha256: thumbnailResp.FileEncSHA256,
JpegThumbnail: jpegBytes,
MediaKey: uploaded.MediaKey,
Mimetype: proto.String(http.DetectContentType(data)),
FileEncSha256: uploaded.FileEncSHA256,
FileSha256: uploaded.FileSHA256,
FileLength: proto.Uint64(uint64(len(data))),
}}
resp, err := cli.SendMessage(context.Background(), recipient, msg)
if err != nil {
log.Errorf("Error sending image message: %v", err)
} else {
log.Infof("Image message sent (server timestamp: %s)", resp.Timestamp)
}
}
case "setstatus":
@HunterXProgrammer
Copy link
Author

HunterXProgrammer commented Feb 7, 2023

Here is a full list of commands that whatsmeow mdtest handles. Extension functionality added to list as well:-

Usage: appstate <types...>
Usage: request-appstate-key <ids...>
Usage: checkuser <phone numbers...>
Usage: subscribepresence <jid>
Usage: presence <available/unavailable>
Usage: chatpresence <jid> <composing/paused> [audio]
Usage: getuser <jids...>
Usage: getavatar <jid> [existing ID] [--preview] [--community]
Usage: getgroup <jid>
Usage: subgroups <jid>
Usage: communityparticipants <jid>
Usage: getinvitelink <jid> [--reset]
Usage: queryinvitelink <link>
Usage: querybusinesslink <link>
Usage: acceptinvitelink <link>
Usage: setdisappeartimer <jid> <days>
Usage: send <jid> <text>
Usage: sendpoll <jid> <max answers> <question> -- <option 1> / <option 2> / ...
Usage: multisend <jids...> -- <text> (the -- is required)
Usage: react <jid> <message ID> <reaction>
Usage: revoke <jid> <message ID>
Usage: senddoc <jid> <document path> <title>
Usage: sendvid <jid> <video path>
Usage: sendaudio <jid> <audio path>
Usage: sendimg <jid> <image path> [caption]
Usage: setstatus <message>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment