LoginSignup
1
0

More than 1 year has passed since last update.

puppeteerを利用してグラフを画像として保存しslackに通知する

Last updated at Posted at 2023-04-17

目的

・利用しているサーバの状態を受動的に確認できるようにする

概要

puppeteerでブラウザを自動操作して、以下の操作を実施する

  1. AWSコンソールからログインする
  2. カスタムダッシュボードの画面を開く
  3. 目的のグラフを画像をダウンロードし、slackに通知する

前提条件

・AWSでBot用のIAMユーザを作成していること
・SlackにAppを作成して特定のchannelに投稿できるように設定していること

スクリプト

・aws-dashboard.js

const fs        = require('fs');
const process   = require('process');
const puppeteer = require('puppeteer');
const request   = require('request');
const common    = require('../lib/common');
// グラフ画像の保存先
const DIR = '/var/bot/aws_dashboard/';
// slackの設定
const CONF_SLACK = {
    'url'     : 'https://slack.com/api/',
    'token'   : '{トークン}',
    'channel' : '{チャンネル}'
};
// URLの設定
const SIGNIN_URL = '{AWSのコンソールのURL}';
const DASHBOARD_URL = '{AWSのカスタムダッシュボードのURL}';
// タイミング調整用の時間設定(ミリ秒)
const WAIT_PAGE_TRANSION =  3000;
const WAIT_LITTLE        =   200;
const WAIT_DASHBOARD     = 10000;
(async () => {
    // 起動引数を取得する
    // ※args[0]はnodeプログラム、args[1]は本スクリプトのパスとなる
    const accountId = process.argv[2];
    const userName  = process.argv[3];
    const password  = process.argv[4];
    
    // ブラウザの初期設定をする
    const browser = await puppeteer.launch({
        headless : true, // ヘッドレスモードで起動(画面を表示しない)
        slowMo   : 20, // Puppeteer の動作を指定されたミリ秒単位で遅らせる
        devtools : false,
        ignoreHTTPSErrors: true, 
        defaultViewport : {
            width  : 1920,
            height : 1080
        },
        args : [
            '--window-size=1920,1080'
        ]
    });
    // ブラウザを起動する
    const page = await browser.newPage();
    // STEP1. AWSコンソール画面からサインインする
    // 通常のサインインの画面を開く
    await page.goto(SIGNIN_URL);
    await page.waitForSelector('#next_button');
    
    // 通常のサインイン画面
    // 以下を選択・入力し、次へを押下する
    // - ユーザ種別
    // - アカウントID
    await common.waitFor(WAIT_PAGE_TRANSION);
    await page.click('input[id="iam_user_radio_button"]');
    await page.type('input[id="resolving_input"]', accountId);
    await common.waitFor(WAIT_LITTLE);
    await page.click('#next_button');
    await page.waitForSelector('#signin_button');
    // IAMユーザとしてサインイン画面
    // 以下を入力し、次へを押下する
    // - ユーザ名
    // - パスワード
    await common.waitFor(WAIT_PAGE_TRANSION);
    await page.type('input[id="username"]', userName);
    await page.type('input[id="password"]', password);
    await common.waitFor(WAIT_LITTLE);
    await page.click('#signin_button');
    await page.waitForSelector('#consoleNavHeader');
    //  STEP2. ダッシュボード画面を開く
    await common.waitFor(WAIT_PAGE_TRANSION);
    await page.goto(dashboardUrl);
    // ローディングが終了するまで待つ
    await page.waitForSelector('.cwdb-widget-container');
    
    //  STEP3. 各画像をダウンロードし、slack通知する
    await common.waitFor(WAIT_DASHBOARD);
    // ダウンロード用のディレクトリを作成する
    const IMAGE_DIR = `${DIR}${common.imageDir()}`;
    fs.mkdirSync(IMAGE_DIR);
  // ダッシュボードのグラフ画像を特定するためのセレクタ一覧
    const selectors = [
        '.widget-2', // CPU使用率
        '.widget-8'  // メモリ使用量
    ];
    let counter = 1;
    for (const selector of selectors) {
        // グラフを画像として保存する
        const filepath = `${IMAGE_DIR}/widget_${counter}.png`;        
        const clip = await page.evaluate(s => {
            const el = document.querySelector(s)
            const { width, height, top: y, left: x } = el.getBoundingClientRect()
            return { width, height, x, y }
        }, selector);
        await page.screenshot({clip, path: filepath});
        // 保存した画像をslackに通知する
        request.post({
            url: `${CONF_SLACK.url}files.upload`,
            formData: {
                token    : CONF_SLACK.token,
                filename : filepath,
                file     : fs.createReadStream(filepath),
                channels : CONF_SLACK.channel
            },
            function (error, response, body) {
                if (!error && response.statusCode == 200) {
                    common.log(`push file to slack is success. filepath : ${filepath}`);
                } else {
                    common.log(`push file to slack is failed. filepath : ${filepath}, status_code : ${response.statusCode}`);
                }
            }
        });
        counter++;
    };
    await common.waitFor(WAIT_PAGE_TRANSION);
    await browser.close();
})();

・lib/common.js

exports.log = function(message) {
  let dateStr = this.simpleDate(new Date(), 'YYYY-MM-DD hh:mm:ss') 
  console.log(`${dateStr}\t${message}`);
}
exports.simpleDate = function(date, format) {
    format = format.replace(/YYYY/, date.getFullYear());
    format = format.replace(/MM/  , ('0' + (date.getMonth() + 1)).slice(-2));
    format = format.replace(/DD/  , ('0' + date.getDate()).slice(-2));
    format = format.replace(/hh/  , ('0' + date.getHours()).slice(-2));
    format = format.replace(/mm/  , ('0' + date.getMinutes()).slice(-2));
    format = format.replace(/ss/  , ('0' + date.getSeconds()).slice(-2));
    return format;
}
exports.imageDir = function() {
  return this.simpleDate(new Date(), 'YYYYMMDD_hhmmss') 
}
exports.waitFor = async function(sec) {
    return new Promise((resolve, reject) => {
      setTimeout(resolve, sec);
    });
}

実行

$ node aws-dashboard.js {アカウントID} {IAMユーザ名} {IAMユーザのパスワード}

実行時のイメージ

・指定したslackのchannelに画像が通知される
dashboard_image.png

参考サイト

https://pptr.dev/
https://deep.tacoskingdom.com/blog/41

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0