<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jaime</title>
    <description>The latest articles on DEV Community by Jaime (@jaimesenoret).</description>
    <link>https://dev.to/jaimesenoret</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F272180%2Fafc484d5-6875-4c9c-a19b-a3d59e77c4ac.jpg</url>
      <title>DEV Community: Jaime</title>
      <link>https://dev.to/jaimesenoret</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jaimesenoret"/>
    <language>en</language>
    <item>
      <title>Get the rain alerts in Telegram</title>
      <dc:creator>Jaime</dc:creator>
      <pubDate>Sat, 27 May 2023 11:36:00 +0000</pubDate>
      <link>https://dev.to/jaimesenoret/get-the-rain-alerts-in-telegram-1ofl</link>
      <guid>https://dev.to/jaimesenoret/get-the-rain-alerts-in-telegram-1ofl</guid>
      <description>&lt;h3&gt;
  
  
  (English version bellow...)
&lt;/h3&gt;

&lt;p&gt;La semana pasada estaba mirando el tiempo en la página de la &lt;a href="https://www.aemet.es/es" rel="noopener noreferrer"&gt;Aemet&lt;/a&gt; y me di cuenta hay disponible un API REST publica para consultar información meteorológica.&lt;/p&gt;

&lt;p&gt;Este API es &lt;a href="https://opendata.aemet.es/" rel="noopener noreferrer"&gt;Open Data&lt;/a&gt; y tiene varios recursos interesantes, como el siguiente:&lt;/p&gt;

&lt;p&gt;Este &lt;a href="https://opendata.aemet.es/dist/index.html?#/predicciones-especificas/Predicci%C3%B3n%20por%20municipios%20horaria.%20Tiempo%20actual." rel="noopener noreferrer"&gt;endpoint&lt;/a&gt; devuelve la predicción de lluvia, tormentas y nieve en un municipio en concreto, el cual le pasamos por parámetro. Este recurso es muy útil ya que la Aemet lo actualiza varias veces al día. &lt;/p&gt;

&lt;p&gt;Teniendo esto en cuenta he desarrollado un proyecto en Node, escrito en &lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt;, que consulta este recurso y en función de los datos, te envía un mensaje usando un bot &lt;a href="https://telegram.org/" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; diciéndote la probabilidad que hay de lluvia, tormentas o nieve en las próximas horas. La lógica es la siguiente:&lt;/p&gt;

&lt;p&gt;El proyecto usa el paquete &lt;a href="https://www.npmjs.com/package/cron" rel="noopener noreferrer"&gt;cron&lt;/a&gt; para establecer la frecuencia con la que se consulta el API de la Aemet, por ejemplo, cada hora.&lt;/p&gt;

&lt;p&gt;Cada vez que obtenemos los datos de la Aemet, comprobamos si los datos han sido actualizados desde la última ejecución.&lt;/p&gt;

&lt;p&gt;Si los datos han sido actualizados, obtenemos los datos diarios y elaboramos el mensaje que se enviara a través de un bot Telegram. Puedes crear tu bot de Telegram siguiendo este &lt;a href="https://www.toptal.com/python/telegram-bot-tutorial-python" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt;. Para enviar mensajes a través del bot se usa este &lt;a href="https://www.npmjs.com/package/node-telegram-bot-api" rel="noopener noreferrer"&gt;paquete&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instalación y ejecución
&lt;/h2&gt;

&lt;p&gt;Este es el &lt;a href="https://github.com/jaime2196/alertaLluvia" rel="noopener noreferrer"&gt;repositorio&lt;/a&gt; del proyecto y estos son los pasos para ejecutarlo en tu local:&lt;/p&gt;

