目的
- 社内グループウェア (Aipo) での出勤操作を自動化したかったんだ....
- Amazon Dash ボタン + maddox/dasher + API Gateway + Lambda で社内グループウェア へログイン → 出勤ボタンをクリック
- LT用のネタとして
構成
AWS Lambda (Node.js 4.3)
下記の設定でひとまず動作した。
- メモリ:
512MB
- タイムアウト:
3分
コードは npm でパッケージ管理を行い、Apex でデプロイを行った。
PhantomJS
実際には、下記のprebuiltなnpmモジュール: phantomjs-prebuilt
を使用した。
開発時・デプロイ時は、このビルド済み PhantomJS バイナリをデプロイパッケージに含めて Lambda へアップロードするため、数十秒〜1分程度かかるつらみがある.....
WebdriverIO
phantomjs-prebuiltのREADME を見ると、下記の2通りの実行方法が記載されている。
- Running via node: PhantomJS に処理を記載したjsファイルを食わせる方法
-
Running with WebDriver:
Selenium WebDriver
のプロトコルを介して (WebdriverIO から) 操る方法
主観的に、なんとなく WebdriverIO を使用した記述方法のほうが見通しが良さそうだったのでこっちの方法を選択した。
準備
.
.
.
"dependencies": {
"phantomjs-prebuilt": "^2.1.14",
"webdriverio": "^4.6.2"
}
.
.
.
$ env PHANTOMJS_PLATFORM="linux" PHANTOMJS_ARCH="x64" npm install
phantomjs-prebuil
は npm install
時にシェルの実行環境を判定してふさわしいバイナリをダウンロードするらしい。
そのため、
macOS でデプロイパッケージ作成
↓
Lambda (Amazon Linux? x64) へアップロード
などの状況の場合は、下記の説明のとおりに環境変数を設定して、Linux x64 用のバイナリをダウンロードさせる (もしくは、Linux x64 な環境で作業を行う)。
If you know in advance that you want to install PhantomJS for a specific architecture, you can set the environment variables: PHANTOMJS_PLATFORM (to set target platform) and PHANTOMJS_ARCH (to set target arch), where platform and arch are valid values for process.platform and process.arch.
Cross-Platform Repositories
Lambda関数
Running with WebDriver に従って記述。
'use strict';
const phantomjs = require('phantomjs-prebuilt');
const webdriverio = require('webdriverio');
const webDriverOpts = {
desiredCapabilities: {
browserName: 'phantomjs',
logLevel: 'verbose',
host: 'localhost',
port: '4444'
}
};
exports.handler = (event, context, callback) => {
phantomjs
.run('--webdriver=4444')
.then((phantom) => {
const client = webdriverio.remote(webDriverOpts).init();
return client
.url("http://example.com/aipo")
.waitForExist('.button[value="ログイン"]', 10000)
.setValue('#member_username', "ユーザー名")
.setValue('#password', "パスワード")
.click('.button[value="ログイン"]')
.waitForExist('=Aipo', 10000)
.click('//input[@type="button" and (@value="出勤" or @value="退勤")]')
.pause(5000)
.then(() => {
phantom.kill();
return Promise.resolve();
})
.catch((err) => {
console.error(`error: ${err}`);
phantom.kill();
return Promise.reject(err);
});
})
.then(() => {
console.log("done");
context.succeed({
statusCode: 200,
body: JSON.stringify({ "message": "done" })
});
})
.catch((err) => {
console.error(`failed: ${err}`);
context.fail({
statusCode: 500,
body: JSON.stringify({ "message": `failed:${err}` })
});
});
};