Browse Source

Release/4.9.0 (#110)

* feat: new api send audio

* feat: update UI v4.9.0

* chore: update docs
pull/112/head v4.9.0
Aldino Kemal 2 years ago
committed by GitHub
parent
commit
8ec5a67d84
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      .github/workflows/deploy-linux.yml
  2. 2
      .github/workflows/deploy-mac.yml
  3. 2
      .github/workflows/deploy-windows.yml
  4. 39
      docs/openapi.yaml
  5. 20
      readme.md
  6. 2
      src/config/settings.go
  7. 8
      src/domains/send/audio.go
  8. 1
      src/domains/send/send.go
  9. 4
      src/go.mod
  10. 35
      src/go.sum
  11. 11
      src/internal/rest/helpers/common.go
  12. 23
      src/internal/rest/send.go
  13. 17
      src/pkg/error/whatsapp_error.go
  14. 60
      src/services/send.go
  15. 42
      src/validations/send_validation.go
  16. 113
      src/views/index.html

2
.github/workflows/deploy-linux.yml

@ -22,7 +22,7 @@ jobs:
- name: Golang Installation
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
- name: Golang setup dependency
run: |
go version

2
.github/workflows/deploy-mac.yml

@ -21,7 +21,7 @@ jobs:
- name: Golang Installation
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
- name: Golang setup dependency
run: |
go version

2
.github/workflows/deploy-windows.yml

@ -34,7 +34,7 @@ jobs:
- name: Golang Installation
uses: actions/setup-go@v3
with:
go-version: '1.20'
go-version: '1.21'
- name: Golang setup dependency
run: |
go version

39
docs/openapi.yaml

@ -286,6 +286,45 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ErrorInternalServer'
/send/audio:
post:
operationId: sendAudio
tags:
- send
summary: Send Audio
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
phone:
type: string
example: '6289685028129@s.whatsapp.net'
description: Phone number with country code
audio:
type: string
format: binary
description: Audio to send
responses:
'200':
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/SendResponse'
'400':
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorBadRequest'
'500':
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorInternalServer'
/send/file:
post:
operationId: sendFile

20
readme.md

@ -104,6 +104,7 @@ API using [openapi-generator](https://openapi-generator.tech/#try)
| ✅ | User My Privacy Setting | GET | /user/my/privacy |
| ✅ | Send Message | POST | /send/message |
| ✅ | Send Image | POST | /send/image |
| ✅ | Send Audio | POST | /send/audio |
| ✅ | Send File | POST | /send/file |
| ✅ | Send Video | POST | /send/video |
| ✅ | Send Contact | POST | /send/contact |
@ -121,7 +122,7 @@ API using [openapi-generator](https://openapi-generator.tech/#try)
### App User Interface
1. Homepage ![Homepage](https://i.ibb.co/vPpnVdg/homepage.png)
1. Homepage ![Homepage](https://i.ibb.co/PZ38XYR/Homepage.png)
2. Login ![Login](https://i.ibb.co/jkcB15R/login.png)
3. Send Message ![Send Message](https://i.ibb.co/rc3NXMX/send-message.png)
4. Send Image ![Send Image](https://i.ibb.co/BcFL3SD/send-image.png)
@ -129,14 +130,15 @@ API using [openapi-generator](https://openapi-generator.tech/#try)
6. Send Video ![Send Video](https://i.ibb.co/PrD3P51/send-video.png)
7. Send Contact ![Send Contact](https://i.ibb.co/4810H7N/send-contact.png)
8. Send Location ![Send Location](https://i.ibb.co/TWsy09G/send-location.png)
9. Revoke Message ![Revoke Message](https://i.ibb.co/yswhvQY/revoke.png?)
10. Reaction Message ![Revoke Message](https://i.ibb.co/BfHgSHG/react-message.png)
11. User Info ![User Info](https://i.ibb.co/3zjX6Cz/user-info.png)
12. User Avatar ![User Avatar](https://i.ibb.co/ZmJZ4ZW/search-avatar.png)
13. My Privacy ![My Privacy](https://i.ibb.co/Cw1sMQz/my-privacy.png)
14. My Group ![My Group](https://i.ibb.co/WB268Xy/list-group.png)
15. Auto Reply ![Auto Reply](https://i.ibb.co/D4rTytX/IMG-20220517-162500.jpg)
16. Basic Auth Prompt ![Basic Auth](https://i.ibb.co/PDjQ92W/Screenshot-2022-11-06-at-14-06-29.png)
9. Send Audio ![Send Location](https://i.ibb.co/p1wL4wh/Send-Audio.png)
10. Revoke Message ![Revoke Message](https://i.ibb.co/yswhvQY/revoke.png?)
11. Reaction Message ![Revoke Message](https://i.ibb.co/BfHgSHG/react-message.png)
12. User Info ![User Info](https://i.ibb.co/3zjX6Cz/user-info.png)
13. User Avatar ![User Avatar](https://i.ibb.co/ZmJZ4ZW/search-avatar.png)
14. My Privacy ![My Privacy](https://i.ibb.co/Cw1sMQz/my-privacy.png)
15. My Group ![My Group](https://i.ibb.co/WB268Xy/list-group.png)
16. Auto Reply ![Auto Reply](https://i.ibb.co/D4rTytX/IMG-20220517-162500.jpg)
17. Basic Auth Prompt ![Basic Auth](https://i.ibb.co/PDjQ92W/Screenshot-2022-11-06-at-14-06-29.png)
### Mac OS NOTE

2
src/config/settings.go

@ -6,7 +6,7 @@ import (
)
var (
AppVersion = "v4.8.4"
AppVersion = "v4.9.0"
AppPort = "3000"
AppDebug = false
AppOs = fmt.Sprintf("AldinoKemal")

8
src/domains/send/audio.go

@ -0,0 +1,8 @@
package send
import "mime/multipart"
type AudioRequest struct {
Phone string `json:"phone" form:"phone"`
Audio *multipart.FileHeader `json:"Audio" form:"Audio"`
}

1
src/domains/send/send.go

@ -12,6 +12,7 @@ type ISendService interface {
SendContact(ctx context.Context, request ContactRequest) (response GenericResponse, err error)
SendLink(ctx context.Context, request LinkRequest) (response GenericResponse, err error)
SendLocation(ctx context.Context, request LocationRequest) (response GenericResponse, err error)
SendAudio(ctx context.Context, request AudioRequest) (response GenericResponse, err error)
}
type GenericResponse struct {

4
src/go.mod

@ -12,14 +12,14 @@ require (
github.com/google/uuid v1.6.0
github.com/h2non/bimg v1.1.9
github.com/markbates/pkger v0.17.1
github.com/mattn/go-sqlite3 v1.14.20
github.com/mattn/go-sqlite3 v1.14.22
github.com/sirupsen/logrus v1.9.3
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/spf13/cobra v1.8.0
github.com/stretchr/testify v1.8.4
github.com/valyala/fasthttp v1.51.0
go.mau.fi/libsignal v0.1.0
go.mau.fi/whatsmeow v0.0.0-20240129221825-0bb41340eb03
go.mau.fi/whatsmeow v0.0.0-20240201213949-57f290eebe9b
google.golang.org/protobuf v1.32.0
)

35
src/go.sum

@ -1,14 +1,9 @@
filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek=
filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=
github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
@ -21,8 +16,6 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fasthttp/websocket v1.5.3 h1:TPpQuLwJYfd4LJPXvHDYPMFWbLjsT91n3GpWtCQtdek=
github.com/fasthttp/websocket v1.5.3/go.mod h1:46gg/UBmTU1kUaTcwQXpUxtRwG2PvIZYeA8oL6vF3Fs=
github.com/fasthttp/websocket v1.5.7 h1:0a6o2OfeATvtGgoMKleURhLT6JqWPg7fYfWnH4KHau4=
github.com/fasthttp/websocket v1.5.7/go.mod h1:bC4fxSono9czeXHQUVKxsC0sNjbm7lPJR04GDFqClfU=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
@ -34,8 +27,6 @@ github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/
github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
github.com/gofiber/template v1.8.2 h1:PIv9s/7Uq6m+Fm2MDNd20pAFFKt5wWs7ZBd8iV9pWwk=
github.com/gofiber/template v1.8.2/go.mod h1:bs/2n0pSNPOkRa5VJ8zTIvedcI/lEYxzV3+YPXdBvq8=
github.com/gofiber/template/html/v2 v2.0.5 h1:BKLJ6Qr940NjntbGmpO3zVa4nFNGDCi/IfUiDB9OC20=
github.com/gofiber/template/html/v2 v2.0.5/go.mod h1:RCF14eLeQDCSUPp0IGc2wbSSDv6yt+V54XB/+Unz+LM=
github.com/gofiber/template/html/v2 v2.1.0 h1:FjwzqhhdJpnhyCvav60Z1ytnBqOUr5sGO/aTeob9/ng=
github.com/gofiber/template/html/v2 v2.1.0/go.mod h1:txXsRQN/G7Fr2cqGfr6zhVHgreCfpsBS+9+DJyrddJc=
github.com/gofiber/utils v1.1.0 h1:vdEBpn7AzIUJRhe+CiTOJdUcTg4Q9RK+pEa0KPbLdrM=
@ -44,20 +35,14 @@ github.com/gofiber/websocket/v2 v2.2.1 h1:C9cjxvloojayOp9AovmpQrk8VqvVnT8Oao3+IU
github.com/gofiber/websocket/v2 v2.2.1/go.mod h1:Ao/+nyNnX5u/hIFPuHl28a+NIkrqK7PRimyKaj4JxVU=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/h2non/bimg v1.1.9 h1:WH20Nxko9l/HFm4kZCA3Phbgu2cbHvYzxwxn9YROEGg=
github.com/h2non/bimg v1.1.9/go.mod h1:R3+UiYwkK4rQl6KVFTOFJHitgLbZXBZNFh2cv3AEbp8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.5 h1:d4vBd+7CHydUqpFBgUEKkSdtSugf9YFmSkvUYPquI5E=
github.com/klauspost/compress v1.17.5/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
@ -74,15 +59,11 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI=
github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.20 h1:BAZ50Ns0OFBNxdAqFhbZqdPcht1Xlb16pDCqkq1spr0=
github.com/mattn/go-sqlite3 v1.14.20/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@ -113,16 +94,12 @@ github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7Fw
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mau.fi/libsignal v0.1.0 h1:vAKI/nJ5tMhdzke4cTK1fb0idJzz1JuEIpmjprueC+c=
go.mau.fi/libsignal v0.1.0/go.mod h1:R8ovrTezxtUNzCQE5PH30StOQWWeBskBsWE55vMfY9I=
go.mau.fi/util v0.2.0 h1:AMGBEdg9Ya/smb/09dljo9wBwKr432EpfjDWF7aFQg0=
go.mau.fi/util v0.2.0/go.mod h1:AxuJUMCxpzgJ5eV9JbPWKRH8aAJJidxetNdUj7qcb84=
go.mau.fi/util v0.3.0 h1:Lt3lbRXP6ZBqTINK0EieRWor3zEwwwrDT14Z5N8RUCs=
go.mau.fi/util v0.3.0/go.mod h1:9dGsBCCbZJstx16YgnVMVi3O2bOizELoKpugLD4FoGs=
go.mau.fi/whatsmeow v0.0.0-20240129221825-0bb41340eb03 h1:EWoQvfZydwqrRK6bYPlqQaLYAOJ8MW//ut6a/x2xlyw=
go.mau.fi/whatsmeow v0.0.0-20240129221825-0bb41340eb03/go.mod h1:5xTtHNaZpGni6z6aE1iEopjW7wNgsKcolZxZrOujK9M=
go.mau.fi/whatsmeow v0.0.0-20240201213949-57f290eebe9b h1:4d7OK8g0F3T92MAcNySmXRZzEdw0OdsWpWzbxeNjJHM=
go.mau.fi/whatsmeow v0.0.0-20240201213949-57f290eebe9b/go.mod h1:5xTtHNaZpGni6z6aE1iEopjW7wNgsKcolZxZrOujK9M=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@ -134,8 +111,6 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -152,8 +127,6 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=

11
src/internal/rest/helpers/common.go

@ -3,6 +3,7 @@ package helpers
import (
"context"
domainApp "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/app"
"mime/multipart"
"time"
)
@ -10,3 +11,13 @@ func SetAutoConnectAfterBooting(service domainApp.IAppService) {
time.Sleep(2 * time.Second)
_ = service.Reconnect(context.Background())
}
func MultipartFormFileHeaderToBytes(fileHeader *multipart.FileHeader) []byte {
file, _ := fileHeader.Open()
defer file.Close()
fileBytes := make([]byte, fileHeader.Size)
_, _ = file.Read(fileBytes)
return fileBytes
}

23
src/internal/rest/send.go

@ -20,6 +20,7 @@ func InitRestSend(app *fiber.App, service domainSend.ISendService) Send {
app.Post("/send/contact", rest.SendContact)
app.Post("/send/link", rest.SendLink)
app.Post("/send/location", rest.SendLocation)
app.Post("/send/audio", rest.SendAudio)
return rest
}
@ -162,3 +163,25 @@ func (controller *Send) SendLocation(c *fiber.Ctx) error {
Results: response,
})
}
func (controller *Send) SendAudio(c *fiber.Ctx) error {
var request domainSend.AudioRequest
err := c.BodyParser(&request)
utils.PanicIfNeeded(err)
audio, err := c.FormFile("audio")
utils.PanicIfNeeded(err)
request.Audio = audio
whatsapp.SanitizePhone(&request.Phone)
response, err := controller.Service.SendAudio(c.UserContext(), request)
utils.PanicIfNeeded(err)
return c.JSON(utils.ResponseData{
Status: 200,
Code: "SUCCESS",
Message: response.Status,
Results: response,
})
}

17
src/pkg/error/whatsapp_error.go

@ -53,6 +53,23 @@ func (e WaCliError) StatusCode() int {
return http.StatusInternalServerError
}
type WaUploadMediaError string
// Error for complying the error interface
func (e WaUploadMediaError) Error() string {
return string(e)
}
// ErrCode will return the error code based on the error data type
func (e WaUploadMediaError) ErrCode() string {
return "UPLOAD_MEDIA_ERROR"
}
// StatusCode will return the HTTP status code based on the error data type
func (e WaUploadMediaError) StatusCode() int {
return http.StatusInternalServerError
}
const (
ErrInvalidJID = InvalidJID("your JID is invalid")
ErrWaCLI = WaCliError("your WhatsApp CLI is invalid or empty")

60
src/services/send.go

@ -6,6 +6,7 @@ import (
"github.com/aldinokemal/go-whatsapp-web-multidevice/config"
"github.com/aldinokemal/go-whatsapp-web-multidevice/domains/app"
domainSend "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/send"
"github.com/aldinokemal/go-whatsapp-web-multidevice/internal/rest/helpers"
pkgError "github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/error"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/utils"
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/whatsapp"
@ -188,18 +189,14 @@ func (service serviceSend) SendFile(ctx context.Context, request domainSend.File
return response, err
}
oriFilePath := fmt.Sprintf("%s/%s", config.PathSendItems, request.File.Filename)
err = fasthttp.SaveMultipartFile(request.File, oriFilePath)
if err != nil {
return response, err
}
fileBytes := helpers.MultipartFormFileHeaderToBytes(request.File)
fileMimeType := http.DetectContentType(fileBytes)
// Send to WA server
dataWaFile, err := os.ReadFile(oriFilePath)
if err != nil {
return response, err
}
uploadedFile, err := service.WaCli.Upload(context.Background(), dataWaFile, whatsmeow.MediaDocument)
uploadedFile, err := service.WaCli.Upload(context.Background(), fileBytes, whatsmeow.MediaDocument)
if err != nil {
fmt.Printf("Failed to upload file: %v", err)
return response, err
@ -207,7 +204,7 @@ func (service serviceSend) SendFile(ctx context.Context, request domainSend.File
msg := &waProto.Message{DocumentMessage: &waProto.DocumentMessage{
Url: proto.String(uploadedFile.URL),
Mimetype: proto.String(http.DetectContentType(dataWaFile)),
Mimetype: proto.String(fileMimeType),
Title: proto.String(request.File.Filename),
FileSha256: uploadedFile.FileSHA256,
FileLength: proto.Uint64(uploadedFile.FileLength),
@ -218,12 +215,6 @@ func (service serviceSend) SendFile(ctx context.Context, request domainSend.File
Caption: proto.String(request.Caption),
}}
ts, err := service.WaCli.SendMessage(ctx, dataWaRecipient, msg)
go func() {
errDelete := utils.RemoveFile(0, oriFilePath)
if errDelete != nil {
fmt.Println(errDelete)
}
}()
if err != nil {
return response, err
}
@ -425,3 +416,44 @@ func (service serviceSend) SendLocation(ctx context.Context, request domainSend.
response.Status = fmt.Sprintf("Send location success %s (server timestamp: %s)", request.Phone, ts.Timestamp.String())
return response, nil
}
func (service serviceSend) SendAudio(ctx context.Context, request domainSend.AudioRequest) (response domainSend.GenericResponse, err error) {
err = validations.ValidateSendAudio(ctx, request)
if err != nil {
return response, err
}
dataWaRecipient, err := whatsapp.ValidateJidWithLogin(service.WaCli, request.Phone)
if err != nil {
return response, err
}
autioBytes := helpers.MultipartFormFileHeaderToBytes(request.Audio)
audioMimeType := http.DetectContentType(autioBytes)
audioUploaded, err := service.WaCli.Upload(ctx, autioBytes, whatsmeow.MediaAudio)
if err != nil {
err = pkgError.WaUploadMediaError(fmt.Sprintf("Failed to upload audio: %v", err))
return response, err
}
msg := &waProto.Message{
AudioMessage: &waProto.AudioMessage{
Url: proto.String(audioUploaded.URL),
DirectPath: proto.String(audioUploaded.DirectPath),
Mimetype: proto.String(audioMimeType),
FileLength: proto.Uint64(audioUploaded.FileLength),
FileSha256: audioUploaded.FileSHA256,
FileEncSha256: audioUploaded.FileEncSHA256,
MediaKey: audioUploaded.MediaKey,
},
}
ts, err := service.WaCli.SendMessage(ctx, dataWaRecipient, msg)
if err != nil {
return response, err
}
response.MessageID = ts.ID
response.Status = fmt.Sprintf("Send audio success %s (server timestamp: %s)", request.Phone, ts.Timestamp.String())
return response, nil
}

42
src/validations/send_validation.go

@ -133,3 +133,45 @@ func ValidateSendLocation(ctx context.Context, request domainSend.LocationReques
return nil
}
func ValidateSendAudio(ctx context.Context, request domainSend.AudioRequest) error {
err := validation.ValidateStructWithContext(ctx, &request,
validation.Field(&request.Phone, validation.Required),
validation.Field(&request.Audio, validation.Required),
)
if err != nil {
return pkgError.ValidationError(err.Error())
}
availableMimes := map[string]bool{
"audio/aac": true,
"audio/amr": true,
"audio/flac": true,
"audio/m4a": true,
"audio/m4r": true,
"audio/mp3": true,
"audio/mpeg": true,
"audio/ogg": true,
"audio/wma": true,
"audio/x-ms-wma": true,
"audio/wav": true,
"audio/vnd.wav": true,
"audio/vnd.wave": true,
"audio/wave": true,
"audio/x-pn-wav": true,
"audio/x-wav": true,
}
availableMimesStr := ""
for k := range availableMimes {
availableMimesStr += k + ","
}
if !availableMimes[request.Audio.Header.Get("Content-Type")] {
return pkgError.ValidationError(fmt.Sprintf("your audio type is not allowed. please use (%s)", availableMimesStr))
}
return nil
}

113
src/views/index.html

@ -133,6 +133,15 @@
</div>
</div>
</div>
<div class="blue card" @click="sendAudioModal()" style="cursor: pointer">
<div class="content">
<a class="ui blue right ribbon label">Send</a>
<div class="header">Send Audio</div>
<div class="description">
Send audio to any whatsapp number
</div>
</div>
</div>
</div>
@ -329,6 +338,47 @@
</div>
</div>
<!-- Modal SendAudio -->
<div class="ui small modal" id="modalSendAudio">
<i class="close icon"></i>
<div class="header">
Send Audio
</div>
<div class="content">
<form class="ui form">
<div class="field">
<label>Type</label>
<select name="audio_type" v-model="audio_type" aria-label="type">
<option value="group">Group Message</option>
<option value="user">Private Message</option>
</select>
</div>
<div class="field">
<label>Phone / Group ID</label>
<input v-model="audio_phone" type="text" placeholder="6289..."
aria-label="phone">
<input :value="audio_phone_id" disabled aria-label="whatsapp_id">
</div>
<div class="field" style="padding-bottom: 30px">
<label>Audio</label>
<input type="file" class="inputfile" id="audio_file" style="display: none"
accept="audio/*"/>
<label for="audio_file" class="ui positive medium green left floated button" style="color: white">
<i class="ui upload icon"></i>
Upload Audio
</label>
</div>
</form>
</div>
<div class="actions">
<div class="ui approve positive right labeled icon button" :class="{'loading': this.audio_loading}"
@click="sendAudioProcess">
Send
<i class="send icon"></i>
</div>
</div>
</div>
<!-- Modal SendFile -->
<div class="ui small modal" id="modalSendFile">
<i class="close icon"></i>
@ -411,7 +461,7 @@
<label>Compress</label>
<div class="ui toggle checkbox">
<input type="checkbox" aria-label="compress" v-model="video_compress">
<label>Check for compressing image to smaller size</label>
<label>Check for compressing video to smaller size</label>
</div>
</div>
<div class="field" style="padding-bottom: 30px">
@ -1062,6 +1112,65 @@
}
}
const sendAudio = {
data() {
return {
audio_phone: '',
audio_type: 'user',
audio_loading: false,
}
},
computed: {
audio_phone_id() {
return this.audio_type === 'user' ? `${this.audio_phone}@${this.type_user}` : `${this.audio_phone}@${this.type_group}`
}
},
methods: {
sendAudioModal() {
$('#modalSendAudio').modal({
onApprove: function () {
return false;
}
}).modal('show');
},
async sendAudioProcess() {
try {
let response = await this.sendAudioApi()
showSuccessInfo(response)
$('#modalSendAudio').modal('hide');
} catch (err) {
showErrorInfo(err)
}
},
sendAudioApi() {
return new Promise(async (resolve, reject) => {
try {
this.audio_loading = true;
let payload = new FormData();
payload.append("phone", this.audio_phone_id)
payload.append("audio", $("#audio_file")[0].files[0])
let response = await http.post(`/send/audio`, payload)
this.sendAudioReset();
resolve(response.data.message)
} catch (error) {
if (error.response) {
reject(error.response.data.message)
} else {
reject(error.message)
}
} finally {
this.audio_loading = false;
}
})
},
sendAudioReset() {
this.audio_phone = '';
this.audio_type = 'user';
$("#audio_file").val('');
},
}
}
const sendVideo = {
data() {
return {
@ -1658,7 +1767,7 @@
},
mixins: [
login, logout, reconnect,
sendMessage, sendImage, sendFile, sendVideo, sendContact, sendLocation,
sendMessage, sendImage, sendFile, sendVideo, sendContact, sendLocation, sendAudio,
messageRevoke, messageReact,
userGroups, userPrivacy, userAvatar, userInfo
]

Loading…
Cancel
Save