Browse Source

update improve on send chat presence and return message id

pull/28/head
Dimas Restu H 4 years ago
parent
commit
63a9987819
  1. 2
      go.mod
  2. 4
      go.sum
  3. 4
      internal/whatsapp/types/response.go
  4. 219
      internal/whatsapp/whatsapp.go
  5. 170
      pkg/whatsapp/whatsapp.go

2
go.mod

@ -10,7 +10,7 @@ require (
github.com/labstack/echo/v4 v4.7.2
github.com/sirupsen/logrus v1.8.1
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
go.mau.fi/whatsmeow v0.0.0-20220427133828-d1efb884b304
go.mau.fi/whatsmeow v0.0.0-20220504135614-f1f2a9d231fb
golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9
google.golang.org/protobuf v1.28.0
modernc.org/sqlite v1.17.0

4
go.sum

@ -139,8 +139,8 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.mau.fi/libsignal v0.0.0-20220425070825-c40c839ee6a0 h1:3IQF2bgAyibdo77hTejwuJe4jlypj9QaE4xCQuxrThM=
go.mau.fi/libsignal v0.0.0-20220425070825-c40c839ee6a0/go.mod h1:kBOXTvYyDG/q1Ihgvd4J6WenGPh7wtEGvPKF6vmf5ak=
go.mau.fi/whatsmeow v0.0.0-20220427133828-d1efb884b304 h1:P9HQ8ZDUYcFXA3+UPgYbSjmd0adzz2JnVHxB5/o3j/o=
go.mau.fi/whatsmeow v0.0.0-20220427133828-d1efb884b304/go.mod h1:iUBgOLNaqShLrR17u0kIiRptIGFH+nbT1tRhaWBEX/c=
go.mau.fi/whatsmeow v0.0.0-20220504135614-f1f2a9d231fb h1:xI4HiJwBMmztBXFzjKWt7Ea8xmOO7LyYCYV0/ROU7kY=
go.mau.fi/whatsmeow v0.0.0-20220504135614-f1f2a9d231fb/go.mod h1:iUBgOLNaqShLrR17u0kIiRptIGFH+nbT1tRhaWBEX/c=
go.opentelemetry.io/otel v0.5.0/go.mod h1:jzBIgIzK43Iu1BpDAXwqOd6UPsSAk+ewVZ5ofSXw4Ek=
go.opentelemetry.io/otel v0.6.0/go.mod h1:jzBIgIzK43Iu1BpDAXwqOd6UPsSAk+ewVZ5ofSXw4Ek=
go.opentelemetry.io/otel v0.7.0/go.mod h1:aZMyHG5TqDOXEgH2tyLiXSUKly1jT3yqE9PmrzIeCdo=

4
internal/whatsapp/types/response.go

@ -4,3 +4,7 @@ type ResponseLogin struct {
QRCode string `json:"qrcode"`
Timeout int `json:"timeout"`
}
type ResponseSendMessage struct {
MsgID string `json:"msgid"`
}

219
internal/whatsapp/whatsapp.go

@ -40,110 +40,6 @@ func convertFileToBytes(file multipart.File) ([]byte, error) {
return buffer.Bytes(), nil
}
func sendMedia(c echo.Context, mediaType string) error {
var err error
jid := jwtPayload(c).JID
var reqSendMessage typWhatsApp.RequestSendMessage
reqSendMessage.RJID = strings.TrimSpace(c.FormValue("msisdn"))
// Read Uploaded File Based on Send Media Type
var fileStream multipart.File
var fileHeader *multipart.FileHeader
switch mediaType {
case "document":
fileStream, fileHeader, err = c.Request().FormFile("document")
reqSendMessage.Message = fileHeader.Filename
case "image":
fileStream, fileHeader, err = c.Request().FormFile("image")
reqSendMessage.Message = strings.TrimSpace(c.FormValue("caption"))
case "audio":
fileStream, fileHeader, err = c.Request().FormFile("audio")
case "video":
fileStream, fileHeader, err = c.Request().FormFile("video")
reqSendMessage.Message = strings.TrimSpace(c.FormValue("caption"))
}
// Don't Forget to Close The File Stream
defer fileStream.Close()
// Get Uploaded File MIME Type
fileType := fileHeader.Header.Get("Content-Type")
// If There are Some Errors While Opeening The File Stream
// Return Bad Request with Original Error Message
if err != nil {
return router.ResponseBadRequest(c, err.Error())
}
// Make Sure RJID is Filled
if len(reqSendMessage.RJID) == 0 {
return router.ResponseBadRequest(c, "Missing Form Value MSISDN")
}
// Issue #7 Old Version Client Cannot Render WebP Format
// If Media Type is "image" and MIME Type is "image/webp"
// Then Convert it as PNG
var fileBytes []byte
isConvertMediaImageWebP := false
isConvertMediaImageWebP, _ = env.GetEnvBool("WHATSAPP_MEDIA_IMAGE_CONVERT_WEBP")
if mediaType == "image" && fileType == "image/webp" && isConvertMediaImageWebP {
// Decode WebP Image
fileWebP, err := webp.Decode(fileStream)
if err != nil {
return router.ResponseInternalError(c, "Error Decoding Image WebP Format")
}
// Encode to PNG Image
filePNG := new(bytes.Buffer)
err = png.Encode(filePNG, fileWebP)
if err != nil {
return router.ResponseInternalError(c, "Error Encoding Image PNG Format")
}
// Set File Stream Bytes and File Type
// To New Encoded PNG Image and File Type to "image/png"
fileBytes = filePNG.Bytes()
fileType = "image/png"
} else {
// Convert File Stream in to Bytes
// Since WhatsApp Proto for Media is only Accepting Bytes format
fileBytes, err = convertFileToBytes(fileStream)
if err != nil {
return router.ResponseInternalError(c, err.Error())
}
}
// Send Media Message Based on Media Type
switch mediaType {
case "document":
err = pkgWhatsApp.WhatsAppSendDocument(jid, reqSendMessage.RJID, fileBytes, fileType, reqSendMessage.Message)
case "image":
err = pkgWhatsApp.WhatsAppSendImage(jid, reqSendMessage.RJID, fileBytes, fileType, reqSendMessage.Message)
case "audio":
err = pkgWhatsApp.WhatsAppSendAudio(jid, reqSendMessage.RJID, fileBytes, fileType)
case "video":
err = pkgWhatsApp.WhatsAppSendVideo(jid, reqSendMessage.RJID, fileBytes, fileType, reqSendMessage.Message)
}
// Return Internal Server Error
// When Detected There are Some Errors While Sending The Media Message
if err != nil {
return router.ResponseInternalError(c, err.Error())
}
return router.ResponseSuccess(c, "Successfully Send Media Message")
}
func Login(c echo.Context) error {
var err error
jid := jwtPayload(c).JID
@ -221,12 +117,13 @@ func SendText(c echo.Context) error {
return router.ResponseBadRequest(c, "Missing Form Value MSISDN")
}
err = pkgWhatsApp.WhatsAppSendText(jid, reqSendMessage.RJID, reqSendMessage.Message)
var resSendMessage typWhatsApp.ResponseSendMessage
resSendMessage.MsgID, err = pkgWhatsApp.WhatsAppSendText(jid, reqSendMessage.RJID, reqSendMessage.Message)
if err != nil {
return router.ResponseInternalError(c, err.Error())
}
return router.ResponseSuccess(c, "Successfully Send Text Message")
return router.ResponseSuccessWithData(c, "Successfully Send Text Message", resSendMessage)
}
func SendLocation(c echo.Context) error {
@ -250,12 +147,13 @@ func SendLocation(c echo.Context) error {
return router.ResponseBadRequest(c, "Missing Form Value MSISDN")
}
err = pkgWhatsApp.WhatsAppSendLocation(jid, reqSendLocation.RJID, reqSendLocation.Latitude, reqSendLocation.Longitude)
var resSendMessage typWhatsApp.ResponseSendMessage
resSendMessage.MsgID, err = pkgWhatsApp.WhatsAppSendLocation(jid, reqSendLocation.RJID, reqSendLocation.Latitude, reqSendLocation.Longitude)
if err != nil {
return router.ResponseInternalError(c, err.Error())
}
return router.ResponseSuccess(c, "Successfully Send Location Message")
return router.ResponseSuccessWithData(c, "Successfully Send Location Message", resSendMessage)
}
func SendDocument(c echo.Context) error {
@ -273,3 +171,108 @@ func SendAudio(c echo.Context) error {
func SendVideo(c echo.Context) error {
return sendMedia(c, "video")
}
func sendMedia(c echo.Context, mediaType string) error {
var err error
jid := jwtPayload(c).JID
var reqSendMessage typWhatsApp.RequestSendMessage
reqSendMessage.RJID = strings.TrimSpace(c.FormValue("msisdn"))
// Read Uploaded File Based on Send Media Type
var fileStream multipart.File
var fileHeader *multipart.FileHeader
switch mediaType {
case "document":
fileStream, fileHeader, err = c.Request().FormFile("document")
reqSendMessage.Message = fileHeader.Filename
case "image":
fileStream, fileHeader, err = c.Request().FormFile("image")
reqSendMessage.Message = strings.TrimSpace(c.FormValue("caption"))
case "audio":
fileStream, fileHeader, err = c.Request().FormFile("audio")
case "video":
fileStream, fileHeader, err = c.Request().FormFile("video")
reqSendMessage.Message = strings.TrimSpace(c.FormValue("caption"))
}
// Don't Forget to Close The File Stream
defer fileStream.Close()
// Get Uploaded File MIME Type
fileType := fileHeader.Header.Get("Content-Type")
// If There are Some Errors While Opeening The File Stream
// Return Bad Request with Original Error Message
if err != nil {
return router.ResponseBadRequest(c, err.Error())
}
// Make Sure RJID is Filled
if len(reqSendMessage.RJID) == 0 {
return router.ResponseBadRequest(c, "Missing Form Value MSISDN")
}
// Issue #7 Old Version Client Cannot Render WebP Format
// If Media Type is "image" and MIME Type is "image/webp"
// Then Convert it as PNG
var fileBytes []byte
isConvertMediaImageWebP := false
isConvertMediaImageWebP, _ = env.GetEnvBool("WHATSAPP_MEDIA_IMAGE_CONVERT_WEBP")
if mediaType == "image" && fileType == "image/webp" && isConvertMediaImageWebP {
// Decode WebP Image
fileWebP, err := webp.Decode(fileStream)
if err != nil {
return router.ResponseInternalError(c, "Error Decoding Image WebP Format")
}
// Encode to PNG Image
filePNG := new(bytes.Buffer)
err = png.Encode(filePNG, fileWebP)
if err != nil {
return router.ResponseInternalError(c, "Error Encoding Image PNG Format")
}
// Set File Stream Bytes and File Type
// To New Encoded PNG Image and File Type to "image/png"
fileBytes = filePNG.Bytes()
fileType = "image/png"
} else {
// Convert File Stream in to Bytes
// Since WhatsApp Proto for Media is only Accepting Bytes format
fileBytes, err = convertFileToBytes(fileStream)
if err != nil {
return router.ResponseInternalError(c, err.Error())
}
}
// Send Media Message Based on Media Type
var resSendMessage typWhatsApp.ResponseSendMessage
switch mediaType {
case "document":
resSendMessage.MsgID, err = pkgWhatsApp.WhatsAppSendDocument(jid, reqSendMessage.RJID, fileBytes, fileType, reqSendMessage.Message)
case "image":
resSendMessage.MsgID, err = pkgWhatsApp.WhatsAppSendImage(jid, reqSendMessage.RJID, fileBytes, fileType, reqSendMessage.Message)
case "audio":
resSendMessage.MsgID, err = pkgWhatsApp.WhatsAppSendAudio(jid, reqSendMessage.RJID, fileBytes, fileType)
case "video":
resSendMessage.MsgID, err = pkgWhatsApp.WhatsAppSendVideo(jid, reqSendMessage.RJID, fileBytes, fileType, reqSendMessage.Message)
}
// Return Internal Server Error
// When Detected There are Some Errors While Sending The Media Message
if err != nil {
return router.ResponseInternalError(c, err.Error())
}
return router.ResponseSuccessWithData(c, "Successfully Send Media Message", resSendMessage)
}

170
pkg/whatsapp/whatsapp.go

@ -264,61 +264,62 @@ func WhatsAppComposeStatus(jid string, rjid types.JID, isComposing bool, isAudio
_ = WhatsAppClient[jid].SendChatPresence(rjid, typeCompose, typeComposeMedia)
}
func WhatsAppSendText(jid string, rjid string, message string) error {
func WhatsAppSendText(jid string, rjid string, message string) (string, error) {
if WhatsAppClient[jid] != nil {
var err error
// Make Sure WhatsApp Client is OK
err = WhatsAppClientIsOK(jid)
if err != nil {
return err
return "", err
}
// Make Sure rJID is Proper JID Type
composeRJID := WhatsAppComposeJID(rjid)
// Make Sure Remote JID is Proper JID Type
remoteJID := WhatsAppComposeJID(rjid)
// Set Chat Presence to Composing
WhatsAppComposeStatus(jid, composeRJID, true, false)
// Set Chat Presence
WhatsAppComposeStatus(jid, remoteJID, true, false)
defer WhatsAppComposeStatus(jid, remoteJID, false, false)
// Compose WhatsApp Proto
content := &waproto.Message{
msgId := whatsmeow.GenerateMessageID()
msgContent := &waproto.Message{
Conversation: proto.String(message),
}
// Send WhatsApp Message Proto
_, err = WhatsAppClient[jid].SendMessage(composeRJID, "", content)
_, err = WhatsAppClient[jid].SendMessage(remoteJID, msgId, msgContent)
if err != nil {
return err
return "", err
}
// Set Chat Presence to Paused
WhatsAppComposeStatus(jid, composeRJID, false, false)
return nil
return msgId, nil
}
// Return Error WhatsApp Client is not Valid
return errors.New("WhatsApp Client is not Valid")
return "", errors.New("WhatsApp Client is not Valid")
}
func WhatsAppSendLocation(jid string, rjid string, latitude float64, longitude float64) error {
func WhatsAppSendLocation(jid string, rjid string, latitude float64, longitude float64) (string, error) {
if WhatsAppClient[jid] != nil {
var err error
// Make Sure WhatsApp Client is OK
err = WhatsAppClientIsOK(jid)
if err != nil {
return err
return "", err
}
// Make Sure rJID is Proper JID Type
composeRJID := WhatsAppComposeJID(rjid)
// Make Sure Remote JID is Proper JID Type
remoteJID := WhatsAppComposeJID(rjid)
// Set Chat Presence to Composing
WhatsAppComposeStatus(jid, composeRJID, true, false)
// Set Chat Presence
WhatsAppComposeStatus(jid, remoteJID, true, false)
defer WhatsAppComposeStatus(jid, remoteJID, false, false)
// Compose WhatsApp Proto
content := &waproto.Message{
msgId := whatsmeow.GenerateMessageID()
msgContent := &waproto.Message{
LocationMessage: &waproto.LocationMessage{
DegreesLatitude: proto.Float64(latitude),
DegreesLongitude: proto.Float64(longitude),
@ -326,45 +327,44 @@ func WhatsAppSendLocation(jid string, rjid string, latitude float64, longitude f
}
// Send WhatsApp Message Proto
_, err = WhatsAppClient[jid].SendMessage(composeRJID, "", content)
_, err = WhatsAppClient[jid].SendMessage(remoteJID, msgId, msgContent)
if err != nil {
return err
return "", err
}
// Set Chat Presence to Paused
WhatsAppComposeStatus(jid, composeRJID, false, false)
return nil
return msgId, nil
}
// Return Error WhatsApp Client is not Valid
return errors.New("WhatsApp Client is not Valid")
return "", errors.New("WhatsApp Client is not Valid")
}
func WhatsAppSendDocument(jid string, rjid string, fileBytes []byte, fileType string, fileName string) error {
func WhatsAppSendDocument(jid string, rjid string, fileBytes []byte, fileType string, fileName string) (string, error) {
if WhatsAppClient[jid] != nil {
var err error
// Make Sure WhatsApp Client is OK
err = WhatsAppClientIsOK(jid)
if err != nil {
return err
return "", err
}
// Make Sure rJID is Proper JID Type
composeRJID := WhatsAppComposeJID(rjid)
// Make Sure Remote JID is Proper JID Type
remoteJID := WhatsAppComposeJID(rjid)
// Set Chat Presence to Composing
WhatsAppComposeStatus(jid, composeRJID, true, false)
// Set Chat Presence
WhatsAppComposeStatus(jid, remoteJID, true, false)
defer WhatsAppComposeStatus(jid, remoteJID, false, false)
// Upload File to WhatsApp Storage Server
fileUploaded, err := WhatsAppClient[jid].Upload(context.Background(), fileBytes, whatsmeow.MediaDocument)
if err != nil {
return errors.New("Error While Uploading Media to WhatsApp Server")
return "", errors.New("Error While Uploading Media to WhatsApp Server")
}
// Compose WhatsApp Proto
content := &waproto.Message{
msgId := whatsmeow.GenerateMessageID()
msgContent := &waproto.Message{
DocumentMessage: &waproto.DocumentMessage{
Url: proto.String(fileUploaded.URL),
DirectPath: proto.String(fileUploaded.DirectPath),
@ -379,45 +379,44 @@ func WhatsAppSendDocument(jid string, rjid string, fileBytes []byte, fileType st
}
// Send WhatsApp Message Proto
_, err = WhatsAppClient[jid].SendMessage(composeRJID, "", content)
_, err = WhatsAppClient[jid].SendMessage(remoteJID, msgId, msgContent)
if err != nil {
return err
return "", err
}
// Set Chat Presence to Paused
WhatsAppComposeStatus(jid, composeRJID, false, false)
return nil
return msgId, nil
}
// Return Error WhatsApp Client is not Valid
return errors.New("WhatsApp Client is not Valid")
return "", errors.New("WhatsApp Client is not Valid")
}
func WhatsAppSendImage(jid string, rjid string, imageBytes []byte, imageType string, imageCaption string) error {
func WhatsAppSendImage(jid string, rjid string, imageBytes []byte, imageType string, imageCaption string) (string, error) {
if WhatsAppClient[jid] != nil {
var err error
// Make Sure WhatsApp Client is OK
err = WhatsAppClientIsOK(jid)
if err != nil {
return err
return "", err
}
// Make Sure rJID is Proper JID Type
composeRJID := WhatsAppComposeJID(rjid)
// Make Sure Remote JID is Proper JID Type
remoteJID := WhatsAppComposeJID(rjid)
// Set Chat Presence to Composing
WhatsAppComposeStatus(jid, composeRJID, true, false)
// Set Chat Presence
WhatsAppComposeStatus(jid, remoteJID, true, false)
defer WhatsAppComposeStatus(jid, remoteJID, false, false)
// Upload Image to WhatsApp Storage Server
imageUploaded, err := WhatsAppClient[jid].Upload(context.Background(), imageBytes, whatsmeow.MediaImage)
if err != nil {
return errors.New("Error While Uploading Media to WhatsApp Server")
return "", errors.New("Error While Uploading Media to WhatsApp Server")
}
// Compose WhatsApp Proto
content := &waproto.Message{
msgId := whatsmeow.GenerateMessageID()
msgContent := &waproto.Message{
ImageMessage: &waproto.ImageMessage{
Url: proto.String(imageUploaded.URL),
DirectPath: proto.String(imageUploaded.DirectPath),
@ -431,45 +430,44 @@ func WhatsAppSendImage(jid string, rjid string, imageBytes []byte, imageType str
}
// Send WhatsApp Message Proto
_, err = WhatsAppClient[jid].SendMessage(composeRJID, "", content)
_, err = WhatsAppClient[jid].SendMessage(remoteJID, msgId, msgContent)
if err != nil {
return err
return "", err
}
// Set Chat Presence to Paused
WhatsAppComposeStatus(jid, composeRJID, false, false)
return nil
return msgId, nil
}
// Return Error WhatsApp Client is not Valid
return errors.New("WhatsApp Client is not Valid")
return "", errors.New("WhatsApp Client is not Valid")
}
func WhatsAppSendAudio(jid string, rjid string, audioBytes []byte, audioType string) error {
func WhatsAppSendAudio(jid string, rjid string, audioBytes []byte, audioType string) (string, error) {
if WhatsAppClient[jid] != nil {
var err error
// Make Sure WhatsApp Client is OK
err = WhatsAppClientIsOK(jid)
if err != nil {
return err
return "", err
}
// Make Sure rJID is Proper JID Type
composeRJID := WhatsAppComposeJID(rjid)
// Make Sure Remote JID is Proper JID Type
remoteJID := WhatsAppComposeJID(rjid)
// Set Chat Presence to Composing
WhatsAppComposeStatus(jid, composeRJID, true, true)
// Set Chat Presence
WhatsAppComposeStatus(jid, remoteJID, true, true)
defer WhatsAppComposeStatus(jid, remoteJID, false, true)
// Upload Audio to WhatsApp Storage Server
audioUploaded, err := WhatsAppClient[jid].Upload(context.Background(), audioBytes, whatsmeow.MediaAudio)
if err != nil {
return errors.New("Error While Uploading Media to WhatsApp Server")
return "", errors.New("Error While Uploading Media to WhatsApp Server")
}
// Compose WhatsApp Proto
content := &waproto.Message{
msgId := whatsmeow.GenerateMessageID()
msgContent := &waproto.Message{
AudioMessage: &waproto.AudioMessage{
Url: proto.String(audioUploaded.URL),
DirectPath: proto.String(audioUploaded.DirectPath),
@ -482,45 +480,44 @@ func WhatsAppSendAudio(jid string, rjid string, audioBytes []byte, audioType str
}
// Send WhatsApp Message Proto
_, err = WhatsAppClient[jid].SendMessage(composeRJID, "", content)
_, err = WhatsAppClient[jid].SendMessage(remoteJID, msgId, msgContent)
if err != nil {
return err
return "", err
}
// Set Chat Presence to Paused
WhatsAppComposeStatus(jid, composeRJID, false, true)
return nil
return msgId, nil
}
// Return Error WhatsApp Client is not Valid
return errors.New("WhatsApp Client is not Valid")
return "", errors.New("WhatsApp Client is not Valid")
}
func WhatsAppSendVideo(jid string, rjid string, videoBytes []byte, videoType string, videoCaption string) error {
func WhatsAppSendVideo(jid string, rjid string, videoBytes []byte, videoType string, videoCaption string) (string, error) {
if WhatsAppClient[jid] != nil {
var err error
// Make Sure WhatsApp Client is OK
err = WhatsAppClientIsOK(jid)
if err != nil {
return err
return "", err
}
// Make Sure rJID is Proper JID Type
composeRJID := WhatsAppComposeJID(rjid)
// Make Sure Remote JID is Proper JID Type
remoteJID := WhatsAppComposeJID(rjid)
// Set Chat Presence to Composing
WhatsAppComposeStatus(jid, composeRJID, true, false)
// Set Chat Presence
WhatsAppComposeStatus(jid, remoteJID, true, false)
defer WhatsAppComposeStatus(jid, remoteJID, false, false)
// Upload Video to WhatsApp Storage Server
videoUploaded, err := WhatsAppClient[jid].Upload(context.Background(), videoBytes, whatsmeow.MediaVideo)
if err != nil {
return errors.New("Error While Uploading Media to WhatsApp Server")
return "", errors.New("Error While Uploading Media to WhatsApp Server")
}
// Compose WhatsApp Proto
content := &waproto.Message{
msgId := whatsmeow.GenerateMessageID()
msgContent := &waproto.Message{
VideoMessage: &waproto.VideoMessage{
Url: proto.String(videoUploaded.URL),
DirectPath: proto.String(videoUploaded.DirectPath),
@ -534,17 +531,14 @@ func WhatsAppSendVideo(jid string, rjid string, videoBytes []byte, videoType str
}
// Send WhatsApp Message Proto
_, err = WhatsAppClient[jid].SendMessage(composeRJID, "", content)
_, err = WhatsAppClient[jid].SendMessage(remoteJID, msgId, msgContent)
if err != nil {
return err
return "", err
}
// Set Chat Presence to Paused
WhatsAppComposeStatus(jid, composeRJID, false, false)
return nil
return msgId, nil
}
// Return Error WhatsApp Client is not Valid
return errors.New("WhatsApp Client is not Valid")
return "", errors.New("WhatsApp Client is not Valid")
}
Loading…
Cancel
Save