@kanaraa/baileys 3.3.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/Utils/link-preview.js +99 -61
- package/package.json +3 -7
|
@@ -1,84 +1,122 @@
|
|
|
1
1
|
import { prepareWAMessageMedia } from './messages.js';
|
|
2
2
|
import { extractImageThumb, getHttpStream } from './messages-media.js';
|
|
3
|
+
|
|
3
4
|
const THUMBNAIL_WIDTH_PX = 192;
|
|
4
|
-
|
|
5
|
+
|
|
6
|
+
const htmlDecode = (s) => s
|
|
7
|
+
.replace(/&#x([0-9a-f]+);/gi, (_, hex) => String.fromCharCode(parseInt(hex, 16)))
|
|
8
|
+
.replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(Number(dec)))
|
|
9
|
+
.replace(/"/g, '"')
|
|
10
|
+
.replace(/'/g, "'")
|
|
11
|
+
.replace(/</g, '<')
|
|
12
|
+
.replace(/>/g, '>')
|
|
13
|
+
.replace(/&/g, '&');
|
|
14
|
+
|
|
15
|
+
const getMetaContent = (html, keys) => {
|
|
16
|
+
const pattern = new RegExp(`<meta[^>]+(?:property|name)=["'](?:${keys.join('|')})["'][^>]*content=["']([^"']*)["']`, 'i');
|
|
17
|
+
const match = html.match(pattern);
|
|
18
|
+
if (match) return htmlDecode(match[1]);
|
|
19
|
+
const reversed = new RegExp(`<meta[^>]+content=["']([^"']*)["'][^>]*(?:property|name)=["'](?:${keys.join('|')})["']`, 'i');
|
|
20
|
+
const alt = html.match(reversed);
|
|
21
|
+
if (alt) return htmlDecode(alt[1]);
|
|
22
|
+
return null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const getTitle = (html) => {
|
|
26
|
+
const meta = getMetaContent(html, ['og:title', 'twitter:title']);
|
|
27
|
+
if (meta) return meta;
|
|
28
|
+
const match = html.match(/<title[^>]*>([^<]*)<\/title>/i);
|
|
29
|
+
return match ? htmlDecode(match[1].trim()) : null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const getFavicon = (html, baseUrl) => {
|
|
33
|
+
const match = html.match(/<link[^>]+href=["']([^"']+)["'][^>]*rel=["'](?:shortcut\s+)?icon["']/i) ||
|
|
34
|
+
html.match(/<link[^>]+rel=["'](?:shortcut\s+)?icon["'][^>]*href=["']([^"']+)["']/i);
|
|
35
|
+
const href = match ? match[1] || match[2] : null;
|
|
36
|
+
try {
|
|
37
|
+
return new URL(href || '/favicon.ico', baseUrl).href;
|
|
38
|
+
} catch {
|
|
39
|
+
return href || `${baseUrl}/favicon.ico`;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
5
43
|
const getCompressedJpegThumbnail = async (url, { thumbnailWidth, fetchOpts }) => {
|
|
6
44
|
const stream = await getHttpStream(url, fetchOpts);
|
|
7
45
|
const result = await extractImageThumb(stream, thumbnailWidth);
|
|
8
46
|
return result;
|
|
9
47
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
48
|
+
|
|
49
|
+
const fetchPage = async (url, fetchOpts = {}) => {
|
|
50
|
+
const controller = new AbortController();
|
|
51
|
+
const timer = setTimeout(() => controller.abort(), fetchOpts.timeout || 3000);
|
|
52
|
+
try {
|
|
53
|
+
const response = await fetch(url, {
|
|
54
|
+
method: 'GET',
|
|
55
|
+
redirect: 'follow',
|
|
56
|
+
signal: controller.signal,
|
|
57
|
+
headers: {
|
|
58
|
+
'user-agent': 'WhatsApp/2.24.6.77',
|
|
59
|
+
accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
|
60
|
+
...(fetchOpts.headers || {})
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
throw new Error(`HTTP ${response.status}`);
|
|
65
|
+
}
|
|
66
|
+
const html = await response.text();
|
|
67
|
+
return {
|
|
68
|
+
html,
|
|
69
|
+
finalUrl: response.url || url
|
|
70
|
+
};
|
|
71
|
+
} finally {
|
|
72
|
+
clearTimeout(timer);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
16
76
|
export const getUrlInfo = async (text, opts = {
|
|
17
77
|
thumbnailWidth: THUMBNAIL_WIDTH_PX,
|
|
18
78
|
fetchOpts: { timeout: 3000 }
|
|
19
79
|
}) => {
|
|
20
80
|
try {
|
|
21
|
-
// retries
|
|
22
|
-
let retries = 0;
|
|
23
|
-
const maxRetry = 5;
|
|
24
|
-
const { getLinkPreview } = await import('link-preview-js');
|
|
25
81
|
let previewLink = text;
|
|
26
|
-
if (!
|
|
82
|
+
if (!previewLink.startsWith('https://') && !previewLink.startsWith('http://')) {
|
|
27
83
|
previewLink = 'https://' + previewLink;
|
|
28
84
|
}
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
description: info.description,
|
|
57
|
-
originalThumbnailUrl: image
|
|
58
|
-
};
|
|
59
|
-
if (opts.uploadImage) {
|
|
60
|
-
const { imageMessage } = await prepareWAMessageMedia({ image: { url: image } }, {
|
|
61
|
-
upload: opts.uploadImage,
|
|
62
|
-
mediaTypeOverride: 'thumbnail-link',
|
|
63
|
-
options: opts.fetchOpts
|
|
64
|
-
});
|
|
65
|
-
urlInfo.jpegThumbnail = imageMessage?.jpegThumbnail ? Buffer.from(imageMessage.jpegThumbnail) : undefined;
|
|
66
|
-
urlInfo.highQualityThumbnail = imageMessage || undefined;
|
|
85
|
+
const { html, finalUrl } = await fetchPage(previewLink, opts.fetchOpts);
|
|
86
|
+
const title = getTitle(html);
|
|
87
|
+
if (!title) {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
const description = getMetaContent(html, ['og:description', 'twitter:description', 'description']);
|
|
91
|
+
const image = getMetaContent(html, ['og:image', 'twitter:image']);
|
|
92
|
+
const favicon = getFavicon(html, finalUrl);
|
|
93
|
+
const urlInfo = {
|
|
94
|
+
'canonical-url': finalUrl,
|
|
95
|
+
'matched-text': text,
|
|
96
|
+
title,
|
|
97
|
+
description,
|
|
98
|
+
originalThumbnailUrl: image || favicon
|
|
99
|
+
};
|
|
100
|
+
if (opts.uploadImage) {
|
|
101
|
+
const { imageMessage } = await prepareWAMessageMedia({ image: { url: urlInfo.originalThumbnailUrl } }, {
|
|
102
|
+
upload: opts.uploadImage,
|
|
103
|
+
mediaTypeOverride: 'thumbnail-link',
|
|
104
|
+
options: opts.fetchOpts
|
|
105
|
+
});
|
|
106
|
+
urlInfo.jpegThumbnail = imageMessage?.jpegThumbnail ? Buffer.from(imageMessage.jpegThumbnail) : undefined;
|
|
107
|
+
urlInfo.highQualityThumbnail = imageMessage || undefined;
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
try {
|
|
111
|
+
urlInfo.jpegThumbnail = (await getCompressedJpegThumbnail(urlInfo.originalThumbnailUrl, opts)).buffer;
|
|
67
112
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
urlInfo.jpegThumbnail = image ? (await getCompressedJpegThumbnail(image, opts)).buffer : undefined;
|
|
71
|
-
}
|
|
72
|
-
catch (error) {
|
|
73
|
-
opts.logger?.debug({ err: error.stack, url: previewLink }, 'error in generating thumbnail');
|
|
74
|
-
}
|
|
113
|
+
catch (error) {
|
|
114
|
+
opts.logger?.debug({ err: error.stack, url: previewLink }, 'error in generating thumbnail');
|
|
75
115
|
}
|
|
76
|
-
return urlInfo;
|
|
77
116
|
}
|
|
117
|
+
return urlInfo;
|
|
78
118
|
}
|
|
79
119
|
catch (error) {
|
|
80
|
-
|
|
81
|
-
throw error;
|
|
82
|
-
}
|
|
120
|
+
opts.logger?.debug({ err: error.stack, url: text }, 'error in generating link preview');
|
|
83
121
|
}
|
|
84
122
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kanaraa/baileys",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.4.0",
|
|
5
5
|
"description": "Modded Baileys v7, Rebuilt on top of official @whiskeysockets/baileys 7.0.0-rc13, with the interactive & rich-message content types (buttons, lists, carousel, cards, shop/collection, native flow, AI rich response, sticker packs, admin invite, payments, etc.).",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"whatsapp",
|
|
@@ -43,10 +43,9 @@
|
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"adm-zip": "^0.5.10",
|
|
45
45
|
"audio-decode": "^2.1.3",
|
|
46
|
-
"fluent-ffmpeg": "^2.1.
|
|
46
|
+
"fluent-ffmpeg": "^2.1.3",
|
|
47
47
|
"jimp": "^1.6.1",
|
|
48
|
-
"
|
|
49
|
-
"sharp": "*"
|
|
48
|
+
"sharp": "^0.35.3"
|
|
50
49
|
},
|
|
51
50
|
"peerDependenciesMeta": {
|
|
52
51
|
"adm-zip": {
|
|
@@ -61,9 +60,6 @@
|
|
|
61
60
|
"jimp": {
|
|
62
61
|
"optional": true
|
|
63
62
|
},
|
|
64
|
-
"link-preview-js": {
|
|
65
|
-
"optional": true
|
|
66
|
-
},
|
|
67
63
|
"sharp": {
|
|
68
64
|
"optional": true
|
|
69
65
|
}
|