ページ

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

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を作ってくれるようになってます。
興味があれる方は是非

2016年7月31日日曜日

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


前回の続きです。
前回はプロフィール情報をログ出力する所までやりました。
今回はまずは未読の件数を取得してみたいと思います。

未読件数一覧

早速試しに以下コマンドを叩いてみたください。
$ curl -H 'Authorization: OAuth {トークン}' https://cloud.feedly.com/v3/markers/counts
ずらずらっと件数とidがセットで返ってくると思います。
例)
{
  "unreadcounts": [
    {
      "count": 605,
      "id": "user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/global.all",
      "updated": 1367539068016
    },
    {
      "count": 601,
      "id": "user/c805fcbf-3acf-4302-a97e-d82f9d7c897f/category/design",
      "updated": 1367539068016
    },
    {
      "count": 508,
      "id": "feed/http://www.autoblog.com/rss.xml",
      "updated": 1367539068016
    }
  ],
  "updated": 1367539057683
}
idがuser/から始まっているものは
user/・・・/category/global.allが全未読記事数
user/・・・/category/designがカテゴリー毎の未読記事数
となっています。
またidがfeed/から始まっているものは
それぞれ登録したブログ毎の未読記事数となります。

feed毎の未読記事件数を取得

前回プロフィール表示した時のsrc/main.jsを修正します。
/**
 * 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',

  /**
   * 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;
    }
  },

  /**
   * 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);
  }
};
追加したunreadメソッドで未読件数をGETするリクエストを投げて、
結果をfeedsの配列に追加していってます。
その際に件数が1件以上、idがfeed/・・のみでフィルタリングしてます。
GoogleDriveにアップロードしてexecを実行するとログに
ブログ毎の未読件数一覧が表示されているかと思います。

アクセストークンが有効期限切れの場合

developer用のアクセストークンは30日で有効期限が切れます。
アクセストークンが有効期限切れの場合、以下のようなメッセージが帰ってきます。
{"errorCode":401, "errorId":"ap2-sv2.XXXXXXXXXX.XXXXXXX", "errorMessage":"token expired: XXXXXXXXXXX (-XXXXXX)"}%
401が帰ってきた場合は再度アクセストークンを作り直す必要があります。
ここら辺を継続的にできるようになればいいのですが・・
次回は未読記事のタイトルとURLをSlackにポストするまでをやってみたいと思います。

2016年7月23日土曜日

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


現在RSSリーダーは主にFeedlyを使ってるんですが、
未読記事を定期的にSlackにポストしてくれたら便利かな〜と思って
作ってみることにしました。
※ もしかしたら既にBotがある?かもしれませんが・・
事前準備

まずはここからログインし、FeedlyのDeveloper用のトークンをゲットします。

※ Feedlyのアカウントを持ってない方はこの機会にどうぞ
(⚠️Feedlyのまわし者ではありませんので・・)
ログインすると登録しているメールアドレスに認証用のメールが送られます。
送られたメールのリンクをクリックするとアクセス用のトークンがもらえます。

FeedlyのAPIを呼ぶ際に必要なトークンとなります。
試しに自分のプロフィール情報を取ってみます。
$ curl -H 'Authorization: OAuth {トークン}' https://cloud.feedly.com/v3/profile
{トークン}部分に上で取得したトークンを貼り付けます。
実行するとずらずらっとプロフィール情報が取得できたかと思います。
開発環境

次にローカルでの開発環境ですが、
こちらも以前の記事で紹介したnode-google-apps-scriptを使用します。
初期設定などは上の記事を参照して下さい。
※事前にnode.jsのインストールが必要です⚠️
まずはパッケージ情報や依存するパッケージなどを記述するpackage.jsonを作成します。
$ npm init
name: (....) feedly_slack_bot⏎ # 名前はfeedly_slack_bot
version: (1.0.0) ⏎
description: ⏎
entry point: (index.js) main.js ⏎ # 今回はmain.jsがメインファイル
・・・ 残りは好きなように設定
Is this ok? (yes) yes ⏎
以下のようなpackage.jsonが作成されているかと思います。
{
  "name": "feedly_slack_bot",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Slowhand0309",
  "license": "MIT"
}
ここで肝心のnode-google-apps-scriptをインストールします。
$ npm install node-google-apps-script --save-dev
これでGASへのアップロードがCLIで行えるようになります。
プロフィール表示

試しにGASでプロフィールを表示するまでをやってみます。
gapps initを行い、ローカルにgapps.config.jsonsrc/*.js
をダウンロードしてきます。
src/main.jsとし、↓に編集します。
/**
 * Entry function.
 */
function exec() {
  FeedlySlackBot.profile();
}

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

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

  /**
   * 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 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);
  }
};
{トークン}は上で取得したトークンを設定して下さい。
アップロードし、
$ gapps upload
Google Drive上でexecを指定し実行するとログにプロフィール情報が
出力されているかと思います。
今回はここまで