Browse Source

feat: redesign UI 4.0 (#36)

* feat: remove send group

* feat: remove type in send payload

* feat: add loading when submit

* feat: update docs

* chore: update docs

* fix: update UI

* feat: add more intuitive UI

* chore: update docs
pull/38/head v4.0.0
Aldino Kemal 3 years ago
committed by GitHub
parent
commit
c4328a316e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 122
      docs/openapi.yaml
  2. 65
      readme.md
  3. 2
      src/cmd/root.go
  4. 2
      src/config/settings.go
  5. 4
      src/domains/app/devices.go
  6. 1
      src/domains/send/contact.go
  7. 1
      src/domains/send/file.go
  8. 1
      src/domains/send/image.go
  9. 1
      src/domains/send/link.go
  10. 2
      src/domains/send/message.go
  11. 5
      src/domains/send/send.go
  12. 1
      src/domains/send/text.go
  13. 1
      src/domains/send/video.go
  14. 2
      src/internal/rest/app_rest.go
  15. 39
      src/internal/rest/send_rest.go
  16. 2
      src/internal/rest/user_rest.go
  17. 15
      src/internal/websocket/websocket.go
  18. 2
      src/services/app_service.go
  19. 2
      src/utils/errors.go
  20. 6
      src/utils/response.go
  21. 7
      src/utils/whatsapp.go
  22. 17
      src/validations/send_validation.go
  23. 4
      src/validations/send_validation_test.go
  24. 5
      src/validations/user_validation.go
  25. 436
      src/views/index.html

122
docs/openapi.yaml

@ -1,7 +1,7 @@
openapi: 3.0.0
info:
title: WhatsApp API MultiDevice
version: 2.5.0
version: 3.0.0
description: This API is used for sending whatsapp via API
servers:
- url: http://localhost:3000
@ -10,8 +10,8 @@ tags:
description: Initial Connection to Whatsapp server
- name: user
description: Getting information
- name: send
description: Send Message (Text/Image/File/Video)
- name: message
description: Send or Manipulate Message (Text/Image/File/Video).
paths:
/app/login:
get:
@ -118,7 +118,7 @@ paths:
in: query
schema:
type: integer
example: '6289685024091'
example: '6289685028129@s.whatsapp.net'
responses:
'200':
description: OK
@ -155,17 +155,17 @@ paths:
status: you are blocked
picture_id: '1635239861'
devices:
- User: '6289685024091'
- User: '6289685028129@s.whatsapp.net'
Agent: 0
Device: UNKNOWN
Server: s.whatsapp.net
AD: true
- User: '6289685024091'
- User: '6289685028129@s.whatsapp.net'
Agent: 0
Device: SAFARI
Server: s.whatsapp.net
AD: true
- User: '6289685024091'
- User: '6289685028129@s.whatsapp.net'
Agent: 0
Device: IPAD
Server: s.whatsapp.net
@ -193,7 +193,7 @@ paths:
in: query
schema:
type: integer
example: '6289685024091'
example: '6289685028129@s.whatsapp.net'
responses:
'200':
description: OK
@ -300,7 +300,7 @@ paths:
post:
operationId: sendMessage
tags:
- send
- message
summary: Send Message
requestBody:
content:
@ -310,14 +310,12 @@ paths:
properties:
phone:
type: integer
example: '6289685024091'
example: '6289685028129@s.whatsapp.net'
description: Phone number with country code
message:
type: string
example: selamat malam
type:
type: string
example: 'user'
description: 'user/group | default: user'
description: Message to send
responses:
'200':
description: OK
@ -341,7 +339,7 @@ paths:
post:
operationId: sendImage
tags:
- send
- message
summary: Send Image
requestBody:
content:
@ -351,23 +349,24 @@ paths:
properties:
phone:
type: integer
example: '6289685024091'
example: '6289685028129@s.whatsapp.net'
description: Phone number with country code
caption:
type: string
example: selamat malam
description: Caption to send
view_once:
type: boolean
example: false
description: View once
image:
type: string
format: binary
type:
type: string
example: 'user'
description: 'user/group | default: user'
description: Image to send
compress:
type: boolean
example: false
description: Compress image
responses:
'200':
description: OK
@ -391,7 +390,7 @@ paths:
post:
operationId: sendFile
tags:
- send
- message
summary: Send File
requestBody:
content:
@ -401,17 +400,16 @@ paths:
properties:
phone:
type: integer
example: '6289685024091'
example: '6289685028129@s.whatsapp.net'
description: Phone number with country code
caption:
type: string
example: selamat malam
description: Caption to send
file:
type: string
format: binary
type:
type: string
example: 'user'
description: 'user/group | default: user'
description: File to send
responses:
'200':
description: OK
@ -435,7 +433,7 @@ paths:
post:
operationId: sendVideo
tags:
- send
- message
summary: Send Video
requestBody:
content:
@ -445,23 +443,24 @@ paths:
properties:
phone:
type: integer
example: '6289685024091'
example: '6289685028129@s.whatsapp.net'
description: Phone number with country code
caption:
type: string
example: ini contoh caption video
description: Caption to send
view_once:
type: boolean
example: 'false'
description: View once
video:
type: string
format: binary
type:
type: string
example: 'user'
description: 'user/group | default: user'
description: Video to send
compress:
type: boolean
example: 'false'
description: Compress video
responses:
'200':
description: OK
@ -485,7 +484,7 @@ paths:
post:
operationId: sendContact
tags:
- send
- message
summary: Send Contact
requestBody:
content:
@ -495,17 +494,16 @@ paths:
properties:
phone:
type: integer
example: '6289685024051'
example: '6289685024051@s.whatsapp.net'
description: Phone number with country code
contact_name:
type: string
example: Aldino Kemal
description: Contact name
contact_phone:
type: string
example: '6289685024992'
type:
type: string
example: 'user'
description: 'user/group | default: user'
description: Contact phone number
responses:
'200':
description: OK
@ -529,7 +527,7 @@ paths:
post:
operationId: sendLink
tags:
- send
- message
summary: Send Link
requestBody:
content:
@ -539,17 +537,55 @@ paths:
properties:
phone:
type: integer
example: '6289685024051'
example: '6289685024051@s.whatsapp.net'
description: Phone number with country code
link:
type: string
example: "https://google.com"
description: Link to send
caption:
type: string
example: 'Halo ini contoh caption'
type:
description: Caption 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'
/message/:message_id/revoke:
post:
operationId: revokeMessage
tags:
- message
summary: Send Link
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
phone:
type: integer
example: '6289685024051@s.whatsapp.net'
description: Phone number with country code
message_id:
type: string
example: 'user'
description: 'user/group | default: user'
example: "3EB007D87686670344D7"
description: Message ID
responses:
'200':
description: OK

65
readme.md

@ -1,6 +1,8 @@
## Go Whatsapp API Multi Device Version
[![buddy pipeline](https://app.buddy.works/aldinokemal/go-whatsapp-web-multidevice/pipelines/pipeline/423077/badge.svg?token=a951a4546fe3f54079e678cc9d0eea12069fbdc21f8ed5ea22e1e95c4f63215f "buddy pipeline")](https://app.buddy.works/aldinokemal/go-whatsapp-web-multidevice/pipelines/pipeline/423077)
[![release version](https://img.shields.io/github/v/release/aldinokemal/go-whatsapp-web-multidevice "release version")](https://github.com/aldinokemal/go-whatsapp-web-multidevice/releases)
<br>
[![release windows](https://github.com/aldinokemal/go-whatsapp-web-multidevice/actions/workflows/deploy-windows.yml/badge.svg "release windows")](https://github.com/aldinokemal/go-whatsapp-web-multidevice/actions/workflows/deploy-windows.yml)
[![release linux](https://github.com/aldinokemal/go-whatsapp-web-multidevice/actions/workflows/deploy-linux.yml/badge.svg "release linux")](https://github.com/aldinokemal/go-whatsapp-web-multidevice/actions/workflows/deploy-linux.yml)
[![release macos](https://github.com/aldinokemal/go-whatsapp-web-multidevice/actions/workflows/deploy-mac.yml/badge.svg "release macos")](https://github.com/aldinokemal/go-whatsapp-web-multidevice/actions/workflows/deploy-mac.yml)
@ -86,23 +88,25 @@ You can fork or edit this source code !
### Current API
You can check [docs/openapi.yml](./docs/openapi.yaml) for detail API
| Feature | Menu | Method | URL |
|---------|-------------------------|--------|------------------|
| ✅ | Login | GET | /app/login |
| ✅ | Logout | GET | /app/logout |
| ✅ | Reconnect | GET | /app/reconnect |
| ✅ | User Info | GET | /user/info |
| ✅ | User Avatar | GET | /user/avatar |
| ✅ | User My Group List | GET | /user/my/groups |
| ✅ | User My Privacy Setting | GET | /user/my/privacy |
| ✅ | Send Message | POST | /send/message |
| ✅ | Send Image | POST | /send/image |
| ✅ | Send File | POST | /send/file |
| ✅ | Send Video | POST | /send/video |
| ✅ | Send Contact | POST | /send/contact |
| ✅ | Send Link | POST | /send/link |
You can check [docs/openapi.yml](./docs/openapi.yaml) for detail API, 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 |
| ✅ | User Info | GET | /user/info |
| ✅ | User Avatar | GET | /user/avatar |
| ✅ | User My Group List | GET | /user/my/groups |
| ✅ | User My Privacy Setting | GET | /user/my/privacy |
| ✅ | Send Message | POST | /send/message |
| ✅ | Send Image | POST | /send/image |
| ✅ | Send File | POST | /send/file |
| ✅ | Send Video | POST | /send/video |
| ✅ | Send Contact | POST | /send/contact |
| ✅ | Send Link | POST | /send/link |
| ✅ | Revoke Messave | POST | /message/:message_id/revoke |
```
✅ = Available
@ -111,19 +115,20 @@ You can check [docs/openapi.yml](./docs/openapi.yaml) for detail API
### App User Interface
1. Homepage ![Homepage](https://i.ibb.co/xjZ7KLr/Screenshot-2022-11-20-at-20-54-52.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/Bng57Ry/Screen-Shot-2022-05-22-at-13-51-13.png)
4. Send Image ![Send Image](https://i.ibb.co/gJj3SrQ/Screen-Shot-2022-05-22-at-13-49-21.png)
5. Send File ![Send File](https://i.ibb.co/nCwhysd/Screen-Shot-2022-05-22-at-13-43-16.png)
6. Send Video ![Send File](https://i.ibb.co/yBXsWXX/Screen-Shot-2022-05-22-at-13-43-24.png)
7. Send Contact ![Send File](https://i.ibb.co/fqwwGK5/Screen-Shot-2022-07-07-at-07-59-26.png)
8. User Info ![User Info](https://i.ibb.co/BC0mNT7/Screen-Shot-2022-02-13-at-13-00-57.png)
9. User Avatar ![User Avatar](https://i.ibb.co/TkzPbLZ/Screen-Shot-2022-02-13-at-13-01-39.png)
10. User Privacy ![User My Privacy](https://i.ibb.co/RQcC5m9/Screen-Shot-2022-02-13-at-12-58-47.png)
11. User Group ![List Group](https://i.ibb.co/jfkgKdG/Screen-Shot-2022-05-12-at-21-12-06.png)
12. Auto Reply ![Auto Reply](https://i.ibb.co/D4rTytX/IMG-20220517-162500.jpg)
13. 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/NNX2wWY/home.png)
2. Login ![Login](https://i.ibb.co/jkcB15R/login.png)
3. Send Message ![Send Message](https://i.ibb.co/DrCVXS7/send-message.png)
4. Send Image ![Send Image](https://i.ibb.co/WykfQc8/send-image.png)
5. Send File ![Send File](https://i.ibb.co/wC4SfRp/send-file.png)
6. Send Video ![Send Video](https://i.ibb.co/VDCRH3G/send-video.png)
7. Send Contact ![Send Contact](https://i.ibb.co/4810H7N/send-contact.png)
8. Revoke Message ![Revoke Message](https://i.ibb.co/yswhvQY/revoke.png)
9. User Info ![User Info](https://i.ibb.co/3zjX6Cz/user-info.png)
10. User Avatar ![User Avatar](https://i.ibb.co/cysjmjT/user-avatar.png)
11. My Privacy ![My Privacy](https://i.ibb.co/Cw1sMQz/my-privacy.png)
12. My Group ![My Group](https://i.ibb.co/B6rW8Sh/list-group.png)
13. Auto Reply ![Auto Reply](https://i.ibb.co/D4rTytX/IMG-20220517-162500.jpg)
14. Basic Auth Prompt ![Basic Auth](https://i.ibb.co/PDjQ92W/Screenshot-2022-11-06-at-14-06-29.png)
### Mac OS NOTE

2
src/cmd/root.go

@ -113,7 +113,7 @@ func runRest(_ *cobra.Command, _ []string) {
})
})
websocket.RegisterRoutes(app)
websocket.RegisterRoutes(app, appService)
go websocket.RunHub()
// Set auto reconnect to whatsapp server after booting

2
src/config/settings.go

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

4
src/domains/app/devices.go

@ -1,6 +1,6 @@
package app
type FetchDevicesResponse struct {
Name string
Device string
Name string `json:"name"`
Device string `json:"device"`
}

1
src/domains/send/contact.go

@ -4,7 +4,6 @@ type ContactRequest struct {
Phone string `json:"phone" form:"phone"`
ContactName string `json:"contact_name" form:"contact_name"`
ContactPhone string `json:"contact_phone" form:"contact_phone"`
Type Type `json:"type" form:"type"`
}
type ContactResponse struct {

1
src/domains/send/file.go

@ -5,7 +5,6 @@ import "mime/multipart"
type FileRequest struct {
Phone string `json:"phone" form:"phone"`
File *multipart.FileHeader `json:"file" form:"file"`
Type Type `json:"type" form:"type"`
}
type FileResponse struct {

1
src/domains/send/image.go

@ -7,7 +7,6 @@ type ImageRequest struct {
Caption string `json:"caption" form:"caption"`
Image *multipart.FileHeader `json:"image" form:"image"`
ViewOnce bool `json:"view_once" form:"view_once"`
Type Type `json:"type" form:"type"`
Compress bool `json:"compress"`
}

1
src/domains/send/link.go

@ -4,7 +4,6 @@ type LinkRequest struct {
Phone string `json:"phone" form:"phone"`
Caption string `json:"caption"`
Link string `json:"link"`
Type Type `json:"type" form:"type"`
}
type LinkResponse struct {

2
src/domains/send/message.go

@ -3,7 +3,6 @@ package send
type RevokeRequest struct {
MessageID string `json:"message_id" uri:"message_id"`
Phone string `json:"phone" form:"phone"`
Type Type `json:"type" form:"type"`
}
type RevokeResponse struct {
@ -15,7 +14,6 @@ type UpdateMessageRequest struct {
MessageID string `json:"message_id" uri:"message_id"`
Message string `json:"message" form:"message"`
Phone string `json:"phone" form:"phone"`
Type Type `json:"type" form:"type"`
}
type UpdateMessageResponse struct {

5
src/domains/send/send.go

@ -4,11 +4,6 @@ import (
"context"
)
type Type string
const TypeUser Type = "user"
const TypeGroup Type = "group"
type ISendService interface {
SendText(ctx context.Context, request MessageRequest) (response MessageResponse, err error)
SendImage(ctx context.Context, request ImageRequest) (response ImageResponse, err error)

1
src/domains/send/text.go

@ -3,7 +3,6 @@ package send
type MessageRequest struct {
Phone string `json:"phone" form:"phone"`
Message string `json:"message" form:"message"`
Type Type `json:"type" form:"type"`
}
type MessageResponse struct {

1
src/domains/send/video.go

@ -6,7 +6,6 @@ type VideoRequest struct {
Phone string `json:"phone" form:"phone"`
Caption string `json:"caption" form:"caption"`
Video *multipart.FileHeader `json:"video" form:"video"`
Type Type `json:"type" form:"type"`
ViewOnce bool `json:"view_once" form:"view_once"`
Compress bool `json:"compress"`
}

2
src/internal/rest/app_rest.go

@ -28,7 +28,7 @@ func (controller *App) Login(c *fiber.Ctx) error {
return c.JSON(utils.ResponseData{
Code: 200,
Message: "Success",
Results: map[string]interface{}{
Results: map[string]any{
"qr_link": fmt.Sprintf("%s://%s/%s", c.Protocol(), c.Hostname(), response.ImagePath),
"qr_duration": response.Duration,
},

39
src/internal/rest/send_rest.go

@ -1,7 +1,6 @@
package rest
import (
"fmt"
domainSend "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/send"
"github.com/aldinokemal/go-whatsapp-web-multidevice/utils"
"github.com/aldinokemal/go-whatsapp-web-multidevice/validations"
@ -33,12 +32,6 @@ func (controller *Send) SendText(c *fiber.Ctx) error {
// add validation send message
validations.ValidateSendMessage(request)
if request.Type == domainSend.TypeGroup {
request.Phone = request.Phone + "@g.us"
} else {
request.Phone = request.Phone + "@s.whatsapp.net"
}
response, err := controller.Service.SendText(c.UserContext(), request)
utils.PanicIfNeeded(err)
@ -64,12 +57,6 @@ func (controller *Send) SendImage(c *fiber.Ctx) error {
//add validation send image
validations.ValidateSendImage(request)
if request.Type == domainSend.TypeGroup {
request.Phone = request.Phone + "@g.us"
} else {
request.Phone = request.Phone + "@s.whatsapp.net"
}
response, err := controller.Service.SendImage(c.UserContext(), request)
utils.PanicIfNeeded(err)
@ -93,12 +80,6 @@ func (controller *Send) SendFile(c *fiber.Ctx) error {
//add validation send image
validations.ValidateSendFile(request)
if request.Type == domainSend.TypeGroup {
request.Phone = request.Phone + "@g.us"
} else {
request.Phone = request.Phone + "@s.whatsapp.net"
}
response, err := controller.Service.SendFile(c.UserContext(), request)
utils.PanicIfNeeded(err)
@ -122,12 +103,6 @@ func (controller *Send) SendVideo(c *fiber.Ctx) error {
//add validation send image
validations.ValidateSendVideo(request)
if request.Type == domainSend.TypeGroup {
request.Phone = request.Phone + "@g.us"
} else {
request.Phone = request.Phone + "@s.whatsapp.net"
}
response, err := controller.Service.SendVideo(c.UserContext(), request)
utils.PanicIfNeeded(err)
@ -146,12 +121,6 @@ func (controller *Send) SendContact(c *fiber.Ctx) error {
// add validation send contect
validations.ValidateSendContact(request)
if request.Type == domainSend.TypeGroup {
request.Phone = request.Phone + "@g.us"
} else {
request.Phone = request.Phone + "@s.whatsapp.net"
}
response, err := controller.Service.SendContact(c.UserContext(), request)
utils.PanicIfNeeded(err)
@ -170,12 +139,6 @@ func (controller *Send) SendLink(c *fiber.Ctx) error {
err = validations.ValidateSendLink(request)
utils.PanicIfNeeded(err)
if request.Type == domainSend.TypeGroup {
request.Phone = request.Phone + "@g.us"
} else {
request.Phone = request.Phone + "@s.whatsapp.net"
}
response, err := controller.Service.SendLink(c.UserContext(), request)
utils.PanicIfNeeded(err)
@ -194,7 +157,6 @@ func (controller *Send) RevokeMessage(c *fiber.Ctx) error {
err = validations.ValidateRevokeMessage(request)
utils.PanicIfNeeded(err)
fmt.Println(request)
response, err := controller.Service.Revoke(c.UserContext(), request)
utils.PanicIfNeeded(err)
@ -214,7 +176,6 @@ func (controller *Send) UpdateMessage(c *fiber.Ctx) error {
err = validations.ValidateUpdateMessage(request)
utils.PanicIfNeeded(err)
fmt.Println(request)
response, err := controller.Service.UpdateMessage(c.UserContext(), request)
utils.PanicIfNeeded(err)

2
src/internal/rest/user_rest.go

@ -36,7 +36,6 @@ func (controller *User) UserInfo(c *fiber.Ctx) error {
// add validation send message
validations.ValidateUserInfo(request)
request.Phone = request.Phone + "@s.whatsapp.net"
response, err := controller.Service.Info(c.Context(), request)
utils.PanicIfNeeded(err)
@ -55,7 +54,6 @@ func (controller *User) UserAvatar(c *fiber.Ctx) error {
// add validation send message
validations.ValidateUserAvatar(request)
request.Phone = request.Phone + "@s.whatsapp.net"
response, err := controller.Service.Avatar(c.Context(), request)
utils.PanicIfNeeded(err)

15
src/internal/websocket/websocket.go

@ -1,7 +1,9 @@
package websocket
import (
"context"
"encoding/json"
domainApp "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/app"
"github.com/gofiber/fiber/v2"
"github.com/gofiber/websocket/v2"
"log"
@ -11,6 +13,7 @@ type client struct{} // Add more data to this type if needed
type BroadcastMessage struct {
Code string `json:"code"`
Message string `json:"message"`
Result any `json:"result"`
}
var Clients = make(map[*websocket.Conn]client) // Note: although large maps with pointer-like types (e.g. strings) as keys are slow, using pointers themselves as keys is acceptable and fast
@ -61,7 +64,7 @@ func RunHub() {
}
}
func RegisterRoutes(app *fiber.App) {
func RegisterRoutes(app *fiber.App, service domainApp.IAppService) {
app.Use("/ws", func(c *fiber.Ctx) error {
if websocket.IsWebSocketUpgrade(c) { // Returns true if the client requested upgrade to the WebSocket protocol
return c.Next()
@ -96,7 +99,15 @@ func RegisterRoutes(app *fiber.App) {
log.Println("error unmarshal message:", err)
return
}
Broadcast <- messageData
if messageData.Code == "FETCH_DEVICES" {
devices, _ := service.FetchDevices(context.Background())
Broadcast <- BroadcastMessage{
Code: "LIST_DEVICES",
Message: "Device found",
Result: devices,
}
}
} else {
log.Println("websocket message received of type", messageType)
}

2
src/services/app_service.go

@ -119,7 +119,7 @@ func (service serviceApp) Reconnect(_ context.Context) (err error) {
return service.WaCli.Connect()
}
func (service serviceApp) FetchDevices(ctx context.Context) (response []domainApp.FetchDevicesResponse, err error) {
func (service serviceApp) FetchDevices(_ context.Context) (response []domainApp.FetchDevicesResponse, err error) {
if service.WaCli == nil {
return response, errors.New("wa cli nil cok")
}

2
src/utils/errors.go

@ -2,7 +2,7 @@ package utils
import "fmt"
func PanicIfNeeded(err interface{}, message ...string) {
func PanicIfNeeded(err any, message ...string) {
if err != nil {
if fmt.Sprintf("%s", err) == "record not found" && len(message) > 0 {
panic(message[0])

6
src/utils/response.go

@ -1,7 +1,7 @@
package utils
type ResponseData struct {
Code int `json:"code"`
Message string `json:"message"`
Results interface{} `json:"results"`
Code int `json:"code"`
Message string `json:"message"`
Results any `json:"results"`
}

7
src/utils/whatsapp.go

@ -140,6 +140,11 @@ func handler(rawEvt interface{}) {
Code: "LOGIN_SUCCESS",
Message: fmt.Sprintf("Successfully pair with %s", evt.ID.String()),
}
case *events.LoggedOut:
websocket.Broadcast <- websocket.BroadcastMessage{
Code: "LIST_DEVICES",
Result: nil,
}
case *events.Connected, *events.PushNameSetting:
if len(cli.Store.PushName) == 0 {
return
@ -238,7 +243,7 @@ func handler(rawEvt interface{}) {
func sendAutoReplyWebhook(evt *events.Message) error {
client := &http.Client{Timeout: 10 * time.Second}
body := map[string]interface{}{
body := map[string]any{
"from": evt.Info.SourceString(),
"message": evt.Message.GetConversation(),
"image": evt.Message.GetImageMessage(),

17
src/validations/send_validation.go

@ -11,8 +11,7 @@ import (
func ValidateSendMessage(request domainSend.MessageRequest) {
err := validation.ValidateStruct(&request,
validation.Field(&request.Phone, validation.Required, validation.Length(10, 25)),
validation.Field(&request.Message, validation.Required, validation.Length(1, 1000)),
validation.Field(&request.Message, validation.Required),
)
if err != nil {
@ -24,8 +23,6 @@ func ValidateSendMessage(request domainSend.MessageRequest) {
func ValidateSendImage(request domainSend.ImageRequest) {
err := validation.ValidateStruct(&request,
validation.Field(&request.Phone, validation.Required, validation.Length(10, 25)),
validation.Field(&request.Caption, validation.When(true, validation.Length(1, 1000))),
validation.Field(&request.Image, validation.Required),
)
@ -50,7 +47,6 @@ func ValidateSendImage(request domainSend.ImageRequest) {
func ValidateSendFile(request domainSend.FileRequest) {
err := validation.ValidateStruct(&request,
validation.Field(&request.Phone, validation.Required, validation.Length(10, 25)),
validation.Field(&request.File, validation.Required),
)
@ -70,7 +66,6 @@ func ValidateSendFile(request domainSend.FileRequest) {
func ValidateSendVideo(request domainSend.VideoRequest) {
err := validation.ValidateStruct(&request,
validation.Field(&request.Phone, validation.Required, validation.Length(10, 25)),
validation.Field(&request.Video, validation.Required),
)
@ -102,7 +97,6 @@ func ValidateSendVideo(request domainSend.VideoRequest) {
func ValidateSendContact(request domainSend.ContactRequest) {
err := validation.ValidateStruct(&request,
validation.Field(&request.Phone, validation.Required, validation.Length(10, 25)),
validation.Field(&request.ContactName, validation.Required),
validation.Field(&request.ContactPhone, validation.Required),
)
@ -116,7 +110,6 @@ func ValidateSendContact(request domainSend.ContactRequest) {
func ValidateSendLink(request domainSend.LinkRequest) error {
err := validation.ValidateStruct(&request,
validation.Field(&request.Phone, validation.Required, validation.Length(10, 25)),
validation.Field(&request.Link, validation.Required),
validation.Field(&request.Caption, validation.Required),
)
@ -132,8 +125,7 @@ func ValidateSendLink(request domainSend.LinkRequest) error {
func ValidateRevokeMessage(request domainSend.RevokeRequest) error {
err := validation.ValidateStruct(&request,
validation.Field(&request.Phone, validation.Required, validation.Length(10, 25)),
validation.Field(&request.MessageID, validation.Required, validation.Length(20, 25)),
validation.Field(&request.MessageID, validation.Required),
)
if err != nil {
@ -147,9 +139,8 @@ func ValidateRevokeMessage(request domainSend.RevokeRequest) error {
func ValidateUpdateMessage(request domainSend.UpdateMessageRequest) error {
err := validation.ValidateStruct(&request,
validation.Field(&request.Phone, validation.Required, validation.Length(10, 25)),
validation.Field(&request.MessageID, validation.Required, validation.Length(20, 25)),
validation.Field(&request.Message, validation.Required, validation.Length(1, 1000)),
validation.Field(&request.MessageID, validation.Required),
validation.Field(&request.Message, validation.Required),
)
if err != nil {

4
src/validations/send_validation_test.go

@ -13,12 +13,12 @@ func TestValidateSendMessage(t *testing.T) {
tests := []struct {
name string
args args
err interface{}
err any
}{
{
name: "success phone & message normal",
args: args{request: domainSend.MessageRequest{
Phone: "6289685024091",
Phone: "1728937129312@s.whatsapp.net",
Message: "Hello this is testing",
}},
err: nil,

5
src/validations/user_validation.go

@ -4,12 +4,11 @@ import (
domainUser "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/user"
"github.com/aldinokemal/go-whatsapp-web-multidevice/utils"
validation "github.com/go-ozzo/ozzo-validation/v4"
"github.com/go-ozzo/ozzo-validation/v4/is"
)
func ValidateUserInfo(request domainUser.InfoRequest) {
err := validation.ValidateStruct(&request,
validation.Field(&request.Phone, validation.Required, is.E164, validation.Length(10, 15)),
validation.Field(&request.Phone, validation.Required),
)
if err != nil {
@ -20,7 +19,7 @@ func ValidateUserInfo(request domainUser.InfoRequest) {
}
func ValidateUserAvatar(request domainUser.AvatarRequest) {
err := validation.ValidateStruct(&request,
validation.Field(&request.Phone, validation.Required, is.E164, validation.Length(10, 15)),
validation.Field(&request.Phone, validation.Required),
)
if err != nil {

436
src/views/index.html

@ -27,6 +27,15 @@
<div class="ui container" id="app">
<h1 class="ui header center aligned">[[ app_name ]]</h1>
<div class="ui success message" v-if="connected_devices != null">
<div class="header">
Device is connected
</div>
<p>
Device ID: <b>[[ connected_devices[0].device ]]</b>
</p>
</div>
<div class="ui horizontal divider">
App
</div>
@ -60,22 +69,22 @@
</div>
<div class="ui horizontal divider">
Send Private Message
Message
</div>
<div class="ui three column doubling grid cards">
<div class="green card" @click="sendMessageModal('user')" style="cursor: pointer">
<div class="blue card" @click="sendMessageModal()" style="cursor: pointer">
<div class="content">
<a class="ui blue right ribbon label">Private</a>
<a class="ui blue right ribbon label">Send</a>
<div class="header">Send Message</div>
<div class="description">
Send any message to any whatsapp number
</div>
</div>
</div>
<div class="green card" @click="sendImageModal('user')" style="cursor:pointer;">
<div class="blue card" @click="sendImageModal()" style="cursor:pointer;">
<div class="content">
<a class="ui blue right ribbon label">Private</a>
<a class="ui blue right ribbon label">Send</a>
<div class="header">Send Image</div>
<div class="description">
Send image with
@ -84,9 +93,9 @@
</div>
</div>
</div>
<div class="green card" @click="sendFileModal('user')" style="cursor: pointer">
<div class="blue card" @click="sendFileModal()" style="cursor: pointer">
<div class="content">
<a class="ui blue right ribbon label">Private</a>
<a class="ui blue right ribbon label">Send</a>
<div class="header">Send File</div>
<div class="description">
Send any file up to
@ -94,9 +103,9 @@
</div>
</div>
</div>
<div class="green card" @click="sendVideoModal('user')" style="cursor: pointer">
<div class="blue card" @click="sendVideoModal()" style="cursor: pointer">
<div class="content">
<a class="ui blue right ribbon label">Private</a>
<a class="ui blue right ribbon label">Send</a>
<div class="header">Send Video</div>
<div class="description">
Send video
@ -106,99 +115,50 @@
</div>
</div>
</div>
<div class="green card" @click="sendContactModal('user')" style="cursor: pointer">
<div class="blue card" @click="sendContactModal()" style="cursor: pointer">
<div class="content">
<a class="ui blue right ribbon label">Private</a>
<a class="ui blue right ribbon label">Send</a>
<div class="header">Send Contact</div>
<div class="description">
Send contact to any whatsapp number
</div>
</div>
</div>
</div>
<div class="ui horizontal divider">
Send Group Message
</div>
<div class="ui three column doubling grid cards">
<div class="green card" @click="sendMessageModal('group')" style="cursor: pointer">
<div class="red card" @click="sendRevokeModal()" style="cursor: pointer">
<div class="content">
<a class="ui teal right ribbon label">Group</a>
<div class="header">Send Message</div>
<a class="ui red right ribbon label">Revoke</a>
<div class="header">Revoke Message</div>
<div class="description">
Send any message to any whatsapp number
</div>
</div>
</div>
<div class="green card" @click="sendImageModal('group')" style="cursor:pointer;">
<div class="content">
<a class="ui teal right ribbon label">Group</a>
<div class="header">Send Image</div>
<div class="description">
Send image with
<div class="ui teal horizontal label">jpg/jpeg/png</div>
type
</div>
</div>
</div>
<div class="green card" @click="sendFileModal('group')" style="cursor: pointer">
<div class="content">
<a class="ui teal right ribbon label">Group</a>
<div class="header">Send File</div>
<div class="description">
Send any file up to
<div class="ui teal horizontal label">{{ .MaxFileSize }}</div>
</div>
</div>
</div>
<div class="green card" @click="sendVideoModal('group')" style="cursor: pointer">
<div class="content">
<a class="ui teal right ribbon label">Group</a>
<div class="header">Send Video</div>
<div class="description">
Send video
<div class="ui teal horizontal label">mp4</div>
up to
<div class="ui teal horizontal label">{{ .MaxVideoSize }}</div>
</div>
</div>
</div>
<div class="green card" @click="sendContactModal('group')" style="cursor: pointer">
<div class="content">
<a class="ui teal right ribbon label">Group</a>
<div class="header">Send Contact</div>
<div class="description">
Send contact to any whatsapp number
Revoke any message in private or group chat
</div>
</div>
</div>
</div>
<div class="ui horizontal divider">
User
Account
</div>
<div class="ui four column doubling grid cards">
<div class="green card" @click="infoModal" style="cursor: pointer;">
<div class="green card" @click="avatarModal" style="cursor: pointer;">
<div class="content">
<div class="header">User Info</div>
<div class="header">Avatar</div>
<div class="description">
You can search someone user info by phone
You can search someone avatar by phone
</div>
</div>
</div>
<div class="green card" @click="avatarModal" style="cursor: pointer;">
<div class="green card" @click="infoModal" style="cursor: pointer;">
<div class="content">
<div class="header">User Avatar</div>
<div class="header">User Info</div>
<div class="description">
You can search someone avatar by phone
You can search someone user info by phone
</div>
</div>
</div>
<div class="green card" @click="groupModal" style="cursor: pointer">
<div class="content">
<div class="header">User List Groups</div>
<div class="header">My List Groups</div>
<div class="description">
Display all groups you joined
</div>
@ -206,7 +166,7 @@
</div>
<div class="green card" @click="privacyModal" style="cursor: pointer">
<div class="content">
<div class="header">User Privacy Setting</div>
<div class="header">My Privacy Setting</div>
<div class="description">
Get your privacy settings
</div>
@ -249,10 +209,18 @@
</div>
<div class="content">
<form class="ui form">
<div class="field">
<label>Type</label>
<select name="message_type" v-model="message_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="message_phone" type="text" placeholder="6289..."
aria-label="phone">
<input :value="message_phone_id" disabled aria-label="whatsapp_id">
</div>
<div class="field">
<label>Message</label>
@ -262,7 +230,8 @@
</form>
</div>
<div class="actions">
<div class="ui positive right labeled icon button" @click="sendMessageProcess">
<div class="ui approve positive right labeled icon button" :class="{'loading': this.message_loading}"
@click="sendMessageProcess">
Send
<i class="send icon"></i>
</div>
@ -277,10 +246,18 @@
</div>
<div class="content">
<form class="ui form">
<div class="field">
<label>Type</label>
<select name="image_type" v-model="image_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="image_phone" type="text" placeholder="6289..."
aria-label="phone">
<input :value="image_phone_id" disabled aria-label="whatsapp_id">
</div>
<div class="field">
<label>Caption</label>
@ -313,7 +290,8 @@
</form>
</div>
<div class="actions">
<div class="ui positive right labeled icon button" @click="sendImageProcess">
<div class="ui approve positive right labeled icon button" :class="{'loading': this.image_loading}"
@click="sendImageProcess">
Send
<i class="send icon"></i>
</div>
@ -328,10 +306,18 @@
</div>
<div class="content">
<form class="ui form">
<div class="field">
<label>Type</label>
<select name="file_type" v-model="file_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="file_phone" type="text" placeholder="6289..."
aria-label="phone">
<input :value="file_phone_id" disabled aria-label="whatsapp_id">
</div>
<div class="field">
<label>Caption</label>
@ -349,14 +335,15 @@
</form>
</div>
<div class="actions">
<div class="ui positive right labeled icon button" @click="sendFileProcess">
<div class="ui approve positive right labeled icon button" :class="{'loading': this.file_loading}"
@click="sendFileProcess">
Send
<i class="send icon"></i>
</div>
</div>
</div>
<!-- Modal SendFile -->
<!-- Modal SendVideo -->
<div class="ui small modal" id="modalSendVideo">
<i class="close icon"></i>
<div class="header">
@ -364,6 +351,13 @@
</div>
<div class="content">
<form class="ui form">
<div class="field">
<label>Type</label>
<select name="video_type" v-model="video_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="video_phone" type="text" placeholder="6289..."
@ -373,6 +367,7 @@
<label>Caption</label>
<input v-model="video_caption" type="text" placeholder="Type some caption (optional)..."
aria-label="caption">
<input :value="video_phone_id" disabled aria-label="whatsapp_id">
</div>
<div class="field">
<label>View Once</label>
@ -399,7 +394,8 @@
</form>
</div>
<div class="actions">
<div class="ui positive right labeled icon button" @click="sendVideoProcess">
<div class="ui approve positive right labeled icon button" :class="{'loading': this.video_loading}"
@click="sendVideoProcess">
Send
<i class="send icon"></i>
</div>
@ -414,10 +410,18 @@
</div>
<div class="content">
<form class="ui form">
<div class="field">
<label>Type</label>
<select name="contact_type" v-model="contact_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="contact_phone" type="text" placeholder="6289..."
aria-label="phone">
<input :value="contact_phone_id" disabled aria-label="whatsapp_id">
</div>
<div class="field">
<label>Contact Name</label>
@ -432,7 +436,45 @@
</form>
</div>
<div class="actions">
<div class="ui positive right labeled icon button" @click="sendContactProcess">
<div class="ui approve positive right labeled icon button" :class="{'loading': this.contact_loading}"
@click="sendContactProcess">
Send
<i class="send icon"></i>
</div>
</div>
</div>
<!-- Modal SendRevoke -->
<div class="ui small modal" id="modalSendRevoke">
<i class="close icon"></i>
<div class="header">
Send Revoke
</div>
<div class="content">
<form class="ui form">
<div class="field">
<label>Type</label>
<select name="revoke_type" v-model="revoke_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="revoke_phone" type="text" placeholder="6289..."
aria-label="phone">
<input :value="revoke_phone_id" disabled aria-label="whatsapp_id">
</div>
<div class="field">
<label>Revoke Message ID</label>
<input v-model="revoke_message_id" type="text" placeholder="Please enter your message id"
aria-label="card_phone">
</div>
</form>
</div>
<div class="actions">
<div class="ui approve positive right labeled icon button" :class="{'loading': this.revoke_loading}"
@click="sendRevokeProcess">
Send
<i class="send icon"></i>
</div>
@ -492,10 +534,18 @@
</div>
<div class="content">
<form class="ui form">
<div class="field">
<label>Type</label>
<select name="avatar_type" v-model="avatar_type" aria-label="type">
<option value="group">Group Message</option>
<option value="user">Private Message</option>
</select>
</div>
<div class="field">
<label>Phone</label>
<input v-model="avatar_phone" type="text" placeholder="6289..."
aria-label="phone">
<input :value="avatar_phone_id" disabled aria-label="whatsapp_id">
</div>
<button type="button" class="ui primary button" :class="{'loading': avatar_loading}"
@ -505,7 +555,7 @@
</form>
<div v-if="avatar_image != null" class="center">
<img :src="avatar_image" alt="profile picture" style="padding-top: 10px;">
<img :src="avatar_image" alt="profile picture" style="padding-top: 10px; max-height: 200px">
</div>
</div>
</div>
@ -518,10 +568,17 @@
</div>
<div class="content">
<form class="ui form">
<div class="field">
<label>Type</label>
<select name="info_type" v-model="info_type" aria-label="type">
<option value="user">Private Message</option>
</select>
</div>
<div class="field">
<label>Phone</label>
<input v-model="info_phone" type="text" placeholder="6289..."
aria-label="phone">
<input :value="info_phone_id" disabled aria-label="whatsapp_id">
</div>
<button type="button" class="ui primary button" :class="{'loading': info_loading}"
@ -620,6 +677,9 @@
try {
await this.logoutApi()
showSuccessInfo("Logout success")
// fetch devices
this.app_ws.send(JSON.stringify({"code": "FETCH_DEVICES"}))
} catch (err) {
showErrorInfo(err)
}
@ -648,6 +708,8 @@
try {
await this.reconnectApi()
showSuccessInfo("Reconnect success")
// fetch devices
this.app_ws.send(JSON.stringify({"code": "FETCH_DEVICES"}))
} catch (err) {
showErrorInfo(err)
}
@ -676,17 +738,27 @@
message_type: 'user',
message_phone: '',
message_text: '',
message_loading: false,
}
},
computed: {
message_phone_id() {
return this.message_type === 'user' ? `${this.message_phone}@${this.type_user}` : `${this.message_phone}@${this.type_group}`
}
},
methods: {
sendMessageModal(type) {
this.message_type = type
$('#modalSendMessage').modal('show');
sendMessageModal() {
$('#modalSendMessage').modal({
onApprove: function () {
return false;
}
}).modal('show');
},
async sendMessageProcess() {
try {
let response = await this.sendMessageApi()
showSuccessInfo(response)
$('#modalSendMessage').modal('hide');
} catch (err) {
showErrorInfo(err)
}
@ -694,10 +766,10 @@
sendMessageApi() {
return new Promise(async (resolve, reject) => {
try {
this.message_loading = true;
let payload = new FormData();
payload.append("phone", this.message_phone)
payload.append("phone", this.message_phone_id)
payload.append("message", this.message_text)
payload.append("type", this.message_type)
let response = await http.post(`/send/message`, payload)
this.sendMessageReset();
resolve(response.data.message)
@ -707,6 +779,8 @@
} else {
reject(error.message)
}
} finally {
this.message_loading = false;
}
})
},
@ -726,17 +800,27 @@
image_compress: false,
image_caption: '',
image_type: 'user',
image_loading: false,
}
},
computed: {
image_phone_id() {
return this.image_type === 'user' ? `${this.image_phone}@${this.type_user}` : `${this.image_phone}@${this.type_group}`
}
},
methods: {
sendImageModal(type) {
this.image_type = type
$('#modalSendImage').modal('show');
sendImageModal() {
$('#modalSendImage').modal({
onApprove: function () {
return false;
}
}).modal('show');
},
async sendImageProcess() {
try {
let response = await this.sendImageApi()
showSuccessInfo(response)
$('#modalSendImage').modal('hide');
} catch (err) {
showErrorInfo(err)
}
@ -744,13 +828,13 @@
sendImageApi() {
return new Promise(async (resolve, reject) => {
try {
this.image_loading = true;
let payload = new FormData();
payload.append("phone", this.image_phone)
payload.append("phone", this.image_phone_id)
payload.append("view_once", this.image_view_once)
payload.append("compress", this.image_compress)
payload.append("caption", this.image_caption)
payload.append("image", $("#image_file")[0].files[0])
payload.append("type", this.image_type)
let response = await http.post(`/send/image`, payload)
this.sendImageReset();
resolve(response.data.message)
@ -760,6 +844,8 @@
} else {
reject(error.message)
}
} finally {
this.image_loading = false;
}
})
},
@ -780,17 +866,27 @@
file_caption: '',
file_type: 'user',
file_phone: '',
file_loading: false,
}
},
computed: {
file_phone_id() {
return this.file_type === 'user' ? `${this.file_phone}@${this.type_user}` : `${this.file_phone}@${this.type_group}`
}
},
methods: {
sendFileModal(type) {
this.file_type = type
$('#modalSendFile').modal('show');
sendFileModal() {
$('#modalSendFile').modal({
onApprove: function () {
return false;
}
}).modal('show');
},
async sendFileProcess() {
try {
let response = await this.sendFileApi()
showSuccessInfo(response)
$('#modalSendFile').modal('hide');
} catch (err) {
showErrorInfo(err)
}
@ -798,11 +894,11 @@
sendFileApi() {
return new Promise(async (resolve, reject) => {
try {
this.file_loading = true;
let payload = new FormData();
payload.append("caption", this.file_caption)
payload.append("phone", this.file_phone)
payload.append("phone", this.file_phone_id)
payload.append("file", $("#file_file")[0].files[0])
payload.append("type", this.file_type)
let response = await http.post(`/send/file`, payload)
this.sendFileReset();
resolve(response.data.message)
@ -812,6 +908,8 @@
} else {
reject(error.message)
}
} finally {
this.file_loading = false;
}
})
},
@ -832,17 +930,27 @@
video_compress: false,
video_type: 'user',
video_phone: '',
video_loading: false,
}
},
computed: {
video_phone_id() {
return this.video_type === 'user' ? `${this.video_phone}@${this.type_user}` : `${this.video_phone}@${this.type_group}`
}
},
methods: {
sendVideoModal(type) {
this.video_type = type
$('#modalSendVideo').modal('show');
sendVideoModal() {
$('#modalSendVideo').modal({
onApprove: function () {
return false;
}
}).modal('show');
},
async sendVideoProcess() {
try {
let response = await this.sendVideoApi()
showSuccessInfo(response)
$('#modalSendVideo').modal('hide');
} catch (err) {
showErrorInfo(err)
}
@ -850,13 +958,13 @@
sendVideoApi() {
return new Promise(async (resolve, reject) => {
try {
this.video_loading = true;
let payload = new FormData();
payload.append("phone", this.video_phone)
payload.append("phone", this.video_phone_id)
payload.append("caption", this.video_caption)
payload.append("view_once", this.video_view_once)
payload.append("compress", this.video_compress)
payload.append("video", $("#video_file")[0].files[0])
payload.append("type", this.video_type)
let response = await http.post(`/send/video`, payload)
this.sendVideoReset();
resolve(response.data.message)
@ -866,6 +974,8 @@
} else {
reject(error.message)
}
} finally {
this.video_loading = false;
}
})
},
@ -887,29 +997,41 @@
contact_phone: '',
contact_card_name: '',
contact_card_phone: '',
contact_loading: false,
}
},
computed: {
contact_phone_id() {
return this.contact_type === 'user' ? `${this.contact_phone}@${this.type_user}` : `${this.contact_phone}@${this.type_group}`
}
},
methods: {
sendContactModal(type) {
this.contact_type = type
$('#modalSendContact').modal('show');
sendContactModal() {
$('#modalSendContact').modal({
onApprove: function () {
return false;
}
}).modal('show');
},
async sendContactProcess() {
try {
this.contact_loading = true;
let response = await this.sendContactApi()
showSuccessInfo(response)
$('#modalSendContact').modal('hide');
} catch (err) {
showErrorInfo(err)
} finally {
this.contact_loading = false;
}
},
sendContactApi() {
return new Promise(async (resolve, reject) => {
try {
let payload = new FormData();
payload.append("phone", this.contact_phone)
payload.append("phone", this.contact_phone_id)
payload.append("contact_name", this.contact_card_name)
payload.append("contact_phone", this.contact_card_phone)
payload.append("type", this.contact_type)
let response = await http.post(`/send/contact`, payload)
this.sendContactReset();
resolve(response.data.message)
@ -926,7 +1048,66 @@
this.contact_phone = '';
this.contact_card_name = '';
this.contact_card_phone = '';
this.message_type = 'user';
this.contact_type = 'user';
},
}
}
const sendRevoke = {
data() {
return {
revoke_type: 'user',
revoke_phone: '',
revoke_message_id: '',
revoke_loading: false,
}
},
computed: {
revoke_phone_id() {
return this.revoke_type === 'user' ? `${this.revoke_phone}@${this.type_user}` : `${this.revoke_phone}@${this.type_group}`
}
},
methods: {
sendRevokeModal() {
$('#modalSendRevoke').modal({
onApprove: function () {
return false;
}
}).modal('show');
},
async sendRevokeProcess() {
try {
let response = await this.sendRevokeApi()
showSuccessInfo(response)
$('#modalSendRevoke').modal('hide');
} catch (err) {
showErrorInfo(err)
}
},
sendRevokeApi() {
return new Promise(async (resolve, reject) => {
try {
this.revoke_loading = true;
let payload = new FormData();
payload.append("phone", this.revoke_phone_id)
let response = await http.post(`/message/${this.revoke_message_id}/revoke`, payload)
this.sendRevokeReset();
resolve(response.data.message)
} catch (error) {
if (error.response) {
reject(error.response.data.message)
} else {
reject(error.message)
}
} finally {
this.revoke_loading = false;
}
})
},
sendRevokeReset() {
this.revoke_phone = '';
this.revoke_message_id = '';
this.revoke_type = 'user';
},
}
}
@ -1006,11 +1187,17 @@
const userAvatar = {
data() {
return {
avatar_type: 'user',
avatar_phone: '',
avatar_image: null,
avatar_loading: false,
}
},
computed: {
avatar_phone_id() {
return this.avatar_type === 'user' ? `${this.avatar_phone}@${this.type_user}` : `${this.avatar_phone}@${this.type_group}`
}
},
methods: {
async avatarModal() {
this.avatarReset();
@ -1030,7 +1217,7 @@
avatarApi() {
return new Promise(async (resolve, reject) => {
try {
let response = await http.get(`/user/avatar?phone=${this.avatar_phone}`)
let response = await http.get(`/user/avatar?phone=${this.avatar_phone_id}`)
this.avatar_image = response.data.results.url;
resolve()
} catch (error) {
@ -1045,6 +1232,7 @@
avatarReset() {
this.avatar_phone = '';
this.avatar_image = null;
this.avatar_type = 'user';
}
}
}
@ -1052,6 +1240,7 @@
const userInfo = {
data() {
return {
info_type: 'user',
info_phone: '',
//
info_name: null,
@ -1061,6 +1250,12 @@
info_loading: false,
}
},
computed: {
info_phone_id() {
return this.info_type === 'user' ? `${this.info_phone}@${this.type_user}` : `${this.info_phone}@${this.type_group}`
}
},
methods: {
async infoModal() {
this.infoReset();
@ -1080,7 +1275,7 @@
infoApi() {
return new Promise(async (resolve, reject) => {
try {
let response = await http.get(`/user/info?phone=${this.info_phone}`)
let response = await http.get(`/user/info?phone=${this.info_phone_id}`)
this.info_name = response.data.results.verified_name;
this.info_status = response.data.results.status;
this.info_devices = response.data.results.devices;
@ -1099,6 +1294,7 @@
this.info_name = null;
this.info_status = null;
this.info_devices = [];
this.info_type = 'user';
}
}
}
@ -1107,27 +1303,39 @@
delimiters: ['[[', ']]'],
data() {
return {
app_ws: null,
app_host: {{ .AppHost }},
app_name: 'Whatsapp API Multi Device ({{ .AppVersion }})',
is_logged_in: false,
connected_devices: null,
type_group: "g.us",
type_user: "s.whatsapp.net"
}
},
mounted() {
if (window["WebSocket"]) {
let wsType = location.protocol !== 'https:' ? 'ws://' : 'wss://';
let conn = new WebSocket(wsType + document.location.host + "/ws");
this.app_ws = new WebSocket(wsType + document.location.host + "/ws");
conn.onclose = (evt) => {
console.log(evt)
this.app_ws.onopen = (evt) => {
this.app_ws.send(JSON.stringify({
"code": "FETCH_DEVICES",
"message": "List device"
}))
};
conn.onmessage = (evt) => {
console.log(evt)
this.app_ws.onmessage = (evt) => {
const message = JSON.parse(evt.data)
switch (message.code) {
case 'LOGIN_SUCCESS':
showSuccessInfo(message.message)
$('#modalLogin').modal('hide');
// fetch devices
this.app_ws.send(JSON.stringify({"code": "FETCH_DEVICES"}))
break;
case 'LIST_DEVICES':
this.connected_devices = message.result
break;
default:
console.log(message)
@ -1144,7 +1352,7 @@
return tanggal.format('LLL');
}
},
mixins: [login, logout, reconnect, sendMessage, sendImage, sendFile, sendVideo, sendContact, userGroups, userPrivacy, userAvatar, userInfo]
mixins: [login, logout, reconnect, sendMessage, sendImage, sendFile, sendVideo, sendContact, sendRevoke, userGroups, userPrivacy, userAvatar, userInfo]
}).mount('#app')
</script>
</body>
Loading…
Cancel
Save