diff --git a/src/go.mod b/src/go.mod index ac19598..b3053eb 100644 --- a/src/go.mod +++ b/src/go.mod @@ -21,6 +21,7 @@ require ( github.com/valyala/fasthttp v1.59.0 go.mau.fi/libsignal v0.1.2 go.mau.fi/whatsmeow v0.0.0-20250305175604-af3dc0346412 + golang.org/x/image v0.25.0 google.golang.org/protobuf v1.36.5 ) @@ -60,7 +61,6 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.36.0 // indirect golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 // indirect - golang.org/x/image v0.25.0 // indirect golang.org/x/net v0.37.0 // indirect golang.org/x/sys v0.31.0 // indirect golang.org/x/text v0.23.0 // indirect diff --git a/src/go.sum b/src/go.sum index f98a419..ce51e55 100644 --- a/src/go.sum +++ b/src/go.sum @@ -126,8 +126,6 @@ go.mau.fi/libsignal v0.1.2 h1:Vs16DXWxSKyzVtI+EEXLCSy5pVWzzCzp/2eqFGvLyP0= go.mau.fi/libsignal v0.1.2/go.mod h1:JpnLSSJptn/s1sv7I56uEMywvz8x4YzxeF5OzdPb6PE= go.mau.fi/util v0.8.5 h1:PwCAAtcfK0XxZ4sdErJyfBMkTEWoQU33aB7QqDDzQRI= go.mau.fi/util v0.8.5/go.mod h1:Ycug9mrbztlahHPEJ6H5r8Nu/xqZaWbE5vPHVWmfz6M= -go.mau.fi/whatsmeow v0.0.0-20250225112721-b7530f3a5056 h1:1JQUOpYXhFSEQgXMEWD/ZH38FrIe5i1yjxSBwa0aN/Q= -go.mau.fi/whatsmeow v0.0.0-20250225112721-b7530f3a5056/go.mod h1:6hRrUtDWI2wTRClOd6m17GwrFE2a8/p5R4pjJsIVn+U= go.mau.fi/whatsmeow v0.0.0-20250305175604-af3dc0346412 h1:AM+t3vKEho3zTDOW2g6KvxB7iGNPwp0SFZpmx4slVVU= go.mau.fi/whatsmeow v0.0.0-20250305175604-af3dc0346412/go.mod h1:6hRrUtDWI2wTRClOd6m17GwrFE2a8/p5R4pjJsIVn+U= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -138,19 +136,11 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= -golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4= -golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= -golang.org/x/exp v0.0.0-20250228200357-dead58393ab7 h1:aWwlzYV971S4BXRS9AmqwDLAD85ouC6X+pocatKY58c= -golang.org/x/exp v0.0.0-20250228200357-dead58393ab7/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ= -golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8= golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -167,10 +157,6 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= -golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= -golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -194,8 +180,6 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= @@ -216,8 +200,6 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/src/pkg/utils/general.go b/src/pkg/utils/general.go index 6f8bfba..30747a4 100644 --- a/src/pkg/utils/general.go +++ b/src/pkg/utils/general.go @@ -4,14 +4,12 @@ import ( "bytes" "fmt" "image" - "image/color" - "image/draw" - _ "image/gif" // Register GIF format - "image/jpeg" // For JPEG encoding - "image/png" // For PNG encoding + _ "image/gif" // Register GIF format + _ "image/jpeg" // For JPEG encoding + _ "image/png" // For PNG encoding "io" - "log" "net/http" + "net/url" "os" "path/filepath" "regexp" @@ -21,6 +19,8 @@ import ( "github.com/PuerkitoBio/goquery" "github.com/aldinokemal/go-whatsapp-web-multidevice/config" + "github.com/sirupsen/logrus" + _ "golang.org/x/image/webp" // Register WebP format ) // RemoveFile is removing file with delay @@ -80,14 +80,35 @@ type Metadata struct { Width *uint32 } -func GetMetaDataFromURL(url string) (meta Metadata, err error) { +func GetMetaDataFromURL(urlStr string) (meta Metadata, err error) { + // Create HTTP client with timeout + client := &http.Client{ + Timeout: 15 * time.Second, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + if len(via) >= 10 { + return fmt.Errorf("too many redirects") + } + return nil + }, + } + + // Parse the base URL for resolving relative URLs later + baseURL, err := url.Parse(urlStr) + if err != nil { + return meta, fmt.Errorf("invalid URL: %v", err) + } + // Send an HTTP GET request to the website - response, err := http.Get(url) + response, err := client.Get(urlStr) if err != nil { return meta, err } defer response.Body.Close() + if response.StatusCode != http.StatusOK { + return meta, fmt.Errorf("HTTP request failed with status: %s", response.Status) + } + // Parse the HTML document document, err := goquery.NewDocumentFromReader(response.Body) if err != nil { @@ -98,159 +119,93 @@ func GetMetaDataFromURL(url string) (meta Metadata, err error) { meta.Description, _ = element.Attr("content") }) - // find title - document.Find("title").Each(func(index int, element *goquery.Selection) { - meta.Title = element.Text() + // find title - try multiple sources + // First try og:title + document.Find("meta[property='og:title']").Each(func(index int, element *goquery.Selection) { + if content, exists := element.Attr("content"); exists && content != "" { + meta.Title = content + } }) + // If og:title not found, try regular title tag + if meta.Title == "" { + document.Find("title").Each(func(index int, element *goquery.Selection) { + meta.Title = element.Text() + }) + } + // Try to find image URL from various sources + // First try og:image document.Find("meta[property='og:image']").Each(func(index int, element *goquery.Selection) { - meta.Image, _ = element.Attr("content") + if content, exists := element.Attr("content"); exists && content != "" { + meta.Image = content + } }) - // If an og:image is found, download it and store its content in ImageThumb + // If og:image not found, try twitter:image + if meta.Image == "" { + document.Find("meta[name='twitter:image']").Each(func(index int, element *goquery.Selection) { + if content, exists := element.Attr("content"); exists && content != "" { + meta.Image = content + } + }) + } + + // If an image URL is found, resolve it if it's relative if meta.Image != "" { - imageResponse, err := http.Get(meta.Image) + imgURL, err := url.Parse(meta.Image) if err != nil { - log.Printf("Failed to download image: %v", err) + logrus.Warnf("Invalid image URL: %v", err) } else { - defer imageResponse.Body.Close() - - // Read image data - imageData, err := io.ReadAll(imageResponse.Body) - if err != nil { - log.Printf("Failed to read image data: %v", err) - } else { - meta.ImageThumb = imageData - - // Get image dimensions from the actual image rather than OG tags - imageReader := bytes.NewReader(imageData) - img, imgFormat, err := image.Decode(imageReader) - if err == nil { - bounds := img.Bounds() - width := uint32(bounds.Max.X - bounds.Min.X) - height := uint32(bounds.Max.Y - bounds.Min.Y) - - // Check if image has transparency (alpha channel) - hasTransparency := false - - // Check for transparency by examining image type and pixels - switch v := img.(type) { - case *image.NRGBA: - // NRGBA format - check alpha values - for y := bounds.Min.Y; y < bounds.Max.Y && !hasTransparency; y++ { - for x := bounds.Min.X; x < bounds.Max.X; x++ { - _, _, _, a := v.At(x, y).RGBA() - if a < 0xffff { - hasTransparency = true - break - } - } - } - case *image.RGBA: - // RGBA format - check alpha values - for y := bounds.Min.Y; y < bounds.Max.Y && !hasTransparency; y++ { - for x := bounds.Min.X; x < bounds.Max.X; x++ { - _, _, _, a := v.At(x, y).RGBA() - if a < 0xffff { - hasTransparency = true - break - } - } - } - case *image.NRGBA64: - // NRGBA64 format - check alpha values - for y := bounds.Min.Y; y < bounds.Max.Y && !hasTransparency; y++ { - for x := bounds.Min.X; x < bounds.Max.X; x++ { - _, _, _, a := v.At(x, y).RGBA() - if a < 0xffff { - hasTransparency = true - break - } - } - } - default: - // For other formats, check if the format typically supports transparency - hasTransparency = imgFormat == "png" || imgFormat == "gif" - } + // Resolve relative URLs against the base URL + meta.Image = baseURL.ResolveReference(imgURL).String() + } - // If image has transparency, create a new image with white background - if hasTransparency { - log.Printf("Image has transparency, setting white background") + // Download the image + imgResponse, err := client.Get(meta.Image) + if err != nil { + logrus.Warnf("Failed to download image: %v", err) + } else { + defer imgResponse.Body.Close() - // Create a new RGBA image with white background - newImg := image.NewRGBA(bounds) - draw.Draw(newImg, bounds, image.NewUniform(color.White), image.Point{}, draw.Src) + if imgResponse.StatusCode != http.StatusOK { + logrus.Warnf("Image download failed with status: %s", imgResponse.Status) + } else { + // Check content type + contentType := imgResponse.Header.Get("Content-Type") + if !strings.HasPrefix(contentType, "image/") { + logrus.Warnf("URL returned non-image content type: %s", contentType) + } else { + // Read image data with size limit + imageData, err := io.ReadAll(io.LimitReader(imgResponse.Body, int64(config.WhatsappSettingMaxImageSize))) + if err != nil { + logrus.Warnf("Failed to read image data: %v", err) + } else if len(imageData) == 0 { + logrus.Warn("Downloaded image data is empty") + } else { + meta.ImageThumb = imageData - // Draw the original image on top of the white background - draw.Draw(newImg, bounds, img, bounds.Min, draw.Over) + // Validate image by decoding it + imageReader := bytes.NewReader(imageData) + img, _, err := image.Decode(imageReader) + if err != nil { + logrus.Warnf("Failed to decode image: %v", err) + } else { + bounds := img.Bounds() + width := uint32(bounds.Max.X - bounds.Min.X) + height := uint32(bounds.Max.Y - bounds.Min.Y) - // Convert the new image back to bytes - var buf bytes.Buffer - switch imgFormat { - case "png": - if err := png.Encode(&buf, newImg); err == nil { - meta.ImageThumb = buf.Bytes() + // Check if image is square (1:1 ratio) + if width == height && width <= 200 { + // For small square images, leave width and height as nil + meta.Width = nil + meta.Height = nil } else { - log.Printf("Failed to encode PNG image: %v", err) + meta.Width = &width + meta.Height = &height } - case "jpeg", "jpg": - if err := jpeg.Encode(&buf, newImg, nil); err == nil { - meta.ImageThumb = buf.Bytes() - } else { - log.Printf("Failed to encode JPEG image: %v", err) - } - case "gif": - // Note: Simple conversion to PNG for GIF with transparency - if err := png.Encode(&buf, newImg); err == nil { - meta.ImageThumb = buf.Bytes() - } else { - log.Printf("Failed to encode GIF as PNG: %v", err) - } - default: - // For other formats, try PNG - if err := png.Encode(&buf, newImg); err == nil { - meta.ImageThumb = buf.Bytes() - } else { - log.Printf("Failed to encode image as PNG: %v", err) - } - } - } - // Check if image is square (1:1 ratio) - if width == height && width <= 200 { - // For 1:1 ratio, leave width and height as nil - meta.Width = nil - meta.Height = nil - } else { - meta.Width = &width - meta.Height = &height - } - - log.Printf("Image dimensions: %dx%d", width, height) - } else { - log.Printf("Failed to decode image to get dimensions: %v", err) - - // Fallback to OG tags if image decoding fails - document.Find("meta[property='og:image:width']").Each(func(index int, element *goquery.Selection) { - if content, exists := element.Attr("content"); exists { - width, _ := strconv.Atoi(content) - widthUint32 := uint32(width) - meta.Width = &widthUint32 + logrus.Debugf("Image dimensions: %dx%d", width, height) } - }) - - document.Find("meta[property='og:image:height']").Each(func(index int, element *goquery.Selection) { - if content, exists := element.Attr("content"); exists { - height, _ := strconv.Atoi(content) - heightUint32 := uint32(height) - meta.Height = &heightUint32 - } - }) - - // Check if the OG tags indicate a 1:1 ratio - if meta.Width != nil && meta.Height != nil && *meta.Width == *meta.Height { - meta.Width = nil - meta.Height = nil } } } diff --git a/src/services/send.go b/src/services/send.go index c16cc1f..bdfdd10 100644 --- a/src/services/send.go +++ b/src/services/send.go @@ -430,38 +430,38 @@ func (service serviceSend) SendLink(ctx context.Context, request domainSend.Link return response, err } - getMetaDataFromURL, err := utils.GetMetaDataFromURL(request.Link) + metadata, err := utils.GetMetaDataFromURL(request.Link) if err != nil { return response, err } // Log image dimensions if available, otherwise note it's a square image or dimensions not available - if getMetaDataFromURL.Width != nil && getMetaDataFromURL.Height != nil { - fmt.Printf("Image dimensions: %dx%d\n", *getMetaDataFromURL.Width, *getMetaDataFromURL.Height) + if metadata.Width != nil && metadata.Height != nil { + logrus.Debugf("Image dimensions: %dx%d", *metadata.Width, *metadata.Height) } else { - fmt.Println("Image dimensions: Square image or dimensions not available") + logrus.Debugf("Image dimensions: Square image or dimensions not available") } // Create the message msg := &waE2E.Message{ExtendedTextMessage: &waE2E.ExtendedTextMessage{ Text: proto.String(fmt.Sprintf("%s\n%s", request.Caption, request.Link)), - Title: proto.String(getMetaDataFromURL.Title), + Title: proto.String(metadata.Title), MatchedText: proto.String(request.Link), - Description: proto.String(getMetaDataFromURL.Description), - JPEGThumbnail: getMetaDataFromURL.ImageThumb, + Description: proto.String(metadata.Description), + JPEGThumbnail: metadata.ImageThumb, }} // If we have a thumbnail image, upload it to WhatsApp's servers - if len(getMetaDataFromURL.ImageThumb) > 0 && getMetaDataFromURL.Height != nil && getMetaDataFromURL.Width != nil { - uploadedThumb, err := service.uploadMedia(ctx, whatsmeow.MediaImage, getMetaDataFromURL.ImageThumb, dataWaRecipient) + if len(metadata.ImageThumb) > 0 && metadata.Height != nil && metadata.Width != nil { + uploadedThumb, err := service.uploadMedia(ctx, whatsmeow.MediaLinkThumbnail, metadata.ImageThumb, dataWaRecipient) if err == nil { // Update the message with the uploaded thumbnail information msg.ExtendedTextMessage.ThumbnailDirectPath = proto.String(uploadedThumb.DirectPath) msg.ExtendedTextMessage.ThumbnailSHA256 = uploadedThumb.FileSHA256 msg.ExtendedTextMessage.ThumbnailEncSHA256 = uploadedThumb.FileEncSHA256 msg.ExtendedTextMessage.MediaKey = uploadedThumb.MediaKey - msg.ExtendedTextMessage.ThumbnailHeight = getMetaDataFromURL.Height - msg.ExtendedTextMessage.ThumbnailWidth = getMetaDataFromURL.Width + msg.ExtendedTextMessage.ThumbnailHeight = metadata.Height + msg.ExtendedTextMessage.ThumbnailWidth = metadata.Width } else { logrus.Warnf("Failed to upload thumbnail: %v, continue without uploaded thumbnail", err) } diff --git a/src/views/components/SendLink.js b/src/views/components/SendLink.js new file mode 100644 index 0000000..7b68ab4 --- /dev/null +++ b/src/views/components/SendLink.js @@ -0,0 +1,138 @@ +import FormRecipient from "./generic/FormRecipient.js"; + +export default { + name: 'SendLink', + components: { + FormRecipient + }, + data() { + return { + type: window.TYPEUSER, + phone: '', + link: '', + caption: '', + reply_message_id: '', + loading: false, + } + }, + computed: { + phone_id() { + return this.phone + this.type; + }, + }, + methods: { + openModal() { + $('#modalSendLink').modal({ + onApprove: function () { + return false; + } + }).modal('show'); + }, + isShowReplyId() { + return this.type !== window.TYPESTATUS; + }, + isValidForm() { + // Validate phone number is not empty except for status type + const isPhoneValid = this.type === window.TYPESTATUS || this.phone.trim().length > 0; + + // Validate link is not empty and has reasonable length + const isLinkValid = this.link.trim().length > 0 && this.link.length <= 4096; + + // Validate caption is not empty and has reasonable length + const isCaptionValid = this.caption.trim().length > 0 && this.caption.length <= 4096; + + return isPhoneValid && isLinkValid && isCaptionValid + }, + async handleSubmit() { + // Add validation check here to prevent submission when form is invalid + if (!this.isValidForm() || this.loading) { + return; + } + try { + const response = await this.submitApi(); + showSuccessInfo(response); + $('#modalSendLink').modal('hide'); + } catch (err) { + showErrorInfo(err); + } + }, + async submitApi() { + this.loading = true; + try { + const payload = { + phone: this.phone_id, + link: this.link.trim(), + caption: this.caption.trim(), + }; + if (this.reply_message_id !== '') { + payload.reply_message_id = this.reply_message_id; + } + + const response = await window.http.post('/send/link', payload); + this.handleReset(); + return response.data.message; + } catch (error) { + if (error.response?.data?.message) { + throw new Error(error.response.data.message); + } + throw error; + } finally { + this.loading = false; + } + }, + handleReset() { + this.phone = ''; + this.link = ''; + this.caption = ''; + this.reply_message_id = ''; + }, + }, + template: ` +
+
+ Send +
Send Link
+
+ Send link to user or group +
+
+
+ + + + ` +} \ No newline at end of file diff --git a/src/views/index.html b/src/views/index.html index ff1c486..97a2d6f 100644 --- a/src/views/index.html +++ b/src/views/index.html @@ -116,6 +116,8 @@ + +
@@ -201,6 +203,7 @@ import SendImage from "./components/SendImage.js"; import SendFile from "./components/SendFile.js"; import SendVideo from "./components/SendVideo.js"; + import SendLink from "./components/SendLink.js"; import SendContact from "./components/SendContact.js"; import SendLocation from "./components/SendLocation.js"; import SendAudio from "./components/SendAudio.js"; @@ -254,7 +257,7 @@ Vue.createApp({ components: { AppLogin, AppLoginWithCode, AppLogout, AppReconnect, - SendMessage, SendImage, SendFile, SendVideo, SendContact, SendLocation, SendAudio, SendPoll, SendPresence, + SendMessage, SendImage, SendFile, SendVideo, SendLink, SendContact, SendLocation, SendAudio, SendPoll, SendPresence, MessageDelete, MessageUpdate, MessageReact, MessageRevoke, GroupList, GroupCreate, GroupJoinWithLink, GroupAddParticipants, NewsletterList,