Steps to get up and running:

1) Create an Unbuntu 17.10 server with at least 2GB RAM

2) `apt update` all dependencies

3) Install Docker and Docker-compose

4) Optional but strongly recommended, install a strong firewall with bruteforce detection.
`apt install apf-firewall`
 To install BFD, see: [http://www.webhostgear.com/60.html](http://www.webhostgear.com/60.html)
 Configure your ports as desired. It is strongly suggested to run your VNC on a non-standard port for security purposes

4) Run `sudo docker-compose up`

5) Connect to your Docker container through VNC Viewer

6) Right click on the Desktop > Applications > Shell > Bash

7) `npm run setup` will run Autoview setup script (and load the Autoview extension automatically)

8) `npm run start` will run Tradingview login script (and load the Autoview extension automatically)

9) Open up a new tab `localhost:9222` and click Autoview. Now you can see the debuggingo output for autoview to see if its working.

10) Setup your TradingView.com alerts and watch them get triggered automatically 24/7!
version: "3"
services:

  virtual-display:
    build: "." # rebuilds the image upon running if needed.
    image: "autoviewbot:latest"
    working_dir: /home/node/app
    # This command runs the NodeJS server using ts-node
    command: >
        sh -c "
            echo Starting Virtual Display Server... &&
            ./launch-virtual-display.sh
        "
    ports:
      - "3903:3903" # HOST:CONTAINER - NOTE THIS PORT MUST MATCH THE -rfbport #### SET IN ./launch-virtual-display.sh
    environment:
      - VNC_SERVER_PASSWORD=yourpass # this is your VNC password to connect
        # On host, run `timedatectl set-timezone UTC` to set the Timezone , change UTC with your actual timezone

    restart: always
    user: node
    privileged: true
    volumes: # use local disk data instead of image data. Useful for localhost development mode
      - ./:/home/node/app/
      - /etc/localtime:/etc/localtime:ro # this syncs the Host server time into the Docker Container
#!/bin/bash

# Based on: http://www.richud.com/wiki/Ubuntu_Fluxbox_GUI_with_x11vnc_and_Xvfb

main() {
    log_i "Starting xvfb virtual display..."
    launch_xvfb
    log_i "Starting window manager..."
    launch_window_manager
    log_i "Starting VNC server..."
    run_vnc_server
}

launch_xvfb() {
    local xvfbLockFilePath="/tmp/.X1-lock"
    if [ -f "${xvfbLockFilePath}" ]
    then
        log_i "Removing xvfb lock file '${xvfbLockFilePath}'..."
        if ! rm -v "${xvfbLockFilePath}"
        then
            log_e "Failed to remove xvfb lock file"
            exit 1
        fi
    fi

    # Set defaults if the user did not specify envs.
    export DISPLAY=${XVFB_DISPLAY:-:1}
    local screen=${XVFB_SCREEN:-0}
    local resolution=${XVFB_RESOLUTION:-1280x960x24}
    local timeout=${XVFB_TIMEOUT:-5}

    # Start and wait for either Xvfb to be fully up or we hit the timeout.
    Xvfb ${DISPLAY} -screen ${screen} ${resolution} &
    local loopCount=0
    until xdpyinfo -display ${DISPLAY} > /dev/null 2>&1
    do
        loopCount=$((loopCount+1))
        sleep 1
        if [ ${loopCount} -gt ${timeout} ]
        then
            log_e "xvfb failed to start"
            exit 1
        fi
    done
}

launch_window_manager() {
    local timeout=${XVFB_TIMEOUT:-5}

    # Start and wait for either fluxbox to be fully up or we hit the timeout.
    fluxbox &
    local loopCount=0
    until wmctrl -m > /dev/null 2>&1
    do
        loopCount=$((loopCount+1))
        sleep 1
        if [ ${loopCount} -gt ${timeout} ]
        then
            log_e "fluxbox failed to start"
            exit 1
        fi
    done
}

run_vnc_server() {
    local passwordArgument='-nopw'

    if [ -n "${VNC_SERVER_PASSWORD}" ]
    then
        local passwordFilePath="${HOME}/.x11vnc.pass"
        if ! x11vnc -storepasswd "${VNC_SERVER_PASSWORD}" "${passwordFilePath}"
        then
            log_e "Failed to store x11vnc password"
            exit 1
        fi
        passwordArgument=-"-rfbauth ${passwordFilePath}"
        log_i "The VNC server will ask for a password"
    else
        log_w "The VNC server will NOT ask for a password"
    fi

    x11vnc -rfbport 3903 -shared -display ${DISPLAY} -forever ${passwordArgument} &
    wait $!
}

log_i() {
    log "[INFO] ${@}"
}

log_w() {
    log "[WARN] ${@}"
}

log_e() {
    log "[ERROR] ${@}"
}

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ${@}"
}

control_c() {
    echo ""
    exit
}

trap control_c SIGINT SIGTERM SIGHUP

main

