Browse Source

feat: add send image API

pull/3/head
Aldino Kemal 4 years ago
parent
commit
ce0dbc057b
  1. 1
      .gitignore
  2. 1
      config/settings.go
  3. 2
      controllers/app_controller.go
  4. 25
      controllers/send_controller.go
  5. 1
      go.mod
  6. 2
      go.sum
  7. 24
      readme.md
  8. 2
      services/send_service.go
  9. 76
      services/send_service_impl.go
  10. 2
      statics/images/sendimages/.gitignore
  11. 13
      structs/auth_struct.go
  12. 23
      structs/send_struct.go
  13. 18
      validations/send_validation.go

1
.gitignore

@ -3,3 +3,4 @@ tmp
.idea
.DS_Store
history-*.json
main

1
config/settings.go

@ -1,4 +1,5 @@
package config
const PathQrCode = "statics/images/qrcode"
const PathSendImage = "statics/images/sendimages"
const DBName = "hydrogenWaCli.db"

2
controllers/app_controller.go

@ -46,7 +46,7 @@ func (controller *AppController) Logout(c *fiber.Ctx) error {
}
func (controller *AppController) Reconnect(c *fiber.Ctx) error {
err := controller.Service.Logout(c)
err := controller.Service.Reconnect(c)
utils.PanicIfNeeded(err)
return c.JSON(utils.ResponseData{

25
controllers/send_controller.go

@ -18,6 +18,7 @@ func NewSendController(service services.SendService) SendController {
func (controller *SendController) Route(app *fiber.App) {
app.Post("/send/message", controller.SendText)
app.Post("/send/image", controller.SendImage)
}
func (controller *SendController) SendText(c *fiber.Ctx) error {
@ -38,3 +39,27 @@ func (controller *SendController) SendText(c *fiber.Ctx) error {
Results: response,
})
}
func (controller *SendController) SendImage(c *fiber.Ctx) error {
var request structs.SendImageRequest
err := c.BodyParser(&request)
utils.PanicIfNeeded(err)
file, err := c.FormFile("image")
utils.PanicIfNeeded(err)
request.Image = file
//add validation send image
validations.ValidateSendImage(request)
request.PhoneNumber = request.PhoneNumber + "@s.whatsapp.net"
response, err := controller.Service.SendImage(c, request)
utils.PanicIfNeeded(err)
return c.JSON(utils.ResponseData{
Code: 200,
Message: "Success",
Results: response,
})
}

1
go.mod

@ -6,6 +6,7 @@ require (
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
github.com/gofiber/fiber/v2 v2.26.0
github.com/gofiber/template v1.6.21
github.com/h2non/bimg v1.1.6
github.com/mattn/go-sqlite3 v1.14.11
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
go.mau.fi/whatsmeow v0.0.0-20220204210537-a425ddb0b16c

2
go.sum

@ -164,6 +164,8 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/h2non/bimg v1.1.6 h1:W/kYCxx5PjiJJrYCB60CLDpm5pl0HJv3hthNp9II9oY=
github.com/h2non/bimg v1.1.6/go.mod h1:R3+UiYwkK4rQl6KVFTOFJHitgLbZXBZNFh2cv3AEbp8=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=

24
readme.md

@ -10,8 +10,22 @@ You can fork or edit this source code !
Current API
| Menu | Method | URL | parameter | type |
|---------------------|--------|---------------|----------------------------------------|-----------|
| Login | GET | /auth/login | | |
| Logout | GET | /auth/logout | | |
| Send Message (Text) | POST | /send/message | phone_number (62...), message (string) | form-data |
| Feature | Menu | Method | URL | parameter | type |
|----------|----------------------|--------|----------------|--------------------------------------------------------------------------|-------------|
| ✅ | Login | GET | /app/login | | |
| ✅ | Logout | GET | /app/logout | | |
| ✅ | Reconnect | GET | /app/reconnect | | |
| ❌ | User Info | GET | /user/info | phone_number (string: 62...) | querystring |
| ❌ | User Avatar | GET | /user/avatar | phone_number (string: 62...) | querystring |
| ✅ | Send Message (Text) | POST | /send/message | phone_number (string: 62...) <br/> message (string) | form-data |
| ✅ | Send Message (Image) | POST | /send/message | phone_number (string: 62...) <br/> caption (string) <br/> image (binary) | form-data |
```
✅ = Available
❌ = Not Available Yet
```
### Mac OS NOTE
Please do this if you have an error (invalid flag in pkg-config --cflags:
-Xpreprocessor) `export CGO_CFLAGS_ALLOW="-Xpreprocessor"`

2
services/send_service.go

@ -7,5 +7,5 @@ import (
type SendService interface {
SendText(c *fiber.Ctx, request structs.SendMessageRequest) (response structs.SendMessageResponse, err error)
SendImage(c *fiber.Ctx)
SendImage(c *fiber.Ctx, request structs.SendImageRequest) (response structs.SendImageResponse, err error)
}

76
services/send_service_impl.go

@ -1,14 +1,20 @@
package services
import (
"context"
"errors"
"fmt"
"github.com/aldinokemal/go-whatsapp-web-multidevice/config"
"github.com/aldinokemal/go-whatsapp-web-multidevice/structs"
"github.com/aldinokemal/go-whatsapp-web-multidevice/utils"
"github.com/gofiber/fiber/v2"
"github.com/h2non/bimg"
"go.mau.fi/whatsmeow"
waProto "go.mau.fi/whatsmeow/binary/proto"
"google.golang.org/protobuf/proto"
"net/http"
"os"
"time"
)
type SendServiceImpl struct {
@ -22,11 +28,11 @@ func NewSendService(waCli *whatsmeow.Client) SendService {
}
func (service SendServiceImpl) SendText(c *fiber.Ctx, request structs.SendMessageRequest) (response structs.SendMessageResponse, err error) {
msg := &waProto.Message{Conversation: proto.String(request.Message)}
recipient, ok := utils.ParseJID(request.PhoneNumber)
if !ok {
return response, errors.New("invalid JID " + request.PhoneNumber)
}
msg := &waProto.Message{Conversation: proto.String(request.Message)}
ts, err := service.WaCli.SendMessage(recipient, "", msg)
if err != nil {
return response, err
@ -36,7 +42,69 @@ func (service SendServiceImpl) SendText(c *fiber.Ctx, request structs.SendMessag
return response, nil
}
func (service SendServiceImpl) SendImage(c *fiber.Ctx) {
//TODO implement me
panic("implement me")
func (service SendServiceImpl) SendImage(c *fiber.Ctx, request structs.SendImageRequest) (response structs.SendImageResponse, err error) {
// Resize image
oriImagePath := fmt.Sprintf("%s/%s", config.PathSendImage, request.Image.Filename)
err = c.SaveFile(request.Image, oriImagePath)
if err != nil {
return response, err
}
openImageBuffer, err := bimg.Read(oriImagePath)
newImage, err := bimg.NewImage(openImageBuffer).Process(bimg.Options{Quality: 90, Width: 400, Height: 400, Embed: true})
if err != nil {
return response, err
}
newImagePath := fmt.Sprintf("%s/new-%s", config.PathSendImage, request.Image.Filename)
err = bimg.Write(newImagePath, newImage)
if err != nil {
return response, err
}
removeFile := func(paths ...string) {
time.Sleep(5 * time.Second)
for _, path := range paths {
err := os.Remove(path)
if err != nil {
fmt.Println("error when delete " + path)
}
}
}
// Send to WA server
dataWaCaption := request.Caption
dataWaRecipient, ok := utils.ParseJID(request.PhoneNumber)
if !ok {
return response, errors.New("invalid JID " + request.PhoneNumber)
}
dataWaImage, err := os.ReadFile(newImagePath)
if err != nil {
return response, err
}
uploadedImage, err := service.WaCli.Upload(context.Background(), dataWaImage, whatsmeow.MediaImage)
if err != nil {
fmt.Printf("Failed to upload file: %v", err)
return response, err
}
msg := &waProto.Message{ImageMessage: &waProto.ImageMessage{
Caption: proto.String(dataWaCaption),
Url: proto.String(uploadedImage.URL),
DirectPath: proto.String(uploadedImage.DirectPath),
MediaKey: uploadedImage.MediaKey,
Mimetype: proto.String(http.DetectContentType(dataWaImage)),
FileEncSha256: uploadedImage.FileEncSHA256,
FileSha256: uploadedImage.FileSHA256,
FileLength: proto.Uint64(uint64(len(dataWaImage))),
ViewOnce: proto.Bool(request.ViewOnce),
}}
ts, err := service.WaCli.SendMessage(dataWaRecipient, "", msg)
if err != nil {
return response, err
} else {
go removeFile(oriImagePath, newImagePath)
response.Status = fmt.Sprintf("Image message sent (server timestamp: %s)", ts)
return response, nil
}
}

2
statics/images/sendimages/.gitignore

@ -0,0 +1,2 @@
*
!.gitignore

13
structs/auth_struct.go

@ -1,18 +1,11 @@
package structs
import "time"
import (
"time"
)
type LoginResponse struct {
ImagePath string `json:"image_path"`
Duration time.Duration `json:"duration"`
Code string `json:"code"`
}
type SendMessageRequest struct {
PhoneNumber string `json:"phone_number" form:"phone_number"`
Message string `json:"message" form:"message"`
}
type SendMessageResponse struct {
Status string `json:"status"`
}

23
structs/send_struct.go

@ -0,0 +1,23 @@
package structs
import "mime/multipart"
type SendMessageRequest struct {
PhoneNumber string `json:"phone_number" form:"phone_number"`
Message string `json:"message" form:"message"`
}
type SendMessageResponse struct {
Status string `json:"status"`
}
type SendImageRequest struct {
PhoneNumber string `json:"phone_number" form:"phone_number"`
Caption string `json:"caption" form:"caption"`
Image *multipart.FileHeader `json:"image" form:"image"`
ViewOnce bool `json:"view_once" form:"view_once"`
}
type SendImageResponse struct {
Status string `json:"status"`
}

18
validations/send_validation.go

@ -24,3 +24,21 @@ func ValidateSendMessage(request structs.SendMessageRequest) {
})
}
}
func ValidateSendImage(request structs.SendImageRequest) {
err := validation.ValidateStruct(&request,
validation.Field(&request.PhoneNumber, validation.Required, is.E164, validation.Length(10, 15)),
validation.Field(&request.Caption, validation.When(true, validation.Length(4, 200))),
validation.Field(&request.Image, validation.Required),
)
if err != nil {
panic(utils.ValidationError{
Message: err.Error(),
})
} else if !strings.HasPrefix(request.PhoneNumber, "62") {
panic(utils.ValidationError{
Message: "this is only work for indonesia country (start with 62)",
})
}
}
Loading…
Cancel
Save