&lt;p&gt;Clonamos el proyecto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; git clone https://github.com/jaime2196/alertaLluvia.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Después hay que configurar una serie de variables en un fichero '.env' en la raiz del proyecto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#Api Aemet
location=28013 #Madrid, por ejemplo
apiKey=???
#Umbral de avisos (%)
avisoTormenta=5
avisoPrecipitacion=5
avisoNieve=5
#Api telegram
token=???
usuarios=???
#Config cron
configCron=*/10 * * * * #Cada 10 minutos
#Node Time Zone
TZ=Europe/Madrid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;location: código de la localidad sobre la que quieres recibir los avisos. Puedes encontrarlo &lt;a href="https://www.ine.es/daco/daco42/codmun/codmunmapa.htm" rel="noopener noreferrer"&gt;aquí&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;apiKey: necesitas una API key para usar los servicios de Aemet. Puede solicitarla &lt;a href="https://opendata.aemet.es/" rel="noopener noreferrer"&gt;aquí&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;avisoTormenta: es el % mínimo de probabilidad con el que quieres recibir los avisos de tormenta. Por ejemplo, si estableces el 5, serás notificado si hay un 5% o más de probabilidad de tormenta.&lt;/li&gt;
&lt;li&gt;avisoPrecipitacion: igual que el anterior, pero para la lluvia.&lt;/li&gt;
&lt;li&gt;avisoNieve: igual que el anterior, pero para la nieve.&lt;/li&gt;
&lt;li&gt;token: es el token de Telegram que has generado al crear tu bot.&lt;/li&gt;
&lt;li&gt;usuarios: ID de usuarios de Telegram (separados por ",") a los que quieres enviar notificaciones. Para conocer tu ID de usuario puedes usar el bot '@userinfobot'&lt;/li&gt;
&lt;li&gt;configCron: frecuencia con la que se consulta el API de Aemet para obtener los datos. Solo te llegara una notificación cada vez que Aemet actualiza sus datos, por lo que es probable que el API de Aemet se consulte varias veces, pero no te lleguen notificaciones, ya que los datos no han cambiado.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Puede obtener más información sobre programar tareas con cron &lt;a href="https://www.redeszone.net/tutoriales/servidores/cron-crontab-linux-programar-tareas/" rel="noopener noreferrer"&gt;aquí&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TZ: si lo ejecutamos desde Docker, es mejor especificar la zona horaria para que los valores de las fechas sean correctos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Después instalamos las dependencias de Node y ejecutamos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install 
npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ejecución con Docker
&lt;/h2&gt;

&lt;p&gt;He construido una imagen para varias arquitecturas para que sea más fácil de ejecutar.&lt;br&gt;
Las arquitecturas disponibles son:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;amd64&lt;/li&gt;
&lt;li&gt;arm/v7&lt;/li&gt;
&lt;li&gt;arm64/v8&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para ejecutarlo con Docker simplemente creamos el archivo '.env' con las variables y ejecutamos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--env-file&lt;/span&gt; ./.env jaime2196/node-alertalluvia
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ejemplo
&lt;/h3&gt;

&lt;p&gt;Este es un aviso recibido en Telegram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabgrz4f17s0xrfucsurl.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabgrz4f17s0xrfucsurl.PNG" alt="Ejemplo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Contacto
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/jaimesenoret/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/JaimeSenoret" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  English version
&lt;/h3&gt;

&lt;p&gt;Last week I was looking at the weather on the page of &lt;a href="https://www.aemet.es/es" rel="noopener noreferrer"&gt;Aemet&lt;/a&gt; (the Spanish meteorological agency) and I realized there is a public REST API available to consult meteorological information.&lt;/p&gt;

&lt;p&gt;This API is &lt;a href="https://opendata.aemet.es/" rel="noopener noreferrer"&gt;Open Data&lt;/a&gt; and has several interesting resources, such as the following:&lt;/p&gt;

&lt;p&gt;This &lt;a href="https://opendata.aemet.es/dist/index.html?#/predicciones-especificas/Predicci%C3%B3n%20por%20municipios%20horaria.%20Tiempo%20actual." rel="noopener noreferrer"&gt;endpoint&lt;/a&gt; returns the rain forecast, storms and snow in a specific town, which we pass as a parameter. This resource is very useful since Aemet updates it several times a day.&lt;/p&gt;

