Browse Source

feat: delete message (#153)

* feat: delete message

* feat: add FE and docs

* feat: update syntax using newest proto

* feat: update docs
pull/156/head v4.14.0
Aldino Kemal 2 years ago
committed by GitHub
parent
commit
1f3948ea4d
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 60
      docs/openapi.yaml
  2. 49
      readme.md
  3. 2
      src/config/settings.go
  4. 34
      src/domains/message/message.go
  5. 12
      src/domains/message/reaction.go
  6. 11
      src/domains/message/revoke.go
  7. 12
      src/domains/message/update.go
  8. 20
      src/go.mod
  9. 20
      src/go.sum
  10. 20
      src/internal/rest/message.go
  11. 2
      src/pkg/whatsapp/whatsapp.go
  12. 59
      src/services/message.go
  13. 66
      src/services/send.go
  14. 13
      src/validations/message_validation.go
  15. 2
      src/views/components/AccountUserInfo.js
  16. 106
      src/views/components/MessageDelete.js
  17. 4
      src/views/index.html

60
docs/openapi.yaml

@ -1,7 +1,7 @@
openapi: 3.0.0
info:
title: WhatsApp API MultiDevice
version: 3.11.0
version: 4.0.0
description: This API is used for sending whatsapp via API
servers:
- url: http://localhost:3000
@ -200,7 +200,7 @@ paths:
summary: Send Message
requestBody:
content:
multipart/form-data:
application/json:
schema:
type: object
properties:
@ -427,7 +427,7 @@ paths:
summary: Send Contact
requestBody:
content:
multipart/form-data:
application/json:
schema:
type: object
properties:
@ -470,7 +470,7 @@ paths:
summary: Send Link
requestBody:
content:
multipart/form-data:
application/json:
schema:
type: object
properties:
@ -513,7 +513,7 @@ paths:
summary: Send Location
requestBody:
content:
multipart/form-data:
application/json:
schema:
type: object
properties:
@ -618,7 +618,49 @@ paths:
description: Message ID
requestBody:
content:
multipart/form-data:
application/json:
schema:
type: object
properties:
phone:
type: string
example: '6289685024051@s.whatsapp.net'
description: Phone number with country code
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'
/message/{message_id}/delete:
post:
operationId: deleteMessage
tags:
- message
summary: Delete Message
parameters:
- in: path
name: message_id
schema:
type: string
required: true
description: Message ID
requestBody:
content:
application/json:
schema:
type: object
properties:
@ -660,7 +702,7 @@ paths:
description: Message ID
requestBody:
content:
multipart/form-data:
application/json:
schema:
type: object
properties:
@ -840,7 +882,7 @@ paths:
summary: Join group with link
requestBody:
content:
multipart/form-data:
application/json:
schema:
type: object
properties:
@ -874,7 +916,7 @@ paths:
summary: Leave group
requestBody:
content:
multipart/form-data:
application/json:
schema:
type: object
properties:

49
readme.md

@ -92,15 +92,18 @@ docker run --detach --publish=3000:3000 --name=whatsapp --restart=always --volum
You can fork or edit this source code !
### Current API
- [Api Specification Document](https://bump.sh/aldinokemal/doc/go-whatsapp-web-multidevice)
- You can check [docs/openapi.yml](./docs/openapi.yaml) for detail API or paste to [SwaggerEditor](https://editor.swagger.io).
- You can check [docs/openapi.yml](./docs/openapi.yaml) for detail API or paste
to [SwaggerEditor](https://editor.swagger.io).
- Furthermore you can generate HTTP Client from this API using [openapi-generator](https://openapi-generator.tech/#try)
| Feature | Menu | Method | URL |
|---------|------------------------------|--------|-----------------------------|
|---------|------------------------------|--------|-------------------------------|
| ✅ | Login | GET | /app/login |
| ✅ | Logout | GET | /app/logout |
| ✅ | Reconnect | GET | /app/reconnect |
| ✅ | Devices | GET | /app/devices |
| ✅ | User Info | GET | /user/info |
| ✅ | User Avatar | GET | /user/avatar |
| ✅ | User My Group List | GET | /user/my/groups |
@ -115,7 +118,8 @@ You can fork or edit this source code !
| ✅ | Send Location | POST | /send/location |
| ✅ | Send Poll / Vote | POST | /send/poll |
| ✅ | Revoke Message | POST | /message/:message_id/revoke |
| ✅ | React Message | POST | /message/:message_id/react |
| ✅ | React Message | POST | /message/:message_id/reaction |
| ✅ | Delete Message | POST | /message/:message_id/delete |
| ✅ | Edit Message | POST | /message/:message_id/update |
| ✅ | Join Group With Link | POST | /group/join-with-link |
| ✅ | Leave Group | POST | /group/leave |
@ -132,25 +136,26 @@ You can fork or edit this source code !
### App User Interface
1. Homepage ![Homepage](https://i.ibb.co/Wn3H1L9/homepage.png)
2. Login ![Login](https://i.ibb.co/jkcB15R/login.png?v=1)
3. Send Message ![Send Message](https://i.ibb.co/rc3NXMX/send-message.png?v1)
4. Send Image ![Send Image](https://i.ibb.co/BcFL3SD/send-image.png?v1)
5. Send File ![Send File](https://i.ibb.co/f4yxjpp/send-file.png)
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. Send Audio ![Send Location](https://i.ibb.co/p1wL4wh/Send-Audio.png)
10. Send Poll ![Send Poll](https://i.ibb.co/mq2fGHz/send-poll.png)
11. Revoke Message ![Revoke Message](https://i.ibb.co/yswhvQY/revoke.png?v1)
12. Reaction Message ![Revoke Message](https://i.ibb.co/BfHgSHG/react-message.png)
13. Edit Message ![Edit Message](https://i.ibb.co/kXfpqJw/update-message.png)
14. User Info ![User Info](https://i.ibb.co/3zjX6Cz/user-info.png?v=1)
15. User Avatar ![User Avatar](https://i.ibb.co/ZmJZ4ZW/search-avatar.png?v=1)
16. My Privacy ![My Privacy](https://i.ibb.co/Cw1sMQz/my-privacy.png)
17. My Group ![My Group](https://i.ibb.co/WB268Xy/list-group.png)
18. Auto Reply ![Auto Reply](https://i.ibb.co/D4rTytX/IMG-20220517-162500.jpg)
19. Basic Auth Prompt ![Basic Auth](https://i.ibb.co/PDjQ92W/Screenshot-2022-11-06-at-14-06-29.png)
1. Homepage ![Homepage](https://i.ibb.co.com/681JTHK/image.png)
2. Login ![Login](https://i.ibb.co.com/jkcB15R/login.png?v=1)
3. Send Message ![Send Message](https://i.ibb.co.com/rc3NXMX/send-message.png?v1)
4. Send Image ![Send Image](https://i.ibb.co.com/BcFL3SD/send-image.png?v1)
5. Send File ![Send File](https://i.ibb.co.com/f4yxjpp/send-file.png)
6. Send Video ![Send Video](https://i.ibb.co.com/PrD3P51/send-video.png)
7. Send Contact ![Send Contact](https://i.ibb.co.com/4810H7N/send-contact.png)
8. Send Location ![Send Location](https://i.ibb.co.com/TWsy09G/send-location.png)
9. Send Audio ![Send Location](https://i.ibb.co.com/p1wL4wh/Send-Audio.png)
10. Send Poll ![Send Poll](https://i.ibb.co.com/mq2fGHz/send-poll.png)
11. Revoke Message ![Revoke Message](https://i.ibb.co.com/yswhvQY/revoke.png?v1)
12. Delete Message ![Delete Message](https://i.ibb.co.com/F70SZ84/image.png)
13. Reaction Message ![Revoke Message](https://i.ibb.co.com/BfHgSHG/react-message.png)
14. Edit Message ![Edit Message](https://i.ibb.co.com/kXfpqJw/update-message.png)
15. User Info ![User Info](https://i.ibb.co.com/3zjX6Cz/user-info.png?v=1)
16. User Avatar ![User Avatar](https://i.ibb.co.com/ZmJZ4ZW/search-avatar.png?v=1)
17. My Privacy ![My Privacy](https://i.ibb.co.com/Cw1sMQz/my-privacy.png)
18. My Group ![My Group](https://i.ibb.co.com/WB268Xy/list-group.png)
19. Auto Reply ![Auto Reply](https://i.ibb.co.com/D4rTytX/IMG-20220517-162500.jpg)
20. Basic Auth Prompt ![Basic Auth](https://i.ibb.co.com/PDjQ92W/Screenshot-2022-11-06-at-14-06-29.png)
### Mac OS NOTE

2
src/config/settings.go

@ -5,7 +5,7 @@ import (
)
var (
AppVersion = "v4.13.0"
AppVersion = "v4.14.0"
AppPort = "3000"
AppDebug = false
AppOs = "AldinoKemal"

34
src/domains/message/message.go

@ -3,7 +3,35 @@ package message
import "context"
type IMessageService interface {
ReactMessage(ctx context.Context, request ReactionRequest) (response ReactionResponse, err error)
RevokeMessage(ctx context.Context, request RevokeRequest) (response RevokeResponse, err error)
UpdateMessage(ctx context.Context, request UpdateMessageRequest) (response UpdateMessageResponse, err error)
ReactMessage(ctx context.Context, request ReactionRequest) (response GenericResponse, err error)
RevokeMessage(ctx context.Context, request RevokeRequest) (response GenericResponse, err error)
UpdateMessage(ctx context.Context, request UpdateMessageRequest) (response GenericResponse, err error)
DeleteMessage(ctx context.Context, request DeleteRequest) (err error)
}
type GenericResponse struct {
MessageID string `json:"message_id"`
Status string `json:"status"`
}
type RevokeRequest struct {
MessageID string `json:"message_id" uri:"message_id"`
Phone string `json:"phone" form:"phone"`
}
type DeleteRequest struct {
MessageID string `json:"message_id" uri:"message_id"`
Phone string `json:"phone" form:"phone"`
}
type ReactionRequest struct {
MessageID string `json:"message_id" form:"message_id"`
Phone string `json:"phone" form:"phone"`
Emoji string `json:"emoji" form:"emoji"`
}
type UpdateMessageRequest struct {
MessageID string `json:"message_id" uri:"message_id"`
Message string `json:"message" form:"message"`
Phone string `json:"phone" form:"phone"`
}

12
src/domains/message/reaction.go

@ -1,12 +0,0 @@
package message
type ReactionRequest struct {
MessageID string `json:"message_id" form:"message_id"`
Phone string `json:"phone" form:"phone"`
Emoji string `json:"emoji" form:"emoji"`
}
type ReactionResponse struct {
MessageID string `json:"message_id"`
Status string `json:"status"`
}

11
src/domains/message/revoke.go

@ -1,11 +0,0 @@
package message
type RevokeRequest struct {
MessageID string `json:"message_id" uri:"message_id"`
Phone string `json:"phone" form:"phone"`
}
type RevokeResponse struct {
MessageID string `json:"message_id"`
Status string `json:"status"`
}

12
src/domains/message/update.go

@ -1,12 +0,0 @@
package message
type UpdateMessageRequest struct {
MessageID string `json:"message_id" uri:"message_id"`
Message string `json:"message" form:"message"`
Phone string `json:"phone" form:"phone"`
}
type UpdateMessageResponse struct {
MessageID string `json:"message_id"`
Status string `json:"status"`
}

20
src/go.mod

@ -3,7 +3,7 @@ module github.com/aldinokemal/go-whatsapp-web-multidevice
go 1.21
require (
github.com/PuerkitoBio/goquery v1.9.1
github.com/PuerkitoBio/goquery v1.9.2
github.com/disintegration/imaging v1.6.2
github.com/dustin/go-humanize v1.0.1
github.com/go-ozzo/ozzo-validation/v4 v4.3.0
@ -16,10 +16,10 @@ require (
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
github.com/spf13/cobra v1.8.0
github.com/stretchr/testify v1.9.0
github.com/valyala/fasthttp v1.52.0
github.com/valyala/fasthttp v1.54.0
go.mau.fi/libsignal v0.1.0
go.mau.fi/whatsmeow v0.0.0-20240327124018-350073db195c
google.golang.org/protobuf v1.33.0
go.mau.fi/whatsmeow v0.0.0-20240523075404-7f13c31d2cb1
google.golang.org/protobuf v1.34.1
)
require (
@ -28,7 +28,7 @@ require (
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fasthttp/websocket v1.5.8 // indirect
github.com/fasthttp/websocket v1.5.9 // indirect
github.com/gofiber/template v1.8.3 // indirect
github.com/gofiber/utils v1.1.0 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
@ -40,16 +40,16 @@ require (
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/zerolog v1.32.0 // indirect
github.com/rs/zerolog v1.33.0 // indirect
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
go.mau.fi/util v0.4.2 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/image v0.15.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/image v0.16.0 // indirect
golang.org/x/net v0.25.0 // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

20
src/go.sum

@ -2,6 +2,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/PuerkitoBio/goquery v1.9.1 h1:mTL6XjbJTZdpfL+Gwl5U2h1l9yEkJjhmlTeV9VPW7UI=
github.com/PuerkitoBio/goquery v1.9.1/go.mod h1:cW1n6TmIMDoORQU5IU/P1T3tGFunOeXEpGP2WHRwkbY=
github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE=
github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
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.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
@ -20,6 +22,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/fasthttp/websocket v1.5.8 h1:k5DpirKkftIF/w1R8ZzjSgARJrs54Je9YJK37DL/Ah8=
github.com/fasthttp/websocket v1.5.8/go.mod h1:d08g8WaT6nnyvg9uMm8K9zMYyDjfKyj3170AtPRuVU0=
github.com/fasthttp/websocket v1.5.9 h1:9deGuzYcCRKjk940kNwSN6Hd14hk4zYwropm4UsUIUQ=
github.com/fasthttp/websocket v1.5.9/go.mod h1:NLzHBFur260OMuZHohOfYQwMTpR7sfSpUnuqKxMpgKA=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 h1:byhDUpfEwjsVQb1vBunvIjh2BHQ9ead57VkAEY4V+Es=
github.com/go-ozzo/ozzo-validation/v4 v4.3.0/go.mod h1:2NKgrcHl3z6cJs+3Oo940FPRiTzuqKbvfrL2RxCj6Ew=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@ -67,6 +71,8 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 h1:KanIMPX0QdEdB4R3CiimCAbxFrhB3j7h0/OvpYGVQa8=
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
@ -87,6 +93,8 @@ github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6Kllzaw
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.52.0 h1:wqBQpxH71XW0e2g+Og4dzQM8pk34aFYlA1Ga8db7gU0=
github.com/valyala/fasthttp v1.52.0/go.mod h1:hf5C4QnVMkNXMspnsUlfM3WitlgYflyhHYoKol/szxQ=
github.com/valyala/fasthttp v1.54.0 h1:cCL+ZZR3z3HPLMVfEYVUMtJqVaui0+gu7Lx63unHwS0=
github.com/valyala/fasthttp v1.54.0/go.mod h1:6dt4/8olwq9QARP/TDuPmWyWcl4byhpvTJ4AAtcz+QM=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@ -96,13 +104,19 @@ go.mau.fi/util v0.4.2 h1:RR3TOcRHmCF9Bx/3YG4S65MYfa+nV6/rn8qBWW4Mi30=
go.mau.fi/util v0.4.2/go.mod h1:PlAVfUUcPyHPrwnvjkJM9UFcPE7qGPDJqk+Oufa1Gtw=
go.mau.fi/whatsmeow v0.0.0-20240327124018-350073db195c h1:a5O4nqmwUWvmC+27RUdefkuy5XzMOEUqR9ji+/BcHZA=
go.mau.fi/whatsmeow v0.0.0-20240327124018-350073db195c/go.mod h1:kNI5foyzqd77d5HaWc1Jico6/rxtZ/UE8nr80hIsbIk=
go.mau.fi/whatsmeow v0.0.0-20240523075404-7f13c31d2cb1 h1:mUEEmZs1xk5QHKXjDxiAP4bYgyj8r7PaZCafHN+KMQg=
go.mau.fi/whatsmeow v0.0.0-20240523075404-7f13c31d2cb1/go.mod h1:0+65CYaE6r4dWzr0dN8i+UZKy0gIfJ79VuSqIl0nKRM=
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.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
golang.org/x/image v0.16.0 h1:9kloLAKhUufZhA12l5fwnx2NZW39/we1UhBesW433jw=
golang.org/x/image v0.16.0/go.mod h1:ugSZItdV4nOxyqp56HmXwH0Ry0nBCpjnZdpDaIHdoPs=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@ -112,6 +126,8 @@ golang.org/x/net v0.6.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.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -128,6 +144,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@ -146,6 +164,8 @@ golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz
golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

20
src/internal/rest/message.go

@ -16,6 +16,7 @@ func InitRestMessage(app *fiber.App, service domainMessage.IMessageService) Mess
rest := Message{Service: service}
app.Post("/message/:message_id/reaction", rest.ReactMessage)
app.Post("/message/:message_id/revoke", rest.RevokeMessage)
app.Post("/message/:message_id/delete", rest.DeleteMessage)
app.Post("/message/:message_id/update", rest.UpdateMessage)
return rest
}
@ -39,6 +40,25 @@ func (controller *Message) RevokeMessage(c *fiber.Ctx) error {
})
}
func (controller *Message) DeleteMessage(c *fiber.Ctx) error {
var request domainMessage.DeleteRequest
err := c.BodyParser(&request)
utils.PanicIfNeeded(err)
request.MessageID = c.Params("message_id")
whatsapp.SanitizePhone(&request.Phone)
err = controller.Service.DeleteMessage(c.UserContext(), request)
utils.PanicIfNeeded(err)
return c.JSON(utils.ResponseData{
Status: 200,
Code: "SUCCESS",
Message: "Message deleted successfully",
Results: nil,
})
}
func (controller *Message) UpdateMessage(c *fiber.Ctx) error {
var request domainMessage.UpdateMessageRequest
err := c.BodyParser(&request)

2
src/pkg/whatsapp/whatsapp.go

@ -188,6 +188,8 @@ func MustLogin(waCli *whatsmeow.Client) {
func handler(rawEvt interface{}) {
switch evt := rawEvt.(type) {
case *events.DeleteForMe:
log.Infof("Deleted message %s for %s", evt.MessageID, evt.SenderJID.String())
case *events.AppStateSyncComplete:
if len(cli.Store.PushName) > 0 && evt.Name == appstate.WAPatchCriticalBlock {
err := cli.SendPresence(types.PresenceAvailable)

59
src/services/message.go

@ -8,7 +8,10 @@ import (
"github.com/aldinokemal/go-whatsapp-web-multidevice/pkg/whatsapp"
"github.com/aldinokemal/go-whatsapp-web-multidevice/validations"
"go.mau.fi/whatsmeow"
waProto "go.mau.fi/whatsmeow/binary/proto"
"go.mau.fi/whatsmeow/appstate"
"go.mau.fi/whatsmeow/proto/waCommon"
"go.mau.fi/whatsmeow/proto/waE2E"
"go.mau.fi/whatsmeow/proto/waSyncAction"
"go.mau.fi/whatsmeow/types"
"google.golang.org/protobuf/proto"
"time"
@ -24,7 +27,7 @@ func NewMessageService(waCli *whatsmeow.Client) domainMessage.IMessageService {
}
}
func (service serviceMessage) ReactMessage(ctx context.Context, request message.ReactionRequest) (response message.ReactionResponse, err error) {
func (service serviceMessage) ReactMessage(ctx context.Context, request message.ReactionRequest) (response message.GenericResponse, err error) {
if err = validations.ValidateReactMessage(ctx, request); err != nil {
return response, err
}
@ -33,15 +36,15 @@ func (service serviceMessage) ReactMessage(ctx context.Context, request message.
return response, err
}
msg := &waProto.Message{
ReactionMessage: &waProto.ReactionMessage{
Key: &waProto.MessageKey{
msg := &waE2E.Message{
ReactionMessage: &waE2E.ReactionMessage{
Key: &waCommon.MessageKey{
FromMe: proto.Bool(true),
Id: proto.String(request.MessageID),
RemoteJid: proto.String(dataWaRecipient.String()),
ID: proto.String(request.MessageID),
RemoteJID: proto.String(dataWaRecipient.String()),
},
Text: proto.String(request.Emoji),
SenderTimestampMs: proto.Int64(time.Now().UnixMilli()),
SenderTimestampMS: proto.Int64(time.Now().UnixMilli()),
},
}
ts, err := service.WaCli.SendMessage(ctx, dataWaRecipient, msg)
@ -54,7 +57,7 @@ func (service serviceMessage) ReactMessage(ctx context.Context, request message.
return response, nil
}
func (service serviceMessage) RevokeMessage(ctx context.Context, request domainMessage.RevokeRequest) (response domainMessage.RevokeResponse, err error) {
func (service serviceMessage) RevokeMessage(ctx context.Context, request domainMessage.RevokeRequest) (response domainMessage.GenericResponse, err error) {
if err = validations.ValidateRevokeMessage(ctx, request); err != nil {
return response, err
}
@ -73,7 +76,41 @@ func (service serviceMessage) RevokeMessage(ctx context.Context, request domainM
return response, nil
}
func (service serviceMessage) UpdateMessage(ctx context.Context, request domainMessage.UpdateMessageRequest) (response domainMessage.UpdateMessageResponse, err error) {
func (service serviceMessage) DeleteMessage(ctx context.Context, request domainMessage.DeleteRequest) (err error) {
if err = validations.ValidateDeleteMessage(ctx, request); err != nil {
return err
}
dataWaRecipient, err := whatsapp.ValidateJidWithLogin(service.WaCli, request.Phone)
if err != nil {
return err
}
isFromMe := "1"
if len(request.MessageID) > 22 {
isFromMe = "0"
}
patchInfo := appstate.PatchInfo{
Timestamp: time.Now(),
Type: appstate.WAPatchRegularHigh,
Mutations: []appstate.MutationInfo{{
Index: []string{appstate.IndexDeleteMessageForMe, dataWaRecipient.String(), request.MessageID, isFromMe, service.WaCli.Store.ID.String()},
Value: &waSyncAction.SyncActionValue{
DeleteMessageForMeAction: &waSyncAction.DeleteMessageForMeAction{
DeleteMedia: proto.Bool(true),
MessageTimestamp: proto.Int64(time.Now().UnixMilli()),
},
},
}},
}
if err = service.WaCli.SendAppState(patchInfo); err != nil {
return err
}
return nil
}
func (service serviceMessage) UpdateMessage(ctx context.Context, request domainMessage.UpdateMessageRequest) (response domainMessage.GenericResponse, err error) {
if err = validations.ValidateUpdateMessage(ctx, request); err != nil {
return response, err
}
@ -83,7 +120,7 @@ func (service serviceMessage) UpdateMessage(ctx context.Context, request domainM
return response, err
}
msg := &waProto.Message{Conversation: proto.String(request.Message)}
msg := &waE2E.Message{Conversation: proto.String(request.Message)}
ts, err := service.WaCli.SendMessage(context.Background(), dataWaRecipient, service.WaCli.BuildEdit(dataWaRecipient, request.MessageID, msg))
if err != nil {
return response, err

66
src/services/send.go

@ -16,7 +16,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/valyala/fasthttp"
"go.mau.fi/whatsmeow"
waProto "go.mau.fi/whatsmeow/binary/proto"
"go.mau.fi/whatsmeow/proto/waE2E"
"google.golang.org/protobuf/proto"
"net/http"
"os"
@ -46,7 +46,7 @@ func (service serviceSend) SendText(ctx context.Context, request domainSend.Mess
}
// Send message
msg := &waProto.Message{Conversation: proto.String(request.Message)}
msg := &waE2E.Message{Conversation: proto.String(request.Message)}
// Reply message
if request.ReplyMessageID != nil && *request.ReplyMessageID != "" {
@ -59,13 +59,13 @@ func (service serviceSend) SendText(ctx context.Context, request domainSend.Mess
participantJID = firstDevice.Device
}
msg = &waProto.Message{
ExtendedTextMessage: &waProto.ExtendedTextMessage{
msg = &waE2E.Message{
ExtendedTextMessage: &waE2E.ExtendedTextMessage{
Text: proto.String(request.Message),
ContextInfo: &waProto.ContextInfo{
StanzaId: request.ReplyMessageID,
ContextInfo: &waE2E.ContextInfo{
StanzaID: request.ReplyMessageID,
Participant: proto.String(participantJID),
QuotedMessage: &waProto.Message{
QuotedMessage: &waE2E.Message{
Conversation: proto.String(request.Message),
},
},
@ -154,15 +154,15 @@ func (service serviceSend) SendImage(ctx context.Context, request domainSend.Ima
return response, pkgError.InternalServerError(fmt.Sprintf("failed to read thumbnail %v", err))
}
msg := &waProto.Message{ImageMessage: &waProto.ImageMessage{
JpegThumbnail: dataWaThumbnail,
msg := &waE2E.Message{ImageMessage: &waE2E.ImageMessage{
JPEGThumbnail: dataWaThumbnail,
Caption: proto.String(dataWaCaption),
Url: proto.String(uploadedImage.URL),
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,
FileEncSHA256: uploadedImage.FileEncSHA256,
FileSHA256: uploadedImage.FileSHA256,
FileLength: proto.Uint64(uint64(len(dataWaImage))),
ViewOnce: proto.Bool(request.ViewOnce),
}}
@ -205,15 +205,15 @@ func (service serviceSend) SendFile(ctx context.Context, request domainSend.File
return response, err
}
msg := &waProto.Message{DocumentMessage: &waProto.DocumentMessage{
Url: proto.String(uploadedFile.URL),
msg := &waE2E.Message{DocumentMessage: &waE2E.DocumentMessage{
URL: proto.String(uploadedFile.URL),
Mimetype: proto.String(fileMimeType),
Title: proto.String(request.File.Filename),
FileSha256: uploadedFile.FileSHA256,
FileSHA256: uploadedFile.FileSHA256,
FileLength: proto.Uint64(uploadedFile.FileLength),
MediaKey: uploadedFile.MediaKey,
FileName: proto.String(request.File.Filename),
FileEncSha256: uploadedFile.FileEncSHA256,
FileEncSHA256: uploadedFile.FileEncSHA256,
DirectPath: proto.String(uploadedFile.DirectPath),
Caption: proto.String(request.Caption),
}}
@ -310,19 +310,19 @@ func (service serviceSend) SendVideo(ctx context.Context, request domainSend.Vid
return response, err
}
msg := &waProto.Message{VideoMessage: &waProto.VideoMessage{
Url: proto.String(uploaded.URL),
msg := &waE2E.Message{VideoMessage: &waE2E.VideoMessage{
URL: proto.String(uploaded.URL),
Mimetype: proto.String(http.DetectContentType(dataWaVideo)),
Caption: proto.String(request.Caption),
FileLength: proto.Uint64(uploaded.FileLength),
FileSha256: uploaded.FileSHA256,
FileEncSha256: uploaded.FileEncSHA256,
FileSHA256: uploaded.FileSHA256,
FileEncSHA256: uploaded.FileEncSHA256,
MediaKey: uploaded.MediaKey,
DirectPath: proto.String(uploaded.DirectPath),
ViewOnce: proto.Bool(request.ViewOnce),
JpegThumbnail: dataWaThumbnail,
ThumbnailEncSha256: dataWaThumbnail,
ThumbnailSha256: dataWaThumbnail,
JPEGThumbnail: dataWaThumbnail,
ThumbnailEncSHA256: dataWaThumbnail,
ThumbnailSHA256: dataWaThumbnail,
ThumbnailDirectPath: proto.String(uploaded.DirectPath),
}}
ts, err := service.WaCli.SendMessage(ctx, dataWaRecipient, msg)
@ -353,7 +353,7 @@ func (service serviceSend) SendContact(ctx context.Context, request domainSend.C
msgVCard := fmt.Sprintf("BEGIN:VCARD\nVERSION:3.0\nN:;%v;;;\nFN:%v\nTEL;type=CELL;waid=%v:+%v\nEND:VCARD",
request.ContactName, request.ContactName, request.ContactPhone, request.ContactPhone)
msg := &waProto.Message{ContactMessage: &waProto.ContactMessage{
msg := &waE2E.Message{ContactMessage: &waE2E.ContactMessage{
DisplayName: proto.String(request.ContactName),
Vcard: proto.String(msgVCard),
}}
@ -379,10 +379,10 @@ func (service serviceSend) SendLink(ctx context.Context, request domainSend.Link
getMetaDataFromURL := utils.GetMetaDataFromURL(request.Link)
msg := &waProto.Message{ExtendedTextMessage: &waProto.ExtendedTextMessage{
msg := &waE2E.Message{ExtendedTextMessage: &waE2E.ExtendedTextMessage{
Text: proto.String(fmt.Sprintf("%s\n%s", request.Caption, request.Link)),
Title: proto.String(getMetaDataFromURL.Title),
CanonicalUrl: proto.String(request.Link),
CanonicalURL: proto.String(request.Link),
MatchedText: proto.String(request.Link),
Description: proto.String(getMetaDataFromURL.Description),
}}
@ -407,8 +407,8 @@ func (service serviceSend) SendLocation(ctx context.Context, request domainSend.
}
// Compose WhatsApp Proto
msg := &waProto.Message{
LocationMessage: &waProto.LocationMessage{
msg := &waE2E.Message{
LocationMessage: &waE2E.LocationMessage{
DegreesLatitude: proto.Float64(utils.StrToFloat64(request.Latitude)),
DegreesLongitude: proto.Float64(utils.StrToFloat64(request.Longitude)),
},
@ -444,14 +444,14 @@ func (service serviceSend) SendAudio(ctx context.Context, request domainSend.Aud
return response, err
}
msg := &waProto.Message{
AudioMessage: &waProto.AudioMessage{
Url: proto.String(audioUploaded.URL),
msg := &waE2E.Message{
AudioMessage: &waE2E.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,
FileSHA256: audioUploaded.FileSHA256,
FileEncSHA256: audioUploaded.FileEncSHA256,
MediaKey: audioUploaded.MediaKey,
},
}

13
src/validations/message_validation.go

@ -48,3 +48,16 @@ func ValidateReactMessage(ctx context.Context, request message.ReactionRequest)
return nil
}
func ValidateDeleteMessage(ctx context.Context, request domainMessage.DeleteRequest) error {
err := validation.ValidateStructWithContext(ctx, &request,
validation.Field(&request.Phone, validation.Required),
validation.Field(&request.MessageID, validation.Required),
)
if err != nil {
return pkgError.ValidationError(err.Error())
}
return nil
}

2
src/views/components/AccountUserInfo.js

@ -95,7 +95,7 @@ export default {
<div v-if="devices.length > 0" class="center">
<ol>
<li>Nama: {{ name }}</li>
<li>Name: {{ name }}</li>
<li>Status: {{ status }}</li>
<li>Device:
<ul>

106
src/views/components/MessageDelete.js

@ -0,0 +1,106 @@
export default {
name: 'DeleteMessage',
data() {
return {
type: 'user',
phone: '',
message_id: '',
loading: false,
}
},
computed: {
phone_id() {
return this.type === 'user' ? `${this.phone}@${window.TYPEUSER}` : `${this.phone}@${window.TYPEGROUP}`
}
},
methods: {
openModal() {
$('#modalMessageDelete').modal({
onApprove: function () {
return false;
}
}).modal('show');
},
async handleSubmit() {
try {
let response = await this.submitApi()
showSuccessInfo(response)
$('#modalMessageDelete').modal('hide');
} catch (err) {
showErrorInfo(err)
}
},
async submitApi() {
this.loading = true;
try {
const payload = {phone: this.phone_id, message: this.new_message}
let response = await window.http.post(`/message/${this.message_id}/delete`, payload)
this.handleReset();
return response.data.message;
} catch (error) {
if (error.response) {
throw new Error(error.response.data.message);
}
throw new Error(error.message);
} finally {
this.loading = false;
}
},
handleReset() {
this.type = 'user';
this.phone = '';
this.message_id = '';
this.new_message = '';
this.loading = false;
},
},
template: `
<div class="red card" @click="openModal()" style="cursor: pointer">
<div class="content">
<a class="ui red right ribbon label">Message</a>
<div class="header">Delete Message</div>
<div class="description">
Delete your sent message
</div>
</div>
</div>
<!-- Modal MessageDelete -->
<div class="ui small modal" id="modalMessageDelete">
<i class="close icon"></i>
<div class="header">
Delete Message
</div>
<div class="content">
<form class="ui form">
<div class="field">
<label>Type</label>
<select name="type" v-model="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="phone" type="text" placeholder="6289..."
aria-label="phone">
<input :value="phone_id" disabled aria-label="whatsapp_id">
</div>
<div class="field">
<label>Message ID</label>
<input v-model="message_id" type="text" placeholder="Please enter your message id"
aria-label="message id">
</div>
</form>
</div>
<div class="actions">
<div class="ui approve positive right labeled icon button" :class="{'loading': this.loading}"
@click="handleSubmit">
Delete
<i class="send icon"></i>
</div>
</div>
</div>
`
}

4
src/views/index.html

@ -68,6 +68,7 @@
</div>
<div class="ui three column doubling grid cards">
<message-delete></message-delete>
<message-revoke></message-revoke>
<message-react></message-react>
<message-update></message-update>
@ -136,6 +137,7 @@
import SendLocation from "./components/SendLocation.js";
import SendAudio from "./components/SendAudio.js";
import SendPoll from "./components/SendPoll.js";
import MessageDelete from "./components/MessageDelete.js";
import MessageUpdate from "./components/MessageUpdate.js";
import MessageReact from "./components/MessageReact.js";
import MessageRevoke from "./components/MessageRevoke.js";
@ -170,7 +172,7 @@
components: {
AppLogin, AppLogout, AppReconnect,
SendMessage, SendImage, SendFile, SendVideo, SendContact, SendLocation, SendAudio, SendPoll,
MessageUpdate, MessageReact, MessageRevoke,
MessageDelete, MessageUpdate, MessageReact, MessageRevoke,
GroupList, GroupCreate, GroupJoinWithLink, GroupAddParticipants,
AccountAvatar, AccountUserInfo, AccountPrivacy
},

Loading…
Cancel
Save