@soyaxell09/zenbot-scraper 1.1.3 → 1.1.4

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/README.md CHANGED
@@ -35,319 +35,7 @@ src/
35
35
  ├── tools/ → Traductor, Clima, QR, Lyrics, ATTP...
36
36
  └── nsfw/ → XNXX, PornHub, XVideos, XHamster, Rule34
37
37
  ```
38
-
39
- ---
40
-
41
- ## 🎬 Scrapers
42
-
43
- <details>
44
- <summary><b>▶ YouTube</b></summary>
45
-
46
- ```js
47
- import { ytSearch, ytDownload, ytInfo, getFileSize } from '@soyaxell09/zenbot-scraper'
48
-
49
- const results = await ytSearch('bad bunny', 5)
50
- // → [{ id, title, url, thumbnail, duration, views, channel, published }]
51
-
52
- const info = await ytInfo('https://youtu.be/dQw4w9WgXcQ')
53
- // → { id, title, uploader, views, thumb, duration, qualities: [...] }
54
-
55
- const video = await ytDownload('https://youtu.be/dQw4w9WgXcQ', 'video', '360p')
56
- const audio = await ytDownload('https://youtu.be/dQw4w9WgXcQ', 'mp3', '128k')
57
- // → { title, uploader, views, thumb, type, quality, size, sizeB, duration, url }
58
-
59
- const size = await getFileSize('https://example.com/file.mp4')
60
- // → '14.5 MB'
61
- ```
62
-
63
- </details>
64
-
65
- <details>
66
- <summary><b>▶ TikTok</b></summary>
67
-
68
- ```js
69
- import { tiktokDownload, tiktokInfo } from '@soyaxell09/zenbot-scraper'
70
-
71
- const result = await tiktokDownload('https://www.tiktok.com/@user/video/123')
72
- // → { id, title, author, thumbnail, duration, nowatermark, watermark,
73
- // audio, images, music, plays, likes, comments, shares }
74
- ```
75
-
76
- </details>
77
-
78
- <details>
79
- <summary><b>▶ Instagram</b></summary>
80
-
81
- ```js
82
- import { igDownload, igReelDownload, igStalk, igStories } from '@soyaxell09/zenbot-scraper'
83
-
84
- const post = await igDownload('https://www.instagram.com/p/ABC123/')
85
- const reel = await igReelDownload('https://www.instagram.com/reel/ABC123/')
86
- const story = await igDownload('https://www.instagram.com/stories/user/123456789/')
87
- // → { type, items: [{ type, url }] }
88
-
89
- const perfil = await igStalk('siamusic')
90
- // → { username, fullName, bio, followers, following, posts, isPrivate, isVerified, avatar, url }
91
-
92
- const stories = await igStories('siamusic')
93
- // → { username, items: [{ type, url }] }
94
- ```
95
-
96
- </details>
97
-
98
- <details>
99
- <summary><b>▶ Facebook</b></summary>
100
-
101
- ```js
102
- import { fbDownload } from '@soyaxell09/zenbot-scraper'
103
-
104
- const result = await fbDownload('https://www.facebook.com/watch?v=123')
105
- // → { videoId, title, hd, sd, thumb, source }
106
- ```
107
-
108
- </details>
109
-
110
- <details>
111
- <summary><b>▶ Twitter / X</b></summary>
112
-
113
- ```js
114
- import { tweetInfo, tweetDownload } from '@soyaxell09/zenbot-scraper'
115
-
116
- const info = await tweetInfo('https://x.com/user/status/123')
117
- // → { id, text, lang, createdAt, likes, replies, author, hashtags, mentions, medias }
118
-
119
- const media = await tweetDownload('https://x.com/user/status/123')
120
- // → { id, text, author, videos: [...], photos: [...] }
121
38
  ```
122
-
123
- </details>
124
-
125
- <details>
126
- <summary><b>▶ Threads</b></summary>
127
-
128
- ```js
129
- import { threadsDownload } from '@soyaxell09/zenbot-scraper'
130
-
131
- const result = await threadsDownload('https://www.threads.net/t/xxx')
132
- // → { title, medias: [{ type, url }] }
133
- ```
134
-
135
- </details>
136
-
137
- <details>
138
- <summary><b>▶ Spotify (SpotiDown)</b></summary>
139
-
140
- ```js
141
- import { spotidownTrack } from '@soyaxell09/zenbot-scraper'
142
-
143
- const result = await spotidownTrack('https://open.spotify.com/track/xxx')
144
- // → { name, artist, album, year, duration, cover, mp3, coverHd }
145
- ```
146
-
147
- </details>
148
-
149
- <details>
150
- <summary><b>▶ MediaFire</b></summary>
151
-
152
- ```js
153
- import { mediafireInfo } from '@soyaxell09/zenbot-scraper'
154
-
155
- const result = await mediafireInfo('https://www.mediafire.com/file/abc123/file')
156
- // → { key, name, size, download, url }
157
- ```
158
-
159
- </details>
160
-
161
- <details>
162
- <summary><b>▶ GitHub</b></summary>
163
-
164
- ```js
165
- import { githubInfo, githubRelease, githubContents, githubSearch } from '@soyaxell09/zenbot-scraper'
166
-
167
- const info = await githubInfo('facebook/react')
168
- const release = await githubRelease('facebook/react')
169
- const contents = await githubContents('facebook/react', 'src')
170
- const repos = await githubSearch('whatsapp bot', 'repositories', 5)
171
- ```
172
-
173
- </details>
174
-
175
- <details>
176
- <summary><b>▶ APK (APKPure)</b></summary>
177
-
178
- ```js
179
- import { apkSearch, apkInfo } from '@soyaxell09/zenbot-scraper'
180
-
181
- const results = await apkSearch('whatsapp', 3)
182
- // → [{ name, developer, pkg, date, icon, appUrl, dlUrl }]
183
-
184
- const info = await apkInfo('com.whatsapp')
185
- // → { name, developer, pkg, date, icon, download, dlLinks, url }
186
- ```
187
-
188
- </details>
189
-
190
- <details>
191
- <summary><b>▶ Google Drive</b></summary>
192
-
193
- ```js
194
- import { gdriveInfo, gdriveDownload } from '@soyaxell09/zenbot-scraper'
195
-
196
- const info = await gdriveInfo('https://drive.google.com/file/d/1ABC.../view')
197
- // → { fileId, name, download, url }
198
-
199
- const file = await gdriveDownload('https://drive.google.com/file/d/1ABC.../view')
200
- // → { fileId, buffer, contentType, size, url }
201
- ```
202
-
203
- </details>
204
-
205
- ---
206
-
207
- ## 🔍 Search
208
-
209
- <details>
210
- <summary><b>▶ Pinterest</b></summary>
211
-
212
- ```js
213
- import { pinsearch, pinimg, pinvid } from '@soyaxell09/zenbot-scraper'
214
-
215
- const imgs = await pinsearch('anime wallpaper', 50)
216
- // → [{ index, image, url, pinId }] — paginación infinita
217
-
218
- const pin = await pinimg('https://www.pinterest.com/pin/123/')
219
- // → { id, title, description, altText, image, images, width, height,
220
- // dominantColor, saves, repins, createdAt, tags, domain, link,
221
- // board, creator, pinner, url }
222
-
223
- const vids = await pinvid('anime', 5)
224
- ```
225
-
226
- </details>
227
-
228
- <details>
229
- <summary><b>▶ GIFs (Tenor)</b></summary>
230
-
231
- ```js
232
- import { giphy, gifNext, giphyBuffer } from '@soyaxell09/zenbot-scraper'
233
-
234
- const gifs = await giphy('funny cat', 5)
235
- // → [{ id, title, url, gif, preview, mp4, width, height }]
236
-
237
- const next = await gifNext('funny cat')
238
- // → cola paginada — cada llamada devuelve el siguiente sin repetir
239
-
240
- const buf = await giphyBuffer('funny cat')
241
- // → { buffer, mimetype, title, url }
242
- ```
243
-
244
- </details>
245
-
246
- <details>
247
- <summary><b>▶ Spotify Search</b></summary>
248
-
249
- ```js
250
- import { spotify } from '@soyaxell09/zenbot-scraper'
251
-
252
- const tracks = await spotify('bad bunny', 'tracks', 5)
253
- // → [{ title, artist, album, duration, thumbnail, url, preview }]
254
- ```
255
-
256
- </details>
257
-
258
- <details>
259
- <summary><b>▶ Google, Anime, Wallpapers, Stickers</b></summary>
260
-
261
- ```js
262
- import { googleSearch, animeImage, wallpaperSearch, stickerSearch } from '@soyaxell09/zenbot-scraper'
263
-
264
- const web = await googleSearch('node.js tutorial', 5)
265
- const anime = await animeImage('naruto', 5)
266
- const walls = await wallpaperSearch('nature', 10)
267
- const stickers = await stickerSearch('anime', 10)
268
- ```
269
-
270
- </details>
271
-
272
- ---
273
-
274
- ## 🛠️ Tools
275
-
276
- <details>
277
- <summary><b>▶ Traductor, Clima, QR, Lyrics, Noticias, Screenshot, Upload, URLs</b></summary>
278
-
279
- ```js
280
- import { translate, weather, qrGenerate, qrRead,
281
- lyricsSearch, lyricsGet, news, screenshot,
282
- upload, shortenUrl, expandUrl, tiktokStalk } from '@soyaxell09/zenbot-scraper'
283
-
284
- await translate('Hello', 'es')
285
- // → { original, translated, from, to, fromName, toName }
286
-
287
- await weather('Buenos Aires')
288
- // → { location, temp, feelsLike, humidity, wind, description, forecast }
289
-
290
- await qrGenerate('https://github.com/axeldev09', 300)
291
- // → { url, buffer, size, text }
292
-
293
- await lyricsGet('Bad Bunny', 'Tití Me Preguntó')
294
- // → { id, title, artist, album, duration, lyrics }
295
-
296
- await news('es', 5)
297
- // → { category, items: [{ title, description, url, image, published }] }
298
-
299
- await screenshot('https://github.com', 1280, 720)
300
- // → { url, buffer, width, height }
301
-
302
- await upload(buffer, 'video.mp4')
303
- // → { url, filename }
304
- ```
305
-
306
- </details>
307
-
308
- <details>
309
- <summary><b>▶ ATTP (Texto animado)</b></summary>
310
-
311
- ```js
312
- import { attp } from '@soyaxell09/zenbot-scraper'
313
-
314
- const gif = await attp('HoLa MuNdO', 5)
315
- // → Buffer (GIF animado con fondo transparente)
316
- ```
317
-
318
- Genera un GIF animado con el texto dado. Fondo transparente, outline negro y colores arcoíris que ciclan frame a frame. El segundo parámetro `scale` es opcional (por defecto `5`).
319
-
320
- </details>
321
-
322
- ---
323
-
324
- ## 🔞 NSFW
325
-
326
- > ⚠️ Solo para bots con verificación de edad.
327
-
328
- <details>
329
- <summary><b>▶ XNXX · PornHub · XVideos · XHamster · Rule34</b></summary>
330
-
331
- ```js
332
- import { xnxxSearch, xnxxDownload,
333
- phSearch, phDownload, phDownloadBuffer,
334
- xvideosSearch, xvideosDownload,
335
- xhamsterSearch, xhamsterDownload, xhamsterDownloadBuffer,
336
- rule34Random } from '@soyaxell09/zenbot-scraper'
337
-
338
- await xnxxSearch('query', 10)
339
- await phSearch('query', 10)
340
- await xvideosSearch('query', 10)
341
- await xhamsterSearch('query', 10)
342
-
343
- await rule34Random('cat_girl')
344
- // → { id, url, full, preview, tags, score, width, height }
345
- ```
346
-
347
- </details>
348
-
349
- ---
350
-
351
39
  ## ⚙️ Requisitos
352
40
 
353
41
  - Node.js `>= 18.0.0`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soyaxell09/zenbot-scraper",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "Scrapers de descarga y búsqueda para bots de WhatsApp — YouTube, TikTok, Instagram, Facebook, Twitter, Pinterest, MediaFire, GitHub, APK, Google Drive, XNXX, PornHub, XVideos, XHamster, Rule34, Screenshot y más.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/index.js CHANGED
@@ -4,8 +4,9 @@
4
4
  * Instagram: @axeldev09
5
5
  * Deja los créditos we 🗣️
6
6
  */
7
-
7
+
8
8
  export { ytInfo, ytDownload, ytSearch, getFileSize } from './scrapers/youtube.js'
9
+ export { youtubeDownload } from './scrapers/youtubev2.js'
9
10
  export { tiktokInfo, tiktokDownload } from './scrapers/tiktok.js'
10
11
  export { igDownload, igReelDownload, igStalk, igStories } from './scrapers/instagram.js'
11
12
  export { threadsDownload } from './scrapers/threads.js'
@@ -17,7 +18,6 @@ export { githubInfo, githubRelease, githubContents, githubSearch } from './scrap
17
18
  export { apkSearch, apkInfo } from './scrapers/apk.js'
18
19
  export { gdriveInfo, gdriveDownload } from './scrapers/gdrive.js'
19
20
  export { googleSearch } from './search/google.js'
20
- export { spotify } from './search/spotify.js'
21
21
  export { giphy, gifNext, giphyBuffer } from './search/giphy.js'
22
22
  export { pinsearch, pinimg, pinvid } from './search/pinterest.js'
23
23
  export { stickerSearch } from './search/stickersearch.js'
@@ -14,8 +14,15 @@ const DL_BASE = 'https://d.apkpure.net';
14
14
  const HEADERS = { 'User-Agent': UA, 'Referer': BASE };
15
15
 
16
16
  function extractPkg(href) {
17
- const m = href?.match(/\/((?:com|org|net|io|co)\.[a-z0-9.]+)(?:\/|$)/i);
18
- return m ? m[1] : '';
17
+ const m = href?.match(/\/((?:com|org|net|io|co)\.[a-zA-Z0-9_.]+)(?:\/|$)/i)
18
+ return m ? m[1] : ''
19
+ }
20
+
21
+ function extractPkgFromEl($, el) {
22
+ const fromAttr = $(el).find('[data-dt-pkg]').attr('data-dt-pkg')
23
+ if (fromAttr) return fromAttr
24
+ const href = $(el).find('a.top').first().attr('href') || ''
25
+ return extractPkg(href)
19
26
  }
20
27
 
21
28
  function parsePageData(data) {
@@ -44,7 +51,7 @@ export async function apkSearch(query, limit = 5) {
44
51
  const date = $(el).find('span.time').first().text().trim();
45
52
  const icon = $(el).find('img.app-icon-img').first().attr('data-original') || '';
46
53
  const appHref = $(el).find('a.top').first().attr('href') || '';
47
- const pkg = extractPkg(appHref);
54
+ const pkg = extractPkgFromEl($, el)
48
55
 
49
56
  if (!name || !pkg || seen.has(pkg)) return;
50
57
  seen.add(pkg);
@@ -66,8 +73,11 @@ export async function apkSearch(query, limit = 5) {
66
73
  }
67
74
 
68
75
  export async function apkInfo(pkgOrUrl) {
69
- let pkg = pkgOrUrl.includes('apkpure') ? extractPkg(pkgOrUrl) : pkgOrUrl;
70
- if (!pkg) pkg = pkgOrUrl;
76
+ if (!pkgOrUrl) throw new Error('Se requiere un package o URL válido')
77
+ let pkg = (typeof pkgOrUrl === 'string' && pkgOrUrl.includes('apkpure'))
78
+ ? extractPkg(pkgOrUrl)
79
+ : pkgOrUrl
80
+ if (!pkg) pkg = pkgOrUrl
71
81
 
72
82
  const shortName = pkg.split('.').slice(-2).join('-').toLowerCase();
73
83
 
@@ -82,7 +92,7 @@ export async function apkInfo(pkgOrUrl) {
82
92
  : $('h1, .title-like').first().text().trim();
83
93
  const version = pageData?.versionName || $('[class*="version"]').first().text().trim().match(/[\d.]+/)?.[0] || '';
84
94
  const size = $('[class*="size"]').first().text().trim();
85
- const icon = $('img.icon, img[itemprop="image"]').first().attr('src') || '';
95
+ const icon = $('img.app-icon-img, img.app-icon').first().attr('data-original') || $('img[src*="winudf"]').first().attr('src') || '';
86
96
  const dev = $('[class*="developer"], .dev-info, [itemprop="author"]').first().text().trim();
87
97
 
88
98
  const dlLinks = [];
@@ -0,0 +1,123 @@
1
+ /*
2
+ * © Created by AxelDev09 🔥
3
+ * GitHub: https://github.com/AxelDev09
4
+ * Instagram: @axeldev09
5
+ * Deja los créditos we 🗣️
6
+ */
7
+
8
+ import axios from 'axios';
9
+
10
+ const UA = 'Mozilla/5.0 (Linux; Android 11; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36';
11
+ const BASE = 'https://app.ytdown.to';
12
+
13
+ async function getSession() {
14
+ const res = await axios.get(`${BASE}/es23/`, {
15
+ headers: { 'User-Agent': UA },
16
+ timeout: 12000
17
+ });
18
+ const cookies = res.headers['set-cookie']?.map(c => c.split(';')[0]).join('; ') || '';
19
+ return cookies;
20
+ }
21
+
22
+ async function fetchData(url) {
23
+ const cookies = await getSession();
24
+ const params = new URLSearchParams();
25
+ params.append('url', url);
26
+
27
+ const { data } = await axios.post(`${BASE}/proxy.php`, params, {
28
+ headers: {
29
+ 'User-Agent': UA,
30
+ 'Content-Type': 'application/x-www-form-urlencoded',
31
+ 'Referer': `${BASE}/es23/`,
32
+ 'Origin': BASE,
33
+ 'Cookie': cookies,
34
+ 'X-Requested-With': 'XMLHttpRequest',
35
+ 'Accept': 'application/json, text/javascript, */*; q=0.01'
36
+ },
37
+ timeout: 25000
38
+ });
39
+
40
+ if (data?.api?.status !== 'ok') {
41
+ const msg = data?.api?.message || 'Error al procesar'
42
+ throw new Error(msg)
43
+ }
44
+ return data.api;
45
+ }
46
+
47
+ const YT_RE = /^https?:\/\/(www\.)?(youtube\.com|youtu\.be)\//i
48
+
49
+ function normalizeUrl(url) {
50
+ try {
51
+ const u = new URL(url)
52
+ if (u.hostname.includes('youtu.be')) {
53
+ return `https://www.youtube.com/watch?v=${u.pathname.slice(1)}`
54
+ }
55
+ if (u.pathname.startsWith('/shorts/')) {
56
+ return `https://www.youtube.com/watch?v=${u.pathname.split('/shorts/')[1].split('/')[0]}`
57
+ }
58
+ if (u.pathname.startsWith('/embed/')) {
59
+ return `https://www.youtube.com/watch?v=${u.pathname.split('/embed/')[1].split('/')[0]}`
60
+ }
61
+ return url
62
+ } catch {
63
+ return url
64
+ }
65
+ }
66
+
67
+ function isShort(url) {
68
+ try { return new URL(url).pathname.startsWith('/shorts/') } catch { return false }
69
+ }
70
+
71
+ export async function youtubeDownload(url) {
72
+ if (!url || !YT_RE.test(url)) throw new Error('Se requiere una URL válida de YouTube.')
73
+ const normalized = normalizeUrl(url)
74
+ let api
75
+ try {
76
+ api = await fetchData(normalized)
77
+ } catch (e) {
78
+ if (isShort(url) && (e.message.includes('unavailable') || e.message.includes('procesar')))
79
+ throw new Error('Los YouTube Shorts no están disponibles en este momento. Intentá con un video normal.')
80
+ throw e
81
+ }
82
+
83
+ const videos = api.mediaItems
84
+ .filter(i => i.type === 'Video')
85
+ .map(i => ({
86
+ quality: i.mediaQuality,
87
+ resolution: i.mediaRes,
88
+ url: i.mediaUrl,
89
+ size: i.mediaFileSize,
90
+ ext: i.mediaExtension.toLowerCase()
91
+ }));
92
+
93
+ const audios = api.mediaItems
94
+ .filter(i => i.type === 'Audio')
95
+ .map(i => ({
96
+ quality: i.mediaQuality,
97
+ url: i.mediaUrl,
98
+ size: i.mediaFileSize,
99
+ ext: i.mediaExtension.toLowerCase()
100
+ }));
101
+
102
+ return {
103
+ title: api.title,
104
+ description: api.description,
105
+ thumb: api.imagePreviewUrl,
106
+ duration: api.mediaItems[0]?.mediaDuration || null,
107
+ channel: {
108
+ name: api.userInfo?.name || null,
109
+ username: api.userInfo?.username || null,
110
+ url: api.userInfo?.internalUrl || null,
111
+ avatar: api.userInfo?.userAvatar || null,
112
+ country: api.userInfo?.accountCountry || null
113
+ },
114
+ stats: {
115
+ views: api.mediaStats?.viewsCount || null,
116
+ followers: api.mediaStats?.followersCount || null
117
+ },
118
+ download: {
119
+ video: videos,
120
+ audio: audios
121
+ }
122
+ };
123
+ }
@@ -5,11 +5,10 @@
5
5
  * Deja los créditos we 🗣️
