10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

RubyでQiitaの投稿をSlackに通知する

Last updated at Posted at 2019-06-14

##概要
社内メンバーのQiita投稿をSlackに通知する仕組みをRubyとAWS lambdaで実装する機会があったので、その手順をまとめます。

おおまかな仕組みとしては、

  1. 会社のQiitaページのフィードを解析し、1時間以内の投稿を取得する
  2. Slackに通知する
  3. それを1時間ごとに実行する

という形式をとりました。

##環境
Ruby 2.5
slack-notifier 2.3.2

###ディレクトリ構成

.
├── Gemfile
├── Gemfile.lock
├── lambda_function.rb
└── vendor
    └── bundle

##1時間以内の投稿を取得する
投稿の取得にはフィードを利用します。フィードには記事の著者や投稿日、更新日といった情報が記載されているので、フィードを解析することで、その投稿が1時間以内になされたものかを判定することができます。

###フィードの取得
QiitaではAtomという形式でフィードを配信しており、Organizationのフィードは、OrganizationのURL に/activities.atom を加えることで取得できます。

http://qiita.com/organizations/<NAME_ORGANIZATION>/activities.atom

参考:Qiita 記事やユーザのフィード URL(フォローしたいユーザーやタグのXML/ATOMのURL)

###フィードの解析
フィードの解析にはRubyの標準ライブラリであるrssライブラリが利用できます。
RSS::Parser.parseにパースしたいフィードを渡すことで、解析した結果のオブジェクトを返してくれます。

require 'rss'

atom_organization = "http://qiita.com/organizations/<NAME_ORGANIZATION>/activities.atom"
atom = RSS::Parser.parse(atom_organization, false)

parseメソッドの第二引数にfalseを指定していますが、これはパースする際にバリデーションを行わないことを意味します。現状Qiitaが提供しているAtomはAtomの構文に完全に即していないのかバリデーション付きでパースを行うとRSS::MissingAttributeErrorが発生します。

entriesにフィードの全ての投稿が入るので、eachで1件ずつ取り出し、投稿日時が1時間以内である記事のURLを全て取得します。

require 'rss'
require 'time'

links = []
current_time = Time.now

atom.entries.each do |entry|
  published_time = entry.published.to_s.gsub(/<.+>(.+)Z<.+>/, '\1')
  published_time = Time.parse(published_time)
  links.push(entry.link.href) if published_time >= current_time - 3600
end

entry.published.contentとしてやれば、gsubやTime.parseを使わなくても直で日時を取得できるのですが、タイムゾーンがUTCになります。そのため、正しい時刻から(日本の場合)9時間ずれることになるので、私は上記のよう方法をとりました。
(ここら辺に関してはもっとスマートな方法がありそうです…)

##Slackに通知
slack-notifierというgemを使うことで、簡単にSlackへ通知をすることができます。
https://github.com/stevenosloan/slack-notifier

まずは事前準備として、以下の2つが必要です。

  • gem slack-notifierのインストール
Gemfile
gem 'slack-notifier'

下記のようにすることで、先ほど取得したQiita記事のリンクをSlackに通知することができます。

require 'slack-notifier'

notifier = Slack::Notifier.new('<取得したWebhook URL>')
notifier.ping('<Qiita記事のリンク>', unfurl_links: true)

unfurl_links: trueはリンクのプレビューを表示するための引数です。無くても問題ありません。
参考:slack-notifierでrailsアプリからslackへ通知

##定期実行
ここまでで①会社のQiitaページのフィードを解析し、1時間以内の投稿を取得すること②その投稿のURLをSlackに通知することができました。最後に①と②を1時間ごとに実行できれば、社内メンバーのQiita投稿をSlackに通知する仕組みの完成です。

###AWS lambda
定期実行の方法としては、概要でも述べたようにAWS lambdaを使用しました。AWS lambdaとは、関数を登録しておくことで、クラウド上で自動的にコードを実行してくれるサービスです。それ以上の深い説明は私にはできません...

####関数の作成
まずは、ローカルでAWS lambdaに登録するSlack通知の関数を作成します。
関数を書くのはAWS lambdaのコンソールで行うこともできますが、Lambdaに標準でインストールされていないライブラリ(今回の場合slack-notifier)を使うには、ローカルでパッケージングしたものをデプロイする必要があります。

私の場合、以下のようなプログラムにしました。

lambda_function.rb
require 'slack-notifier'
require 'rss'
require 'time'

def get_recent_links(atom, current_time)
  links = []

  atom.entries.each do |entry|
    published_time = entry.published.to_s.gsub(/<.+>(.+)Z<.+>/, '\1')
    published_time = Time.parse(published_time)
    links.push(entry.link.href) if published_time >= current_time - 3600
  end

  return links
end

def slack_notifier(message)
  #環境変数SLACK_WEBHOOK_URLはlambdaのコンソールで設定します
  notifier = Slack::Notifier.new(ENV['SLACK_WEBHOOK_URL'])
  notifier.ping(message, unfurl_links: true)
end

def lambda_handler(event:, context:)
  #同じくTARGET_ATOM_URLはlambdaのコンソールで設定します
  atom_organization = ENV['TARGET_ATOM_URL']
  atom = RSS::Parser.parse(atom_organization, false)
  current_time = Time.now
  qiita_links = get_recent_links(atom, current_time)
  return if qiita_links.empty?

  message = <<~EOS
    [Qiita新着投稿]
    Qiitaに新着投稿がありました。
    #{qiita_links.join("\n")}
  EOS

  slack_notifier(message)
end

####デプロイ
デプロイ方法については以下の2つが参考になりました。私と同じくAWSに慣れていない方は、zipに圧縮してコンソールからアップロードするというやり方が一番わかりやすいかと思います(以下はこの方法をとった前提で進めます)。
Ruby support for AWS Lambdaを使ってみる
AWS Lambda の Ruby ランタイムを試す

リンク先にも書かれていますが、AWS LambdaのRubyバージョンは2.5です(2019年6月現在)。開発側のRubyのバージョンが2.5でないとデプロイを行ってもrequireするときにエラーになります。

####環境変数の設定
デプロイ後は環境変数の設定をします。
関数のエディタの下に設定できる欄があるので、プログラム中で用いている2つのURLに加え、タイムゾーンをJSTに変更するため、TZの値をAsia/Tokyoに設定します。
参考:AWS Lambdaのタイムゾーン変更
image.png

####トリガーの設定
トリガーは画像中の左側のリストからCloudWatch Eventsを選択します。
image.png

すると下側にトリガーの設定が表示されるので、新規ルールの作成を選択し、1時間ごとに実行するよう設定します。
image.png
設定が完了したらトリガーを追加し、関数を保存して完成です。

参考
【AWS】lambdaファンクションを定期的に実行する

10
2
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
10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?