ページ

ラベル GAS の投稿を表示しています。 すべての投稿を表示
ラベル GAS の投稿を表示しています。 すべての投稿を表示

2018年9月24日月曜日

GASをclasp+typescriptで実装してみる

概要

  • claspがtypescriptサポートした 🎉
  • 筆者がエディッタをVSCodeに乗り換えたので試したい!
  • claspをもう少し詳しく触ってみる

環境

折角なので一から環境作ってみる
$ node -v
v9.5.0

$ npm i @google/clasp -g
古い環境をいったんログアウト
$ clasp logout
※この時古い~/.clasprc.jsonは削除された
再度ログイン
$ clasp login
どのアカウントでログインされているか分からない場合、
$ clasp list
で直近5件分のプロジェクトが表示される

簡単なサンプル作成

$ mkdir ts-sample
$ cd ts-sample
$ clasp create ts-sample
※この時Google Apps Script APIが無効になっていると以下エラーが出る。
Error: Permission denied. Enable the Apps Script API:
https://script.google.com/home/usersettings
表示されているURLへいきGoogle Apps Script APIを有効にする
image.png (25.3 kB)

その後pull
$ clasp pull
必要なパッケージをインストール
$  yarn add typescript @types/google-apps-script
$ yarn add tslint --dev
$ ./node_modules/.bin/tslint --init
.clasp.jsonrootDirを追加
josn
{
  "scriptId":"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "rootDir": "src"
}
シンプルにアラートが表示される

push

以下のコマンドでjsへトランスパイルしpushしてくれます。便利!
$ clasp push

エラー

実行時に以下のエラーが表示された場合
image.png (146.4 kB)
「詳細」を開いて、承認を行えばOK
image.png (98.2 kB)

2018年3月19日月曜日

Google公式GASのCLIツールclaspを試してみる

clasp

GASのCLIツールがGoogle公式のが出てたので使ってみる。

:computer:環境構築


まずは対象アカウントでこちらにアクセスし、
Google Apps Script APIをオンにする。
image.png (13.2 kB)
次にclaspをインストール
sh
$ npm i @google/clasp -g
claspでログイン
sh
$ clasp login
ブラウザが開クノで対象のアカウントでログイン
この時、~/.clasprc.jsonが作成される様子。

:pencil: 実装


簡単なサンプル

  1. Googleドライブ上でGASのテストプロジェクトを作成
  2. 作成したプロジェクトをローカルにクローン
    • $ clasp clone [プロジェクトID]
    • プロジェクトIDはファイル→プロジェクトのプロパティで表示された以下赤枠の部分
    • image.png (86.7 kB)
    • クローンされるとappsscript.json.clasp.jsonも作成される
  3. ファイルを修正してアップロードしてみる。
    • $ clasp push で無事アップロードされていればOK

