#はじめに
この記事は前回投稿した記事の解説編です。
スクリプトを使いたい方は、前回の記事を参照してください。
Slackのemojiを別のTeamに引っ越しするスクリプトをRubyで作ってみた
今回Slackのemoji引っ越しスクリプトを作成して、Mechanizeの便利さを知りました。
Mechanizeを使用することでAPIが用意されていないサービスでも色々自動化出来そうだなと思いましたので、簡単に作ったスクリプトの解説を書いてみます。
少しでもお役に立ちましたら幸いですm(_ _)m
#ソースについて
こちらにアップしております。
slack_emoji_mover
そのまんまでセンスのない名前ですね^^;
#解説
##概要
このスクリプトは以下の作業を自動化しています。
- 引越し元のemojiリストを取得する
- 引越し元のemojiリストから画像ファイルを取得する
- 引越し先のSlackのTeamにログインする
- Webページにemojiの情報を記入する
- formデータをsubmitする(「Save New Emoji」のボタンを押す)
- アップロードが完了していない場合4.に戻る
上記以外に設定ファイルの読み込み等を行っています。
色々やっているようですが、スクリプトは200行以下ですし、有効行だけなら150行もありません。
それでは、処理順に解説していきます。
※requireや変数宣言などは省略します。
##詳細
###設定ファイルの読み込み
以下の関数は、設定ファイルの読み込みを行っています。
ConfigFile = 'emoji_conf.txt'
def readConfigFile
config = Array.new
File.open(ConfigFile, 'r') do |file|
file.each_line do |line|
stripLine = line.strip
if stripLine !~ /^#/ && !stripLine.empty?
config.push(line.strip)
end
end
end
@@url = config[0] # target slack team URL
@@email = config[1] # email to login
@@password = config[2] # password
@@token = config[3] # target slack team token
end
上記関数で行っていることは単純で、ファイルを読み込んで一行づつArrayに追加した後に、設定保持用のローカル変数にセットしています。
工夫した点として、if stripLine !~ /^#/ && !stripLine.empty?
で先頭が#か空行の場合は無視するようにした点です。
これでコメントを複数行書けるようにしました。
実際にコミットされている設定ファイルは以下の様になっています。
# Slack team URL to import emoji
https://xxxx.slack.com/
# login mail address
xxxx@xxxx
# login password
xxxx
# Slack Token to get emoji list from team
xxxx-xxxx-xxxx
以下の関数は、デフォルトでSlackの#内に登録されているemojiを弾くために作ったリストを読み込む関数です。
設定ファイルを読み込む関数を流用したため、ほぼ同じ構造です。
(書いていて思いましたが、共通化出来そうですね・・・。反省点です。)
DefaultFile = 'default_emoji.txt'
def readDefaultEmojiFile
@@default = Array.new
File.open(DefaultFile, 'r') do |file|
file.each_line do |line|
stripLine = line.strip
if stripLine !~ /^#/ && !stripLine.empty?
@@default.push(line.strip)
end
end
end
end
###引越し元のemojiの取得
以下の関数で、引越し元のemojiのリストを取得します。
Slackに用意されているAPIを使用し、返ってきたレスポンスをファイルに書き出します。
参考記事:
slackに登録した custom emoji を取得する
ApiUrl = 'https://slack.com/api/emoji.list?pretty=1&token='
def getEmojiText
open(EmojiFile, 'wb') do |output|
open(ApiUrl + @@token) do |data|
output.write(data.read)
end
end
end
上記APIから、以下の様なJSONが取得できます。
{
"ok": true,
"emoji": {
"bowtie": "https:\/\/emoji.slack-edge.com\/T1DKGLDQA\/bowtie\/f3ec6f2bb0.png",
"squirrel": "https:\/\/emoji.slack-edge.com\/T1DKGLDQA\/squirrel\/465f40c0e0.png",
"glitch_crab": "https:\/\/emoji.slack-edge.com\/T1DKGLDQA\/glitch_crab\/db049f1f9c.png",
...
emoji内にemojiの名前とその画像ファイルのURLがペアになって記載されています。
この情報があれば、引越し元のemojiの情報は十分ですね。
それでは、上記の情報から画像ファイルを取得しましょう。
まずは先程取得したJSONを読み込みます。
この時、一部のemojiは標準で登録されているため、最初に読み込んだデフォルトのemojiリストにヒットした場合は削除するようにしています。
また、aliasで始まる場合はエイリアスなので、こちらも削除しています。
def readEmojiJSON
File.open(EmojiFile, 'r') do |file|
@@hash = JSON.load(file)
end
@@hash = @@hash['emoji']
@@hash.each do |node|
if !@@default.index(node[0]).nil? || node[1] =~ /^alias/
@@hash.delete(node[0])
end
end
end
上記関数で@@hash
内にカスタマイズしたemojiのみ残りました。
画像ファイルを全てローカルにダウンロードしましょう。
open
はファイルを開くだけでなく、URLを指定することで画像ファイルなどをダウンロードすることも出来ます。
参考記事:
ファイルをWebからダウンロードして保存する
def getEmojiData
@@hash.each do |node| # loop each node
name = File.basename(node[1]) # get file name from URL
if name !~ /^alias/
open(name, 'wb') do |output|
open(node[1]) do |data|
output.write(data.read) # write to local
end
end
end
sleep 3
end
end
画像をダウンロードする際に、サーバーの負担を軽減するためにsleep
を3秒入れています。
あまりサーバーに負荷をかけると攻撃と変わらないので、少し間を挟むようにしています。
(なお、aliasから始まっている場合スキップするようにしていますが、すでに@@hash
から削除しているので不要かもしれないですね・・・。)
###emojiのアップロード
さて、画像は全てダウンロードしたので後はアップロードするだけです。
少し長いので、分けて解説します。
def uploadEmoji()
log_file = open(LogFile, 'wb')
agent = Mechanize.new
agent.user_agent = 'Windows Mozilla'
# login to Slack team
agent.get(@@url) do |page|
response = page.form_with(:action => '/') do |form|
formdata = {
:email => @@email, # login mail address
:password => @@password # login password
}
form.field_with(:name => 'email').value = formdata[:email]
form.field_with(:name => 'password').value = formdata[:password]
end.submit
# if login fails, write error to log file and exit
if response.code != '200' || response.body.include?(LoginFail)
log_file.write("Login failed! Please check Slack url, email and password.\n")
return -1
end
log_file.write("Login success!\n")
# loop upload emoji
@@hash.each do |node|
name = File.basename(node[1])
agent.get(@@url + 'customize/emoji') do |emoji|
if File.exist?('./' + name)
eresponse = emoji.form_with(:action => '/customize/emoji') do |eform|
eform.field_with(:name => 'name').value = node[0]
eform.radiobuttons_with(:name => 'mode')[0].check
eform.file_upload_with(:name => 'img').file_name = './' + name
end.submit
# write result to log
# check responce code and body to decide success or failer
if eresponse.code != '200' # check response code
log_file.write("F Name:[" + node[0] + "] FileName:[" + name + "] Result: ")
log_file.write("Respose code is not 200. Failed to add emoji.\n")
elsif eresponse.body.include?(AddSuccess) # add success log
log_file.write("S Name:[" + node[0] + "] FileName:[" + name + "] Result: ")
log_file.write("Successfully added.\n")
elsif eresponse.body.include?(DuplicateEmoji) # add error log - duplicate error
log_file.write("F Name:[" + node[0] + "] FileName:[" + name + "] Result: ")
log_file.write("Same emoji name already exist.\n")
else # add error log - unknown error
log_file.write("F Name:[" + node[0] + "] FileName:[" + name + "] Result: ")
log_file.write("Unknown error occured.\n")
end
else # add error log - file not found
log_file.write("F Name:[" + node[0] + "] FileName:[" + name + "] Result: ")
log_file.write("File not exist.\n")
end
end
sleep 3 # wait 3 seconds for slack server
end
end
return 0 # success
end
以下の部分はSlackのチームにログインしている箇所です。
参考記事:
[Ruby]Mechanizeでログイン機能があるサイトにログインする
# login to Slack team
agent.get(@@url) do |page|
response = page.form_with(:action => '/') do |form|
formdata = {
:email => @@email, # login mail address
:password => @@password # login password
}
form.field_with(:name => 'email').value = formdata[:email]
form.field_with(:name => 'password').value = formdata[:password]
end.submit
# if login fails, write error to log file and exit
if response.code != '200' || response.body.include?(LoginFail)
log_file.write("Login failed! Please check Slack url, email and password.\n")
return -1
end
log_file.write("Login success!\n")
まずMechanizeを使用して、Slackのチームのページを取得してきます。
agent.get(@@url) do |page|
(@@url
には引越し先のSlackのURLが入ります)で、page
にページを読み込みます。
その後、response = page.form_with(:action => '/') do |form|
でページ内のformを取得します。
※page.form_with(:action => xx)
に設定する値は、html内のform要素に設定されている値となります。
このform内にはemail
とpassword
という記入項目があるので、設定した値をセットします。
こちらもinputのnameに設定されている値をブラウザの開発ツール等使用して調べることが出来ます。
または、form_with
で取得してきた値をp xxx
でdumpすれば、どのような要素があるか確認出来ます。
全ての値をセットしたら、end.submit
で送信します。
結果はresponse
内のcode
とbody
の値をチェックし、正しくログインされたかどうか確認します。
code
はサーバーからのレスポンスコードになるので、200以外は失敗となります。
以下の部分で、ログインに成功した状態を保ったままemojiをアップロードしていきます。
@@hash.each do |node|
name = File.basename(node[1])
agent.get(@@url + 'customize/emoji') do |emoji|
if File.exist?('./' + name)
eresponse = emoji.form_with(:action => '/customize/emoji') do |eform|
eform.field_with(:name => 'name').value = node[0]
eform.radiobuttons_with(:name => 'mode')[0].check
eform.file_upload_with(:name => 'img').file_name = './' + name
end.submit
まずループを開始し、全てのemojiを処理するようにします。
先程ログインしたagent
を使用して、emojiを設定するページを読み込み、formを取得します。
emojiの名前、チェックボックスのチェック、画像ファイルを設定し、送信します。
参考記事:
RubyのMechanizeで画像をPOSTする方法
残りは結果のチェックです。
# write result to log
# check responce code and body to decide success or failer
if eresponse.code != '200' # check response code
log_file.write("F Name:[" + node[0] + "] FileName:[" + name + "] Result: ")
log_file.write("Respose code is not 200. Failed to add emoji.\n")
elsif eresponse.body.include?(AddSuccess) # add success log
log_file.write("S Name:[" + node[0] + "] FileName:[" + name + "] Result: ")
log_file.write("Successfully added.\n")
elsif eresponse.body.include?(DuplicateEmoji) # add error log - duplicate error
log_file.write("F Name:[" + node[0] + "] FileName:[" + name + "] Result: ")
log_file.write("Same emoji name already exist.\n")
else # add error log - unknown error
log_file.write("F Name:[" + node[0] + "] FileName:[" + name + "] Result: ")
log_file.write("Unknown error occured.\n")
end
else # add error log - file not found
log_file.write("F Name:[" + node[0] + "] FileName:[" + name + "] Result: ")
log_file.write("File not exist.\n")
end
end
sleep 3 # wait 3 seconds for slack server
end
end
return 0 # success
end
レスポンスコードが200でない場合、正常でないため失敗したことをログに残します。
コードが200で成功した場合は、返ってきたbody内に記載されている文言で判定します。
(nokogiriを使用しないようにするつもりでしたが、Mechanizeが依存しているということに後で気付きました・・・。nokogiriを使えばもう少し柔軟な判定が出来そうです。)
また、画像のダウンロードが失敗した場合を考慮し、アップする画像がローカルにない場合はエラーをログに残します。
後は3秒のsleepを挟み、emojiをアップし続けます。
放置しておけば、勝手にemojiが全てアップされます。
#最後に
そこそこ長い記事となってしまいました。
一気に書いてしまい結構端折ってしまった部分もあり、htmlの知識などが前提になってしまっている部分もあるため、分かりにくいかもしれません。
分かりにくい点があれば、コメントして頂けると幸いです。
また、今回の手法はURLなどが完全に決め打ちとなってしまっている部分もあるため、非常に仕様変更に弱いです。
SlackのWebページの仕様が変わった途端に動かなくなる可能性が大です。
頻繁にデザインが変わるページの場合は注意してください。
手作業では大変な作業を自動化したい時に、今回の記事が役立ちましたら幸いです。
次回はこちらのスクリプトを応用して作った、簡単にemojiをアップするスクリプトを解説できたらと思います。