&lt;p&gt;With this in mind I have developed a project in Node, written in &lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt;, which queries this resource and based on the data, sends you a message using a &lt;a href="https://telegram.org/" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt; bot telling you the probability of rain, storms or snow in the next few hours. This project only works with cities in Spain, since the meteorological data is provided by the Spanish meteorological agency. &lt;br&gt;
The logic is the following:&lt;/p&gt;

&lt;p&gt;The project uses the &lt;a href="https://www.npmjs.com/package/cron" rel="noopener noreferrer"&gt;cron&lt;/a&gt; package to set the frequency with which the Aemet API is queried, for example, every hour.&lt;/p&gt;

&lt;p&gt;Every time we get the data from Aemet, we check if the data has been updated since the last run.&lt;/p&gt;

&lt;p&gt;If the data has been updated, we obtain the daily data and prepare the message that will be sent through a Telegram bot. You can create your Telegram bot by following this &lt;a href="https://www.toptal.com/python/telegram-bot-tutorial-python" rel="noopener noreferrer"&gt;tutorial&lt;/a&gt;. To send messages through the bot use this &lt;a href="https://www.npmjs.com/package/node-telegram-bot-api" rel="noopener noreferrer"&gt;package&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Installation and execution
&lt;/h2&gt;

&lt;p&gt;This is the &lt;a href="https://github.com/jaime2196/alertaLluvia" rel="noopener noreferrer"&gt;repository&lt;/a&gt; of the project and these are the steps to run it:&lt;/p&gt;

&lt;p&gt;Clone the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; git clone https://github.com/jaime2196/alertaLluvia.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you have to configure a series of variables in an '.env' file in the root of the project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#Api Aemet
location=28013 #Madrid, for example
apiKey=???
#Threshold of warnings (%)
avisoTormenta=5
avisoPrecipitacion=5
avisoNieve=5
#Api telegram
token=???
usuarios=???
#Config cron
configCron=*/10 * * * * #Cada 10 minutos
#Node Time Zone
TZ=Europe/Madrid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;location: code of the town about which you want to receive notifications. You can find it &lt;a href="https://www.ine.es/daco/daco42/codmun/codmunmapa.htm" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;apiKey: you need an API key to use Aemet services. You can request it &lt;a href="https://opendata.aemet.es/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;avisoTormenta: it is the minimum % probability with which you want to receive storm warnings. For example, if you set 5, you will be notified if there is a 5% or greater chance of a storm.&lt;/li&gt;
&lt;li&gt;avisoPrecipitation: the same as the previous one, but for the rain.&lt;/li&gt;
&lt;li&gt;avisoNieve: same as above, but for snow.&lt;/li&gt;
&lt;li&gt;token: it is the Telegram token that you have generated when creating your bot.&lt;/li&gt;
&lt;li&gt;usuarios: Telegram user ID (separated by ",") to whom you want to send notifications. To know your user ID you can use the bot '@userinfobot'.&lt;/li&gt;
&lt;li&gt;configCron: frequency with which the Aemet API is consulted to obtain the data. You will only receive a notification each time Aemet updates its data, so it is likely that the Aemet API is queried several times, but you do not receive notifications, since the data has not changed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can get more information about scheduling tasks with cron &lt;a href="https://www.redeszone.net/tutoriales/servidores/cron-crontab-linux-schedule-tasks/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TZ: if we run it from Docker, it is better to specify the time zone so that the date values are correct.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then install the Node dependencies and execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install
npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running with Docker
&lt;/h2&gt;

&lt;p&gt;I've built a multi-arch image to make it easier to run.&lt;br&gt;
The available architectures are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;amd64
*arm/v7
*arm64/v8&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To run it with Docker simply create the '.env' file with the variables and execute:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--env-file&lt;/span&gt; ./.env jaime2196/node-rainalert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;This is a notification received on Telegram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabgrz4f17s0xrfucsurl.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabgrz4f17s0xrfucsurl.PNG" alt="Example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What it means: &lt;br&gt;
❗ From 14:00 to 20:00, there is a 35% chance of rain 🌧 and a 35% chance of thunderstorm ⛈&lt;br&gt;
❗ From 20:00 to 02:00, there is a 5% chance of rain 🌧 and a 5% chance of thunderstorm ⛈&lt;/p&gt;

