<?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: Colum Kelly</title>
    <description>The latest articles on DEV Community by Colum Kelly (@columk1).</description>
    <link>https://dev.to/columk1</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%2F1485341%2F7ec08f30-e789-4c5e-917c-b2713d9d6b1f.png</url>
      <title>DEV Community: Colum Kelly</title>
      <link>https://dev.to/columk1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/columk1"/>
    <language>en</language>
    <item>
      <title>How to Build a Javascript Booking Automation Bot</title>
      <dc:creator>Colum Kelly</dc:creator>
      <pubDate>Sat, 13 Jul 2024 20:45:02 +0000</pubDate>
      <link>https://dev.to/columk1/how-to-build-a-javascript-booking-automation-bot-32em</link>
      <guid>https://dev.to/columk1/how-to-build-a-javascript-booking-automation-bot-32em</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;I was recently made aware of a waitlist opening up for a new service in my local area. I thought it would be a nice exercise to automate the registration process and secure one of the first places on the list. &lt;/p&gt;

&lt;h4&gt;
  
  
  Registration System
&lt;/h4&gt;

&lt;p&gt;The online form already existed as it was used to register for a number of different services. The provider announced that there would be a new option added to the form in the coming days. &lt;/p&gt;

&lt;p&gt;My plan was to watch the site for any changes. As soon as the new checkbox option appeared, my script would fill out the rest of the form with my details, check the new input and submit. &lt;/p&gt;

&lt;p&gt;Since the form was loaded with javascript, I would use a headless browser to monitor the page for changes using Puppeteer. I also decided to implement an SMS notification system using Twilio. This would notify me if there were any issues submitting the form.&lt;/p&gt;

&lt;h4&gt;
  
  
  Plan
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Load the target web page in a headless browser.&lt;/li&gt;
&lt;li&gt;Check if there are more checkboxes on the page than before.&lt;/li&gt;
&lt;li&gt;If not, repeat steps 1 and 2.&lt;/li&gt;
&lt;li&gt;If so, open a browser window, fill out and submit the form.&lt;/li&gt;
&lt;li&gt;Send an SMS notification.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Let's Begin
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Step 1. Initialize Local Project
&lt;/h4&gt;

&lt;p&gt;Create a new folder and then navigate to it and run the following commands from the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm init&lt;br&gt;
npm i puppeteer&lt;br&gt;
touch index.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2. Create Browser Instance
&lt;/h4&gt;

&lt;p&gt;Create a headless browser instance using Puppeteer to check the target web page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TargetURL&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headlessBrowser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headlessPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;headlessBrowser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;headlessPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Step 3. Check for Updates
&lt;/h4&gt;

&lt;p&gt;Develop a function to check the page for the expected condition. There are many different ways to compare pages. I checked if there was a new checkbox on the page using CSS selectors. The function returns true if the number of checkboxes on the page is greater than the number provided as an argument. You could also check the text of the page for diffs or use regex to search for matches.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;hasAdditionalCheckbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialCount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;headlessPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reload&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkboxCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;headlessPage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;input[type='checkbox']&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;checkboxCount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;initialCount&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By setting &lt;code&gt;waitUntil&lt;/code&gt; to "load", we instruct Puppeteer to wait for the load event of the page to be fired before proceeding. This means Puppeteer will ensure that all resources, including scripts, stylesheets, and images, have been fully loaded before moving on to the next line of code where we select form elements.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you don't need to access javascript elements it is much easier, as you can simply use Node's native &lt;code&gt;fetch&lt;/code&gt; method to grab the HTML of a page or to fetch a response from an API. In that case Puppeteer is overkill for this function.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Step 4. Create a function to autofill and submit the form.
&lt;/h4&gt;

&lt;p&gt;This will fill out the form using a combination of Puppeteer's page interaction APIs and regular CSS selectors. The &lt;code&gt;click&lt;/code&gt; and &lt;code&gt;type&lt;/code&gt; functions are self-explanatory. The &lt;code&gt;evaluate&lt;/code&gt; function takes a callback which allows you to run javascript inside of the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;autofillForm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;waitUntil&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;load&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#date-picker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;22/07/1989&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#radio-button-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;John Doe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;johndoe@gmail.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkboxes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;input[type="checkbox"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;checkboxes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;checkbox&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;checkbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;previousElementSibling&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;expectedText&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;checkbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#submit-btn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I created a new browser instance using the options argument &lt;code&gt;headless: false&lt;/code&gt; in this function because I wanted to see the confirmation of my form submission on my screen. It would also have been fine to use the existing headless instance and then parse the result of the form submission in Node.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 5. Set up SMS notifications with Twilio.
&lt;/h4&gt;

&lt;p&gt;You may want to be notified when your conditional is triggered. In my case, this was to check if the form had been submitted correctly. Another use-case would be if your form requires manual completion of a captcha or entry of sensitive billing information.&lt;/p&gt;

&lt;p&gt;Go to &lt;a href="https://www.twilio.com/" rel="noopener noreferrer"&gt;https://www.twilio.com/&lt;/a&gt; and create a new account. Once you have verified your phone number, go to the console and navigate to the messages section. You should see a heading titled "Send your First SMS" with some code snippets below. Click the Node.js tab and copy the code snippet provided. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This will include your API keys but it's fine since we'll be running the app locally. If you are hosting your script in a public repo or sharing it you should place these in an environmental variable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In your project directory, run &lt;code&gt;npm i twilio&lt;/code&gt;&lt;br&gt;
Create a new async function called &lt;code&gt;sendSMS&lt;/code&gt; and paste in the snippet from Twilio. It should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendSMS&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accountSid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ACCOUNT_SID&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH_TOKEN&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;twilio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountSid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Waitlist is open!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;yourTwilioPhoneNumber&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;yourPhoneNumber&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also trigger an email or a phone call if preferred.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 6. Putting it All Together
&lt;/h4&gt;

&lt;p&gt;Now we can put everything in an interval:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;waitlistScraper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isWaitlistOpen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;hasAdditionalCheckbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isWaitlistOpen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;autoFillForm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;sendSMS&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;clearInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;waitlistScraper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 5 seconds&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we're done! Use the command &lt;code&gt;node index.js&lt;/code&gt; when you are ready to run the application. You may need to adjust your machine's sleep settings to ensure that the script stays running. Mac users can use the command &lt;code&gt;caffeinate&lt;/code&gt; to achieve this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;This project demonstrates the flexibility of automation through web scraping and browser control. Such techniques can be applied to various scenarios, making it a valuable skill in web development. &lt;/p&gt;

&lt;p&gt;Whether you're booking tickets, monitoring product availability, or gathering data, these skills can greatly enhance your productivity. Plus, they're fun little projects, especially when they save you from staying up all night refreshing a page!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>webscraping</category>
      <category>automation</category>
    </item>
    <item>
      <title>Create and Deploy a Discord Bot for Free with Fly.io</title>
      <dc:creator>Colum Kelly</dc:creator>
      <pubDate>Mon, 13 May 2024 20:14:40 +0000</pubDate>
      <link>https://dev.to/columk1/create-and-deploy-a-discord-bot-for-free-with-flyio-dp9</link>
      <guid>https://dev.to/columk1/create-and-deploy-a-discord-bot-for-free-with-flyio-dp9</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;A Discord Bot needs to be able to receive requests at all times. Unfortunately, it can be tricky to get a server up and running for free. Many of the existing free tiers involve frequent shut downs and lengthy spin up times. Going serverless is an option but it comes with its own unique challenges.&lt;/p&gt;

&lt;p&gt;Thankfully, Fly.io offers a trial which includes $5 free credit. This is enough to run a small full-stack app, full-time, for one month. It is more than enough to run a Discord bot. I'll go through the steps required to get it up and running.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(If you are lucky to have made an account on Fly last year, their Legacy Hobby plan includes a $5 free allowance each month, making it free to run forever)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 - Create your Discord Bot
&lt;/h2&gt;

&lt;p&gt;I recommend using a library like Discord.js for this. It will handle the gateway connection, caching users, channels etc. for you. You can follow &lt;a href="https://discordjs.guide/preparations/"&gt;this guide&lt;/a&gt; to get a simple bot running locally within a few minutes.&lt;/p&gt;

&lt;p&gt;When you get to the configuration section, don't create a config.json. Instead, create a .env file and add your token, client ID and guild ID. You will copy these over to the server later.&lt;/p&gt;

&lt;p&gt;Continue along with the guide and don't forget to add your bot to the server using an invite link. &lt;/p&gt;

&lt;p&gt;When you have finished creating the bot, run the &lt;code&gt;deploy-commands.js&lt;/code&gt; file to register your slash commands with the server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 - Create an Account on Fly.io
&lt;/h2&gt;

&lt;p&gt;Sign up for a Hobby account and add your payment information. You won't be charged until the $5 of trial credit has been used up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 - Fly Launch Configuration
&lt;/h2&gt;

&lt;p&gt;Create a file called &lt;code&gt;fly.toml&lt;/code&gt; in the root of your project directory and add the text from the snippet below. (Check &lt;a href="https://fly.io/docs/reference/regions/"&gt;here&lt;/a&gt; for a list of available regions)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app = '&amp;lt;YOUR_APP_NAME_HERE&amp;gt;'
primary_region = '&amp;lt;YOUR_REGION_HERE&amp;gt;'

[build]

[[services]]
  internal_port = 3000
  protocol = "tcp"

  # Allow machines to run continuously
  auto_start_machines = false
  auto_stop_machines = false

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 3 - Launch the Application
&lt;/h2&gt;

&lt;p&gt;Follow the instructions &lt;a href="//fly.io/docs/hands-on/install-flyctl/"&gt;here&lt;/a&gt; to install the Fly command line utility. When it is installed, run the command &lt;code&gt;fly launch&lt;/code&gt; from the source directory of your project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 - Add Environment Variables
&lt;/h2&gt;

&lt;p&gt;You should now see your app on the Fly.io dashboard. Go to the Secrets page and add your environment variables. When you are finished, the app will automatically redeploy with the new variables. &lt;/p&gt;

&lt;p&gt;Now, navigate to the Live Log page. If you followed the starter bot example in the Discord.js docs, you should see the text "Ready! Logged in as " in the console. &lt;/p&gt;

&lt;p&gt;If so, congratulations! Your bot is up and running. You should now be able to run commands in your Discord server.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>discord</category>
    </item>
  </channel>
</rss>
