├── .gitignore ├── .husky └── pre-commit ├── .env.example ├── yarn.lock ├── table.md ├── package.json ├── index.mjs ├── link-check.mjs ├── statuses.json ├── README.md ├── statuses.mjs └── data.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | node index.mjs 4 | git add README.md 5 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | TWITCH_CLIENT_ID=your-twitch-client-id 2 | TWITCH_CLIENT_SECRET=your-twitch-client-secret 3 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | dotenv@^17.2.3: 6 | version "17.2.3" 7 | resolved "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz" 8 | integrity sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w== 9 | -------------------------------------------------------------------------------- /table.md: -------------------------------------------------------------------------------- 1 | 2 | # Awesome EStreamers Coders 3 | ℹ️ Si estas haciendo streaming en Twitch o YouTube sobre contenido relacionado a la tecnología o programación eres bienvenida o bienvenido de hacer un PR agregando tu información en esta lista. 4 | 5 | 6 | | Branch | Commit | 7 | | ------- | ---------------- | 8 | | main | 0123456789abcdef | 9 | | staging | fedcba9876543210 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "awesome-estreamers-coders", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "repository": "git@github.com:glrodasz/awesome-estreamers-coders.git", 6 | "author": "Guillermo Rodas ", 7 | "license": "MIT", 8 | "scripts": { 9 | "generate": "node index.mjs", 10 | "statuses": "node statuses.mjs", 11 | "check-links": "node link-check.mjs", 12 | "prepare": "git config core.hooksPath .husky" 13 | }, 14 | "dependencies": { 15 | "dotenv": "^17.2.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /index.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'fs/promises' 2 | 3 | const data = JSON.parse(await fs.readFile('data.json', 'utf-8')) 4 | const statuses = await readStatuses() 5 | const statusByName = new Map(statuses.entries.map((entry) => [entry.name, entry])) 6 | 7 | const heading = `# Awesome EStreamers Coders 8 | 9 | ℹ️ Si estas haciendo streaming en Twitch o YouTube sobre contenido relacionado a la tecnología o programación eres bienvenida o bienvenido de hacer un PR agregando tu información en esta lista. 10 | ` 11 | 12 | const countryOrder = data.reduce((order, entry) => { 13 | if (!order.includes(entry.country)) order.push(entry.country) 14 | return order 15 | }, []) 16 | 17 | const sections = countryOrder.map((country) => { 18 | const people = data.filter((item) => item.country === country) 19 | const lines = [`## ${country}\n`] 20 | 21 | people.forEach((person) => { 22 | const { name, description } = person 23 | lines.push(`- **${name}** — ${description}`) 24 | const formattedLinks = buildLinks(person) 25 | lines.push(` - ${formattedLinks}`) 26 | 27 | const status = statusByName.get(name) 28 | const activityLine = buildActivityLine(status) 29 | if (activityLine) { 30 | lines.push(` - ${activityLine}`) 31 | } 32 | }) 33 | 34 | return lines.join('\n') 35 | }) 36 | 37 | const content = `${heading}\n${sections.join('\n\n')}\n` 38 | 39 | await fs.writeFile('README.md', content, { encoding: 'utf-8' }) 40 | console.log('✅ Successfully generated README.md') 41 | 42 | function buildActivityLine(status) { 43 | if (!status) return 'Última actividad → Desconocida' 44 | 45 | const parts = [] 46 | if (status.youtube?.lastUpload) { 47 | parts.push(`YouTube: ${formatTimestamp(status.youtube.lastUpload)}`) 48 | } else if (status.youtube) { 49 | parts.push('YouTube: Desconocida') 50 | } 51 | 52 | if (status.twitch?.lastLive) { 53 | parts.push(`Twitch última vez en vivo: ${formatTimestamp(status.twitch.lastLive)}`) 54 | } else if (status.twitch?.lastVideo) { 55 | parts.push(`Twitch último video: ${formatTimestamp(status.twitch.lastVideo)}`) 56 | } else if (status.twitch) { 57 | parts.push('Twitch: Desconocida') 58 | } 59 | 60 | if (!parts.length) return 'Última actividad → Desconocida' 61 | return `Última actividad → ${parts.join(' · ')}` 62 | } 63 | 64 | function buildLinks(person) { 65 | const links = [] 66 | 67 | if (person.website) { 68 | links.push({ label: 'Sitio web', url: person.website }) 69 | } 70 | 71 | if (person.youtube) { 72 | links.push({ label: 'YouTube', url: buildYouTubeUrl(person.youtube) }) 73 | } 74 | 75 | if (person.twitch) { 76 | links.push({ label: 'Twitch', url: buildTwitchUrl(person.twitch) }) 77 | } 78 | 79 | if (person.twitter) { 80 | links.push({ label: 'Twitter', url: buildTwitterUrl(person.twitter) }) 81 | } 82 | 83 | if (person.facebook) { 84 | links.push({ label: 'Facebook', url: buildFacebookUrl(person.facebook) }) 85 | } 86 | 87 | if (Array.isArray(person.otherLinks)) { 88 | links.push(...person.otherLinks) 89 | } 90 | 91 | return links.map((link) => `[${link.label}](${link.url})`).join(' · ') 92 | } 93 | 94 | function buildYouTubeUrl(identifier) { 95 | if (/^https?:\/\//i.test(identifier)) return identifier 96 | return `https://www.youtube.com/${identifier}` 97 | } 98 | 99 | function buildTwitchUrl(login) { 100 | if (/^https?:\/\//i.test(login)) return login 101 | return `https://www.twitch.tv/${login}` 102 | } 103 | 104 | function buildTwitterUrl(handle) { 105 | if (/^https?:\/\//i.test(handle)) return handle 106 | return `https://twitter.com/${handle.replace(/^@/, '')}` 107 | } 108 | 109 | function buildFacebookUrl(handle) { 110 | if (/^https?:\/\//i.test(handle)) return handle 111 | return `https://www.facebook.com/${handle}` 112 | } 113 | 114 | function formatTimestamp(value) { 115 | try { 116 | return new Intl.DateTimeFormat('es-ES', { 117 | dateStyle: 'medium', 118 | }).format(new Date(value)) 119 | } catch (error) { 120 | return value 121 | } 122 | } 123 | 124 | async function readStatuses() { 125 | try { 126 | const raw = await fs.readFile('statuses.json', 'utf-8') 127 | const parsed = JSON.parse(raw) 128 | if (Array.isArray(parsed.entries)) return parsed 129 | } catch (error) { 130 | console.warn('No statuses.json found, skipping activity section') 131 | } 132 | return { generatedAt: null, entries: [] } 133 | } 134 | -------------------------------------------------------------------------------- /link-check.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'fs/promises' 2 | 3 | const data = JSON.parse(await fs.readFile('data.json', 'utf-8')) 4 | 5 | function buildYouTubeUrl(identifier) { 6 | if (/^https?:\/\//i.test(identifier)) return identifier 7 | return `https://www.youtube.com/${identifier}` 8 | } 9 | 10 | function buildTwitchUrl(login) { 11 | if (/^https?:\/\//i.test(login)) return login 12 | return `https://www.twitch.tv/${login}` 13 | } 14 | 15 | function buildTwitterUrl(handle) { 16 | if (/^https?:\/\//i.test(handle)) return handle 17 | return `https://twitter.com/${handle.replace(/^@/, '')}` 18 | } 19 | 20 | function buildFacebookUrl(handle) { 21 | if (/^https?:\/\//i.test(handle)) return handle 22 | return `https://www.facebook.com/${handle}` 23 | } 24 | 25 | function buildLinks(person) { 26 | const links = [] 27 | 28 | if (person.website) { 29 | links.push({ label: 'Sitio web', url: person.website }) 30 | } 31 | 32 | if (person.youtube) { 33 | links.push({ label: 'YouTube', url: buildYouTubeUrl(person.youtube) }) 34 | } 35 | 36 | if (person.twitch) { 37 | links.push({ label: 'Twitch', url: buildTwitchUrl(person.twitch) }) 38 | } 39 | 40 | if (person.twitter) { 41 | links.push({ label: 'Twitter', url: buildTwitterUrl(person.twitter) }) 42 | } 43 | 44 | if (person.facebook) { 45 | links.push({ label: 'Facebook', url: buildFacebookUrl(person.facebook) }) 46 | } 47 | 48 | if (Array.isArray(person.otherLinks)) { 49 | links.push(...person.otherLinks) 50 | } 51 | 52 | return links 53 | } 54 | 55 | async function checkUrl(url, timeoutMs = 10000) { 56 | const checkedAt = new Date().toISOString() 57 | const result = { status: 'broken', httpStatus: null, checkedAt } 58 | 59 | const attempts = [ 60 | { method: 'HEAD' }, 61 | { method: 'GET' }, 62 | ] 63 | 64 | for (const attempt of attempts) { 65 | const controller = new AbortController() 66 | const timeoutId = setTimeout(() => controller.abort(), timeoutMs) 67 | 68 | try { 69 | const response = await fetch(url, { 70 | method: attempt.method, 71 | redirect: 'follow', 72 | signal: controller.signal, 73 | }) 74 | clearTimeout(timeoutId) 75 | result.httpStatus = response.status 76 | if (response.ok) { 77 | result.status = 'ok' 78 | return result 79 | } 80 | } catch (error) { 81 | clearTimeout(timeoutId) 82 | if (error.name === 'AbortError') { 83 | result.error = 'Request timeout' 84 | } else { 85 | result.error = error.message 86 | } 87 | } 88 | } 89 | 90 | return result 91 | } 92 | 93 | async function checkLinks() { 94 | // Collect all links with person info 95 | const allLinks = [] 96 | for (const person of data) { 97 | const links = buildLinks(person) 98 | for (const link of links) { 99 | allLinks.push({ person, link }) 100 | } 101 | } 102 | 103 | const totalLinks = allLinks.length 104 | console.log(`Checking ${totalLinks} links across ${data.length} streamers...\n`) 105 | 106 | // Process links in batches for concurrency 107 | const batchSize = 10 108 | const checkedLinks = [] 109 | let processed = 0 110 | 111 | for (let i = 0; i < allLinks.length; i += batchSize) { 112 | const batch = allLinks.slice(i, i + batchSize) 113 | const batchPromises = batch.map(async ({ person, link }) => { 114 | const status = await checkUrl(link.url) 115 | return { person, link, status } 116 | }) 117 | 118 | const results = await Promise.allSettled(batchPromises) 119 | for (let j = 0; j < results.length; j++) { 120 | const result = results[j] 121 | if (result.status === 'fulfilled') { 122 | checkedLinks.push(result.value) 123 | } else { 124 | // Handle promise rejection (shouldn't happen, but just in case) 125 | const batchItem = batch[j] 126 | checkedLinks.push({ 127 | person: batchItem.person, 128 | link: batchItem.link, 129 | status: { status: 'broken', error: result.reason?.message || 'Unknown error', checkedAt: new Date().toISOString() }, 130 | }) 131 | } 132 | } 133 | 134 | processed += batch.length 135 | const percentage = Math.round((processed / totalLinks) * 100) 136 | console.log(`Progress: ${processed}/${totalLinks} (${percentage}%)`) 137 | } 138 | 139 | // Group checked links back by person 140 | const personMap = new Map() 141 | for (const person of data) { 142 | personMap.set(person, []) 143 | } 144 | 145 | for (const { person, link, status } of checkedLinks) { 146 | personMap.get(person)?.push({ ...link, ...status }) 147 | } 148 | 149 | const updated = Array.from(personMap.entries()).map(([person, linkStatuses]) => ({ 150 | ...person, 151 | linkStatuses, 152 | })) 153 | 154 | const broken = updated 155 | .flatMap((entry) => entry.linkStatuses.map((link) => ({ ...link, name: entry.name }))) 156 | .filter((link) => link.status !== 'ok') 157 | 158 | await fs.writeFile('data.json', JSON.stringify(updated, null, 2), 'utf-8') 159 | 160 | console.log(`\nChecked ${totalLinks} links across ${updated.length} streamers.`) 161 | if (broken.length) { 162 | console.log('\nBroken links:') 163 | broken.forEach((item) => { 164 | const statusText = item.httpStatus ? ` (${item.httpStatus})` : '' 165 | console.log(`- ${item.name} → ${item.label}: ${item.url}${statusText}${item.error ? ` — ${item.error}` : ''}`) 166 | }) 167 | } else { 168 | console.log('All links look good!') 169 | } 170 | } 171 | 172 | await checkLinks() 173 | -------------------------------------------------------------------------------- /statuses.json: -------------------------------------------------------------------------------- 1 | { 2 | "generatedAt": "2025-12-15T10:25:05.904Z", 3 | "entries": [ 4 | { 5 | "name": "Jorge Cano", 6 | "youtube": { 7 | "channelId": "UCdp9sM22GW5uIOaU9eZ7n6w", 8 | "lastUpload": "2025-06-06T17:00:06+00:00" 9 | } 10 | }, 11 | { 12 | "name": "Gonzalo Pozzo", 13 | "youtube": { 14 | "channelId": "UCCvaRcYdZCZBrBQVnsUBg5Q", 15 | "lastUpload": "2025-10-29T14:53:39+00:00" 16 | }, 17 | "twitch": { 18 | "userId": "48388828", 19 | "login": "goncypozzo", 20 | "lastLive": "2025-12-09T21:58:30Z", 21 | "lastVideo": "2025-12-09T21:58:30Z" 22 | } 23 | }, 24 | { 25 | "name": "Noe VamoaCodear", 26 | "youtube": { 27 | "channelId": "UCI4kOPuHdMoCzrE9QfSC0nQ", 28 | "lastUpload": "2023-10-10T18:26:12+00:00" 29 | }, 30 | "twitch": { 31 | "userId": "577749808", 32 | "login": "vamoacodear", 33 | "lastLive": null, 34 | "lastVideo": null 35 | } 36 | }, 37 | { 38 | "name": "Sebastián Carroza", 39 | "twitch": { 40 | "userId": "144242244", 41 | "login": "razor7w_w", 42 | "lastLive": null, 43 | "lastVideo": null 44 | } 45 | }, 46 | { 47 | "name": "Ignacio Gutiérrez", 48 | "youtube": { 49 | "channelId": "UCH7IANkyEcsVW_y1IlpkamQ", 50 | "lastUpload": "2025-12-03T14:00:06+00:00" 51 | }, 52 | "twitch": { 53 | "userId": "517911408", 54 | "login": "bluuweb", 55 | "lastLive": "2025-11-12T21:29:56Z", 56 | "lastVideo": "2025-11-12T21:29:56Z" 57 | } 58 | }, 59 | { 60 | "name": "Ana Belisa Martínez", 61 | "youtube": { 62 | "channelId": "UC1kRtcxh5vxl4CSwNZ2U-Sw", 63 | "lastUpload": "2025-10-04T04:17:53+00:00" 64 | }, 65 | "twitch": { 66 | "userId": "517322178", 67 | "login": "anabelisam", 68 | "lastLive": null, 69 | "lastVideo": null 70 | } 71 | }, 72 | { 73 | "name": "Guillermo Rodas", 74 | "youtube": { 75 | "channelId": "UCA9rep71JxeR7tZotHqFDig", 76 | "lastUpload": "2025-07-09T09:30:42+00:00" 77 | }, 78 | "twitch": { 79 | "userId": "152590097", 80 | "login": "guillermorodas", 81 | "lastLive": null, 82 | "lastVideo": "2024-09-20T11:20:01Z" 83 | } 84 | }, 85 | { 86 | "name": "Julián Duque", 87 | "youtube": { 88 | "channelId": "UCf_ymsPEZLmSM7_OvLF87KQ", 89 | "lastUpload": "2024-05-29T21:22:03+00:00" 90 | }, 91 | "twitch": { 92 | "userId": "105683011", 93 | "login": "julianduque", 94 | "lastLive": null, 95 | "lastVideo": "2022-11-22T21:09:09Z" 96 | } 97 | }, 98 | { 99 | "name": "Lina Castro", 100 | "twitch": { 101 | "userId": "246117544", 102 | "login": "lirrums", 103 | "lastLive": null, 104 | "lastVideo": "2020-09-09T19:43:04Z" 105 | } 106 | }, 107 | { 108 | "name": "Daniel Suarez Dev", 109 | "youtube": { 110 | "channelId": "UC_zzfLSjrYNKrOIGK_js_AA", 111 | "lastUpload": "2025-06-26T15:57:44+00:00" 112 | }, 113 | "twitch": { 114 | "userId": "550376014", 115 | "login": "danielsuarezdev", 116 | "lastLive": null, 117 | "lastVideo": "2022-03-02T00:29:40Z" 118 | } 119 | }, 120 | { 121 | "name": "Nicolas Molina", 122 | "youtube": { 123 | "channelId": "UCz8-0dWiVLY17Rea7qi2cSQ", 124 | "lastUpload": "2025-12-01T22:59:31+00:00" 125 | } 126 | }, 127 | { 128 | "name": "Desiré Carmona", 129 | "twitch": { 130 | "userId": "485423281", 131 | "login": "helle_world", 132 | "lastLive": null, 133 | "lastVideo": null 134 | } 135 | }, 136 | { 137 | "name": "Carlos Azaustre", 138 | "youtube": { 139 | "channelId": "UCJgGc8pQO1lv04VXrBxA_Hg", 140 | "lastUpload": "2025-11-19T12:06:09+00:00" 141 | }, 142 | "twitch": { 143 | "userId": "245405656", 144 | "login": "carlosazaustre", 145 | "lastLive": null, 146 | "lastVideo": "2022-07-26T12:17:03Z" 147 | } 148 | }, 149 | { 150 | "name": "Brais Moure", 151 | "youtube": { 152 | "channelId": "UCxPD7bsocoAMq8Dj18kmGyQ", 153 | "lastUpload": "2025-12-11T17:00:50+00:00" 154 | }, 155 | "twitch": { 156 | "userId": "499886526", 157 | "login": "mouredev", 158 | "lastLive": "2025-11-12T18:58:02Z", 159 | "lastVideo": "2025-11-12T18:58:02Z" 160 | } 161 | }, 162 | { 163 | "name": "Miguel Ángel Durán", 164 | "youtube": { 165 | "channelId": "UC8LeXCWOalN8SxlrPcG-PaQ", 166 | "lastUpload": "2025-12-13T20:11:48+00:00" 167 | }, 168 | "twitch": { 169 | "userId": "422915264", 170 | "login": "midudev", 171 | "lastLive": "2025-12-08T17:01:44Z", 172 | "lastVideo": "2025-12-08T17:01:44Z" 173 | } 174 | }, 175 | { 176 | "name": "Pablo Sirera", 177 | "youtube": { 178 | "channelId": "UCl41m8HBifhzM6Dh1V04wqA", 179 | "lastUpload": "2025-11-18T17:01:42+00:00" 180 | }, 181 | "twitch": { 182 | "userId": "421932815", 183 | "login": "pablosirera", 184 | "lastLive": null, 185 | "lastVideo": null 186 | } 187 | }, 188 | { 189 | "name": "Santiago Martín", 190 | "twitch": { 191 | "userId": "30664176", 192 | "login": "santima10", 193 | "lastLive": null, 194 | "lastVideo": "2020-05-17T13:16:26Z" 195 | } 196 | }, 197 | { 198 | "name": "Jorge Baumann", 199 | "youtube": { 200 | "channelId": "UCTTj5ztXnGeDRPFVsBp7VMA", 201 | "lastUpload": "2025-02-04T17:36:29+00:00" 202 | }, 203 | "twitch": { 204 | "userId": "41107227", 205 | "login": "baumannzone", 206 | "lastLive": null, 207 | "lastVideo": "2021-09-25T08:45:30Z" 208 | } 209 | }, 210 | { 211 | "name": "Dorian Designs", 212 | "youtube": { 213 | "channelId": "UCzuwt7Pi_VB8cP5q5UE4u-A", 214 | "lastUpload": "2025-01-26T12:30:06+00:00" 215 | }, 216 | "twitch": { 217 | "userId": "101327309", 218 | "login": "doriandesings", 219 | "lastLive": null, 220 | "lastVideo": null 221 | } 222 | }, 223 | { 224 | "name": "Oscar Barajas", 225 | "youtube": { 226 | "channelId": "UCw05fUBPwmpu-ehXFMqfdMw", 227 | "lastUpload": "2025-06-10T23:55:24+00:00" 228 | } 229 | }, 230 | { 231 | "name": "Ricardo Celis", 232 | "twitch": { 233 | "userId": "24863137", 234 | "login": "celismx", 235 | "lastLive": null, 236 | "lastVideo": null 237 | } 238 | }, 239 | { 240 | "name": "Pablo Trinidad", 241 | "youtube": { 242 | "channelId": "UC-Ol76LwNXj8scU_VtKDxVg", 243 | "lastUpload": "2020-04-03T02:33:23+00:00" 244 | } 245 | }, 246 | { 247 | "name": "Héctor De León", 248 | "youtube": { 249 | "channelId": "UCDUdeFslCNoM29MAlZOfdWQ", 250 | "lastUpload": "2025-12-09T18:21:40+00:00" 251 | }, 252 | "twitch": { 253 | "userId": "401736135", 254 | "login": "hdeleonnet", 255 | "lastLive": "2025-12-12T19:36:15Z", 256 | "lastVideo": "2025-12-12T19:36:15Z" 257 | } 258 | }, 259 | { 260 | "name": "Jose Jesus Guzman Eusebio", 261 | "youtube": { 262 | "channelId": "UCDUdeFslCNoM29MAlZOfdWQ", 263 | "lastUpload": "2025-12-09T18:21:40+00:00" 264 | }, 265 | "twitch": { 266 | "userId": "638366392", 267 | "login": "brujeriatech", 268 | "lastLive": null, 269 | "lastVideo": "2023-05-01T22:17:12Z" 270 | } 271 | }, 272 | { 273 | "name": "Erifranck Nuñez", 274 | "youtube": { 275 | "channelId": "UCvBuIO5PR2saqJm-doTyZrA", 276 | "lastUpload": "2025-12-09T12:26:52+00:00" 277 | }, 278 | "twitch": { 279 | "userId": "133237970", 280 | "login": "erifranck", 281 | "lastLive": "2025-12-04T01:47:10Z", 282 | "lastVideo": "2025-12-04T01:47:10Z" 283 | } 284 | }, 285 | { 286 | "name": "Leonidas Esteban", 287 | "youtube": { 288 | "channelId": "UCLsSfk2x6p3XvlknDi39zCQ", 289 | "lastUpload": "2025-04-15T00:43:41+00:00" 290 | } 291 | }, 292 | { 293 | "name": "Fazttech", 294 | "youtube": { 295 | "channelId": "UCX9NJ471o7Wie1DQe94RVIg", 296 | "lastUpload": "2025-12-13T21:05:23+00:00" 297 | }, 298 | "twitch": { 299 | "userId": "223370704", 300 | "login": "fazttech", 301 | "lastLive": null, 302 | "lastVideo": null 303 | } 304 | } 305 | ] 306 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Awesome EStreamers Coders 2 | 3 | ℹ️ Si estas haciendo streaming en Twitch o YouTube sobre contenido relacionado a la tecnología o programación eres bienvenida o bienvenido de hacer un PR agregando tu información en esta lista. 4 | 5 | ## Argentina 6 | 7 | - **Jorge Cano** — #Angular #GDE · Owner de @ngbaires · @ngconf organizer · Principal architect @HeroDevs @scullyIO · @angulareando · #EStreamerCoders. 8 | - [YouTube](https://www.youtube.com/JorgeCano) · [Twitter](https://twitter.com/jorgeucano) 9 | - Última actividad → YouTube: 6 jun 2025 10 | - **Gonzalo Pozzo** — Solutions architect @ Vercel, Frontend / React ❤. 11 | - [Sitio web](https://goncy.dev) · [YouTube](https://www.youtube.com/c/GonzaloPozzo) · [Twitch](https://www.twitch.tv/goncypozzo) · [Twitter](https://twitter.com/goncy) · [Facebook](https://www.facebook.com/goncy.pozzo) 12 | - Última actividad → YouTube: 29 oct 2025 · Twitch última vez en vivo: 9 dic 2025 13 | - **Noe VamoaCodear** — Desarrolladora Frontend 🟣 • Aprendemos tecnologías en Twitch. 14 | - [YouTube](https://www.youtube.com/c/vamoacodear) · [Twitch](https://www.twitch.tv/vamoacodear) · [Twitter](https://twitter.com/vamoacodear) 15 | - Última actividad → YouTube: 10 oct 2023 · Twitch: Desconocida 16 | 17 | ## Chile 18 | 19 | - **Sebastián Carroza** — Desarrollador Frontend enfocado a Javascript, Vue, Typescript, Vuetify. 20 | - [Twitch](https://www.twitch.tv/razor7w_w) 21 | - Última actividad → Twitch: Desconocida 22 | - **Ignacio Gutiérrez** — Desarrollador Frontend; comparte contenido sobre React, NextJS, Vue y más. 23 | - [Sitio web](https://linkr.bio/bluuweb) · [YouTube](https://www.youtube.com/bluuweb) · [Twitch](https://www.twitch.tv/bluuweb) · [Twitter](https://twitter.com/bluuweb) · [Facebook](https://www.facebook.com/bluuweb) 24 | - Última actividad → YouTube: 3 dic 2025 · Twitch última vez en vivo: 12 nov 2025 25 | 26 | ## Colombia 27 | 28 | - **Ana Belisa Martínez** — Master Coach at @platzi · Systems Engineer · Web Developer · Mom Bebeloper · Lead at DevC Bogotá · Founder @wcfcolombia · Speaker · Head of #ElCodigoRosa. 29 | - [YouTube](https://www.youtube.com/anabelisam) · [Twitch](https://www.twitch.tv/anabelisam) · [Twitter](https://twitter.com/anabelisam_) · [Facebook](https://www.facebook.com/anabelisam) 30 | - Última actividad → YouTube: 4 oct 2025 · Twitch: Desconocida 31 | - **Guillermo Rodas** — Made in 🇨🇴 Colombia living in Sweden 🇸🇪 — Google Developer Expert en Tecnologías Web. 32 | - [Sitio web](https://guillermorodas.com/) · [YouTube](https://www.youtube.com/guillermorodas) · [Twitch](https://www.twitch.tv/guillermorodas) · [Twitter](https://twitter.com/glrodasz) 33 | - Última actividad → YouTube: 9 jul 2025 · Twitch último video: 20 sept 2024 34 | - **Julián Duque** — Developer and Educator at MNTD · Community Leader · NodeConf and JSConf Colombia organizer · Sr. Developer Advocate at Salesforce Heroku. 35 | - [Sitio web](https://www.julianduque.co) · [YouTube](https://www.youtube.com/user/julianduquej) · [Twitch](https://www.twitch.tv/julianduque) · [Twitter](https://twitter.com/julian_duque) 36 | - Última actividad → YouTube: 29 may 2024 · Twitch último video: 22 nov 2022 37 | - **Lina Castro** — Software Developer | Linux Enthusiastic | Developer Ubuntu Touch #OpenSource #FreeSoftware. 38 | - [Twitch](https://www.twitch.tv/lirrums) · [Twitter](https://twitter.com/lirrums) 39 | - Última actividad → Twitch último video: 9 sept 2020 40 | - **Daniel Suarez Dev** — Frontend Developer and Educator | Freelancer | JavaScript, HTML, CSS, Figma y todo tema que aporte a la comunidad frontend. 41 | - [YouTube](https://www.youtube.com/channel/UC_zzfLSjrYNKrOIGK_js_AA) · [Twitch](https://www.twitch.tv/danielsuarezdev) · [Twitter](https://twitter.com/DanielSuarezDev) 42 | - Última actividad → YouTube: 26 jun 2025 · Twitch último video: 2 mar 2022 43 | - **Nicolas Molina** — GDE in Web Technologies and Angular. Senior Front-end developer and teacher at Platzi. 44 | - [Sitio web](https://nicobytes.com/) · [YouTube](https://www.youtube.com/c/nicobytes) · [Twitter](https://twitter.com/nicobytes) 45 | - Última actividad → YouTube: 1 dic 2025 46 | 47 | ## España 48 | 49 | - **Desiré Carmona** — Unity programmer from Spain. She used to be a web developer/designer and UX-UI designer. Now she creates games! 50 | - [Twitch](https://www.twitch.tv/helle_world) · [Twitter](https://twitter.com/helleworld_) 51 | - Última actividad → Twitch: Desconocida 52 | - **Carlos Azaustre** — Desarrollador web desde hace más de 8 años. Google Developer Expert (GDE) en Tecnologías Web. 53 | - [Sitio web](https://mypublicinbox.com/carlosazaustre) · [YouTube](https://www.youtube.com/CarlosAzaustre) · [Twitch](https://www.twitch.tv/carlosazaustre) · [Twitter](https://twitter.com/carlosazaustre) 54 | - Última actividad → YouTube: 19 nov 2025 · Twitch último video: 26 jul 2022 55 | - **Brais Moure** — Desarrollador de software profesional desde hace más de 10 años. Actualmente trabaja como freelance creando apps para iOS y Android. 56 | - [Sitio web](https://moure.dev/) · [YouTube](https://www.youtube.com/MouredevApps) · [Twitch](https://www.twitch.tv/mouredev) · [Twitter](https://twitter.com/MoureDev) · [Facebook](https://www.facebook.com/mouredev) 57 | - Última actividad → YouTube: 11 dic 2025 · Twitch última vez en vivo: 12 nov 2025 58 | - **Miguel Ángel Durán** — Javascript ☕️, React ⚛️, CSS🎨, HTML, Node.js, Firebase y mucho más. 59 | - [Sitio web](https://midu.dev/) · [YouTube](https://www.youtube.com/midudev) · [Twitch](https://www.twitch.tv/midudev) · [Twitter](https://twitter.com/midudev) · [Facebook](https://www.facebook.com/midudev.frontend) 60 | - Última actividad → YouTube: 13 dic 2025 · Twitch última vez en vivo: 8 dic 2025 61 | - **Pablo Sirera** — Desarrollador Frontend enfocado con Javascript, Vue 💚, Nuxt, Firebase y más 🔥. 62 | - [Sitio web](https://pablosirera.com/) · [YouTube](https://www.youtube.com/PabloSirera) · [Twitch](https://www.twitch.tv/pablosirera) · [Twitter](https://twitter.com/pablosirera) 63 | - Última actividad → YouTube: 18 nov 2025 · Twitch: Desconocida 64 | - **Santiago Martín** — Software Engineer 🚀 TypeScript, React, NEXT.js, Node, Deno, Google Cloud (+ Firebase)... todo lo necesario para hacer un proyecto desde 0. 65 | - [Sitio web](https://www.santiagomartin.dev/) · [Twitch](https://www.twitch.tv/santima10) · [Twitter](https://twitter.com/SantiMA10b) 66 | - Última actividad → Twitch último video: 17 may 2020 67 | - **Jorge Baumann** — Open Source Developer at #RambitoJS 🐶 · JavaScript · Vue.js · Testing · Firebase · CSS · Node.js. 68 | - [Sitio web](https://www.baumannzone.dev/) · [YouTube](https://www.youtube.com/channel/UCTTj5ztXnGeDRPFVsBp7VMA) · [Twitch](https://www.twitch.tv/baumannzone) · [Twitter](https://twitter.com/baumannzone) 69 | - Última actividad → YouTube: 4 feb 2025 · Twitch último video: 25 sept 2021 70 | - **Dorian Designs** — Apasionado de las nuevas tecnologías y en especial del ecosistema de javascript. 71 | - [YouTube](https://www.youtube.com/channel/UCzuwt7Pi_VB8cP5q5UE4u-A) · [Twitch](https://www.twitch.tv/doriandesings) · [Twitter](https://twitter.com/DorianDesings) · [Facebook](https://www.facebook.com/yoaprendocondorian) 72 | - Última actividad → YouTube: 26 ene 2025 · Twitch: Desconocida 73 | 74 | ## Mexico 75 | 76 | - **Oscar Barajas** — Frontend & Foundation Layer at @platzi #education · Lead at Developer Circles from Facebook · ReactJS, Speaker & Blogger. I teach ReactJS in @platzi - 🇲🇽🇨🇴 77 | - [Sitio web](https://gndx.dev/) · [YouTube](https://www.youtube.com/c/oscarbarajas) · [Twitter](https://twitter.com/gndx) 78 | - Última actividad → YouTube: 11 jun 2025 79 | - **Ricardo Celis** — Streams Science & Technology, Among Us and Apex Legends. 80 | - [Twitch](https://www.twitch.tv/celismx) · [Twitter](https://twitter.com/CelisMX) 81 | - Última actividad → Twitch: Desconocida 82 | - **Pablo Trinidad** — Software Engineer; ha trabajado para Google, Microsoft y startups de LATAM. Estudiante de la Facultad de Ciencias de la UNAM [Go/Python/C++/Punch Cards]. 83 | - [YouTube](https://www.youtube.com/channel/UC-Ol76LwNXj8scU_VtKDxVg) · [Twitter](https://twitter.com/_pablotrinidad_) 84 | - Última actividad → YouTube: 3 abr 2020 85 | - **Héctor De León** — Ingeniero en computación, aficionado a la inteligencia artificial y al desarrollo de sistemas que optimicen procesos. Creador de contenido para Youtube y Udemy. Microsoft Most Valuable Professional 2022 🏆. 86 | - [Sitio web](http://hdeleon.net/) · [YouTube](https://www.youtube.com/c/hdeleonnet) · [Twitch](https://www.twitch.tv/hdeleonnet) · [Twitter](https://twitter.com/powerhdeleon) 87 | - Última actividad → YouTube: 9 dic 2025 · Twitch última vez en vivo: 12 dic 2025 88 | - **Jose Jesus Guzman Eusebio** — Le encanta enseñar y compartir conocimiento en tecnología como Sherpa Digital de Microsoft y host del podcast "No es Brujería, es Tecnología". 89 | - [YouTube](https://www.youtube.com/c/hdeleonnet) · [Twitch](https://www.twitch.tv/brujeriatech) · [Twitter](https://twitter.com/powerhdeleon) 90 | - Última actividad → YouTube: 9 dic 2025 · Twitch último video: 2 may 2023 91 | 92 | ## Venezuela 93 | 94 | - **Erifranck Nuñez** — Desarrollador frontend en Globant; amante de la ilustración y la animación; entrenador pokemon en tiempos libres. 95 | - [Sitio web](https://www.erifranck.art/) · [YouTube](https://www.youtube.com/c/erifrancknunez) · [Twitch](https://www.twitch.tv/erifranck) · [Twitter](https://twitter.com/erifranckn) 96 | - Última actividad → YouTube: 9 dic 2025 · Twitch última vez en vivo: 4 dic 2025 97 | 98 | ## Perú 99 | 100 | - **Leonidas Esteban** — Google Developer Expert, programming teacher, speaker and Frontend professional. 101 | - [Sitio web](https://leonidasesteban.com/cursos) · [YouTube](https://www.youtube.com/LeonidasEsteban) · [Twitter](https://twitter.com/LeonidasEsteban) · [Facebook](https://www.facebook.com/LeonidasEsteban) 102 | - Última actividad → YouTube: 15 abr 2025 103 | - **Fazttech** — Programador y Desarrollador Web que comparte tutoriales y juega online :). 104 | - [Sitio web](https://faztweb.com/) · [YouTube](https://www.youtube.com/fazttech) · [Twitch](https://www.twitch.tv/fazttech) · [Twitter](https://twitter.com/fazttech) · [Facebook](https://www.facebook.com/FaztTech) 105 | - Última actividad → YouTube: 13 dic 2025 · Twitch: Desconocida 106 | -------------------------------------------------------------------------------- /statuses.mjs: -------------------------------------------------------------------------------- 1 | import fs from 'fs/promises' 2 | import path from 'path' 3 | import { config } from 'dotenv' 4 | 5 | config() 6 | 7 | const dataPath = path.resolve('data.json') 8 | const data = JSON.parse(await fs.readFile(dataPath, 'utf-8')) 9 | 10 | const TWITCH_CLIENT_ID = process.env.TWITCH_CLIENT_ID 11 | const TWITCH_CLIENT_SECRET = process.env.TWITCH_CLIENT_SECRET 12 | const youtubeCache = new Map() 13 | let twitchAuth 14 | 15 | function sleep(ms) { 16 | return new Promise((resolve) => setTimeout(resolve, ms)) 17 | } 18 | 19 | function buildYouTubeUrl(identifier) { 20 | if (/^https?:\/\//i.test(identifier)) return identifier 21 | 22 | // Handle different YouTube URL formats 23 | if (identifier.startsWith('@')) { 24 | return `https://www.youtube.com/${identifier}` 25 | } 26 | if (identifier.startsWith('c/') || identifier.startsWith('user/') || identifier.startsWith('channel/')) { 27 | return `https://www.youtube.com/${identifier}` 28 | } 29 | 30 | // Try handle format first for simple identifiers 31 | return `https://www.youtube.com/@${identifier}` 32 | } 33 | 34 | async function resolveYouTubeChannelId(identifier) { 35 | if (!identifier) return null 36 | if (/^UC[A-Za-z0-9_-]{22}$/i.test(identifier)) return identifier 37 | 38 | const slug = identifier.replace(/^channel\//i, '') 39 | if (/^UC[A-Za-z0-9_-]{22}$/i.test(slug)) return slug 40 | 41 | // Build channel page URLs to try 42 | const channelUrls = [] 43 | 44 | if (/^https?:\/\//i.test(identifier)) { 45 | channelUrls.push(identifier) 46 | } else if (identifier.startsWith('@')) { 47 | channelUrls.push(`https://www.youtube.com/${identifier}`) 48 | } else if (identifier.startsWith('c/')) { 49 | channelUrls.push(`https://www.youtube.com/${identifier}`) 50 | } else if (identifier.startsWith('user/')) { 51 | channelUrls.push(`https://www.youtube.com/${identifier}`) 52 | } else { 53 | // Try handle format first, then fallback formats 54 | channelUrls.push(`https://www.youtube.com/@${identifier}`) 55 | channelUrls.push(`https://www.youtube.com/c/${identifier}`) 56 | channelUrls.push(`https://www.youtube.com/user/${identifier}`) 57 | } 58 | 59 | // Check cache first 60 | for (const url of channelUrls) { 61 | const cached = youtubeCache.get(url) 62 | if (cached) return cached 63 | } 64 | 65 | // Scrape channel page HTML to extract channel ID 66 | for (const url of channelUrls) { 67 | try { 68 | const response = await fetch(url, { 69 | headers: { 70 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' 71 | } 72 | }) 73 | if (!response.ok) continue 74 | 75 | const html = await response.text() 76 | 77 | // Method 1: Look for channel ID in JSON-LD structured data 78 | const jsonLdMatch = html.match(/