@soyaxell09/zenbot-scraper 1.0.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.
@@ -0,0 +1,88 @@
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 tryLyricsOvh(artist, title) {
15
+ const res = await axios.get(
16
+ `https://api.lyrics.ovh/v1/${encodeURIComponent(artist)}/${encodeURIComponent(title)}`,
17
+ { headers: HEADERS, timeout: 10000 }
18
+ )
19
+ if (!res.data?.lyrics) throw new Error('Sin letra')
20
+ return res.data.lyrics.trim()
21
+ }
22
+
23
+ async function tryLrclib(query) {
24
+ const res = await axios.get(
25
+ `https://lrclib.net/api/search?q=${encodeURIComponent(query)}`,
26
+ { headers: HEADERS, timeout: 10000 }
27
+ )
28
+ const results = res.data
29
+ if (!results?.length) throw new Error('Sin resultados')
30
+ return results.map(r => ({
31
+ id: r.id,
32
+ title: r.trackName || r.name || '',
33
+ artist: r.artistName || '',
34
+ album: r.albumName || '',
35
+ duration: r.duration || 0,
36
+ lyrics: r.plainLyrics || null,
37
+ lrc: r.syncedLyrics || null,
38
+ }))
39
+ }
40
+
41
+ export async function lyricsSearch(query, limit = 5) {
42
+ const errors = []
43
+
44
+ try {
45
+ const results = await tryLrclib(query)
46
+ return results.slice(0, limit)
47
+ } catch (e) { errors.push('lrclib: ' + e.message) }
48
+
49
+ const parts = query.trim().split(/\s+/)
50
+ const mid = Math.ceil(parts.length / 2)
51
+ const artist = parts.slice(0, mid).join(' ')
52
+ const title = parts.slice(mid).join(' ') || artist
53
+
54
+ try {
55
+ const lyrics = await tryLyricsOvh(artist, title)
56
+ return [{
57
+ id: null,
58
+ title: title,
59
+ artist: artist,
60
+ album: '',
61
+ duration: 0,
62
+ lyrics,
63
+ lrc: null,
64
+ }]
65
+ } catch (e) { errors.push('lyrics.ovh: ' + e.message) }
66
+
67
+ throw new Error('No se encontraron letras. Errores: ' + errors.join(' | '))
68
+ }
69
+
70
+ export async function lyricsGet(artist, title) {
71
+ const errors = []
72
+
73
+ try {
74
+ const results = await tryLrclib(`${artist} ${title}`)
75
+ const match = results.find(r =>
76
+ r.title.toLowerCase().includes(title.toLowerCase()) ||
77
+ r.artist.toLowerCase().includes(artist.toLowerCase())
78
+ ) || results[0]
79
+ if (match) return match
80
+ } catch (e) { errors.push('lrclib: ' + e.message) }
81
+
82
+ try {
83
+ const lyrics = await tryLyricsOvh(artist, title)
84
+ return { id: null, title, artist, album: '', duration: 0, lyrics, lrc: null }
85
+ } catch (e) { errors.push('lyrics.ovh: ' + e.message) }
86
+
87
+ throw new Error('No se encontró la letra. Errores: ' + errors.join(' | '))
88
+ }
@@ -0,0 +1,54 @@
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
+ const FEEDS = {
15
+ es: 'https://feeds.bbci.co.uk/mundo/rss.xml',
16
+ en: 'https://feeds.bbci.co.uk/news/rss.xml',
17
+ pt: 'https://feeds.bbci.co.uk/portuguese/rss.xml',
18
+ tech: 'https://feeds.feedburner.com/TechCrunch',
19
+ sports: 'https://www.espn.com/espn/rss/news',
20
+ science: 'https://feeds.feedburner.com/sciencedaily',
21
+ world: 'https://feeds.bbci.co.uk/news/world/rss.xml',
22
+ }
23
+
24
+ function parseRss2Json(data, limit) {
25
+ return (data.items || []).slice(0, limit).map(item => ({
26
+ title: item.title || '',
27
+ description: item.description?.replace(/<[^>]+>/g, '').slice(0, 200) || '',
28
+ url: item.link || '',
29
+ image: item.thumbnail || item.enclosure?.link || '',
30
+ published: item.pubDate || '',
31
+ source: data.feed?.title || '',
32
+ }))
33
+ }
34
+
35
+ export async function news(category = 'es', limit = 5) {
36
+ const feedUrl = FEEDS[category] || FEEDS.es
37
+
38
+ const res = await axios.get(
39
+ `https://rss2json.com/api.json?rss_url=${encodeURIComponent(feedUrl)}&count=${limit}`,
40
+ { headers: HEADERS, timeout: 15000 }
41
+ )
42
+
43
+ if (res.data?.status !== 'ok') throw new Error('Error al obtener noticias')
44
+
45
+ return {
46
+ category,
47
+ source: res.data.feed?.title || '',
48
+ items: parseRss2Json(res.data, limit),
49
+ }
50
+ }
51
+
52
+ export function newsCategories() {
53
+ return Object.keys(FEEDS)
54
+ }
@@ -0,0 +1,40 @@
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
+ import * as cheerio from 'cheerio'
10
+
11
+ const HEADERS = {
12
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36',
13
+ }
14
+
15
+ export async function qrGenerate(text, size = 300) {
16
+ if (!text?.trim()) throw new Error('Texto vacío')
17
+ const url = `https://api.qrserver.com/v1/create-qr-code/?size=${size}x${size}&data=${encodeURIComponent(text)}&format=png&ecc=M`
18
+ const res = await axios.get(url, { headers: HEADERS, responseType: 'arraybuffer', timeout: 15000 })
19
+ return {
20
+ url,
21
+ buffer: Buffer.from(res.data),
22
+ size,
23
+ text,
24
+ }
25
+ }
26
+
27
+ export async function qrRead(imageUrl) {
28
+ if (!imageUrl?.trim()) throw new Error('URL de imagen vacía')
29
+ const res = await axios.get(
30
+ `https://api.qrserver.com/v1/read-qr-code/?fileurl=${encodeURIComponent(imageUrl)}`,
31
+ { headers: HEADERS, timeout: 15000 }
32
+ )
33
+ const result = res.data?.[0]?.symbol?.[0]
34
+ if (!result) throw new Error('No se pudo leer el QR')
35
+ if (result.error) throw new Error('QR inválido: ' + result.error)
36
+ return {
37
+ text: result.data || '',
38
+ type: result.type || '',
39
+ }
40
+ }
@@ -0,0 +1,68 @@
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
+ function formatNumber(n) {
15
+ if (!n) return '0'
16
+ if (n >= 1_000_000_000) return (n / 1_000_000_000).toFixed(1).replace(/\.0$/, '') + 'B'
17
+ if (n >= 1_000_000) return (n / 1_000_000).toFixed(1).replace(/\.0$/, '') + 'M'
18
+ if (n >= 1_000) return (n / 1_000).toFixed(1).replace(/\.0$/, '') + 'K'
19
+ return String(n)
20
+ }
21
+
22
+ export async function tiktokStalk(username) {
23
+ const clean = username.replace(/^@/, '').trim()
24
+ if (!clean) throw new Error('Username inválido')
25
+
26
+ const res = await axios.get(
27
+ `https://tikwm.com/api/user/info?unique_id=${encodeURIComponent(clean)}`,
28
+ { headers: HEADERS, timeout: 15000 }
29
+ )
30
+
31
+ const data = res.data
32
+ if (data?.code !== 0 || !data?.data) throw new Error('tikwm: ' + (data?.msg || 'usuario no encontrado'))
33
+
34
+ const u = data.data.user
35
+ const s = data.data.stats
36
+
37
+ const followers = s.followerCount || 0
38
+ const following = s.followingCount || 0
39
+ const likes = s.heartCount || 0
40
+ const videos = s.videoCount || 0
41
+ const avgLikes = videos > 0 ? Math.round(likes / videos) : 0
42
+
43
+ return {
44
+ id: u.id || '',
45
+ username: u.uniqueId || '',
46
+ nickname: u.nickname || '',
47
+ bio: u.signature || '',
48
+ avatar: u.avatarLarger || u.avatarMedium || u.avatarThumb || '',
49
+ verified: u.verified || false,
50
+ private: u.privateAccount || false,
51
+ bioLink: u.bioLink?.link || null,
52
+ instagram: u.ins_id || null,
53
+ twitter: u.twitter_id || null,
54
+ youtube: u.youtube_channel_title || null,
55
+ createdAt: u.createTime ? new Date(u.createTime * 1000).toISOString().slice(0, 10) : null,
56
+ stats: {
57
+ followers: followers,
58
+ following: following,
59
+ likes: likes,
60
+ videos: videos,
61
+ followersStr: formatNumber(followers),
62
+ followingStr: formatNumber(following),
63
+ likesStr: formatNumber(likes),
64
+ avgLikesStr: formatNumber(avgLikes),
65
+ },
66
+ url: `https://www.tiktok.com/@${u.uniqueId}`,
67
+ }
68
+ }
@@ -0,0 +1,82 @@
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
+ const LANGS = {
15
+ es: 'Español', en: 'English', pt: 'Português', fr: 'Français',
16
+ de: 'Deutsch', it: 'Italiano', ja: '日本語', ko: '한국어',
17
+ zh: '中文', ru: 'Русский', ar: 'العربية', hi: 'हिन्दी',
18
+ tr: 'Türkçe', nl: 'Nederlands', pl: 'Polski', sv: 'Svenska',
19
+ uk: 'Українська', vi: 'Tiếng Việt', id: 'Bahasa Indonesia',
20
+ }
21
+
22
+ async function tryGoogle(text, from, to) {
23
+ const res = await axios.get(
24
+ `https://translate.googleapis.com/translate_a/single?client=gtx&sl=${from}&tl=${to}&dt=t&q=${encodeURIComponent(text)}`,
25
+ { headers: HEADERS, timeout: 10000 }
26
+ )
27
+ const data = res.data
28
+ if (!Array.isArray(data?.[0])) throw new Error('Respuesta inválida')
29
+ const translated = data[0].map(s => s[0]).filter(Boolean).join('')
30
+ if (!translated) throw new Error('Sin traducción')
31
+ const detectedLang = data[2] || from
32
+ return { translated, detectedLang }
33
+ }
34
+
35
+ async function tryMyMemory(text, from, to) {
36
+ const res = await axios.get(
37
+ `https://api.mymemory.translated.net/get?q=${encodeURIComponent(text)}&langpair=${from}|${to}`,
38
+ { headers: HEADERS, timeout: 10000 }
39
+ )
40
+ const translated = res.data?.responseData?.translatedText
41
+ if (!translated) throw new Error('Sin traducción')
42
+ return { translated, detectedLang: from }
43
+ }
44
+
45
+ export async function translate(text, to = 'es', from = 'auto') {
46
+ if (!text?.trim()) throw new Error('Texto vacío')
47
+
48
+ const errors = []
49
+
50
+ try {
51
+ const result = await tryGoogle(text, from, to)
52
+ return {
53
+ original: text,
54
+ translated: result.translated,
55
+ from: result.detectedLang,
56
+ to,
57
+ fromName: LANGS[result.detectedLang] || result.detectedLang,
58
+ toName: LANGS[to] || to,
59
+ source: 'google',
60
+ }
61
+ } catch (e) { errors.push('google: ' + e.message) }
62
+
63
+ try {
64
+ const langFrom = from === 'auto' ? 'en' : from
65
+ const result = await tryMyMemory(text, langFrom, to)
66
+ return {
67
+ original: text,
68
+ translated: result.translated,
69
+ from: langFrom,
70
+ to,
71
+ fromName: LANGS[langFrom] || langFrom,
72
+ toName: LANGS[to] || to,
73
+ source: 'mymemory',
74
+ }
75
+ } catch (e) { errors.push('mymemory: ' + e.message) }
76
+
77
+ throw new Error('No se pudo traducir. Errores: ' + errors.join(' | '))
78
+ }
79
+
80
+ export function getLangs() {
81
+ return Object.entries(LANGS).map(([code, name]) => ({ code, name }))
82
+ }
@@ -0,0 +1,70 @@
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
+ import FormData from 'form-data'
10
+ import { createReadStream, existsSync } from 'fs'
11
+ import { basename } from 'path'
12
+
13
+ async function uploadBuffer(buffer, filename) {
14
+ const form = new FormData()
15
+ form.append('reqtype', 'fileupload')
16
+ form.append('fileToUpload', buffer, { filename })
17
+ const res = await axios.post(
18
+ 'https://catbox.moe/user/api.php',
19
+ form,
20
+ { headers: form.getHeaders(), timeout: 60000, maxBodyLength: Infinity }
21
+ )
22
+ const url = res.data?.trim()
23
+ if (!url?.startsWith('https://')) throw new Error('Respuesta inválida: ' + url)
24
+ return url
25
+ }
26
+
27
+ async function uploadPath(filePath) {
28
+ if (!existsSync(filePath)) throw new Error('Archivo no encontrado: ' + filePath)
29
+ const form = new FormData()
30
+ form.append('reqtype', 'fileupload')
31
+ form.append('fileToUpload', createReadStream(filePath), { filename: basename(filePath) })
32
+ const res = await axios.post(
33
+ 'https://catbox.moe/user/api.php',
34
+ form,
35
+ { headers: form.getHeaders(), timeout: 60000, maxBodyLength: Infinity }
36
+ )
37
+ const url = res.data?.trim()
38
+ if (!url?.startsWith('https://')) throw new Error('Respuesta inválida: ' + url)
39
+ return url
40
+ }
41
+
42
+ async function uploadUrl(fileUrl) {
43
+ const res = await axios.get(fileUrl, { responseType: 'arraybuffer', timeout: 30000 })
44
+ const ext = fileUrl.split('?')[0].split('.').pop().slice(0, 5) || 'bin'
45
+ const name = `file.${ext}`
46
+ return uploadBuffer(Buffer.from(res.data), name)
47
+ }
48
+
49
+ export async function upload(input, filename) {
50
+ if (!input) throw new Error('Input vacío')
51
+
52
+ if (Buffer.isBuffer(input)) {
53
+ const name = filename || 'file.bin'
54
+ return { url: await uploadBuffer(input, name), filename: name }
55
+ }
56
+
57
+ if (typeof input === 'string') {
58
+ if (input.startsWith('http://') || input.startsWith('https://')) {
59
+ const ext = input.split('?')[0].split('.').pop().slice(0, 5) || 'bin'
60
+ const name = filename || `file.${ext}`
61
+ const url = await uploadUrl(input)
62
+ return { url, filename: name }
63
+ }
64
+ const name = filename || basename(input)
65
+ const url = await uploadPath(input)
66
+ return { url, filename: name }
67
+ }
68
+
69
+ throw new Error('Input inválido — usa Buffer, ruta de archivo o URL')
70
+ }
@@ -0,0 +1,54 @@
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 tryIsGd(url) {
15
+ const res = await axios.get(
16
+ `https://is.gd/create.php?format=json&url=${encodeURIComponent(url)}`,
17
+ { headers: HEADERS, timeout: 10000 }
18
+ )
19
+ if (!res.data?.shorturl) throw new Error('Sin resultado')
20
+ return { short: res.data.shorturl, source: 'is.gd' }
21
+ }
22
+
23
+ async function tryTinyUrl(url) {
24
+ const res = await axios.get(
25
+ `https://tinyurl.com/api-create.php?url=${encodeURIComponent(url)}`,
26
+ { headers: HEADERS, timeout: 10000 }
27
+ )
28
+ if (!res.data?.startsWith('http')) throw new Error('Sin resultado')
29
+ return { short: res.data.trim(), source: 'tinyurl' }
30
+ }
31
+
32
+ export async function shortenUrl(url) {
33
+ if (!url?.trim()) throw new Error('URL vacía')
34
+ if (!url.startsWith('http')) throw new Error('URL inválida — debe empezar con http/https')
35
+
36
+ const errors = []
37
+ try { return await tryIsGd(url) } catch (e) { errors.push('is.gd: ' + e.message) }
38
+ try { return await tryTinyUrl(url) } catch (e) { errors.push('tinyurl: ' + e.message) }
39
+
40
+ throw new Error('No se pudo acortar. Errores: ' + errors.join(' | '))
41
+ }
42
+
43
+ export async function expandUrl(shortUrl) {
44
+ if (!shortUrl?.trim()) throw new Error('URL vacía')
45
+ const res = await axios.get(shortUrl, {
46
+ headers: HEADERS,
47
+ maxRedirects: 10,
48
+ timeout: 10000,
49
+ })
50
+ return {
51
+ original: shortUrl,
52
+ expanded: res.request?.res?.responseUrl || res.config?.url || shortUrl,
53
+ }
54
+ }
@@ -0,0 +1,72 @@
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
+ const WEATHER_CODES = {
15
+ 113: '☀️ Despejado', 116: '⛅ Parcialmente nublado', 119: '☁️ Nublado',
16
+ 122: '☁️ Cubierto', 143: '🌫️ Neblina', 176: '🌦️ Lluvia dispersa',
17
+ 179: '🌨️ Nevada dispersa', 182: '🌧️ Aguanieve', 185: '🌧️ Llovizna helada',
18
+ 200: '⛈️ Tormenta', 227: '🌨️ Ventisca', 230: '❄️ Ventisca fuerte',
19
+ 248: '🌫️ Niebla', 260: '🌫️ Niebla helada', 263: '🌦️ Llovizna',
20
+ 266: '🌧️ Llovizna ligera', 281: '🌧️ Llovizna helada', 284: '🌧️ Llovizna helada fuerte',
21
+ 293: '🌦️ Lluvia ligera', 296: '🌧️ Lluvia ligera', 299: '🌧️ Lluvia moderada',
22
+ 302: '🌧️ Lluvia moderada', 305: '🌧️ Lluvia fuerte', 308: '🌧️ Lluvia muy fuerte',
23
+ 311: '🌧️ Aguanieve ligera', 314: '🌧️ Aguanieve moderada', 317: '🌧️ Aguanieve',
24
+ 320: '🌨️ Nevada ligera', 323: '🌨️ Nevada ligera', 326: '🌨️ Nevada moderada',
25
+ 329: '❄️ Nevada moderada', 332: '❄️ Nevada fuerte', 335: '❄️ Nevada fuerte',
26
+ 338: '❄️ Nevada muy fuerte', 350: '🌧️ Granizo', 353: '🌦️ Lluvia ligera',
27
+ 356: '🌧️ Lluvia fuerte', 359: '🌧️ Lluvia torrencial', 362: '🌧️ Aguanieve ligera',
28
+ 365: '🌧️ Aguanieve moderada', 368: '🌨️ Nevada ligera', 371: '❄️ Nevada moderada',
29
+ 374: '🌧️ Granizo ligero', 377: '🌧️ Granizo', 386: '⛈️ Tormenta ligera',
30
+ 389: '⛈️ Tormenta fuerte', 392: '⛈️ Tormenta con nevada', 395: '❄️ Tormenta de nieve',
31
+ }
32
+
33
+ export async function weather(location) {
34
+ if (!location?.trim()) throw new Error('Ubicación vacía')
35
+
36
+ const res = await axios.get(
37
+ `https://wttr.in/${encodeURIComponent(location)}?format=j1`,
38
+ { headers: HEADERS, timeout: 15000 }
39
+ )
40
+
41
+ const data = res.data?.data || res.data
42
+ const current = data.weather?.[0]?.hourly?.[0] || {}
43
+ const now = data.current_condition?.[0] || {}
44
+ const req = data.nearest_area?.[0]
45
+
46
+ const city = req?.areaName?.[0]?.value || location
47
+ const country = req?.country?.[0]?.value || ''
48
+ const code = parseInt(now.weatherCode || 113)
49
+ const desc = WEATHER_CODES[code] || now.weatherDesc?.[0]?.value || ''
50
+
51
+ return {
52
+ location: `${city}${country ? ', ' + country : ''}`,
53
+ temp: parseInt(now.temp_C || 0),
54
+ feelsLike: parseInt(now.FeelsLikeC || 0),
55
+ humidity: parseInt(now.humidity || 0),
56
+ wind: parseInt(now.windspeedKmph || 0),
57
+ windDir: now.winddir16Point || '',
58
+ visibility: parseInt(now.visibility || 0),
59
+ pressure: parseInt(now.pressure || 0),
60
+ uvIndex: parseInt(now.uvIndex || 0),
61
+ description: desc,
62
+ code,
63
+ forecast: (data.weather || []).map(d => ({
64
+ date: d.date,
65
+ maxTemp: parseInt(d.maxtempC),
66
+ minTemp: parseInt(d.mintempC),
67
+ desc: WEATHER_CODES[parseInt(d.hourly?.[4]?.weatherCode)] || d.hourly?.[4]?.weatherDesc?.[0]?.value || '',
68
+ sunrise: d.astronomy?.[0]?.sunrise || '',
69
+ sunset: d.astronomy?.[0]?.sunset || '',
70
+ })),
71
+ }
72
+ }