exit
{
  "name": "chrome-headless-autoview-tradingview-bot",
  "version": "0.0.1",
  "description": "A virtual Chrome instance that run the Autoview bot while logged into TradingView.com, to enable serverside rendering of all AutoView bot commands.",
  "author": "Cryptonyght",
  "license": "MIT",
  "repository": "",
  "main": "start.sh",
  "dependencies": {
    "chromeless": "^1.5.2"
  },
  "devDependencies": {
    "@types/node": "^10.1.0",
    "make-runnable": "^1.3.6",
    "npm-run-all": "^4.1.3",
    "ts-node": "^6.0.3",
    "tslib": "^1.9.1",
    "typescript": "^2.8.3",
    "zip-dir": "^1.0.2"
  },
  "scripts": {
    "server": "docker-compose up",
    "chrome": "npm run loadChrome:withextension",
    "setup": "run-p loadChrome:withextension autoviewSetup",
    "start": "run-p loadChrome:withextension tradingViewLogin",
    "loadChrome:withextension": "google-chrome-stable --user-data-dir=\"chrome-profile\" --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222 --load-extension=\\extension",
    "run:zipdir": "npm run clean:zipdir && node zipdir.js",
    "clean:zipdir": "rimraf -- autoview-trading-bot.tar.gz",
    "docker:build": "docker build -t autoviewbot:latest .",
    "autoviewSetup": "ts-node bot-setup.ts autoviewSetup",
    "tradingViewLogin": "ts-node bot.ts tradingViewLogin",
    "loadChrome": "google-chrome-stable --user-data-dir=\"chrome-profile\" --remote-debugging-address=0.0.0.0 --remote-debugging-port=9222",
    "build": "tsc",
    "clean": "rimraf -- node_modules",
    "npm:install": "npm install",
    "loadAutoview:headless": "google-chrome-stable --disable-gpu --headless --remote-debugging-port=9222  --load-extension=\\extension"
  }
  
}
// This file just automates logging into TradingView.com - only a nice to have but can be extended to do anything on any website

import { Chromeless } from 'chromeless';

declare var TradingView;

// Make this runnable from package.json through NPM scripts using `make-runnable`
module.exports = {

    loadAutoview: async function () {
        const chromeless = new Chromeless({
            launchChrome: true
        });

        const screenshot = await
            chromeless
                .setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3430.0 Safari/537.36')
                // .setViewport({width: 800, height: 600, scale: 1})
                .goto('chrome-extension://okdhadoplaoehmeldlpakhpekjcpljmb/_generated_background_page.html')
                .wait(2000)
                .screenshot()
                .catch((err) => {
                    console.error(err);
                });

        console.log(screenshot); // prints local file path or S3 url
        // await chromeless.end();
    },

    tradingViewSignout: async function () {
        const chromeless = new Chromeless({
            launchChrome: true
        });

        const screenshot = await
            chromeless
                .setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3430.0 Safari/537.36')
                // .setViewport({width: 1920, height: 1080, scale: 1})
                .goto('https://www.tradingview.com/')
                .wait(4000)
                .evaluate(() => {
                    if (TradingView.isPro()) {
                        TradingView.signOut();
                        return false;
                    }
                })
                .screenshot()
                .catch((err) => {
                    console.error(err);
                });
        console.log(screenshot); // prints local file path or S3 url
        await chromeless.end();
    },

    tradingViewLogin: async function () {
        const chromeless = new Chromeless({
            launchChrome: true,
            waitTimeout: 20000
        });

        const screenshot = await chromeless
            .setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3430.0 Safari/537.36')
            // .setViewport({width: 2485, height: 1380, scale: 1})
            .goto('https://www.tradingview.com/')
            .wait('.tv-header')
            .wait(9000)
            .evaluate(() => {
                if (TradingView.isPro()) {
                    TradingView.signOut();
                    return false;
                }
            })
            .wait(9000)
            .wait('a.tv-header__link--signin')
            .click('a.tv-header__link--signin')
            .wait(1000)
            .type('', 'input[name="username"]') // enter your TradingView.com user name here
            .type('', 'input[name="password"]') // enter your TradingView.com password here
            .wait(10000)
            .click('button[type="submit"]')
            .wait(6000)
            .goto('https://www.tradingview.com/chart/')
            .wait('.chart-markup-table', 30000)
            .wait(7000)
            .screenshot()
            .catch((err) => {
                console.error(err);
            });

        console.log(screenshot); // prints local file path or S3 url

    }
}

require('make-runnable'); // must be at the END of the file

// Run directly from this script
// login().catch(console.error.bind(console));

// Run directly from this script
// module.exports.tradingViewLogin().catch(console.error.bind(console));

// Run directly from this script
// signout().catch(console.error.bind(console));
// This file automates logging into Autoview, useful if you have a lot of exchanges.

import { Chromeless } from 'chromeless';

declare var TradingView;