Webpackを導入しES6で書けるようにする

  1. yarn initしてpackage.json作成
  2. 以下パッケージ追加
    json
      "devDependencies": {
        "babel-core": "^6.26.0",
        "babel-loader": "^7.1.2",
        "babel-preset-es2015": "^6.24.1",
        "dotenv": "^5.0.0",
        "gas-webpack-plugin": "^0.2.1",
        "webpack": "^3.10.0"
      }
    
  3. lib/entry.jsを以下の内容で作成
    js
    global.execute = function() {
      Logger.log('---- execute start ----');
      Logger.log('---- execute end ----');
    }
    
  4. webpack.config.jsを以下の内容で作成
    js
    var path = require('path');
    var GasPlugin = require("gas-webpack-plugin");
    
    require('dotenv').config();
    
    module.exports = {
      context: __dirname + '/lib',
      entry: './entry.js',
      output: {
        path: __dirname + '/src',
        filename: 'main.js'
      },
      plugins: [
        new GasPlugin()
      ],
      module: {
        loaders: [
          {
            test: /\.js$/,
            loader: 'babel-loader',
            exclude: /(node_modules|bower_components)/,
            query: {
              presets: 'es2015'
            }
          }
        ]
      },
    };
    
  5. ビルドしてみる npm run build
    • src/main.jsが作成されとけばOK
  6. この時点でclasp pushしちゃうとnode_modules配下もあげようとするので
    以下のような .claspignoreを作る
    **/**
    !src/main.js
    !appsscript.json
    
  7. またはルートディレクトリを変更してもOK .clasp,jsonを修正する
    {"scriptId":"x-x-x-x-x", "rootDir": "src"}

2017年1月16日月曜日

GASをES6で書く時に気にすること


GASをES6で書く時に気にすること

個人用のSlackに執事的なボットをGASで作って、
色々やってもらおうと思ってES6で作りました。
相変わらず名前のセンスは無く、まんまです・・・
この時に気をつけた事を書いてみようかと思います。

前提


ES6で書いてwebpackを使ってBabelでES5に変換
node-google-apps-scriptでGoogle Drive上にアップしてます。

「関数を選択」に関数が出てこない!!


これ↓



普通に次のような関数を定義すると
function main() {
  ...
}
関数を選択のドロップダウンにmainが出てきますが、
今回の場合BabelでES5に変換する為うまく出てきません😱
global.main = function() {
  ...
}
Google Drive上にアップした後にfunction main() ~とか書くと
当たり前ですが選択項目に出てきます。が、毎回これやるのも...
そんな時はgas-webpack-pluginが便利です
npm install --save-dev gas-webpack-pluginして、
webpack.config.js
var GasPlugin = require("gas-webpack-plugin");
module.exports = {
  ...
  plugins: [
    new GasPlugin()
  ],
  ...
}
こんな感じで追加してやると、function main() ~を作ってくれます!!

ウェブアプリケーションとして導入しても反映されない!!


こっから選択してGASをウェブアプリとして公開するやつです。

ES6に関わらずですが、何回やっても反映されない場合は次を試してみて下さい
「ファイル」>「版を管理」を選択

「新しいバージョンを保存」を選択し版数を増やします。

再度ウェブアプリケーションとして導入を開き、
プロジェクトバージョンに先ほどの版数を選択します。

これで最新のコードが反映されてると思います


とまあ、とりあえず気にする事を書いてみましたが
他にも気にする事があるって方はコメント頂けると幸いです。

2016年8月6日土曜日

Feedlyの未読記事をSlackへGASで通知するBotの作成 3


前回の続きです。
前回未読一覧を取得したので、今回は未読記事のタイトルとURLを
SlackにPostするようにして完成させたいと思います。
ID毎の記事一覧取得

前回、
例)
{
  "unreadcounts": [
    {
      "count": 508,
      "id": "feed/http://www.autoblog.com/rss.xml",
      "updated": 1367539068016
    }
  ]
}
上のようなidを取得しましたが、そのidにぶら下がる記事一覧を取得します。
試しに以下のurlを叩いてみてください。
$ curl -H 'Authorization: OAuth {トークン}' \
  https://cloud.feedly.com/v3/streams/contents?streamId={id}
実行すると以下のようなJSONが取得できたかと思います、
例)
{
  "direction": "ltr",
  "title": "The Verge -  All Posts",
  "id": "feed/http => //www.theverge.com/rss/full.xml",
  "continuation": "gRtwnDeqCDpZ42bXE9Sp7dNhm4R6NsipqFVbXn2XpDA=_13fb9d6f274:2ac9c5:f5718180",
  "updated": 1367539068016,
  "items": [
    {
      "engagement": 15,
      "published": 1367539068016,
      "crawled": 1367539068016,
      "title": "NBC's reviled sci-fi drama 'Heroes' may get a second lease on life as Xbox Live exclusive", 
      "author": "Nathan Ingraham",
      "id": "gRtwnDeqCDpZ42bXE9Sp7dNhm4R6NsipqFVbXn2XpDA=_13fb9d6f274:2ac9c5:f5718180",
      "content": {
        "direction": "ltr",
        "content": "..."
      },
      "updated": 1367539068016,
      "unread": true,
      "tags": [
        {
          "id": "user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/tag/inspiration",
          "label": "inspiration"
        }
      ],
      "origin": {
        "title": "The Verge -  All Posts",
        "htmlUrl": "http://www.theverge.com/",
        "streamId": "feed/http://www.theverge.com/rss/full.xml"
      },
      "alternate": [
        {
          "href": "http://www.theverge.com/2013/4/17/4236096/nbc-heroes-may-get-a-second-lease-on-life-on-xbox-live", 
          "type": "text/html"
        }
      ],
      "categories": [
        {
          "id": "user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/tech",
          "label": "tech"
        }
      ]
    }
  ],
  ・・・
}
この中で、SlackにtitlehrefをPostします(★が付いている所)
GASでSlackにPostするまでを実装したものが↓になります。
/**
 * Entry function.
 */
