23 changed files with 620 additions and 575 deletions
-
6src/controllers/app_controller.go
-
29src/controllers/send_controller.go
-
19src/controllers/user_controller.go
-
13src/domains/app/app.go
-
6src/domains/app/login.go
-
12src/domains/send/contact.go
-
13src/domains/send/file.go
-
16src/domains/send/image.go
-
18src/domains/send/send.go
-
11src/domains/send/text.go
-
16src/domains/send/video.go
-
48src/domains/user/account.go
-
12src/domains/user/user.go
-
116src/services/app_service.go
-
118src/services/app_service_impl.go
-
310src/services/send_service.go
-
310src/services/send_service_impl.go
-
13src/services/user_service.go
-
18src/services/user_service_impl.go
-
67src/structs/send_struct.go
-
12src/validations/send_validation.go
-
6src/validations/send_validation_test.go
-
6src/validations/user_validation.go
@ -1,6 +1,15 @@ |
|||||
package structs |
|
||||
|
package app |
||||
|
|
||||
import "go.mau.fi/whatsmeow/types" |
|
||||
|
import ( |
||||
|
"github.com/gofiber/fiber/v2" |
||||
|
"go.mau.fi/whatsmeow/types" |
||||
|
) |
||||
|
|
||||
|
type IAppService interface { |
||||
|
Login(c *fiber.Ctx) (response LoginResponse, err error) |
||||
|
Logout(c *fiber.Ctx) (err error) |
||||
|
Reconnect(c *fiber.Ctx) (err error) |
||||
|
} |
||||
|
|
||||
type UserInfoRequest struct { |
type UserInfoRequest struct { |
||||
Phone string `json:"phone" query:"phone"` |
Phone string `json:"phone" query:"phone"` |
||||
@ -1,8 +1,6 @@ |
|||||
package structs |
|
||||
|
package app |
||||
|
|
||||
import ( |
|
||||
"time" |
|
||||
) |
|
||||
|
import "time" |
||||
|
|
||||
type LoginResponse struct { |
type LoginResponse struct { |
||||
ImagePath string `json:"image_path"` |
ImagePath string `json:"image_path"` |
||||
@ -0,0 +1,12 @@ |
|||||
|
package send |
||||
|
|
||||
|
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 { |
||||
|
Status string `json:"status"` |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
package send |
||||
|
|
||||
|
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 { |
||||
|
Status string `json:"status"` |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
package send |
||||
|
|
||||
|
import "mime/multipart" |
||||
|
|
||||
|
type ImageRequest struct { |
||||
|
Phone string `json:"phone" form:"phone"` |
||||
|
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"` |
||||
|
} |
||||
|
|
||||
|
type ImageResponse struct { |
||||
|
Status string `json:"status"` |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
package send |
||||
|
|
||||
|
import ( |
||||
|
"github.com/gofiber/fiber/v2" |
||||
|
) |
||||
|
|
||||
|
type Type string |
||||
|
|
||||
|
const TypeUser Type = "user" |
||||
|
const TypeGroup Type = "group" |
||||
|
|
||||
|
type ISendService interface { |
||||
|
SendText(c *fiber.Ctx, request MessageRequest) (response MessageResponse, err error) |
||||
|
SendImage(c *fiber.Ctx, request ImageRequest) (response ImageResponse, err error) |
||||
|
SendFile(c *fiber.Ctx, request FileRequest) (response FileResponse, err error) |
||||
|
SendVideo(c *fiber.Ctx, request VideoRequest) (response VideoResponse, err error) |
||||
|
SendContact(c *fiber.Ctx, request ContactRequest) (response ContactResponse, err error) |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
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 { |
||||
|
Status string `json:"status"` |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
package send |
||||
|
|
||||
|
import "mime/multipart" |
||||
|
|
||||
|
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"` |
||||
|
} |
||||
|
|
||||
|
type VideoResponse struct { |
||||
|
Status string `json:"status"` |
||||
|
} |
||||
@ -0,0 +1,48 @@ |
|||||
|
package user |
||||
|
|
||||
|
import "go.mau.fi/whatsmeow/types" |
||||
|
|
||||
|
type InfoRequest struct { |
||||
|
Phone string `json:"phone" query:"phone"` |
||||
|
} |
||||
|
|
||||
|
type InfoResponseDataDevice struct { |
||||
|
User string |
||||
|
Agent uint8 |
||||
|
Device string |
||||
|
Server string |
||||
|
AD bool |
||||
|
} |
||||
|
|
||||
|
type InfoResponseData struct { |
||||
|
VerifiedName string `json:"verified_name"` |
||||
|
Status string `json:"status"` |
||||
|
PictureID string `json:"picture_id"` |
||||
|
Devices []InfoResponseDataDevice `json:"devices"` |
||||
|
} |
||||
|
|
||||
|
type InfoResponse struct { |
||||
|
Data []InfoResponseData `json:"data"` |
||||
|
} |
||||
|
|
||||
|
type AvatarRequest struct { |
||||
|
Phone string `json:"phone" query:"phone"` |
||||
|
} |
||||
|
|
||||
|
type AvatarResponse struct { |
||||
|
URL string `json:"url"` |
||||
|
ID string `json:"id"` |
||||
|
Type string `json:"type"` |
||||
|
} |
||||
|
|
||||
|
type MyPrivacySettingResponse struct { |
||||
|
GroupAdd string `json:"group_add"` |
||||
|
LastSeen string `json:"last_seen"` |
||||
|
Status string `json:"status"` |
||||
|
Profile string `json:"profile"` |
||||
|
ReadReceipts string `json:"read_receipts"` |
||||
|
} |
||||
|
|
||||
|
type MyListGroupsResponse struct { |
||||
|
Data []types.GroupInfo `json:"data"` |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
package user |
||||
|
|
||||
|
import ( |
||||
|
"github.com/gofiber/fiber/v2" |
||||
|
) |
||||
|
|
||||
|
type IUserService interface { |
||||
|
Info(c *fiber.Ctx, request InfoRequest) (response InfoResponse, err error) |
||||
|
Avatar(c *fiber.Ctx, request AvatarRequest) (response AvatarResponse, err error) |
||||
|
MyListGroups(c *fiber.Ctx) (response MyListGroupsResponse, err error) |
||||
|
MyPrivacySetting(c *fiber.Ctx) (response MyPrivacySettingResponse, err error) |
||||
|
} |
||||
@ -1,12 +1,118 @@ |
|||||
package services |
package services |
||||
|
|
||||
import ( |
import ( |
||||
"github.com/aldinokemal/go-whatsapp-web-multidevice/structs" |
|
||||
|
"context" |
||||
|
"errors" |
||||
|
"fmt" |
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/config" |
||||
|
domainApp "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/app" |
||||
"github.com/gofiber/fiber/v2" |
"github.com/gofiber/fiber/v2" |
||||
|
fiberUtils "github.com/gofiber/fiber/v2/utils" |
||||
|
"github.com/skip2/go-qrcode" |
||||
|
"go.mau.fi/whatsmeow" |
||||
|
"os" |
||||
|
"path/filepath" |
||||
|
"time" |
||||
) |
) |
||||
|
|
||||
type AppService interface { |
|
||||
Login(c *fiber.Ctx) (response structs.LoginResponse, err error) |
|
||||
Logout(c *fiber.Ctx) (err error) |
|
||||
Reconnect(c *fiber.Ctx) (err error) |
|
||||
|
type serviceApp struct { |
||||
|
WaCli *whatsmeow.Client |
||||
|
} |
||||
|
|
||||
|
func NewAppService(waCli *whatsmeow.Client) domainApp.IAppService { |
||||
|
return &serviceApp{ |
||||
|
WaCli: waCli, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (service serviceApp) Login(c *fiber.Ctx) (response domainApp.LoginResponse, err error) { |
||||
|
if service.WaCli == nil { |
||||
|
return response, errors.New("wa cli nil cok") |
||||
|
} |
||||
|
|
||||
|
// Disconnect for reconnecting
|
||||
|
service.WaCli.Disconnect() |
||||
|
|
||||
|
chImage := make(chan string) |
||||
|
|
||||
|
ch, err := service.WaCli.GetQRChannel(context.Background()) |
||||
|
if err != nil { |
||||
|
// This error means that we're already logged in, so ignore it.
|
||||
|
if errors.Is(err, whatsmeow.ErrQRStoreContainsID) { |
||||
|
_ = service.WaCli.Connect() // just connect to websocket
|
||||
|
if service.WaCli.IsLoggedIn() { |
||||
|
return response, errors.New("you already logged in :)") |
||||
|
} |
||||
|
return response, errors.New("your session have been saved, please wait to connect 2 second and refresh again") |
||||
|
} else { |
||||
|
return response, errors.New("Error when GetQRChannel:" + err.Error()) |
||||
|
} |
||||
|
} else { |
||||
|
go func() { |
||||
|
for evt := range ch { |
||||
|
response.Code = evt.Code |
||||
|
response.Duration = evt.Timeout / time.Second / 2 |
||||
|
if evt.Event == "code" { |
||||
|
qrPath := fmt.Sprintf("%s/scan-qr-%s.png", config.PathQrCode, fiberUtils.UUIDv4()) |
||||
|
err = qrcode.WriteFile(evt.Code, qrcode.Medium, 512, qrPath) |
||||
|
if err != nil { |
||||
|
fmt.Println("error when write qrImage file", err.Error()) |
||||
|
} |
||||
|
go func() { |
||||
|
time.Sleep(response.Duration * time.Second) |
||||
|
err := os.Remove(qrPath) |
||||
|
if err != nil { |
||||
|
fmt.Println("Failed to remove qrPath " + qrPath) |
||||
|
} |
||||
|
}() |
||||
|
chImage <- qrPath |
||||
|
} else { |
||||
|
fmt.Printf("QR channel result: %s", evt.Event) |
||||
|
} |
||||
|
} |
||||
|
}() |
||||
|
} |
||||
|
|
||||
|
err = service.WaCli.Connect() |
||||
|
if err != nil { |
||||
|
return response, errors.New("Failed to connect bro " + err.Error()) |
||||
|
} |
||||
|
response.ImagePath = <-chImage |
||||
|
|
||||
|
return response, nil |
||||
|
} |
||||
|
|
||||
|
func (service serviceApp) Logout(c *fiber.Ctx) (err error) { |
||||
|
// delete history
|
||||
|
files, err := filepath.Glob("./history-*") |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
|
||||
|
for _, f := range files { |
||||
|
err = os.Remove(f) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
} |
||||
|
// delete qr images
|
||||
|
qrImages, err := filepath.Glob("./statics/images/qrcode/scan-*") |
||||
|
if err != nil { |
||||
|
panic(err) |
||||
|
} |
||||
|
|
||||
|
for _, f := range qrImages { |
||||
|
err = os.Remove(f) |
||||
|
if err != nil { |
||||
|
return err |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
err = service.WaCli.Logout() |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func (service serviceApp) Reconnect(c *fiber.Ctx) (err error) { |
||||
|
service.WaCli.Disconnect() |
||||
|
return service.WaCli.Connect() |
||||
} |
} |
||||
@ -1,118 +0,0 @@ |
|||||
package services |
|
||||
|
|
||||
import ( |
|
||||
"context" |
|
||||
"errors" |
|
||||
"fmt" |
|
||||
"github.com/aldinokemal/go-whatsapp-web-multidevice/config" |
|
||||
"github.com/aldinokemal/go-whatsapp-web-multidevice/structs" |
|
||||
"github.com/gofiber/fiber/v2" |
|
||||
fiberutils "github.com/gofiber/fiber/v2/utils" |
|
||||
"github.com/skip2/go-qrcode" |
|
||||
"go.mau.fi/whatsmeow" |
|
||||
"os" |
|
||||
"path/filepath" |
|
||||
"time" |
|
||||
) |
|
||||
|
|
||||
type AppServiceImpl struct { |
|
||||
WaCli *whatsmeow.Client |
|
||||
} |
|
||||
|
|
||||
func NewAppService(waCli *whatsmeow.Client) AppService { |
|
||||
return &AppServiceImpl{ |
|
||||
WaCli: waCli, |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (service AppServiceImpl) Login(c *fiber.Ctx) (response structs.LoginResponse, err error) { |
|
||||
if service.WaCli == nil { |
|
||||
return response, errors.New("wa cli nil cok") |
|
||||
} |
|
||||
|
|
||||
// Disconnect for reconnecting
|
|
||||
service.WaCli.Disconnect() |
|
||||
|
|
||||
chImage := make(chan string) |
|
||||
|
|
||||
ch, err := service.WaCli.GetQRChannel(context.Background()) |
|
||||
if err != nil { |
|
||||
// This error means that we're already logged in, so ignore it.
|
|
||||
if errors.Is(err, whatsmeow.ErrQRStoreContainsID) { |
|
||||
_ = service.WaCli.Connect() // just connect to websocket
|
|
||||
if service.WaCli.IsLoggedIn() { |
|
||||
return response, errors.New("you already logged in :)") |
|
||||
} |
|
||||
return response, errors.New("your session have been saved, please wait to connect 2 second and refresh again") |
|
||||
} else { |
|
||||
return response, errors.New("Error when GetQRChannel:" + err.Error()) |
|
||||
} |
|
||||
} else { |
|
||||
go func() { |
|
||||
for evt := range ch { |
|
||||
response.Code = evt.Code |
|
||||
response.Duration = evt.Timeout / time.Second / 2 |
|
||||
if evt.Event == "code" { |
|
||||
qrPath := fmt.Sprintf("%s/scan-qr-%s.png", config.PathQrCode, fiberutils.UUIDv4()) |
|
||||
err = qrcode.WriteFile(evt.Code, qrcode.Medium, 512, qrPath) |
|
||||
if err != nil { |
|
||||
fmt.Println("error when write qrImage file", err.Error()) |
|
||||
} |
|
||||
go func() { |
|
||||
time.Sleep(response.Duration * time.Second) |
|
||||
err := os.Remove(qrPath) |
|
||||
if err != nil { |
|
||||
fmt.Println("Failed to remove qrPath " + qrPath) |
|
||||
} |
|
||||
}() |
|
||||
chImage <- qrPath |
|
||||
} else { |
|
||||
fmt.Printf("QR channel result: %s", evt.Event) |
|
||||
} |
|
||||
} |
|
||||
}() |
|
||||
} |
|
||||
|
|
||||
err = service.WaCli.Connect() |
|
||||
if err != nil { |
|
||||
return response, errors.New("Failed to connect bro " + err.Error()) |
|
||||
} |
|
||||
response.ImagePath = <-chImage |
|
||||
|
|
||||
return response, nil |
|
||||
} |
|
||||
|
|
||||
func (service AppServiceImpl) Logout(c *fiber.Ctx) (err error) { |
|
||||
// delete history
|
|
||||
files, err := filepath.Glob("./history-*") |
|
||||
if err != nil { |
|
||||
panic(err) |
|
||||
} |
|
||||
|
|
||||
for _, f := range files { |
|
||||
err = os.Remove(f) |
|
||||
if err != nil { |
|
||||
return err |
|
||||
} |
|
||||
} |
|
||||
// delete qr images
|
|
||||
qrImages, err := filepath.Glob("./statics/images/qrcode/scan-*") |
|
||||
if err != nil { |
|
||||
panic(err) |
|
||||
} |
|
||||
|
|
||||
for _, f := range qrImages { |
|
||||
err = os.Remove(f) |
|
||||
if err != nil { |
|
||||
return err |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
err = service.WaCli.Logout() |
|
||||
return |
|
||||
} |
|
||||
|
|
||||
func (service AppServiceImpl) Reconnect(c *fiber.Ctx) (err error) { |
|
||||
service.WaCli.Disconnect() |
|
||||
return service.WaCli.Connect() |
|
||||
} |
|
||||
@ -1,14 +1,310 @@ |
|||||
package services |
package services |
||||
|
|
||||
import ( |
import ( |
||||
"github.com/aldinokemal/go-whatsapp-web-multidevice/structs" |
|
||||
|
"context" |
||||
|
"errors" |
||||
|
"fmt" |
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/config" |
||||
|
domainSend "github.com/aldinokemal/go-whatsapp-web-multidevice/domains/send" |
||||
|
"github.com/aldinokemal/go-whatsapp-web-multidevice/utils" |
||||
"github.com/gofiber/fiber/v2" |
"github.com/gofiber/fiber/v2" |
||||
|
fiberUtils "github.com/gofiber/fiber/v2/utils" |
||||
|
"github.com/h2non/bimg" |
||||
|
"go.mau.fi/whatsmeow" |
||||
|
waProto "go.mau.fi/whatsmeow/binary/proto" |
||||
|
"google.golang.org/protobuf/proto" |
||||
|
"net/http" |
||||
|
"os" |
||||
|
"os/exec" |
||||
) |
) |
||||
|
|
||||
type SendService interface { |
|
||||
SendText(c *fiber.Ctx, request structs.SendMessageRequest) (response structs.SendMessageResponse, err error) |
|
||||
SendImage(c *fiber.Ctx, request structs.SendImageRequest) (response structs.SendImageResponse, err error) |
|
||||
SendFile(c *fiber.Ctx, request structs.SendFileRequest) (response structs.SendFileResponse, err error) |
|
||||
SendVideo(c *fiber.Ctx, request structs.SendVideoRequest) (response structs.SendVideoResponse, err error) |
|
||||
SendContact(c *fiber.Ctx, request structs.SendContactRequest) (response structs.SendContactResponse, err error) |
|
||||
|
type serviceSend struct { |
||||
|
WaCli *whatsmeow.Client |
||||
|
} |
||||
|
|
||||
|
func NewSendService(waCli *whatsmeow.Client) domainSend.ISendService { |
||||
|
return &serviceSend{ |
||||
|
WaCli: waCli, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (service serviceSend) SendText(c *fiber.Ctx, request domainSend.MessageRequest) (response domainSend.MessageResponse, err error) { |
||||
|
utils.MustLogin(service.WaCli) |
||||
|
|
||||
|
recipient, ok := utils.ParseJID(request.Phone) |
||||
|
if !ok { |
||||
|
return response, errors.New("invalid JID " + request.Phone) |
||||
|
} |
||||
|
msg := &waProto.Message{Conversation: proto.String(request.Message)} |
||||
|
ts, err := service.WaCli.SendMessage(c.Context(), recipient, "", msg) |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} else { |
||||
|
response.Status = fmt.Sprintf("Message sent to %s (server timestamp: %s)", request.Phone, ts) |
||||
|
} |
||||
|
return response, nil |
||||
|
} |
||||
|
|
||||
|
func (service serviceSend) SendImage(c *fiber.Ctx, request domainSend.ImageRequest) (response domainSend.ImageResponse, err error) { |
||||
|
utils.MustLogin(service.WaCli) |
||||
|
|
||||
|
var ( |
||||
|
imagePath string |
||||
|
imageThumbnail string |
||||
|
deletedItems []string |
||||
|
) |
||||
|
|
||||
|
// Save image to server
|
||||
|
oriImagePath := fmt.Sprintf("%s/%s", config.PathSendItems, request.Image.Filename) |
||||
|
err = c.SaveFile(request.Image, oriImagePath) |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} |
||||
|
deletedItems = append(deletedItems, oriImagePath) |
||||
|
|
||||
|
// Generate thumbnail with smalled image
|
||||
|
openThumbnailBuffer, err := bimg.Read(oriImagePath) |
||||
|
imageThumbnail = fmt.Sprintf("%s/thumbnails-%s", config.PathSendItems, request.Image.Filename) |
||||
|
thumbnailImage, err := bimg.NewImage(openThumbnailBuffer).Process(bimg.Options{Quality: 90, Width: 100, Embed: true}) |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} |
||||
|
err = bimg.Write(imageThumbnail, thumbnailImage) |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} |
||||
|
deletedItems = append(deletedItems, imageThumbnail) |
||||
|
|
||||
|
if request.Compress { |
||||
|
// Resize image
|
||||
|
openImageBuffer, err := bimg.Read(oriImagePath) |
||||
|
newImage, err := bimg.NewImage(openImageBuffer).Process(bimg.Options{Quality: 90, Width: 600, Embed: true}) |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} |
||||
|
|
||||
|
newImagePath := fmt.Sprintf("%s/new-%s", config.PathSendItems, request.Image.Filename) |
||||
|
err = bimg.Write(newImagePath, newImage) |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} |
||||
|
deletedItems = append(deletedItems, newImagePath) |
||||
|
imagePath = newImagePath |
||||
|
} else { |
||||
|
imagePath = oriImagePath |
||||
|
} |
||||
|
|
||||
|
// Send to WA server
|
||||
|
dataWaCaption := request.Caption |
||||
|
dataWaRecipient, ok := utils.ParseJID(request.Phone) |
||||
|
if !ok { |
||||
|
return response, errors.New("invalid JID " + request.Phone) |
||||
|
} |
||||
|
dataWaImage, err := os.ReadFile(imagePath) |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} |
||||
|
uploadedImage, err := service.WaCli.Upload(context.Background(), dataWaImage, whatsmeow.MediaImage) |
||||
|
if err != nil { |
||||
|
fmt.Printf("Failed to upload file: %v", err) |
||||
|
return response, err |
||||
|
} |
||||
|
dataWaThumbnail, err := os.ReadFile(imageThumbnail) |
||||
|
|
||||
|
msg := &waProto.Message{ImageMessage: &waProto.ImageMessage{ |
||||
|
JpegThumbnail: dataWaThumbnail, |
||||
|
Caption: proto.String(dataWaCaption), |
||||
|
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, |
||||
|
FileLength: proto.Uint64(uint64(len(dataWaImage))), |
||||
|
ViewOnce: proto.Bool(request.ViewOnce), |
||||
|
}} |
||||
|
ts, err := service.WaCli.SendMessage(c.Context(), dataWaRecipient, "", msg) |
||||
|
go func() { |
||||
|
errDelete := utils.RemoveFile(0, deletedItems...) |
||||
|
if errDelete != nil { |
||||
|
fmt.Println("error when deleting picture: ", errDelete) |
||||
|
} |
||||
|
}() |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} else { |
||||
|
response.Status = fmt.Sprintf("Message sent to %s (server timestamp: %s)", request.Phone, ts) |
||||
|
return response, nil |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (service serviceSend) SendFile(c *fiber.Ctx, request domainSend.FileRequest) (response domainSend.FileResponse, err error) { |
||||
|
utils.MustLogin(service.WaCli) |
||||
|
|
||||
|
oriFilePath := fmt.Sprintf("%s/%s", config.PathSendItems, request.File.Filename) |
||||
|
err = c.SaveFile(request.File, oriFilePath) |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} |
||||
|
|
||||
|
// Send to WA server
|
||||
|
dataWaRecipient, ok := utils.ParseJID(request.Phone) |
||||
|
if !ok { |
||||
|
return response, errors.New("invalid JID " + request.Phone) |
||||
|
} |
||||
|
dataWaFile, err := os.ReadFile(oriFilePath) |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} |
||||
|
uploadedFile, err := service.WaCli.Upload(context.Background(), dataWaFile, whatsmeow.MediaDocument) |
||||
|
if err != nil { |
||||
|
fmt.Printf("Failed to upload file: %v", err) |
||||
|
return response, err |
||||
|
} |
||||
|
|
||||
|
msg := &waProto.Message{DocumentMessage: &waProto.DocumentMessage{ |
||||
|
Url: proto.String(uploadedFile.URL), |
||||
|
Mimetype: proto.String(http.DetectContentType(dataWaFile)), |
||||
|
Title: proto.String(request.File.Filename), |
||||
|
FileSha256: uploadedFile.FileSHA256, |
||||
|
FileLength: proto.Uint64(uploadedFile.FileLength), |
||||
|
MediaKey: uploadedFile.MediaKey, |
||||
|
FileName: proto.String(request.File.Filename), |
||||
|
FileEncSha256: uploadedFile.FileEncSHA256, |
||||
|
DirectPath: proto.String(uploadedFile.DirectPath), |
||||
|
}} |
||||
|
ts, err := service.WaCli.SendMessage(c.Context(), dataWaRecipient, "", msg) |
||||
|
go func() { |
||||
|
errDelete := utils.RemoveFile(0, oriFilePath) |
||||
|
if errDelete != nil { |
||||
|
fmt.Println(errDelete) |
||||
|
} |
||||
|
}() |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} else { |
||||
|
response.Status = fmt.Sprintf("Document sent to %s (server timestamp: %s)", request.Phone, ts) |
||||
|
return response, nil |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (service serviceSend) SendVideo(c *fiber.Ctx, request domainSend.VideoRequest) (response domainSend.VideoResponse, err error) { |
||||
|
utils.MustLogin(service.WaCli) |
||||
|
|
||||
|
var ( |
||||
|
videoPath string |
||||
|
videoThumbnail string |
||||
|
deletedItems []string |
||||
|
) |
||||
|
|
||||
|
generateUUID := fiberUtils.UUIDv4() |
||||
|
// Save video to server
|
||||
|
oriVideoPath := fmt.Sprintf("%s/%s", config.PathSendItems, generateUUID+request.Video.Filename) |
||||
|
err = c.SaveFile(request.Video, oriVideoPath) |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} |
||||
|
|
||||
|
// Get thumbnail video with ffmpeg
|
||||
|
thumbnailVideoPath := fmt.Sprintf("%s/%s", config.PathSendItems, generateUUID+".png") |
||||
|
cmdThumbnail := exec.Command("ffmpeg", "-i", oriVideoPath, "-ss", "00:00:01.000", "-vframes", "1", thumbnailVideoPath) |
||||
|
err = cmdThumbnail.Run() |
||||
|
utils.PanicIfNeeded(err, "error when getting thumbnail") |
||||
|
|
||||
|
// Resize Thumbnail
|
||||
|
openImageBuffer, err := bimg.Read(thumbnailVideoPath) |
||||
|
resize, err := bimg.NewImage(openImageBuffer).Process(bimg.Options{Quality: 90, Width: 600, Embed: true}) |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} |
||||
|
thumbnailResizeVideoPath := fmt.Sprintf("%s/%s", config.PathSendItems, generateUUID+"_resize.png") |
||||
|
err = bimg.Write(thumbnailResizeVideoPath, resize) |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} |
||||
|
|
||||
|
deletedItems = append(deletedItems, thumbnailVideoPath) |
||||
|
deletedItems = append(deletedItems, thumbnailResizeVideoPath) |
||||
|
videoThumbnail = thumbnailResizeVideoPath |
||||
|
|
||||
|
if request.Compress { |
||||
|
compresVideoPath := fmt.Sprintf("%s/%s", config.PathSendItems, generateUUID+".mp4") |
||||
|
// Compress video with ffmpeg
|
||||
|
cmdCompress := exec.Command("ffmpeg", "-i", oriVideoPath, "-strict", "-2", compresVideoPath) |
||||
|
err = cmdCompress.Run() |
||||
|
utils.PanicIfNeeded(err, "error when compress video") |
||||
|
|
||||
|
videoPath = compresVideoPath |
||||
|
deletedItems = append(deletedItems, compresVideoPath) |
||||
|
} else { |
||||
|
videoPath = oriVideoPath |
||||
|
deletedItems = append(deletedItems, oriVideoPath) |
||||
|
} |
||||
|
|
||||
|
//Send to WA server
|
||||
|
dataWaRecipient, ok := utils.ParseJID(request.Phone) |
||||
|
if !ok { |
||||
|
return response, errors.New("invalid JID " + request.Phone) |
||||
|
} |
||||
|
dataWaVideo, err := os.ReadFile(videoPath) |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} |
||||
|
uploadedFile, err := service.WaCli.Upload(context.Background(), dataWaVideo, whatsmeow.MediaVideo) |
||||
|
if err != nil { |
||||
|
fmt.Printf("Failed to upload file: %v", err) |
||||
|
return response, err |
||||
|
} |
||||
|
dataWaThumbnail, err := os.ReadFile(videoThumbnail) |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} |
||||
|
|
||||
|
msg := &waProto.Message{VideoMessage: &waProto.VideoMessage{ |
||||
|
Url: proto.String(uploadedFile.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), |
||||
|
ViewOnce: proto.Bool(request.ViewOnce), |
||||
|
JpegThumbnail: dataWaThumbnail, |
||||
|
}} |
||||
|
ts, err := service.WaCli.SendMessage(c.Context(), dataWaRecipient, "", msg) |
||||
|
go func() { |
||||
|
errDelete := utils.RemoveFile(0, deletedItems...) |
||||
|
if errDelete != nil { |
||||
|
fmt.Println(errDelete) |
||||
|
} |
||||
|
}() |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} else { |
||||
|
response.Status = fmt.Sprintf("Video sent to %s (server timestamp: %s)", request.Phone, ts) |
||||
|
return response, nil |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (service serviceSend) SendContact(c *fiber.Ctx, request domainSend.ContactRequest) (response domainSend.ContactResponse, err error) { |
||||
|
utils.MustLogin(service.WaCli) |
||||
|
|
||||
|
recipient, ok := utils.ParseJID(request.Phone) |
||||
|
if !ok { |
||||
|
return response, errors.New("invalid JID " + request.Phone) |
||||
|
} |
||||
|
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{ |
||||
|
DisplayName: proto.String(request.ContactName), |
||||
|
Vcard: proto.String(msgVCard), |
||||
|
}} |
||||
|
ts, err := service.WaCli.SendMessage(c.Context(), recipient, "", msg) |
||||
|
if err != nil { |
||||
|
return response, err |
||||
|
} else { |
||||
|
response.Status = fmt.Sprintf("Contact sent to %s (server timestamp: %s)", request.Phone, ts) |
||||
|
} |
||||
|
return response, nil |
||||
} |
} |
||||
@ -1,310 +0,0 @@ |
|||||
package services |
|
||||
|
|
||||
import ( |
|
||||
"context" |
|
||||
"errors" |
|
||||
"fmt" |
|
||||
"github.com/aldinokemal/go-whatsapp-web-multidevice/config" |
|
||||
"github.com/aldinokemal/go-whatsapp-web-multidevice/structs" |
|
||||
"github.com/aldinokemal/go-whatsapp-web-multidevice/utils" |
|
||||
"github.com/gofiber/fiber/v2" |
|
||||
fiberUtils "github.com/gofiber/fiber/v2/utils" |
|
||||
"github.com/h2non/bimg" |
|
||||
"go.mau.fi/whatsmeow" |
|
||||
waProto "go.mau.fi/whatsmeow/binary/proto" |
|
||||
"google.golang.org/protobuf/proto" |
|
||||
"net/http" |
|
||||
"os" |
|
||||
"os/exec" |
|
||||
) |
|
||||
|
|
||||
type SendServiceImpl struct { |
|
||||
WaCli *whatsmeow.Client |
|
||||
} |
|
||||
|
|
||||
func NewSendService(waCli *whatsmeow.Client) SendService { |
|
||||
return &SendServiceImpl{ |
|
||||
WaCli: waCli, |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (service SendServiceImpl) SendText(c *fiber.Ctx, request structs.SendMessageRequest) (response structs.SendMessageResponse, err error) { |
|
||||
utils.MustLogin(service.WaCli) |
|
||||
|
|
||||
recipient, ok := utils.ParseJID(request.Phone) |
|
||||
if !ok { |
|
||||
return response, errors.New("invalid JID " + request.Phone) |
|
||||
} |
|
||||
msg := &waProto.Message{Conversation: proto.String(request.Message)} |
|
||||
ts, err := service.WaCli.SendMessage(c.Context(), recipient, "", msg) |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} else { |
|
||||
response.Status = fmt.Sprintf("Message sent to %s (server timestamp: %s)", request.Phone, ts) |
|
||||
} |
|
||||
return response, nil |
|
||||
} |
|
||||
|
|
||||
func (service SendServiceImpl) SendImage(c *fiber.Ctx, request structs.SendImageRequest) (response structs.SendImageResponse, err error) { |
|
||||
utils.MustLogin(service.WaCli) |
|
||||
|
|
||||
var ( |
|
||||
imagePath string |
|
||||
imageThumbnail string |
|
||||
deletedItems []string |
|
||||
) |
|
||||
|
|
||||
// Save image to server
|
|
||||
oriImagePath := fmt.Sprintf("%s/%s", config.PathSendItems, request.Image.Filename) |
|
||||
err = c.SaveFile(request.Image, oriImagePath) |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} |
|
||||
deletedItems = append(deletedItems, oriImagePath) |
|
||||
|
|
||||
// Generate thumbnail with smalled image
|
|
||||
openThumbnailBuffer, err := bimg.Read(oriImagePath) |
|
||||
imageThumbnail = fmt.Sprintf("%s/thumbnails-%s", config.PathSendItems, request.Image.Filename) |
|
||||
thumbnailImage, err := bimg.NewImage(openThumbnailBuffer).Process(bimg.Options{Quality: 90, Width: 100, Embed: true}) |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} |
|
||||
err = bimg.Write(imageThumbnail, thumbnailImage) |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} |
|
||||
deletedItems = append(deletedItems, imageThumbnail) |
|
||||
|
|
||||
if request.Compress { |
|
||||
// Resize image
|
|
||||
openImageBuffer, err := bimg.Read(oriImagePath) |
|
||||
newImage, err := bimg.NewImage(openImageBuffer).Process(bimg.Options{Quality: 90, Width: 600, Embed: true}) |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} |
|
||||
|
|
||||
newImagePath := fmt.Sprintf("%s/new-%s", config.PathSendItems, request.Image.Filename) |
|
||||
err = bimg.Write(newImagePath, newImage) |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} |
|
||||
deletedItems = append(deletedItems, newImagePath) |
|
||||
imagePath = newImagePath |
|
||||
} else { |
|
||||
imagePath = oriImagePath |
|
||||
} |
|
||||
|
|
||||
// Send to WA server
|
|
||||
dataWaCaption := request.Caption |
|
||||
dataWaRecipient, ok := utils.ParseJID(request.Phone) |
|
||||
if !ok { |
|
||||
return response, errors.New("invalid JID " + request.Phone) |
|
||||
} |
|
||||
dataWaImage, err := os.ReadFile(imagePath) |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} |
|
||||
uploadedImage, err := service.WaCli.Upload(context.Background(), dataWaImage, whatsmeow.MediaImage) |
|
||||
if err != nil { |
|
||||
fmt.Printf("Failed to upload file: %v", err) |
|
||||
return response, err |
|
||||
} |
|
||||
dataWaThumbnail, err := os.ReadFile(imageThumbnail) |
|
||||
|
|
||||
msg := &waProto.Message{ImageMessage: &waProto.ImageMessage{ |
|
||||
JpegThumbnail: dataWaThumbnail, |
|
||||
Caption: proto.String(dataWaCaption), |
|
||||
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, |
|
||||
FileLength: proto.Uint64(uint64(len(dataWaImage))), |
|
||||
ViewOnce: proto.Bool(request.ViewOnce), |
|
||||
}} |
|
||||
ts, err := service.WaCli.SendMessage(c.Context(), dataWaRecipient, "", msg) |
|
||||
go func() { |
|
||||
errDelete := utils.RemoveFile(0, deletedItems...) |
|
||||
if errDelete != nil { |
|
||||
fmt.Println("error when deleting picture: ", errDelete) |
|
||||
} |
|
||||
}() |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} else { |
|
||||
response.Status = fmt.Sprintf("Message sent to %s (server timestamp: %s)", request.Phone, ts) |
|
||||
return response, nil |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (service SendServiceImpl) SendFile(c *fiber.Ctx, request structs.SendFileRequest) (response structs.SendFileResponse, err error) { |
|
||||
utils.MustLogin(service.WaCli) |
|
||||
|
|
||||
oriFilePath := fmt.Sprintf("%s/%s", config.PathSendItems, request.File.Filename) |
|
||||
err = c.SaveFile(request.File, oriFilePath) |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} |
|
||||
|
|
||||
// Send to WA server
|
|
||||
dataWaRecipient, ok := utils.ParseJID(request.Phone) |
|
||||
if !ok { |
|
||||
return response, errors.New("invalid JID " + request.Phone) |
|
||||
} |
|
||||
dataWaFile, err := os.ReadFile(oriFilePath) |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} |
|
||||
uploadedFile, err := service.WaCli.Upload(context.Background(), dataWaFile, whatsmeow.MediaDocument) |
|
||||
if err != nil { |
|
||||
fmt.Printf("Failed to upload file: %v", err) |
|
||||
return response, err |
|
||||
} |
|
||||
|
|
||||
msg := &waProto.Message{DocumentMessage: &waProto.DocumentMessage{ |
|
||||
Url: proto.String(uploadedFile.URL), |
|
||||
Mimetype: proto.String(http.DetectContentType(dataWaFile)), |
|
||||
Title: proto.String(request.File.Filename), |
|
||||
FileSha256: uploadedFile.FileSHA256, |
|
||||
FileLength: proto.Uint64(uploadedFile.FileLength), |
|
||||
MediaKey: uploadedFile.MediaKey, |
|
||||
FileName: proto.String(request.File.Filename), |
|
||||
FileEncSha256: uploadedFile.FileEncSHA256, |
|
||||
DirectPath: proto.String(uploadedFile.DirectPath), |
|
||||
}} |
|
||||
ts, err := service.WaCli.SendMessage(c.Context(), dataWaRecipient, "", msg) |
|
||||
go func() { |
|
||||
errDelete := utils.RemoveFile(0, oriFilePath) |
|
||||
if errDelete != nil { |
|
||||
fmt.Println(errDelete) |
|
||||
} |
|
||||
}() |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} else { |
|
||||
response.Status = fmt.Sprintf("Document sent to %s (server timestamp: %s)", request.Phone, ts) |
|
||||
return response, nil |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (service SendServiceImpl) SendVideo(c *fiber.Ctx, request structs.SendVideoRequest) (response structs.SendVideoResponse, err error) { |
|
||||
utils.MustLogin(service.WaCli) |
|
||||
|
|
||||
var ( |
|
||||
videoPath string |
|
||||
videoThumbnail string |
|
||||
deletedItems []string |
|
||||
) |
|
||||
|
|
||||
generateUUID := fiberUtils.UUIDv4() |
|
||||
// Save video to server
|
|
||||
oriVideoPath := fmt.Sprintf("%s/%s", config.PathSendItems, generateUUID+request.Video.Filename) |
|
||||
err = c.SaveFile(request.Video, oriVideoPath) |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} |
|
||||
|
|
||||
// Get thumbnail video with ffmpeg
|
|
||||
thumbnailVideoPath := fmt.Sprintf("%s/%s", config.PathSendItems, generateUUID+".png") |
|
||||
cmdThumbnail := exec.Command("ffmpeg", "-i", oriVideoPath, "-ss", "00:00:01.000", "-vframes", "1", thumbnailVideoPath) |
|
||||
err = cmdThumbnail.Run() |
|
||||
utils.PanicIfNeeded(err, "error when getting thumbnail") |
|
||||
|
|
||||
// Resize Thumbnail
|
|
||||
openImageBuffer, err := bimg.Read(thumbnailVideoPath) |
|
||||
resize, err := bimg.NewImage(openImageBuffer).Process(bimg.Options{Quality: 90, Width: 600, Embed: true}) |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} |
|
||||
thumbnailResizeVideoPath := fmt.Sprintf("%s/%s", config.PathSendItems, generateUUID+"_resize.png") |
|
||||
err = bimg.Write(thumbnailResizeVideoPath, resize) |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} |
|
||||
|
|
||||
deletedItems = append(deletedItems, thumbnailVideoPath) |
|
||||
deletedItems = append(deletedItems, thumbnailResizeVideoPath) |
|
||||
videoThumbnail = thumbnailResizeVideoPath |
|
||||
|
|
||||
if request.Compress { |
|
||||
compresVideoPath := fmt.Sprintf("%s/%s", config.PathSendItems, generateUUID+".mp4") |
|
||||
// Compress video with ffmpeg
|
|
||||
cmdCompress := exec.Command("ffmpeg", "-i", oriVideoPath, "-strict", "-2", compresVideoPath) |
|
||||
err = cmdCompress.Run() |
|
||||
utils.PanicIfNeeded(err, "error when compress video") |
|
||||
|
|
||||
videoPath = compresVideoPath |
|
||||
deletedItems = append(deletedItems, compresVideoPath) |
|
||||
} else { |
|
||||
videoPath = oriVideoPath |
|
||||
deletedItems = append(deletedItems, oriVideoPath) |
|
||||
} |
|
||||
|
|
||||
//Send to WA server
|
|
||||
dataWaRecipient, ok := utils.ParseJID(request.Phone) |
|
||||
if !ok { |
|
||||
return response, errors.New("invalid JID " + request.Phone) |
|
||||
} |
|
||||
dataWaVideo, err := os.ReadFile(videoPath) |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} |
|
||||
uploadedFile, err := service.WaCli.Upload(context.Background(), dataWaVideo, whatsmeow.MediaVideo) |
|
||||
if err != nil { |
|
||||
fmt.Printf("Failed to upload file: %v", err) |
|
||||
return response, err |
|
||||
} |
|
||||
dataWaThumbnail, err := os.ReadFile(videoThumbnail) |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} |
|
||||
|
|
||||
msg := &waProto.Message{VideoMessage: &waProto.VideoMessage{ |
|
||||
Url: proto.String(uploadedFile.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), |
|
||||
ViewOnce: proto.Bool(request.ViewOnce), |
|
||||
JpegThumbnail: dataWaThumbnail, |
|
||||
}} |
|
||||
ts, err := service.WaCli.SendMessage(c.Context(), dataWaRecipient, "", msg) |
|
||||
go func() { |
|
||||
errDelete := utils.RemoveFile(0, deletedItems...) |
|
||||
if errDelete != nil { |
|
||||
fmt.Println(errDelete) |
|
||||
} |
|
||||
}() |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} else { |
|
||||
response.Status = fmt.Sprintf("Video sent to %s (server timestamp: %s)", request.Phone, ts) |
|
||||
return response, nil |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
func (service SendServiceImpl) SendContact(c *fiber.Ctx, request structs.SendContactRequest) (response structs.SendContactResponse, err error) { |
|
||||
utils.MustLogin(service.WaCli) |
|
||||
|
|
||||
recipient, ok := utils.ParseJID(request.Phone) |
|
||||
if !ok { |
|
||||
return response, errors.New("invalid JID " + request.Phone) |
|
||||
} |
|
||||
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{ |
|
||||
DisplayName: proto.String(request.ContactName), |
|
||||
Vcard: proto.String(msgVCard), |
|
||||
}} |
|
||||
ts, err := service.WaCli.SendMessage(c.Context(), recipient, "", msg) |
|
||||
if err != nil { |
|
||||
return response, err |
|
||||
} else { |
|
||||
response.Status = fmt.Sprintf("Contact sent to %s (server timestamp: %s)", request.Phone, ts) |
|
||||
} |
|
||||
return response, nil |
|
||||
} |
|
||||
@ -1,13 +0,0 @@ |
|||||
package services |
|
||||
|
|
||||
import ( |
|
||||
"github.com/aldinokemal/go-whatsapp-web-multidevice/structs" |
|
||||
"github.com/gofiber/fiber/v2" |
|
||||
) |
|
||||
|
|
||||
type UserService interface { |
|
||||
UserInfo(c *fiber.Ctx, request structs.UserInfoRequest) (response structs.UserInfoResponse, err error) |
|
||||
UserAvatar(c *fiber.Ctx, request structs.UserAvatarRequest) (response structs.UserAvatarResponse, err error) |
|
||||
UserMyListGroups(c *fiber.Ctx) (response structs.UserMyListGroupsResponse, err error) |
|
||||
UserMyPrivacySetting(c *fiber.Ctx) (response structs.UserMyPrivacySettingResponse, err error) |
|
||||
} |
|
||||
@ -1,67 +0,0 @@ |
|||||
package structs |
|
||||
|
|
||||
import ( |
|
||||
"mime/multipart" |
|
||||
) |
|
||||
|
|
||||
type SendType string |
|
||||
|
|
||||
const TypeUser SendType = "user" |
|
||||
const TypeGroup SendType = "group" |
|
||||
|
|
||||
type SendMessageRequest struct { |
|
||||
Phone string `json:"phone" form:"phone"` |
|
||||
Message string `json:"message" form:"message"` |
|
||||
Type SendType `json:"type" form:"type"` |
|
||||
} |
|
||||
|
|
||||
type SendMessageResponse struct { |
|
||||
Status string `json:"status"` |
|
||||
} |
|
||||
|
|
||||
type SendImageRequest struct { |
|
||||
Phone string `json:"phone" form:"phone"` |
|
||||
Caption string `json:"caption" form:"caption"` |
|
||||
Image *multipart.FileHeader `json:"image" form:"image"` |
|
||||
ViewOnce bool `json:"view_once" form:"view_once"` |
|
||||
Type SendType `json:"type" form:"type"` |
|
||||
Compress bool `json:"compress"` |
|
||||
} |
|
||||
|
|
||||
type SendImageResponse struct { |
|
||||
Status string `json:"status"` |
|
||||
} |
|
||||
|
|
||||
type SendFileRequest struct { |
|
||||
Phone string `json:"phone" form:"phone"` |
|
||||
File *multipart.FileHeader `json:"file" form:"file"` |
|
||||
Type SendType `json:"type" form:"type"` |
|
||||
} |
|
||||
|
|
||||
type SendFileResponse struct { |
|
||||
Status string `json:"status"` |
|
||||
} |
|
||||
|
|
||||
type SendVideoRequest struct { |
|
||||
Phone string `json:"phone" form:"phone"` |
|
||||
Caption string `json:"caption" form:"caption"` |
|
||||
Video *multipart.FileHeader `json:"video" form:"video"` |
|
||||
Type SendType `json:"type" form:"type"` |
|
||||
ViewOnce bool `json:"view_once" form:"view_once"` |
|
||||
Compress bool `json:"compress"` |
|
||||
} |
|
||||
|
|
||||
type SendVideoResponse struct { |
|
||||
Status string `json:"status"` |
|
||||
} |
|
||||
|
|
||||
type SendContactRequest struct { |
|
||||
Phone string `json:"phone" form:"phone"` |
|
||||
ContactName string `json:"contact_name" form:"contact_name"` |
|
||||
ContactPhone string `json:"contact_phone" form:"contact_phone"` |
|
||||
Type SendType `json:"type" form:"type"` |
|
||||
} |
|
||||
|
|
||||
type SendContactResponse struct { |
|
||||
Status string `json:"status"` |
|
||||
} |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue