JavaScript Basic Spread and Rest (…) usage

The Spread and Rest operators i.e. the three dots (…) can be used to make code cleaner and more concise.

Difference between Spread and Rest

Spread: Works on elements on the right side of the = operator, and breaks them out into individual elements.

Rest: Works on the left hand side of the = operator, and compresses them into an array.

Using Spread to Iterate over Arrays

Spread works on iterables like strings, arrays, maps and sets.

The spread operator operates similar to taking all the elements out of an array and operating on them or writing them to a new array. Say for instance we have an array of computers and we want to log each element to the console.

const computersA = ['Acer', 'Apple', 'ASUS']

We can log each element by running

console.log(computersA[0], computersA[1], computersA[2])

Or we can use the spread operator

console.log(...computersA)

The output is the same.

Joining Arrays

We can also use the spread operator to join two arrays together. Say we have two arrays

const computersA = ['Acer', 'Apple', 'ASUS']
const computersB = ['HP', 'Dell', 'Lenovo']

And we want to concatenate them together. We can do that simply by

const computerAll = [...computersA, ...computersB]

Rest Example

Rest is simply the opposite of spread. Spread take an item like an array and expands it out into elements we can use. Rest takes elements and packs them into an array. This can be extremely helpful if we want to pass in an unknown amount of elements into a function for processing.

const computersA = ['Acer', 'Apple', 'ASUS']
function writeToLog (...arr) {
  for (const element of arr) {
    console.log(element)
  }
}

Now we can call the function with as many elements in the array and they will all get logged to the console.

writeToLog('Razer', 'Alienware', 'Legion')

We could also use both the Spread and Rest functions

const gamingLaptops = ['Razer', 'Alienware', 'Legion']
writeToLog(...gamingLaptops)

Now as we add more laptops to the gamingLaptops array, the function will automatically process the line and write to console.

https://www.freecodecamp.org/news/three-dots-operator-in-javascript/

JavaScript Delete Object if it Collides or Overlaps with another object that has CSS Class X

Imagine we have a page that has anywhere from 1 to 100 floating blocks. Now what if I want the block(s) to disappear if it runs into another type of block or a boarder? How would we do that?

First lets get a list of all elements with X and Y class. We could swap one of these classes out for an ID if we wanted to.

const elements1 = document.querySelectorAll('.boxes')
const elements2 = document.querySelectorAll('.borders')

The variable elements1 and 2 are both arrays of all the elements that have class .boxes and boarders.

Now lets create a function to detect a collision.

function detectCollision(class1, class2) {
  for (let i = 0; i < class1.length; i++) {
    const e1Rectangle = class1[i].getBoundingClientRect()
    for (let i2 = 0; i2 < e2.length; i2++) {
      const e2Rectangle = class2[i2].getBoundingClientRect()
      if (
        e1Rectangle.left < e2Rectangle.right &&
        e1Rectangle.right > e2Rectangle.left &&
        e1Rectangle.top < e2Rectangle.bottom &&
        e1Rectangle.bottom > e2Rectangle.top
      ) {
        destroyElement(class1[i])
        destroyElement(class1[i2])
      }
    }
  }
}

This function takes two arrays as inputs, loops over each array object and compares to see if any are overlapping. If they are overlapping, run the destroyElement() function for those two specific elements.

Create the destroyElement function with

 function destroyElement(element) {
  elementId.parentNode.removeChild(elementId)
}

Here is our full code

const elements1 = document.querySelectorAll('.boxes')
const elements2 = document.querySelectorAll('.borders')

function detectCollision (class1, class2) {
  for (let i = 0; i < class1.length; i++) {
    const e1Rectangle = class1[i].getBoundingClientRect()
    for (let i2 = 0; i2 < e2.length; i2++) {
      const e2Rectangle = class2[i2].getBoundingClientRect()
      if (
        e1Rectangle.left < e2Rectangle.right &&
        e1Rectangle.right > e2Rectangle.left &&
        e1Rectangle.top < e2Rectangle.bottom &&
        e1Rectangle.bottom > e2Rectangle.top
      ) {
        destroyElement(class1[i])
        destroyElement(class1[i2])
      }
    }
  }
}

// Element is element[arraynumber].etc
function destroyElement(element) {
  elementId.parentNode.removeChild(elementId)
}

// Every second, let's run our collision function to check for collisions
setInterval(function () {
   detectCollision(elements1, elements2)
  }, 1000)

Notes on Buttons with JavaScript

Here are some very basic notes on using buttons to change elements on a web page

We have a very simple page with three buttons that change the background color when the button is clicked.

Clicking a button changes the text and the background color.

Create a button in our html file.

    <button type="button" class="button green">Green</button>

We have two classes assigned to this button, button which is used for styling, and the green, which JavaScript will use to know which button is clicked.

In our JavaScript, we will set up an event listener for when the button is clicked.

document.querySelector('.green').addEventListener('click', function () {
  document.querySelector('body').style.backgroundColor = 'Green'
  console.log('Green')
})

We use the document.querySelector to interact with HTML objects. So we setup the event listener to listen to the button that is in Class green, and we wait for the click event. Once that happens, we run the code in the function(){ }.

Line 2 is what changes our background color. We query the body tag, and set the backgroundColor to Green. Notice that the background color name is slightly different between JavaScript and HTML.
HTML and CSS it has a – in it: background-color
JavaScript is it Camel Case: backgroundColor

Here is the full code for the above screenshots. There are three files. index.html, style.css, and index.js. Should be able to copy them to the same folder and run the index.html file.

Index.html code

<!DOCTYPE html>
<html lang="en">
  <head>
    <link rel="stylesheet" href="style.css" />
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>JavaScript DOM Practice</title>
  </head>
  <br />
  <section class="mainwindow">
    <div class="output">Click a button to change the background color</div>
    <br />
    <br />
    <br />
    <button type="button" class="button green">Green</button>
    <button type="button" class="button blue">Blue</button>
    <button type="button" class="button yellow">Yellow</button>
  </section>

  <body>
    <script src="index.js"></script>
  </body>
</html>

JavaScript index.js

'use strict'

document.querySelector('.green').addEventListener('click', function () {
  ChangeBackgrounColor('Green')
  LogBackgroundColor('Green')
})

document.querySelector('.blue').addEventListener('click', function () {
  ChangeBackgrounColor('Blue')
  LogBackgroundColor('Blue')
})

document.querySelector('.yellow').addEventListener('click', function () {
  ChangeBackgrounColor('Yellow')
  LogBackgroundColor('Yellow')
})

function ChangeBackgrounColor (color) {
  document.querySelector('body').style.backgroundColor = color
}

function LogBackgroundColor (color) {
  document.querySelector('.output').innerHTML = `Background is ${color}`
  console.log(`Background color is ${color}`)
}

CSS File style.css

.mainwindow {
  padding: 1rem;
}
body {
  background-color: white;
}
button {
  border: none;
  font-size: 24px;
  cursor: pointer;
  padding: 1rem 1.5rem;
  margin: 1rem;
}
.output {
  font-size: 2rem;
}

Voxer Bot Attempts 1 of 3

The following is part one of hopefully a full 3 part series on notes and findings on trying to create a “Bot” for Voxer. Voxer doesn’t really have any options for web hooks, and the SDK is still in beta. We’ll be exploring sending messages to a channel, how to use Curl or Fetch to send messages, and hopefully figure out how to automate signing in.

Looking at Voxer

Voxer is primarily used via the modile apps, but there is also a web version at web.voxer.com

Using Firefox, we can play with the Web Tools to get a better idea of what is going on.

All the Javascript is easily readable. api.js is of interest. This can helps us understand how messages are sent.

The Web interface seems to be somewhat buggy, could be it just doesn’t like going through burp, but had better luck monitoring the channel for new messages from a phone.

Proxying the traffic through Burp is tricky. The log in does not appear to work while the proxy is active, but you can log in, and then activate the proxy to capture and replay sending messages.

If you get “Voxer is open in another tab. Please click ‘OK’ then close this tab.” clear all voxer cookies and log in again. Seems to happen quite often.

Interestingly, if you send a message, then send that message POST request to Repeater, change the text and resubmit it, it “Updates” the text of the message. So if you know the message_id, or maybe the create_time, you can change the text in messages.

About Sending Messages

When you send a message, there are 2 POST requests sent. The first one sends the message and the second one consumes the message. It looks like you really only need the first post message request to actually send messages. Think the consume message post is for the browser to trigger a refresh on the messages in the message list.

There are two variables that change message to message

message_id and create_time

Looking into the api.js file we see that create_time is done with the following code

now = new Date().getTime() / 1000,

And here is the code used to generate the message_id. First part is the time, the next part is random.

window.generate_message_id = function (type) {
        var message_id = new Date().getTime() + "_" + Math.floor((Math.random() * 10000000000) + 1000);

        if (type && type === "image") {
            message_id += "_v1.jpg";
        }

        return message_id;
    };

Structure of the POST request

The following entries are slightly abbreviated

Cookies

There are a bunch of tracking cookies, the session cookie is the one we are interested in. The Rv_session_key is what will allow us to actually send messages. As a side note, it appears that every time we send a message, there is analytics that is also sent, saying who the message was sent to, and when.

session={"gcp1-prod":{"user_id":"MyVoxerUser_ID","Rv_session_key":"db0bc8a069148140151a38bab2098a01"}}

Body

{"message_id":"1681191108600_7318671093","create_time":1681191108,"model":"User_Agent","content_type":"text","from":"MyVoxerUser_ID","subject":"Walkie","body":"This is the text of the message","thread_id":"This.is.the.thread_id"}

Using JavaScript / fetch

After some trial an error, the following solution finally worked. You can copy the following code, and run with “node file.js”

// Change variables as needed
let body = "Hello World!"
// let body = process.argv.slice(2) // You can use this if you want to pass in the message as an argumant. i.e. node voxer.js "Voxer Message"
const threadID = 'ThreadID'
const fromID = 'UserID'
let cookie = `session={"gcp1-prod":{"user_id":"${fromID}","Rv_session_key":"RVSESSIONID"}}`
let time = new Date().getTime() / 1000
let messageID = new Date().getTime() + '_' + Math.floor(Math.random() * 10000000000 + 1000)

// Send message.
function sendMessage () {
  fetch(`https://gcp1-prod-nr60.voxer.com/2/cs/post_message?now=${time}`, {
    credentials: 'include',
    credentials: 'same-origin',
    headers: {
      'User-Agent': 'UserAgent',
      Accept: '*/*',
      'Accept-Language': 'en-US,en;q=0.5',
      'Content-Type': 'text/plain',
      'Sec-Fetch-Dest': 'empty',
      'Sec-Fetch-Mode': 'cors',
      'Sec-Fetch-Site': 'same-site',
      'sec-ch-ua-platform': '"Windows"',
      'sec-ch-ua': '"Opera";v="97", "Chromium";v="97", "Not=A?Brand";v="24"',
      'sec-ch-ua-mobile': '?0',
      Cookie: cookie
    },
    referrer: 'https://web.voxer.com/',
    body: `{\"message_id\":\"${messageID}\",\"create_time\":${time},\"model\":\"\",\"content_type\":\"text\",\"from\":\"${fromID}\",\"subject\":\"CHANGING\",\"body\":\"${body}\\n\",\"thread_id\":\"${threadID}\"}\r\n`,
    method: 'POST',
    mode: 'cors'
  })}

 sendMessage()

You can find the thread_id, user_id, session cookie by toggling the developer console, logging into Voxer, and send a message. We’ll plan on exploring that in part two, as well as using curl to send a message.

Send an Email with Node.JS

In this post, we will be using Node.JS and the nodemailer library to send email. We need to have an email account with an email provider to send email. Gmail or some other email provider should work.

Prerequisites

First lets install some tools

sudo apt install nodejs npm

Now lets install nodemailer

npm install nodemailer

Writing the Code to Send Email

Now that we have nodemailer installed, we can write or copy our code. Create a file called maill.js and make it look similar to the following.

// We can pass in the email text as an argument
const emailText = process.argv.slice(2);
// Or we can just have it as a variable
// const emailText = "NodeJS test email message."
console.log("args " + args)

const nodemailer = require("nodemailer");

const transporter = nodemailer.createTransport({
  host: "mail.emailserver.com",
  port: 465,    //  If your email server does not support TLS, change to 587
  secure: true, // If you are using port 587, change to false.  Upgrade later with STARTTLS
  auth: {
    user: "smtpuser@emailserver.com",
    pass: "notpassword)",
  },
});

const mailOptions = {
  from: 'user@emailserver.com',
  to: "touser@email.com",
  subject: 'Test Email using NodeJS',
  text: `${emailText}`
};

transporter.sendMail(mailOptions, function(error, info){
  if (error) {
    console.log(error);
  } else {
    console.log('Email sent: ' + info.response);
  }
});

Update the following variables

  • host: to your host email server
  • user: to the email user that is sending email. It should have an account on the email server
  • pass: password for your email user that is sending the email
  • from: email address that is sending the email
  • to: email account(s) you are sending email to
  • subject: subject of your email

Now we can proceed to send email

Sending Email

We can now run the code by saving our file and running it directly with NodeJS

nodejs ./mail.js "This is the body text for the email"

Hit Return and look for the email. If something went wrong, it should throw an error.

You can change the emailText variable if you would rather have the message body inside the code.

Code Explanation and Notes

A little explanation on the code.

The second line “const emailText = process.argv.slice(2);” is used to pass in a command line argument to use as the text for the body of the email. You can delete the line and uncomment line 4 if you would rather use a variable inside the code.

Your email server should support using SSL/TLS on port 465. If it does not, you may need to use STARTTLS which uses port 587, and then set secure to false. STARTTLS should upgrade the connection to be encrypted. But it’s opportunistic. You can read more about STARTTLS, SSL/TLS here https://mailtrap.io/blog/starttls-ssl-tls/

You can change the “to: ” in the mailOptions object to an array of email addresses to send the email to multiple people at once.

to: ["email1@email.com", "email2@email.com", "etc"],

Simple JavaScript Object – Code Example

Below is a code example for creating a basic object and using a function to calculate the fuel economy.

// New object Car
const car = {
    make: 'Honda',
    model: 'Civic',
    topSpeed: 100,
    tankCapacity: 10,
    range: 300,
    MPG: function() {
        this.mpg = this.range / this.tankCapacity;
        return this.mpg
    }
}

car.MPG();  // We need to call this to calculate the MPG, otherwise we get undefined

console.log(`My car is a ${car.make + " " + car.model }, can go ${car.topSpeed}/MPH, and gets ${car.mpg}/MPG `)

// Alternatively we can call the function car.MPG() directly.  
// This keeps us from having to run the function before logging.
console.log(`My car is a ${car.make + " " + car.model }, can go ${car.topSpeed}/MPH, and gets ${car.MPG()}/MPG `)