module.exports = {
    autoviewSetup: async function () {
        const chromeless = new Chromeless({
            launchChrome: true,
            waitTimeout: 20000
        });

        const kraken = await chromeless
            .setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3430.0 Safari/537.36')
            // .setViewport({width: 2485, height: 1400, scale: 1})
            .goto('chrome-extension://okdhadoplaoehmeldlpakhpekjcpljmb/options.html')
            .wait(2000)
            .click('[data-page="settings"]')
            .wait(200)
            .click('button[name="grant"][data-exchange="KRAKEN"]')
            .wait(2000)
            .click('[data-page="exchange-kraken"]')
            .wait(200)
            .type('', '#exchange-kraken-private-0') // private key
            .type('', '#exchange-kraken-public-0') // public key
            .click('button[name="test"][data-exchange="KRAKEN"]')
            .wait(200)
            .click('button[data-action="action_access_save"][data-exchange="KRAKEN"]')
            .wait(5000)
            .catch((err) => {
                console.error(err);
            });
        console.log('Finished Enabling 1 Exchange! (Kraken)'); // prints local file path or S3 url

        const bitfinex = await chromeless
            .setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3430.0 Safari/537.36')
            // .setViewport({width: 2485, height: 1400, scale: 1})
            .goto('chrome-extension://okdhadoplaoehmeldlpakhpekjcpljmb/options.html')
            .wait(2000)
            .click('[data-page="settings"]')
            .wait(200)
            .click('button[name="grant"][data-exchange="BITFINEX"]')
            .wait(2000)
            .click('[data-page="exchange-bitfinex"]')
            .wait(200)
            .type('', '#exchange-bitfinex-private-0') // private key
            .type('', '#exchange-bitfinex-public-0') // public key
            .click('button[name="test"][data-exchange="BITFINEX"]')
            .wait(200)
            .click('button[data-action="action_access_save"][data-exchange="BITFINEX"]')
            .wait(5000)
            .catch((err) => {
                console.error(err);
            });
        console.log('Finished Enabling 1 Exchange! (Bitfinex)'); // prints local file path or S3 url



        await chromeless.end();

    }
}

// Make this runnable from package.json through NPM scripts using `make-runnable`
require('make-runnable'); // must be at the END of the file
.git
.idea
##########################################################
## These first commands are all run under the `root` user
##########################################################
## specify the node base image with your desired version node:<version>
FROM ubuntu:16.04

## Install latest chrome dev package.
## Note: this installs the necessary libs to make the bundled version of Chromium that Puppeteer installs, work. Also works for Chromeless, and Chrome in -headless mode.

RUN apt-get update && apt-get clean && apt-get install -y \
    x11vnc \
    xvfb \
    fluxbox \
    wmctrl \
    wget \
    && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \
    && echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list \
    && apt-get update && apt-get -y install google-chrome-stable \
    && apt-get -y autoclean

# Add additional dependencies here, to not invalidate the primary cache
RUN apt-get install -y nano \
                       curl

# Tini the "zombie reaper" is now available at /sbin/tini
# Whatever you put in the CMD [] section is what Tini will run as its default args
# Add Tini
ENV TINI_VERSION v0.16.1
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini
ENTRYPOINT ["/tini", "--"]


# Adds a user to the OS
RUN useradd node

# Replace shell with bash so we can source files
RUN rm /bin/sh && ln -s /bin/bash /bin/sh

# NVM environment variables
ENV HOME=/home/node
ENV NVM_DIR=$HOME/.nvm
ENV NODE_VERSION 10.1.0

# Creates the dir including its "parents" -with p
RUN mkdir -p $HOME/.nvm

# Install nvm
# Install node and npm
# https://github.com/creationix/nvm#install-script
RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash \
    && source $NVM_DIR/nvm.sh \
    && nvm install $NODE_VERSION \
    && nvm alias default $NODE_VERSION \
    && nvm use default

# Add node and npm to path so the commands are available
ENV NODE_PATH $NVM_DIR/v$NODE_VERSION/lib/node_modules
ENV PATH $NVM_DIR/versions/node/v$NODE_VERSION/bin:$PATH


# Confirm installation
RUN node -v
RUN npm -v

# Install app dependencies
COPY . /home/node/app/

# Set NPM global install path into home directory so permissions are correct
RUN mkdir /home/node/.npm-global
RUN npm config set prefix "/home/node/.npm-global"
ENV PATH="/home/node/.npm-global/bin:${PATH}"

# Fix permissions so user Node can work with files
RUN chown -v -R node:node /home/node

#########################################################
# NOW, the following commands are run under the `node` user
#########################################################
# USER node MUST BE FIRST HERE!
USER node

# Install global packages, then local packages on frontend, then local packages on backend-nodejs, then build frontend, then build backend.
WORKDIR /home/node/app

# Run your program under Tini
# Starts the primary app - Virtual Display Manager
CMD './launch-virtual-display.sh
Copy the entire Autoview Extension into the /extension directory
// This is where the Docker Container will store the Chrome User Profile data, so it is persistent in case you need to restart the Docker Container or your Server