function exec() {
  FeedlySlackBot.unread();
}


/**
 * FeedlySlackBot object.
 */
var FeedlySlackBot = {

  PROFILE_URL: 'https://cloud.feedly.com/v3/profile',

  UNREAD_COUNT_URL: 'https://cloud.feedly.com/v3/markers/counts',

  STREAM_URL: 'https://cloud.feedly.com/v3/streams/contents?streamId=',

  SLACK_URL: 'https://hooks.slack.com/services/XXXXXXXXXXXXXXXXXXXXXXXXXXXXX',

  initialize: function() {
  },

  /**
   * Get auth string.
   *
   * @return {String}
   */
  auth: function() {
    return 'OAuth {トークン}';
  },

  /**
   * Log out profile info.
   */
  profile: function() {

    var auth = this.auth();
    var response = this.get(this.PROFILE_URL, auth);
    Logger.log(response.getContentText("UTF-8"));
  },

  /**
   * Get unread feeds.
   */
  unread: function() {

    var auth = this.auth();
    var response = this.get(this.UNREAD_COUNT_URL, auth);

    var feeds = [];
    // Parse json.
    var obj = JSON.parse(response.getContentText("UTF-8"));
    var unreadcounts = obj.unreadcounts;
    for (var i = 0; i < unreadcounts.length; i++) {
      var unread = obj.unreadcounts[i];
      if (unread.count > 0 && unread.id.indexOf('feed/') > -1) {
        Logger.log('unread => id : ' + unread.id + ', count : ' + unread.count);
        feeds.push(unread);
      }
    }

    if (feeds.length == 0) {
      Logger.log('Unread feed not found.');
      return;
    }

    this.stream(feeds);
  },

  /**
   * Collect unread streams.
   *
   * @param {Array} unreads
   */
   stream: function(unreads) {

     var streams = {};

     var auth = this.auth();
     for (var i = 0; i < unreads.length; i++) {
       var id = unreads[i].id;
       var response = this.get(this.STREAM_URL + id + '&count=3', auth);

       var obj = JSON.parse(response.getContentText("UTF-8"));
       var table = [];
       for (var j = 0; j < obj.items.length; j++) {
         var item = obj.items[j];
         var content = {
           'title': item.title,
           'href': item.alternate[0].href
         }
         table.push(content);
       }
       streams[id] = table;
     }

     this.postSlack(streams);
   },

  postSlack: function(streams) {

    for (key in streams) {
      var table = streams[key];
      for (var i = 0; i < table.length; i++) {
        var payload = {
          'text': table[i].title + '\n' + table[i].href,
          'channel': '#general',
          'username': 'XXXXXX',
          'icon_url': 'http://XXXXX/XXXXX.png'
        };
        var r = this.post(this.SLACK_URL, payload);
        Logger.log(r.getContentText("UTF-8"));
      }
    }
  },

  /**
   * Get response with auth.
   *
   * @param {String} url
   * @param {String} auth
   */
  get: function(url, auth) {
    var headers = {'Authorization' : auth};
    var options = {
      'method' : 'get',
      'contentType' : 'application/json;charset=utf-8',
      'headers' : headers
    };

    return UrlFetchApp.fetch(url, options);
  },

  /**
   * Post to url with payload.
   *
   * @param {String} url
   * @param {Object} payload
   */
  post: function(url, payload) {

    var options = {
      'method' : 'POST',
      'payload' : JSON.stringify(payload)
    };

    return UrlFetchApp.fetch(url, options);
  }
};
SLACK_URLにはWebHooksのURLを設定します。
URLの取得方法はこちらを参考にして下さい。
channelusernameicon_urlは好きなように設定して下さい。
また、今回は記事を最大3件分だけ取ってくるようにしています。=> &count=3
結構Feedly放置してると、とんでもない数のPostが飛んでくるので制限をかけましたw
ここまでできたら、Google Driveにアップロードしexecを実行してみて下さい。
Slackに未読記事がPostされてたら成功です。
トリガーを設定して毎日定期的にPostされるようにすればBotの完成です。
一応このプロジェクトは
に上げてます。テンプレートからトークンやらその他設定を.envに書いておけば
main.jsを作ってくれるようになってます。
興味があれる方は是非