diff --git a/controllers/send_controller.go b/controllers/send_controller.go index abff757..6e1aab6 100644 --- a/controllers/send_controller.go +++ b/controllers/send_controller.go @@ -30,7 +30,12 @@ func (controller *SendController) SendText(c *fiber.Ctx) error { // add validation send message validations.ValidateSendMessage(request) - request.Phone = request.Phone + "@s.whatsapp.net" + if request.Type == structs.TypeGroup { + request.Phone = request.Phone + "@g.us" + } else { + request.Phone = request.Phone + "@s.whatsapp.net" + } + response, err := controller.Service.SendText(c, request) utils.PanicIfNeeded(err) @@ -54,7 +59,12 @@ func (controller *SendController) SendImage(c *fiber.Ctx) error { //add validation send image validations.ValidateSendImage(request) - request.Phone = request.Phone + "@s.whatsapp.net" + if request.Type == structs.TypeGroup { + request.Phone = request.Phone + "@g.us" + } else { + request.Phone = request.Phone + "@s.whatsapp.net" + } + response, err := controller.Service.SendImage(c, request) utils.PanicIfNeeded(err) @@ -78,7 +88,12 @@ func (controller *SendController) SendFile(c *fiber.Ctx) error { //add validation send image validations.ValidateSendFile(request) - request.Phone = request.Phone + "@s.whatsapp.net" + if request.Type == structs.TypeGroup { + request.Phone = request.Phone + "@g.us" + } else { + request.Phone = request.Phone + "@s.whatsapp.net" + } + response, err := controller.Service.SendFile(c, request) utils.PanicIfNeeded(err) diff --git a/go.mod b/go.mod index 3f2c195..0732ff0 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( 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 + github.com/stretchr/testify v1.7.0 go.mau.fi/whatsmeow v0.0.0-20220210171358-894bfaa70e7b google.golang.org/protobuf v1.27.1 ) @@ -17,12 +18,15 @@ require ( filippo.io/edwards25519 v1.0.0-rc.1 // indirect github.com/andybalholm/brotli v1.0.4 // indirect github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/klauspost/compress v1.14.2 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.33.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect go.mau.fi/libsignal v0.0.0-20211109153248-a67163214910 // indirect golang.org/x/crypto v0.0.0-20220210151621-f4118a5b28e2 // indirect golang.org/x/sys v0.0.0-20220209214540-3681064d5158 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect ) diff --git a/go.sum b/go.sum index a940ebc..224c771 100644 --- a/go.sum +++ b/go.sum @@ -272,6 +272,7 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= @@ -305,6 +306,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -805,6 +807,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/readme.md b/readme.md index 2a9be1b..e3bdb5d 100644 --- a/readme.md +++ b/readme.md @@ -10,8 +10,7 @@ - `sudo apt install libvips-dev` - Windows (not recomended, prefer using [WSL](https://docs.microsoft.com/en-us/windows/wsl/install)): - install vips library, or you can check here https://www.libvips.org/install.html - - `choco install nip2` - - `choco install pkgconfiglite` + - add to [environment variable](https://www.google.com/search?q=windows+add+to+environment+path) ### How to use @@ -23,19 +22,19 @@ You can fork or edit this source code ! ### Current API -| Feature | Menu | Method | URL | Payload | -|---------|-------------------------|--------|------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| ✅ | Login | GET | /app/login | | -| ✅ | Logout | GET | /app/logout | | -| ✅ | Reconnect | GET | /app/reconnect | | -| ✅ | User Info | GET | /user/info |
Param Type Type Example
phonestringquerystring6289685024099
| -| ✅ | User Avatar | GET | /user/avatar |
Param Type Type Example
phonestringquerystring6289685024099
| -| ✅ | User My Group List | GET | /user/my/groups | | -| ✅ | User My Privacy Setting | GET | /user/my/privacy | | -| ✅ | Send Message (Text) | POST | /send/message |
Param Type Type Example
phonestringform-data6289685024099
messagestringform-dataHello guys this is testing
| -| ✅ | Send Message (Image) | POST | /send/image |
Param Type Type Example
phonestringform-data6289685024099
captionstringform-dataHello guys this is caption
view_onceboolform-datafalse
imagebinaryform-dataimage/jpg,image/jpeg,image/png
| -| ✅ | Send Message (File) | POST | /send/file |
ParamTypeTypeExample
phonestringform-data6289685024099
filebinaryform-dataany (max: 10MB)
| -| ❌ | Send Message (Video) | POST | /send/video |
ParamTypeTypeExample
phonestringform-data6289685024099
videobinaryform-datamp4/avi/mkv
| +| Feature | Menu | Method | URL | Payload | +|---------|-------------------------|--------|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ✅ | Login | GET | /app/login | | +| ✅ | Logout | GET | /app/logout | | +| ✅ | Reconnect | GET | /app/reconnect | | +| ✅ | User Info | GET | /user/info |
Param Type Type Example
phonestringquerystring6289685024099
| +| ✅ | User Avatar | GET | /user/avatar |
Param Type Type Example
phonestringquerystring6289685024099
| +| ✅ | User My Group List | GET | /user/my/groups | | +| ✅ | User My Privacy Setting | GET | /user/my/privacy | | +| ✅ | Send Message (Text) | POST | /send/message |
Param Data Type Type Example
phonestringform-data6289685024099
messagestringform-dataHello guys this is testing
typestring (user/group)form-datauser
| +| ✅ | Send Message (Image) | POST | /send/image |
Param Type Type Example
phonestringform-data6289685024099
captionstringform-dataHello guys this is caption
view_onceboolform-datafalse
imagebinaryform-dataimage/jpg,image/jpeg,image/png
typestring (user/group)form-datauser
| +| ✅ | Send Message (File) | POST | /send/file |
ParamTypeTypeExample
phonestringform-data6289685024099
filebinaryform-dataany (max: 10MB)
typestring (user/group)form-datauser
| +| ❌ | Send Message (Video) | POST | /send/video |
ParamTypeTypeExample
phonestringform-data6289685024099
videobinaryform-datamp4/avi/mkv
typestring (user/group)form-datauser
| ``` ✅ = Available @@ -44,7 +43,7 @@ You can fork or edit this source code ! ### App User Interface -1. Homepage ![Homepage](https://i.ibb.co/gWnzy2F/Screen-Shot-2022-02-13-at-12-55-39.png) +1. Homepage ![Homepage](https://i.ibb.co/xg6r0BV/Screen-Shot-2022-04-23-at-19-55-56.png) 2. Login ![Login](https://i.ibb.co/Yp3YJKM/Screen-Shot-2022-02-13-at-12-55-54.png) 3. Send Message ![Send Message](https://i.ibb.co/YcSfvmP/Screen-Shot-2022-02-13-at-12-58-58.png) 4. Send Image ![Send Image](https://i.ibb.co/HDVJZSN/Screen-Shot-2022-02-13-at-12-59-06.png) diff --git a/services/send_service_impl.go b/services/send_service_impl.go index 2d08e3b..9cc6a41 100644 --- a/services/send_service_impl.go +++ b/services/send_service_impl.go @@ -39,7 +39,7 @@ func (service SendServiceImpl) SendText(_ *fiber.Ctx, request structs.SendMessag if err != nil { return response, err } else { - response.Status = fmt.Sprintf("Message sent (server timestamp: %s)", ts) + response.Status = fmt.Sprintf("Message sent to %s (server timestamp: %s)", request.Phone, ts) } return response, nil } @@ -108,7 +108,7 @@ func (service SendServiceImpl) SendImage(c *fiber.Ctx, request structs.SendImage if err != nil { return response, err } else { - response.Status = fmt.Sprintf("Image message sent (server timestamp: %s)", ts) + response.Status = fmt.Sprintf("Message sent to %s (server timestamp: %s)", request.Phone, ts) return response, nil } } @@ -165,7 +165,7 @@ func (service SendServiceImpl) SendFile(c *fiber.Ctx, request structs.SendFileRe if err != nil { return response, err } else { - response.Status = fmt.Sprintf("File message sent (server timestamp: %s)", ts) + response.Status = fmt.Sprintf("Message sent to %s (server timestamp: %s)", request.Phone, ts) return response, nil } } diff --git a/structs/send_struct.go b/structs/send_struct.go index 50df8be..bd6dd9f 100644 --- a/structs/send_struct.go +++ b/structs/send_struct.go @@ -56,9 +56,15 @@ type UserMyListGroupsResponse struct { // ============================== SEND ============================== +type SendType string + +const TypeUser SendType = "user" +const TypeGroup SendType = "group" + type SendMessageRequest struct { - Phone string `json:"phone" form:"phone"` - Message string `json:"message" form:"message"` + Phone string `json:"phone" form:"phone"` + Message string `json:"message" form:"message"` + Type SendType `json:"type" form:"type"` } type SendMessageResponse struct { @@ -70,6 +76,7 @@ type SendImageRequest struct { Caption string `json:"caption" form:"caption"` Image *multipart.FileHeader `json:"image" form:"image"` ViewOnce bool `json:"view_once" form:"view_once"` + Type SendType `json:"type" form:"message"` } type SendImageResponse struct { @@ -79,6 +86,7 @@ type SendImageResponse struct { type SendFileRequest struct { Phone string `json:"phone" form:"phone"` File *multipart.FileHeader `json:"file" form:"file"` + Type SendType `json:"type" form:"message"` } type SendFileResponse struct { diff --git a/validations/send_validation.go b/validations/send_validation.go index a308261..11c73a1 100644 --- a/validations/send_validation.go +++ b/validations/send_validation.go @@ -5,12 +5,11 @@ import ( "github.com/aldinokemal/go-whatsapp-web-multidevice/utils" validation "github.com/go-ozzo/ozzo-validation/v4" "github.com/go-ozzo/ozzo-validation/v4/is" - "strings" ) func ValidateSendMessage(request structs.SendMessageRequest) { err := validation.ValidateStruct(&request, - validation.Field(&request.Phone, validation.Required, is.E164, validation.Length(10, 15)), + validation.Field(&request.Phone, validation.Required, is.Digit, validation.Length(10, 25)), validation.Field(&request.Message, validation.Required, validation.Length(1, 50)), ) @@ -18,16 +17,12 @@ func ValidateSendMessage(request structs.SendMessageRequest) { panic(utils.ValidationError{ Message: err.Error(), }) - } else if !strings.HasPrefix(request.Phone, "62") { - panic(utils.ValidationError{ - Message: "phone number only work for indonesia country (start with 62)", - }) } } func ValidateSendImage(request structs.SendImageRequest) { err := validation.ValidateStruct(&request, - validation.Field(&request.Phone, validation.Required, is.E164, validation.Length(10, 15)), + validation.Field(&request.Phone, validation.Required, is.Digit, validation.Length(10, 25)), validation.Field(&request.Caption, validation.When(true, validation.Length(1, 200))), validation.Field(&request.Image, validation.Required), ) @@ -36,10 +31,6 @@ func ValidateSendImage(request structs.SendImageRequest) { panic(utils.ValidationError{ Message: err.Error(), }) - } else if !strings.HasPrefix(request.Phone, "62") { - panic(utils.ValidationError{ - Message: "phone number only work for indonesia country (start with 62)", - }) } availableMimes := map[string]bool{ @@ -58,7 +49,7 @@ func ValidateSendImage(request structs.SendImageRequest) { func ValidateSendFile(request structs.SendFileRequest) { err := validation.ValidateStruct(&request, - validation.Field(&request.Phone, validation.Required, is.E164, validation.Length(10, 15)), + validation.Field(&request.Phone, validation.Required, is.Digit, validation.Length(10, 25)), validation.Field(&request.File, validation.Required), ) @@ -66,10 +57,6 @@ func ValidateSendFile(request structs.SendFileRequest) { panic(utils.ValidationError{ Message: err.Error(), }) - } else if !strings.HasPrefix(request.Phone, "62") { - panic(utils.ValidationError{ - Message: "phone number only work for indonesia country (start with 62)", - }) } if request.File.Size > 10240000 { // 10MB diff --git a/validations/send_validation_test.go b/validations/send_validation_test.go new file mode 100644 index 0000000..57d31bf --- /dev/null +++ b/validations/send_validation_test.go @@ -0,0 +1,61 @@ +package validations + +import ( + "github.com/aldinokemal/go-whatsapp-web-multidevice/structs" + "github.com/aldinokemal/go-whatsapp-web-multidevice/utils" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestValidateSendMessage(t *testing.T) { + type args struct { + request structs.SendMessageRequest + } + tests := []struct { + name string + args args + err interface{} + }{ + { + name: "success phone & message normal", + args: args{request: structs.SendMessageRequest{ + Phone: "6289685024091", + Message: "Hello this is testing", + }}, + err: nil, + }, + { + name: "error invalid phone", + args: args{request: structs.SendMessageRequest{ + Phone: "some-random-phone", + Message: "Hello this is testing", + }}, + err: utils.ValidationError{ + Message: "phone: must contain digits only.", + }, + }, + { + name: "error invalid phone contains dash (-)", + args: args{request: structs.SendMessageRequest{ + Phone: "6289-748-291", + Message: "Hello this is testing", + }}, + err: utils.ValidationError{ + Message: "phone: must contain digits only.", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.err == nil { + ValidateSendMessage(tt.args.request) + } else { + assert.PanicsWithValue(t, tt.err, func() { + ValidateSendMessage(tt.args.request) + }) + } + + }) + } +} diff --git a/views/index.html b/views/index.html index 76aaefc..e2a1c2c 100644 --- a/views/index.html +++ b/views/index.html @@ -59,20 +59,18 @@
-
+
-
Send Message (Text)
+
Send Message (Text) | Private Message
App
- Send any message to any -
indonesian
- whatsapp number + Send any message to any whatsapp number
-
+
-
Send Message (Image)
+
Send Message (Image) | Private Message
App
Send image with @@ -81,9 +79,42 @@
-
+
-
Send Message (File)
+
Send Message (File) | Private Message
+
App
+
+ Send any file up to +
10MB
+
+
+
+
+ +
+
+
+
Send Message (Text) | Group
+
App
+
+ Send any message to any whatsapp number +
+
+
+
+
+
Send Message (Image) | Group
+
App
+
+ Send image with +
jpg/jpeg/png
+ type +
+
+
+
+
+
Send Message (File) | Group
App
Send any file up to @@ -141,7 +172,7 @@
- + qrCodeLogin
Please scan to connect
@@ -165,7 +196,7 @@
- +
@@ -193,7 +224,7 @@
- +
@@ -230,7 +261,7 @@
- +
@@ -473,12 +504,14 @@ const sendMessage = { data() { return { + message_type: 'user', message_phone: '', message_text: '', } }, methods: { - sendMessageModal() { + sendMessageModal(type) { + this.message_type = type $('#modalSendMessage').modal('show'); }, async sendMessageProcess() { @@ -495,6 +528,7 @@ let payload = new FormData(); payload.append("phone", this.message_phone) payload.append("message", this.message_text) + payload.append("type", this.message_type) let response = await axios.post(`${this.app_host}/send/message`, payload) this.sendMessageReset(); resolve(response.data.message) @@ -510,6 +544,7 @@ sendMessageReset() { this.message_phone = ''; this.message_text = ''; + this.message_type = 'user'; }, } } @@ -519,10 +554,12 @@ return { image_phone: '', image_caption: '', + image_type: 'user', } }, methods: { - sendImageModal() { + sendImageModal(type) { + this.image_type = type $('#modalSendImage').modal('show'); }, async sendImageProcess() { @@ -540,6 +577,7 @@ payload.append("phone", this.image_phone) payload.append("caption", this.image_caption) payload.append("image", $("#image_file")[0].files[0]) + payload.append("type", this.image_type) let response = await axios.post(`${this.app_host}/send/image`, payload) this.sendImageReset(); resolve(response.data.message) @@ -555,6 +593,7 @@ sendImageReset() { this.image_phone = ''; this.image_caption = ''; + this.image_type = 'user'; $("#image_file").val(''); }, } @@ -563,11 +602,13 @@ const sendFile = { data() { return { + file_type: 'user', file_phone: '', } }, methods: { - sendFileModal() { + sendFileModal(type) { + this.file_type = type $('#modalSendFile').modal('show'); }, async sendFileProcess() { @@ -584,6 +625,7 @@ let payload = new FormData(); payload.append("phone", this.file_phone) payload.append("file", $("#file_file")[0].files[0]) + payload.append("type", this.file_type) let response = await axios.post(`${this.app_host}/send/file`, payload) this.sendFileReset(); resolve(response.data.message) @@ -598,7 +640,7 @@ }, sendFileReset() { this.file_phone = ''; - this.file_caption = ''; + this.file_type = 'user'; $("#file_file").val(''); }, }