diff --git a/.github/workflows/deploy-linux.yml b/.github/workflows/deploy-linux.yml index aad8d56..f7f42af 100644 --- a/.github/workflows/deploy-linux.yml +++ b/.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 diff --git a/.github/workflows/deploy-mac.yml b/.github/workflows/deploy-mac.yml index 5008a90..ab47cfa 100644 --- a/.github/workflows/deploy-mac.yml +++ b/.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 diff --git a/.github/workflows/deploy-windows.yml b/.github/workflows/deploy-windows.yml index f0ccf87..305783f 100644 --- a/.github/workflows/deploy-windows.yml +++ b/.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 diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 8157d22..42e7251 100644 --- a/docs/openapi.yaml +++ b/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 diff --git a/readme.md b/readme.md index 605519b..ebc0b6d 100644 --- a/readme.md +++ b/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 diff --git a/src/config/settings.go b/src/config/settings.go index 93ea637..6bd5b03 100644 --- a/src/config/settings.go +++ b/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") diff --git a/src/domains/send/audio.go b/src/domains/send/audio.go new file mode 100644 index 0000000..f2e5063 --- /dev/null +++ b/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"` +} diff --git a/src/domains/send/send.go b/src/domains/send/send.go index 6402588..c848050 100644 --- a/src/domains/send/send.go +++ b/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 { diff --git a/src/go.mod b/src/go.mod index e8244f9..7d393ad 100644 --- a/src/go.mod +++ b/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 ) diff --git a/src/go.sum b/src/go.sum index 8bea583..0bf8d5a 100644 --- a/src/go.sum +++ b/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= diff --git a/src/internal/rest/helpers/common.go b/src/internal/rest/helpers/common.go index a0686bd..592b4e4 100644 --- a/src/internal/rest/helpers/common.go +++ b/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 +} diff --git a/src/internal/rest/send.go b/src/internal/rest/send.go index efcec4f..a27cfbe 100644 --- a/src/internal/rest/send.go +++ b/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, + }) +} diff --git a/src/pkg/error/whatsapp_error.go b/src/pkg/error/whatsapp_error.go index 2dfd099..db2432a 100644 --- a/src/pkg/error/whatsapp_error.go +++ b/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") diff --git a/src/services/send.go b/src/services/send.go index ff93a23..57b04c7 100644 --- a/src/services/send.go +++ b/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 +} diff --git a/src/validations/send_validation.go b/src/validations/send_validation.go index cbef06a..344589c 100644 --- a/src/validations/send_validation.go +++ b/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 +} diff --git a/src/views/index.html b/src/views/index.html index d1ae713..d544f96 100644 --- a/src/views/index.html +++ b/src/views/index.html @@ -133,6 +133,15 @@ +
+
+ Send +
Send Audio
+
+ Send audio to any whatsapp number +
+
+
@@ -329,6 +338,47 @@ + + +
@@ -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 ]