6
6
  */
7
7
 
8
- export { ytSearch } from './youtube.js'
9
- export { googleSearch } from './google.js'
10
- export { spotify } from './spotify.js'
11
- export { giphy, gifNext, giphyBuffer } from './giphy.js'
12
- export { pinsearch, pinimg, pinvid } from './pinterest.js'
13
- export { stickerSearch } from './stickersearch.js'
14
- export { animeImage } from './anime.js'
15
- export { wallpaperSearch } from './wallpaper.js'
8
+ export { ytSearch } from './youtube.js'
9
+ export { googleSearch } from './google.js'
10
+ export { giphy, gifNext, giphyBuffer } from './giphy.js'
11
+ export { pinsearch, pinimg, pinvid } from './pinterest.js'
12
+ export { stickerSearch } from './stickersearch.js'
13
+ export { animeImage } from './anime.js'
14
+ export { wallpaperSearch } from './wallpaper.js'
@@ -0,0 +1,74 @@
1
+ /*
2
+ * © Created by AxelDev09 🔥
3
+ * GitHub: https://github.com/AxelDev09
4
+ * Instagram: @axeldev09
5
+ * Deja los créditos we 🗣️
6
+ */
7
+
8
+ import axios from 'axios'
9
+
10
+ const UA = 'Mozilla/5.0 (Linux; Android 11; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36'
11
+ const BASE = 'https://api.delirius.store'
12
+
13
+ const headers = { 'User-Agent': UA }
14
+
15
+ export async function copilot(query) {
16
+ if (!query) throw new Error('query es requerido')
17
+ const { data } = await axios.get(`${BASE}/ia/copilot`, {
18
+ params: { query },
19
+ headers,
20
+ timeout: 12000,
21
+ })
22
+ if (!data?.text) throw new Error('Sin respuesta')
23
+ return {
24
+ text: data.text,
25
+ citations: data.citations || [],
26
+ conversationId: data.conversationId || null,
27
+ }
28
+ }
29
+
30
+ export async function gemini(query) {
31
+ if (!query) throw new Error('query es requerido')
32
+ const { data } = await axios.get(`${BASE}/ia/gemini`, {
33
+ params: { query },
34
+ headers,
35
+ timeout: 12000,
36
+ })
37
+ if (!data?.status || !data?.data?.result) throw new Error('Sin respuesta')
38
+ return { text: data.data.result }
39
+ }
40
+
41
+ export async function chatgpt(query) {
42
+ if (!query) throw new Error('query es requerido')
43
+ const { data } = await axios.get(`${BASE}/ia/chatgpt`, {
44
+ params: { q: query },
45
+ headers,
46
+ timeout: 12000,
47
+ })
48
+ if (!data?.status || !data?.data) throw new Error('Sin respuesta')
49
+ return { text: data.data }
50
+ }
51
+
52
+ export async function gptPrompt(text, prompt = '') {
53
+ if (!text) throw new Error('text es requerido')
54
+ const params = { text }
55
+ if (prompt) params.prompt = prompt
56
+ const { data } = await axios.get(`${BASE}/ia/gptprompt`, {
57
+ params,
58
+ headers,
59
+ timeout: 12000,
60
+ })
61
+ if (!data?.status || !data?.data) throw new Error('Sin respuesta')
62
+ return { text: data.data }
63
+ }
64
+
65
+ export async function ripleai(query) {
66
+ if (!query) throw new Error('query es requerido')
67
+ const { data } = await axios.get(`${BASE}/ia/ripleai`, {
68
+ params: { query },
69
+ headers,
70
+ timeout: 12000,
71
+ })
72
+ if (!data?.status || !data?.data?.result) throw new Error('Sin respuesta')
73
+ return { text: data.data.result }
74
+ }
package/test.js ADDED
@@ -0,0 +1,130 @@
1
+ /*
2
+ * TEST — apk.js
3
+ * Corre con: node test.js
4
+ */
5
+
6
+ import axios from 'axios'
7
+ import * as cheerio from 'cheerio'
8
+ import { createWriteStream, statSync } from 'fs'
9
+ import { pipeline } from 'stream/promises'
10
+
11
+ const UA = 'Mozilla/5.0 (Linux; Android 11; Redmi Note 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36'
12
+ const BASE = 'https://apkpure.net'
13
+ const DL_BASE = 'https://d.apkpure.net'
14
+ const HEADERS = { 'User-Agent': UA, 'Referer': BASE }
15
+
16
+ const QUERY = 'Google Fotos'
17
+ const PKG = 'org.telegram.messenger'
18
+ const SAVE_PATH = '/sdcard/test-apk.apk'
19
+
20
+ async function test(name, fn) {
21
+ console.log(`\n── ${name} ──`)
22
+ try {
23
+ const r = await fn()
24
+ console.log(`✅ OK`)
25
+ console.log(` ${JSON.stringify(r).slice(0, 300)}`)
26
+ return r
27
+ } catch (e) {
28
+ console.log(`❌ ERROR: ${e.message}`)
29
+ return null
30
+ }
31
+ }
32
+
33
+ console.log('='.repeat(50))
34
+ console.log(' TEST — apk.js')
35
+ console.log('='.repeat(50))
36
+
37
+ // ── 1. Búsqueda
38
+ await test(`apkSearch("${QUERY}")`, async () => {
39
+ const { data } = await axios.get(`${BASE}/search?q=${encodeURIComponent(QUERY)}`, {
40
+ headers: HEADERS, timeout: 15000
41
+ })
42
+ const $ = cheerio.load(data)
43
+ const items = []
44
+ $('.search-brand-container').each((_, el) => {
45
+ if (items.length >= 3) return false
46
+ const name = $(el).find('a.top').first().text().trim()
47
+ const pkg = $(el).find('[data-dt-pkg]').attr('data-dt-pkg') || ''
48
+ const appHref = $(el).find('a.top').first().attr('href') || ''
49
+ const appUrl = appHref.startsWith('http') ? appHref : `${BASE}${appHref}`
50
+ if (name && pkg) items.push({ name, pkg, appUrl, dlUrl: `${DL_BASE}/b/APK/${pkg}?version=latest` })
51
+ })
52
+ if (!items.length) throw new Error('Sin resultados — el selector puede haber cambiado')
53
+ return items
54
+ })
55
+
56
+ // ── 2. Info del APK
57
+ await test(`apkInfo("${PKG}")`, async () => {
58
+ const shortName = PKG.split('.').slice(-2).join('-').toLowerCase()
59
+ const { data } = await axios.get(`${BASE}/${shortName}/${PKG}/download`, {
60
+ headers: HEADERS, timeout: 15000, validateStatus: () => true
61
+ })
62
+ const $ = cheerio.load(data)
63
+ const version = $('[class*="version"]').first().text().trim()
64
+ const size = $('[class*="size"]').first().text().trim()
65
+ const title = $('title').text().trim()
66
+ const dlLinks = []
67
+ $(`a[href*="d.apkpure.net"]`).each((_, a) => {
68
+ const href = $(a).attr('href') || ''
69
+ if (href.includes(PKG)) dlLinks.push(href)
70
+ })
71
+ return { title, version, size, dlLinksCount: dlLinks.length, firstDlLink: dlLinks[0] || null }
72
+ })
73
+
74
+ // ── 3. HEAD — verificar URL de descarga
75
+ const DL_URL = `${DL_BASE}/b/APK/${PKG}?version=latest`
76
+ await test(`HEAD ${DL_URL.slice(0, 60)}...`, async () => {
77
+ const res = await axios.head(DL_URL, {
78
+ headers: { ...HEADERS, 'Accept': 'application/vnd.android.package-archive' },
79
+ timeout: 15000, maxRedirects: 5, validateStatus: () => true
80
+ })
81
+ return {
82
+ status: res.status,
83
+ contentType: res.headers['content-type'],
84
+ sizeMB: res.headers['content-length']
85
+ ? (parseInt(res.headers['content-length']) / 1024 / 1024).toFixed(2) + ' MB'
86
+ : 'desconocido',
87
+ finalUrl: res.request?.res?.responseUrl?.slice(0, 80) || '',
88
+ }
89
+ })
90
+
91
+ // ── 4. Descarga real → /sdcard/test-apk.apk
92
+ await test(`Descarga real → ${SAVE_PATH}`, async () => {
93
+ const res = await axios.get(DL_URL, {
94
+ headers: { ...HEADERS, 'Accept': 'application/vnd.android.package-archive' },
95
+ responseType: 'stream',
96
+ timeout: 120000,
97
+ maxRedirects: 10,
98
+ })
99
+
100
+ const ct = res.headers['content-type'] || ''
101
+ const APK_TYPES = ['apk', 'octet-stream', 'zip', 'java-archive', 'android']
102
+ const esApk = APK_TYPES.some(t => ct.includes(t))
103
+
104
+ if (!esApk) {
105
+ // Leer primeros bytes como texto para ver si es HTML de error
106
+ const chunks = []
107
+ for await (const chunk of res.data) {
108
+ chunks.push(chunk)
109
+ if (Buffer.concat(chunks).length > 300) break
110
+ }
111
+ const preview = Buffer.concat(chunks).toString('utf8', 0, 200)
112
+ throw new Error(`Content-Type inesperado: ${ct}\nPreview: ${preview}`)
113
+ }
114
+
115
+ await pipeline(res.data, createWriteStream(SAVE_PATH))
116
+
117
+ const { size } = statSync(SAVE_PATH)
118
+ if (size < 1000) throw new Error(`Archivo guardado pero muy pequeño (${size} bytes) — probablemente un error HTML`)
119
+
120
+ return {
121
+ contentType: ct,
122
+ sizeMB: (size / 1024 / 1024).toFixed(2) + ' MB',
123
+ guardado: SAVE_PATH,
124
+ valido: size > 100_000 ? '✅ APK válido' : '⚠️ Muy pequeño, verificar',
125
+ }
126
+ })
127
+
128
+ console.log('\n' + '='.repeat(50))
129
+ console.log(' Test terminado')
130
+ console.log('='.repeat(50))
@@ -1,11 +0,0 @@
1
- export { ytInfo, ytDownload, ytSearch, getFileSize } from './youtube.js'
2
- export { tiktokInfo, tiktokDownload } from './tiktok.js'
3
- export { fbDownload } from './facebook.js'
4
- export { tweetInfo, tweetDownload } from './twitter.js'
5
- export { mediafireInfo } from './mediafire.js'
6
- export { githubInfo, githubRelease, githubContents, githubSearch } from './github.js'
7
- export { apkSearch, apkInfo } from './apk.js'
8
- export { gdriveInfo, gdriveDownload } from './gdrive.js'
9
- export { igDownload, igReelDownload, igStalk, igStories } from './instagram.js'
10
- export { threadsDownload } from './threads.js'
11
- export { spotidownTrack } from './spotidown.js'
@@ -1,56 +0,0 @@
1
- /*
2
- * © Created by AxelDev09 🔥
3
- * GitHub: https://github.com/AxelDev09
4
- * Instagram: @axeldev09
5
- * Deja los créditos we 🗣️
6
- */
7
-
8
- import axios from 'axios'
9
-
10
- const HEADERS = {
11
- 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36',
12
- }
13
-
14
- async function searchTracks(query, limit = 5) {
15
- const res = await axios.get(
16
- `https://api.deezer.com/search?q=${encodeURIComponent(query)}&limit=${limit}`,
17
- { headers: HEADERS, timeout: 15000 }
18
- )
19
- const tracks = res.data?.data || []
20
- if (!tracks.length) throw new Error('Sin resultados')
21
- return tracks.map(t => ({
22
- type: 'track',
23
- title: t.title,
24
- artist: t.artist?.name || '',
25
- album: t.album?.title || '',
26
- duration: t.duration || 0,
27
- thumbnail: t.album?.cover_big || t.album?.cover || '',
28
- url: t.link || '',
29
- preview: t.preview || null,
30
- id: String(t.id),
31
- }))
32
- }
33
-
34
- async function searchAlbums(query, limit = 12) {
35
- const res = await axios.get(
36
- `https://api.deezer.com/search/album?q=${encodeURIComponent(query)}&limit=${limit}`,
37
- { headers: HEADERS, timeout: 15000 }
38
- )
39
- const albums = res.data?.data || []
40
- if (!albums.length) throw new Error('Sin resultados')
41
- return albums.map(a => ({
42
- type: 'album',
43
- title: a.title,
44
- artist: a.artist?.name || '',
45
- thumbnail: a.cover_big || a.cover || '',
46
- url: a.link || '',
47
- tracks: a.nb_tracks || 0,
48
- year: a.release_date?.slice(0, 4) || '',
49
- id: String(a.id),
50
- }))
51
- }
52
-
53
- export async function spotify(query, type = 'track', limit = 5) {
54
- if (type === 'album') return searchAlbums(query, limit)
55
- return searchTracks(query, limit)
56
- }