&lt;h2&gt;
  
  
  Stay in touch
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/jaimesenoret/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/JaimeSenoret" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>node</category>
      <category>typescript</category>
      <category>aemet</category>
      <category>telegram</category>
    </item>
    <item>
      <title>Wordle guesser with NodeJS</title>
      <dc:creator>Jaime</dc:creator>
      <pubDate>Wed, 16 Mar 2022 18:50:37 +0000</pubDate>
      <link>https://dev.to/jaimesenoret/wordle-guesser-with-nodejs-1j2i</link>
      <guid>https://dev.to/jaimesenoret/wordle-guesser-with-nodejs-1j2i</guid>
      <description>&lt;h3&gt;
  
  
  (English version bellow...)
&lt;/h3&gt;

&lt;p&gt;Últimamente se ha puesto de moda jugar a &lt;a href="https://es.wikipedia.org/wiki/Wordle_(videojuego)" rel="noopener noreferrer"&gt;Wordle&lt;/a&gt; y como no soy una excepción, yo también me puse jugar. Me di cuenta que al refrescar la página no se pierden las jugadas y además guarda las estadísticas de días anteriores. Entonces me entro curiosidad como estaba desarrollado:&lt;/p&gt;

&lt;p&gt;Me puse a investigar y desde la consola de Google Chrome descubrí cosas interesantes en el 'local storage', vamos a ver:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthj8oayj8d9fykagaz2c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthj8oayj8d9fykagaz2c.png" alt="Local storage de Wordle"&gt;&lt;/a&gt;&lt;br&gt;
Hay varias keys interesantes: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Statistics: contiene un objeto 'guesses', el cual es un array que indica el número de jugadas que hemos necesitado para adivinar la palabra en días anteriores.&lt;/li&gt;
&lt;li&gt;Solution: parece la solución pasada por una &lt;a href="https://es.wikipedia.org/wiki/Funci%C3%B3n_hash" rel="noopener noreferrer"&gt;función hash&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Board: aquí guarda las palabras que hemos usado para intentar adivinar la palabra de hoy. 
Esto es lo más interesante, ya que puedo editarlo, poner las palabras que yo quiera y Wordle lo valida, &lt;strong&gt;incluso si la palabra no existe&lt;/strong&gt;:
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu22r351pzxp74fl5q4qc.png" alt="Panel de Wordle"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aprovechando este error de validación, se me ocurrió usar la librería &lt;a href="https://www.npmjs.com/package/puppeteer" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt; de &lt;a href="https://es.wikipedia.org/wiki/Node.js" rel="noopener noreferrer"&gt;NodeJS&lt;/a&gt; para automatizar el proceso y probar todas las letras del abecedario de una en una hasta encontrar la solución. Vamos a verlo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function tryCombinations(page){
    let lengthWord= getLengthWord();
    //let alphabet = 'abcdefghijklmnñopqrstuvwxyz'.split('');
    let alphabet = 'eaosrnidlctumpbgvyqhfzjñxkw'.split('');
    let word='';
    let correctWords=[];
    let isWordComplete=false;
    for(let i=0;i!=alphabet.length &amp;amp;&amp;amp; !isWordComplete;i++){
        for(let j=0;j!=lengthWord;j++){
            word=word+alphabet[i];
        }
        await tryWord(page, word);
        correctWords=await getHits(page,correctWords);
        if(correctWords.length==lengthWord){
            isWordComplete=true;
        }
        word='';
    }
    return correctWords;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta función recorre cada una de las letras del alfabeto generando una 'palabra' de 5 letras (AAAAA, BBBBB, ...). Para optimizar la búsqueda, puse las letras del alfabeto ordenadas por su frecuencia de uso, según &lt;a href="https://es.wikipedia.org/wiki/Frecuencia_de_aparici%C3%B3n_de_letras#:~:text=Ordenadas%20de%20mayor%20a%20menor,alrededor%20del%2045%25%20del%20texto." rel="noopener noreferrer"&gt;este artículo&lt;/a&gt; de Wikipedia y de además deja buscar una vez tenemos las 5 letras de la palabra. Una vez generada la 'palabra', la guardo en el local storage de la página mediante la función tryWord:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function tryWord(page, word){
    await page.evaluate((word) =&amp;gt; {
        window.localStorage.setItem('board', `[\"${word}\"]`);
    }, word);
    await page.reload({ waitUntil: ["networkidle0", "domcontentloaded"] });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Una vez ha sido validada por Wordle, ejecuto la función getHits para analizar el HTML y obtener los aciertos (si los hay)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function getHits(page, correctWords){
    await page.waitForSelector('.grid-cols-5');
    let bodyHTML = await page.evaluate(() =&amp;gt; document.body.innerHTML);
    bodyHTML=bodyHTML.substring(bodyHTML.indexOf('&amp;lt;main'), bodyHTML.indexOf('&amp;lt;/main'));
    let firstRow= bodyHTML.split('&amp;lt;div class="grid grid-cols-5 gap-[5px] w-full"&amp;gt;')[1];
    let words = firstRow.split('&amp;lt;div class="w-full h-full max-w-[62px] max-h-[62px] inline-flex justify-center items-center text-lg uppercase font-bold select-none text-white');
    let count=0;
    let numStr=getNumStr(getLengthWord());
    for(let i=0;i!=words.length;i++){

        if(words[i].indexOf('bg-correct')!=-1){ //Contains bg-correct
            correctWords.push(getCorrectWord(words[i],count));
            printCorrectWords(correctWords,numStr);
            count++;
        }else if(words[i].indexOf('bg-absent')!=-1 ||
            words[i].indexOf('bg-present')!=-1){
                count++;
        }

    }
    return correctWords;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A medida que va encontrando letras correctas en la palabra, las imprime por consola y cuando la palabra esta completa, imprime el resultado final y el tiempo que ha tardado en la búsqueda. En el peor de los casos (la palabra contenga la letra 'W'), tarda alrededor de un minuto en encontrar la palabra.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fek6lygvlqcsdo69zcafn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fek6lygvlqcsdo69zcafn.gif" alt="Resultado"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Por último, escribe la palabra encontrada como solución en Wordle y toma una captura de pantalla del resultado:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iwoevvsnqvqpvgfchfi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iwoevvsnqvqpvgfchfi.png" alt="Captura de pantalla del resultado"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  English version:
&lt;/h3&gt;

&lt;p&gt;Lately everyone is playing &lt;a href="https://en.wikipedia.org/wiki/Wordle" rel="noopener noreferrer"&gt;Wordle&lt;/a&gt; (&lt;a href="https://wordle.danielfrg.com/" rel="noopener noreferrer"&gt;the spanish version&lt;/a&gt;) and since I'm not an exception, I started playing too. Then, I realized that by refreshing the page the plays are not lost and it also saves the statistics of previous days. Then I got curious how it was developed:&lt;/p&gt;

&lt;p&gt;I began to investigate and from the Google Chrome console I discovered interesting things in the 'local storage', let's see:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthj8oayj8d9fykagaz2c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fthj8oayj8d9fykagaz2c.png" alt="Wordle local storage"&gt;&lt;/a&gt;&lt;br&gt;
There are several interesting keys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Statistics: contains a 'guesses' object, which is an array that indicates the number of attempts we have needed to guess the word in previous days.&lt;/li&gt;
&lt;li&gt;Solution: looks like the solution passed through a &lt;a href="https://en.wikipedia.org/wiki/Hash_function" rel="noopener noreferrer"&gt;hash function&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Board: here it saves the words that we have used to try to guess today's word.
This is the most interesting, since I can edit it, put the words I want and Wordle validates it, &lt;strong&gt;even if the word does not exist&lt;/strong&gt;:
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu22r351pzxp74fl5q4qc.png" alt="Panel de Wordle"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Taking advantage of this validation error, I had the idea of using the &lt;a href="https://www.npmjs.com/package/puppeteer" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt; library from &lt;a href="https://en.wikipedia.org/wiki/Node.js" rel="noopener noreferrer"&gt;NodeJS&lt;/a&gt; to automate the process and try all the letters of the alphabet one at a time until the solution is found. Let's see it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function tryCombinations(page){
    let lengthWord= getLengthWord();
    //let alphabet = 'abcdefghijklmnñopqrstuvwxyz'.split('');
    let alphabet = 'eaosrnidlctumpbgvyqhfzjñxkw'.split('');
    let word='';
    let correctWords=[];
    let isWordComplete=false;
    for(let i=0;i!=alphabet.length &amp;amp;&amp;amp; !isWordComplete;i++){
        for(let j=0;j!=lengthWord;j++){
            word=word+alphabet[i];
        }
        await tryWord(page, word);
        correctWords=await getHits(page,correctWords);
        if(correctWords.length==lengthWord){
            isWordComplete=true;
        }
        word='';
    }
    return correctWords;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function iterates through each of the letters of the alphabet generating a 5-letter 'word' (AAAAA, BBBBB, ...). To optimize the search, I put the letters of the Spanish alphabet ordered by their frequency of use, according to &lt;a href="https://es.wikipedia.org/wiki/Frecuencia_de_aparici%C3%B3n_de_letras#:~:text=Ordenadas%20de%20mayor%20a%20menor,alrededor%20del%2045%25%20del%20texto." rel="noopener noreferrer"&gt;this article&lt;/a&gt; from Wikipedia and also lets search once we have the 5 letters of the word. Once the 'word' is generated, I store it in the page's local storage using the tryWord function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function tryWord(page, word){
    await page.evaluate((word) =&amp;gt; {
        window.localStorage.setItem('board', `[\"${word}\"]`);
    }, word);
    await page.reload({ waitUntil: ["networkidle0", "domcontentloaded"] });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once it has been validated by Wordle, I run the getHits function to parse the HTML and get the hits (if any)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function getHits(page, correctWords){
    await page.waitForSelector('.grid-cols-5');
    let bodyHTML = await page.evaluate(() =&amp;gt; document.body.innerHTML);
    bodyHTML=bodyHTML.substring(bodyHTML.indexOf('&amp;lt;main'), bodyHTML.indexOf('&amp;lt;/main'));
    let firstRow= bodyHTML.split('&amp;lt;div class="grid grid-cols-5 gap-[5px] w-full"&amp;gt;')[1];
    let words = firstRow.split('&amp;lt;div class="w-full h-full max-w-[62px] max-h-[62px] inline-flex justify-center items-center text-lg uppercase font-bold select-none text-white');
    let count=0;
    let numStr=getNumStr(getLengthWord());
    for(let i=0;i!=words.length;i++){

        if(words[i].indexOf('bg-correct')!=-1){ //Contains bg-correct
            correctWords.push(getCorrectWord(words[i],count));
            printCorrectWords(correctWords,numStr);
            count++;
        }else if(words[i].indexOf('bg-absent')!=-1 ||
            words[i].indexOf('bg-present')!=-1){
                count++;
        }

    }
    return correctWords;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As it finds correct letters in the word, it prints them on the console and when the word is complete, it prints the final result and the time it took to search. In the worst case (the word contains the letter 'W'), it takes about a minute to find the word.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fek6lygvlqcsdo69zcafn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fek6lygvlqcsdo69zcafn.gif" alt="Result"&gt;&lt;/a&gt;&lt;br&gt;
Finally, write the found word as a solution in Wordle and take a screenshot of the result:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iwoevvsnqvqpvgfchfi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3iwoevvsnqvqpvgfchfi.png" alt="Screenshot of the result"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>wordle</category>
      <category>puppeteer</category>
    </item>
  </channel>
</rss>
