Browse Source

feat: add send link (#29)

* feat: add send link

feat: add more response when send api

* feat: remove url

* feat: update open api

* chore: update docs
pull/31/head
Aldino Kemal 3 years ago
committed by GitHub
parent
commit
05ec7f827f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 111
      docs/openapi.yaml
  2. 2
      readme.md
  3. 2
      src/config/settings.go
  4. 1
      src/domains/send/contact.go
  5. 1
      src/domains/send/file.go
  6. 1
      src/domains/send/image.go
  7. 13
      src/domains/send/link.go
  8. 1
      src/domains/send/send.go
  9. 1
      src/domains/send/text.go
  10. 1
      src/domains/send/video.go
  11. 25
      src/internal/rest/send_controller.go
  12. 81
      src/services/send_service.go
  13. 16
      src/validations/send_validation.go

111
docs/openapi.yaml

@ -1,7 +1,7 @@
openapi: 3.0.0
info:
title: WhatsApp API MultiDevice
version: 2.3.0
version: 2.4.0
description: This API is used for sending whatsapp via API
servers:
- url: http://localhost:3000
@ -15,6 +15,7 @@ tags:
paths:
/app/login:
get:
operationId: authLogin
tags:
- auth
summary: Login to whatsapp server
@ -47,6 +48,7 @@ paths:
http://localhost:3000/statics/images/qrcode/scan-qr-b0b7bb43-9a22-455a-814f-5a225c743310.png
/app/logout:
get:
operationId: authLogout
tags:
- auth
summary: Remove database and logout
@ -96,6 +98,7 @@ paths:
example: the store doesn't contain a device JID
/app/reconnect:
get:
operationId: authReconnect
tags:
- auth
summary: Reconnecting to whatsapp server
@ -125,6 +128,7 @@ paths:
results: null
/user/info:
get:
operationId: userInfo
tags:
- user
summary: User Info
@ -241,6 +245,7 @@ paths:
results: null
/user/avatar:
get:
operationId: userAvatar
tags:
- user
summary: User Avatar
@ -303,6 +308,7 @@ paths:
results: null
/user/my/privacy:
get:
operationId: userMyPrivacy
tags:
- user
summary: User My Privacy Setting
@ -368,6 +374,7 @@ paths:
results: null
/user/my/groups:
get:
operationId: userMyGroups
tags:
- user
summary: User My List Groups
@ -397,6 +404,7 @@ paths:
results: null
/send/message:
post:
operationId: sendMessage
tags:
- send
summary: Send Message
@ -491,6 +499,7 @@ paths:
results: null
/send/image:
post:
operationId: sendImage
tags:
- send
summary: Send Image
@ -594,6 +603,7 @@ paths:
results: null
/send/file:
post:
operationId: sendFile
tags:
- send
summary: Send File
@ -691,6 +701,7 @@ paths:
results: null
/send/video:
post:
operationId: sendVideo
tags:
- send
summary: Send Video
@ -793,6 +804,7 @@ paths:
results: null
/send/contact:
post:
operationId: sendContact
tags:
- send
summary: Send Contact
@ -887,4 +899,101 @@ paths:
code: 500
message: you are not loggin
results: null
/send/link:
post:
operationId: sendLink
tags:
- send
summary: Send Link
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
phone:
type: integer
example: '6289685024051'
link:
type: string
example: "https://google.com"
caption:
type: string
example: 'Halo ini contoh caption'
type:
type: string
example: 'user'
description: 'user/group | default: user'
responses:
'200':
description: OK
headers:
Date:
schema:
type: string
example: Fri, 11 Feb 2022 03:42:57 GMT
Content-Type:
schema:
type: string
example: application/json
Content-Length:
schema:
type: integer
example: '123'
content:
application/json:
schema:
type: object
example:
code: 200
message: Success
results:
status: >-
Link sent to 6289685024051@s.whatsapp.net (server timestamp: 2022-05-17 14:39:29 +0700 WIB)
'400':
description: Bad Request
headers:
Date:
schema:
type: string
example: Fri, 11 Feb 2022 03:02:17 GMT
Content-Type:
schema:
type: string
example: application/json
Content-Length:
schema:
type: integer
example: '70'
content:
application/json:
schema:
type: object
example:
code: 400
message: 'phone: cannot be blank.'
results: null
'500':
description: Internal Server Error
headers:
Date:
schema:
type: string
example: Fri, 11 Feb 2022 03:02:48 GMT
Content-Type:
schema:
type: string
example: application/json
Content-Length:
schema:
type: integer
example: '58'
content:
application/json:
schema:
type: object
example:
code: 500
message: you are not loggin
results: null

2
readme.md

@ -1,4 +1,5 @@
## 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 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)
@ -101,6 +102,7 @@ You can check [docs/openapi.yml](./docs/openapi.yaml) for detail API
| ✅ | Send File | POST | /send/file |
| ✅ | Send Video | POST | /send/video |
| ✅ | Send Contact | POST | /send/contact |
| ✅ | Send Link | POST | /send/link |
```
✅ = Available

2
src/config/settings.go

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

1
src/domains/send/contact.go

@ -8,5 +8,6 @@ type ContactRequest struct {
}
type ContactResponse struct {
MessageID string `json:"message_id"`
Status string `json:"status"`
}

1
src/domains/send/file.go

@ -9,5 +9,6 @@ type FileRequest struct {
}
type FileResponse struct {
MessageID string `json:"message_id"`
Status string `json:"status"`
}

1
src/domains/send/image.go

@ -12,5 +12,6 @@ type ImageRequest struct {
}
type ImageResponse struct {
MessageID string `json:"message_id"`
Status string `json:"status"`
}

13
src/domains/send/link.go

@ -0,0 +1,13 @@
package send
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 {
MessageID string `json:"message_id"`
Status string `json:"status"`
}

1
src/domains/send/send.go

@ -15,4 +15,5 @@ type ISendService interface {
SendFile(ctx context.Context, request FileRequest) (response FileResponse, err error)
SendVideo(ctx context.Context, request VideoRequest) (response VideoResponse, err error)
SendContact(ctx context.Context, request ContactRequest) (response ContactResponse, err error)
SendLink(ctx context.Context, request LinkRequest) (response LinkResponse, err error)
}

1
src/domains/send/text.go

@ -7,5 +7,6 @@ type MessageRequest struct {
}
type MessageResponse struct {
MessageID string `json:"message_id"`
Status string `json:"status"`
}

1
src/domains/send/video.go

@ -12,5 +12,6 @@ type VideoRequest struct {
}
type VideoResponse struct {
MessageID string `json:"message_id"`
Status string `json:"status"`
}

25
src/internal/rest/send_controller.go

@ -18,6 +18,7 @@ func InitRestSend(app *fiber.App, service domainSend.ISendService) Send {
app.Post("/send/file", rest.SendFile)
app.Post("/send/video", rest.SendVideo)
app.Post("/send/contact", rest.SendContact)
app.Post("/send/link", rest.SendLink)
return rest
}
@ -158,3 +159,27 @@ func (controller *Send) SendContact(c *fiber.Ctx) error {
Results: response,
})
}
func (controller *Send) SendLink(c *fiber.Ctx) error {
var request domainSend.LinkRequest
err := c.BodyParser(&request)
utils.PanicIfNeeded(err)
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)
return c.JSON(utils.ResponseData{
Code: 200,
Message: response.Status,
Results: response,
})
}

81
src/services/send_service.go

@ -35,13 +35,16 @@ func (service serviceSend) SendText(ctx context.Context, request domainSend.Mess
if !ok {
return response, errors.New("invalid JID " + request.Phone)
}
msgId := whatsmeow.GenerateMessageID()
msg := &waProto.Message{Conversation: proto.String(request.Message)}
ts, err := service.WaCli.SendMessage(ctx, recipient, "", msg)
ts, err := service.WaCli.SendMessage(ctx, recipient, msgId, msg)
if err != nil {
return response, err
} else {
response.Status = fmt.Sprintf("Message sent to %s (server timestamp: %s)", request.Phone, ts)
}
response.MessageID = msgId
response.Status = fmt.Sprintf("Message sent to %s (server timestamp: %s)", request.Phone, ts)
return response, nil
}
@ -111,6 +114,7 @@ func (service serviceSend) SendImage(ctx context.Context, request domainSend.Ima
}
dataWaThumbnail, err := os.ReadFile(imageThumbnail)
msgId := whatsmeow.GenerateMessageID()
msg := &waProto.Message{ImageMessage: &waProto.ImageMessage{
JpegThumbnail: dataWaThumbnail,
Caption: proto.String(dataWaCaption),
@ -123,7 +127,7 @@ func (service serviceSend) SendImage(ctx context.Context, request domainSend.Ima
FileLength: proto.Uint64(uint64(len(dataWaImage))),
ViewOnce: proto.Bool(request.ViewOnce),
}}
ts, err := service.WaCli.SendMessage(ctx, dataWaRecipient, "", msg)
ts, err := service.WaCli.SendMessage(ctx, dataWaRecipient, msgId, msg)
go func() {
errDelete := utils.RemoveFile(0, deletedItems...)
if errDelete != nil {
@ -132,10 +136,11 @@ func (service serviceSend) SendImage(ctx context.Context, request domainSend.Ima
}()
if err != nil {
return response, err
} else {
}
response.MessageID = msgId
response.Status = fmt.Sprintf("Message sent to %s (server timestamp: %s)", request.Phone, ts)
return response, nil
}
}
func (service serviceSend) SendFile(ctx context.Context, request domainSend.FileRequest) (response domainSend.FileResponse, err error) {
@ -162,6 +167,7 @@ func (service serviceSend) SendFile(ctx context.Context, request domainSend.File
return response, err
}
msgId := whatsmeow.GenerateMessageID()
msg := &waProto.Message{DocumentMessage: &waProto.DocumentMessage{
Url: proto.String(uploadedFile.URL),
Mimetype: proto.String(http.DetectContentType(dataWaFile)),
@ -173,7 +179,7 @@ func (service serviceSend) SendFile(ctx context.Context, request domainSend.File
FileEncSha256: uploadedFile.FileEncSHA256,
DirectPath: proto.String(uploadedFile.DirectPath),
}}
ts, err := service.WaCli.SendMessage(ctx, dataWaRecipient, "", msg)
ts, err := service.WaCli.SendMessage(ctx, dataWaRecipient, msgId, msg)
go func() {
errDelete := utils.RemoveFile(0, oriFilePath)
if errDelete != nil {
@ -182,10 +188,11 @@ func (service serviceSend) SendFile(ctx context.Context, request domainSend.File
}()
if err != nil {
return response, err
} else {
}
response.MessageID = msgId
response.Status = fmt.Sprintf("Document sent to %s (server timestamp: %s)", request.Phone, ts)
return response, nil
}
}
func (service serviceSend) SendVideo(ctx context.Context, request domainSend.VideoRequest) (response domainSend.VideoResponse, err error) {
@ -250,7 +257,7 @@ func (service serviceSend) SendVideo(ctx context.Context, request domainSend.Vid
if err != nil {
return response, err
}
uploadedFile, err := service.WaCli.Upload(context.Background(), dataWaVideo, whatsmeow.MediaVideo)
uploaded, err := service.WaCli.Upload(context.Background(), dataWaVideo, whatsmeow.MediaVideo)
if err != nil {
fmt.Printf("Failed to upload file: %v", err)
return response, err
@ -260,19 +267,20 @@ func (service serviceSend) SendVideo(ctx context.Context, request domainSend.Vid
return response, err
}
msgId := whatsmeow.GenerateMessageID()
msg := &waProto.Message{VideoMessage: &waProto.VideoMessage{
Url: proto.String(uploadedFile.URL),
Url: proto.String(uploaded.URL),
Mimetype: proto.String(http.DetectContentType(dataWaVideo)),
Caption: proto.String(request.Caption),
FileLength: proto.Uint64(uploadedFile.FileLength),
FileSha256: uploadedFile.FileSHA256,
FileEncSha256: uploadedFile.FileEncSHA256,
MediaKey: uploadedFile.MediaKey,
DirectPath: proto.String(uploadedFile.DirectPath),
FileLength: proto.Uint64(uploaded.FileLength),
FileSha256: uploaded.FileSHA256,
FileEncSha256: uploaded.FileEncSHA256,
MediaKey: uploaded.MediaKey,
DirectPath: proto.String(uploaded.DirectPath),
ViewOnce: proto.Bool(request.ViewOnce),
JpegThumbnail: dataWaThumbnail,
}}
ts, err := service.WaCli.SendMessage(ctx, dataWaRecipient, "", msg)
ts, err := service.WaCli.SendMessage(ctx, dataWaRecipient, msgId, msg)
go func() {
errDelete := utils.RemoveFile(0, deletedItems...)
if errDelete != nil {
@ -281,10 +289,11 @@ func (service serviceSend) SendVideo(ctx context.Context, request domainSend.Vid
}()
if err != nil {
return response, err
} else {
}
response.MessageID = msgId
response.Status = fmt.Sprintf("Video sent to %s (server timestamp: %s)", request.Phone, ts)
return response, nil
}
}
func (service serviceSend) SendContact(ctx context.Context, request domainSend.ContactRequest) (response domainSend.ContactResponse, err error) {
@ -296,6 +305,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)
msgId := whatsmeow.GenerateMessageID()
msg := &waProto.Message{ContactMessage: &waProto.ContactMessage{
DisplayName: proto.String(request.ContactName),
Vcard: proto.String(msgVCard),
@ -303,8 +313,39 @@ func (service serviceSend) SendContact(ctx context.Context, request domainSend.C
ts, err := service.WaCli.SendMessage(ctx, recipient, "", msg)
if err != nil {
return response, err
} else {
}
response.MessageID = msgId
response.Status = fmt.Sprintf("Contact sent to %s (server timestamp: %s)", request.Phone, ts)
return response, nil
}
func (service serviceSend) SendLink(ctx context.Context, request domainSend.LinkRequest) (response domainSend.LinkResponse, err error) {
utils.MustLogin(service.WaCli)
recipient, ok := utils.ParseJID(request.Phone)
if !ok {
return response, errors.New("invalid JID " + request.Phone)
}
msgId := whatsmeow.GenerateMessageID()
msg := &waProto.Message{ExtendedTextMessage: &waProto.ExtendedTextMessage{
Text: proto.String(request.Caption),
MatchedText: proto.String(request.Caption),
CanonicalUrl: proto.String(request.Link),
ContextInfo: &waProto.ContextInfo{
ActionLink: &waProto.ActionLink{
Url: proto.String(request.Link),
ButtonTitle: proto.String(request.Caption),
},
},
}}
ts, err := service.WaCli.SendMessage(ctx, recipient, msgId, msg)
if err != nil {
return response, err
}
response.MessageID = msgId
response.Status = fmt.Sprintf("Link sent to %s (server timestamp: %s)", request.Phone, ts)
return response, nil
}

16
src/validations/send_validation.go

@ -113,3 +113,19 @@ 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),
)
if err != nil {
return utils.ValidationError{
Message: err.Error(),
}
}
return nil
}
Loading…
Cancel
Save