Perl入学式 オンライン 2020 第3回お疲れ様でした
受講された方、サポーターの方、お疲れ様でした。 講師をやったジャージの人です。
講義に利用したスライド・動画類は以下 Perl 入学式の公式サイトで公開しています。参加された方も、参加できなかった方も、ぜひ復習に使ってください。
問題の意味がわからない、とか、このような解答例はどうだろう?という方は Discord の Perl 入学式チャンネル(招待コード)や、twitter でハッシュタグ #Perl入学式 をつけて聞いてみてください。
Perl入学式 オンライン 2020 第3回
今回は以下の項目について学習しました。
- if 文
- 配列
- for 文
そして最後にまとめとして「fizzbuzz」をやりました。第 1 回 〜 第 3 回で学習した内容を全て含む良問です。
fizzbuzz についてはスライドでも紹介していますが、こちらの記事が有名です。
Perl のイベント紹介
この時期になると、各エンジニアコミュニティがアドベントカレンダーというものを始めます。
12 月 1 日から 12 月 25 日にかけて 1 日ずつ持ち寄りで記事を投稿していくという企画です。
2020 年の Perl のアドベントカレンダーはこちらです。
見て分からなくても 3 年後、4 年後困った時に「昔、何かで見たな・・・?」と繋がることがありますので目を通しておくこと良いと思います。
また、来年 2 月に Perl のオンラインイベントが開催されることになりました。
楽しみですね〜
おまたせしました!オンラインPerlカンファレンス、https://t.co/zuK0JlFovB の開催が決定しました!2/18(木) 2/19(金)で開催する予定です。詳細につきましては続報をお待ち下さい! #yapcjapan #perl
— yapcjapan (@yapcjapan) 2020年12月2日
次回オンライン第 4 回!
2021 年の 1 月開催予定です。
次回は配列に要素を加えたり取り出したりといった配列の操作関数をやります。
そして最後の変数であるハッシュをやります。
第 4 回ともなると、まったくの初心者の方が途中から入るのは厳しいです。
とはいえ、過去の動画や講義資料は Perl 入学式の公式ページに掲載しております。
ぜひ閲覧いただいた上で参加ください!お待ちしております!!
Perl で Chatwork API を使って添付ファイルをダウンロードする
チャットツール変更
お仕事で使うチャットツールを Chatwork から Slack に変更することになりました。
ユーザーも作り、運用ルールはメルカリさんの物をちょっと変えて・・・メルカリさんありがとうございます。
さて、これでめでたしめでたし。
で・・・終わることもなく。Chatwork のログの抽出を行うことになりました。
いろいろな手段があり、いろいろと情報もあります。スクレイピングツールとか、Ruby の Gem とか。
が、今回は正攻法で Chatwork のプランをエンタープライズプランに変更した上でログのエクスポートを行いました。
やはり、正攻法ならではの安心感がありますね。
エンタープライズプランにするにあたって最低限の課金にするため、必要最低限のメンバー以外は一気に退会させました。
あれ?添付ファイルは・・・?
エンタープライズプランの管理者(1名)は、メニューからログのエクスポートが可能です。
翌朝、ダウンロードリンクが記されたメールが届きます。早速ダウンロード・・・あれ?圧縮済みとはいえ全ログのサイズが 10MB 以下?
そう、Chatwork のログエクスポートは csv ファイルのみ。添付ファイルはダウンロードリンクが files1.csv ってな csv ファイルにまとめられています。
面倒くさいことは Perl で
この files1.csv に記載されている 内容 はこんな感じです。
アップロード日時 ルームID ルーム名 アカウントID アカウント名 ファイル名 ダウンロードURL ファイルサイズ
肝心のダウンロード URL はこんな感じ
https://www.chatwork.com/service/packages/chatwork/subpackages/archive/download_file.php?fid=?????????
なお、ファイルサイズはテキストで 18.70 KB とか 3.27 MB って文字列で書いてあるので、サイズの比較できないですね・・・
このダウンロードリンクからダウンロードできるのはエンタープライズプランの管理者(1名)のみです。
え
これをフォルダに分けつつ、心を込めて 1 つ 1 つ保存していくの? 無理無理ー
添付ファイル保存依頼のものだけで 10000 ファイルを超えます。 人力では絶対無理ー
ここはダウンローダーで一気に処理でしょ!と思うも、このダウンロードのパスにどうエンタープライズプランの管理者(1名)の認証情報を加えればいいかわかりません。
basic 認証、ってこともないはずですが、ダメ元でやってダメでした。
Perl のモジュールで Chatwork 扱えないかなぁ、と思ったものの、API のバージョンが古いものしかないみたいです。残念。
ってことで、モジュール作ることにしました。
API の概要はここです。
これを見ると、ダウンロードしたいファイルの room_id
と file_id
がわかればいけそう。
ただし、この API でできるのはダウンロードするリンクを生成するまで。そのダウンロードリンクの制限時間 30 秒というちょっと捻った感じです。
ということで、files1.csv から ダウンロードリンク を抜き出し、そのリンクから room_id
と file_id
を抽出。
それをもとに URL を組み立てて、API にアクセス、返ってきたダウンロードリンクからダウンロードします。
API の利用制限は 5 分で 300 回、1 秒に 1 回ですね・・・ってことで、ダウンロードごとに 1 秒の sleep
入れると良さそうです。
モジュールにしたものの
この勢いで Chatwork の API 網羅するモジュール作ったろ!って思ったのですが、自分がエンタープライズプランの管理者でいられる時間は短く、あと 1 ヶ月未満です。
だって Chatwork を解約前提なのだもの・・・
ということで、モジュールは作ってみたものの、自分が使う最低限だけ実装したのでした。
もっと早く管理者権限を手にしていればなー、と思ったのでした。
Perl で Google Sheet API をつかってセルに値を書き込む
さくっとやりました!と言いたいところなのですが
Google Sheet API 叩き職人の朝は早い
— sironekotoro (@sironekotoro) 2020年11月23日
「'status' => '400',
'reason' => 'Bad Request',
これが出てからが本番ってことさ」
この道3ヶ月の sironekotoro はそう言って笑う
3 ヶ月じゃないな 2 ヶ月くらいだな。
Google Sheet API でセルの内容を書き換えるメソッドは 3 つあるんですが、自分の用途に合致した values/batchUpdate
を使いました。
後、Qiita のこの記事がとても参考になりました。
こんな感じでセルを埋めることができます。
#!/usr/bin/env perl use strict; use warnings; use Data::Dumper; use HTTP::Tiny; use JSON; use URI; use utf8; my $ACCESS_TOKEN = ''; my $SPREADSHEET_ID = '1sVNSpvtWPPkv5Qnb0tifYHpKMxaehxj5ChAeQ1G1kgA'; my $GOOGLE_SHEET_API = "https://sheets.googleapis.com/v4/spreadsheets/"; my $bearer = join " ", ( 'Bearer', $ACCESS_TOKEN ); # Method: spreadsheets.values.batchUpdate | Sheets API # https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/batchUpdate values_batchUpdate(); sub values_batchUpdate { my $ht = HTTP::Tiny->new( default_headers => { Authorization => $bearer, } ); my $SHEET_URI = $GOOGLE_SHEET_API . $SPREADSHEET_ID . '/values:batchUpdate'; my $URI = URI->new($SHEET_URI); my $response = $ht->request( 'POST', $URI, { content => encode_json( { valueInputOption => 'USER_ENTERED', data => [ { range => 'シート1!A1', values => [ [ 200, 100, '=A1+B1' ], [ 'hoge', '', 'fuga' ] ] } ], includeValuesInResponse => 'true', responseValueRenderOption => 'UNFORMATTED_VALUE', responseDateTimeRenderOption => 'FORMATTED_STRING', } ) }, ); # print Dumper $response; }
Perl で Google Sheet API をつかってセルの値を取得する
これも GET だけでできるのでお手軽ー
説明よりもコードを読んでもらったほうが早い気がする。
配列リファレンスの中の配列リファレンス、てな形でデータが取得できるので、csv にするなりなんなり後は自由自在ですね(力量による)
ちょっと詰まったところ
この API は取得するセルの範囲を指定する必要があります。
シート1!A1:C2
こんな感じで。
もう一段楽をするためには、値が入っているセルの範囲を取得する必要があります。
値が入っているセルの行と列の最大値が欲しい・・・!
Google Apps Script だと getLastRow()
とか getLastColumn()
みたいなやつ。
となると、API でアクセスして、情報が入っている最後の Row(行)と Column(列)を把握して・・・あれー、そんな関数見当たらないぞー
困ったときは Google 先生、そして stack overflow。
You can set the range to "A2:D" and this would fetch as far as the last data row in your sheet.
あ、そういう・・・列さえ把握しておけばいい的な・・・
で解決したのでした。
Perl で Gogle Sheet API をつかってセルの値を取得する
以下がコードです。
情報をとるときには 2 つの方法 values.get
, values.batchGet
があるので、太っ腹に両方やってみました。
#!/usr/bin/env perl use strict; use warnings; use Data::Dumper; use HTTP::Tiny; use JSON; use URI::QueryParam; use URI; use utf8; my $SPREADSHEET_ID = '1sVNSpvtWPPkv5Qnb0tifYHpKMxaehxj5ChAeQ1G1kgA'; my $ACCESS_TOKEN = ''; my $GOOGLE_SHEET_API = "https://sheets.googleapis.com/v4/spreadsheets/"; my $bearer = join " ", ( 'Bearer', $ACCESS_TOKEN ); values_get(); values_batchGet(); # Method: spreadsheets.values.get # https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/get sub values_get { my $ht = HTTP::Tiny->new( default_headers => { Authorization => $bearer, } ); my $SHEET_URI = $GOOGLE_SHEET_API . $SPREADSHEET_ID . '/values/シート1!A1:C'; my $URI = URI->new($SHEET_URI); my $res = $ht->get($URI); my $data = decode_json( $res->{content} ); print Dumper $data->{values}; } # Method: spreadsheets.values.batchGet # https://developers.google.com/sheets/api/reference/rest/v4/spreadsheets.values/batchGet sub values_batchGet { my $ht = HTTP::Tiny->new( default_headers => { Authorization => $bearer, } ); my $URI = URI->new( 'https://sheets.googleapis.com/v4/spreadsheets/' . $SPREADSHEET_ID . '/values:batchGet' ); $URI->query_param( ranges => 'シート1!A1:C'); my $res = $ht->get($URI); my $data = decode_json( $res->{content} ); print Dumper $data->{valueRanges}->[0]->{values}; }
Perl で Google Sheet API をつかってタイトルを変更する
なんか、適当に文言を入れ替えて自動生成したみたいなタイトルになってきたなぁ・・・
それはそれとして、Google Drive API に引き続き、Google Sheet API を使ってホゲホゲするってやつです。
以前、モジュールを使ってやってみたんですが、せっかく Web API 投げる方法もわかったことだし、この方法でやってみよう!ってことで。
使う都度 Any::Moose の警告が出るのが気に食わないってのもあります。
Google Sheet API の許可とアクセストークン が必要です
かつての記事で書いてます・・・なんか、遠い昔の気がする
タイトルを変更する
まずはジャブから・・・早速、公式のリファレンスを確認します。
おぉ、わかりやすい。もっと他の言語での実装例を参考にして回るかと思ってた。あっさり。
リファレンスにあるリクエストボディ は json にしておく必要があります。
これは先のページには書いてないんですが、正しい構造のJSON 送らないとエラーで「まともな JSON で送って〜」と言われます。
今回は好みで HTTP::Tiny 使いました。
リクエストボディを設定するときは公式リファレンスの \%options
のところで content
をキーにする必要があるので注意です。
エラーメッセージを見て直しつつ完成したのが以下スクリプトです。
#!/usr/bin/env perl use strict; use warnings; use HTTP::Tiny; use JSON; use URI; use utf8; my $SPREADSHEET_ID = '1sVNSpvtWPPkv5Qnb0tifYHpKMxaehxj5ChAeQ1G1kgA'; my $ACCESS_TOKEN = ''; my $GOOGLE_SHEET_API = "https://sheets.googleapis.com/v4/spreadsheets/"; my $bearer = join " ", ( 'Bearer', $ACCESS_TOKEN ); my $ht = HTTP::Tiny->new( default_headers => { Authorization => $bearer, } ); my $url = URI->new( join "", ( $GOOGLE_SHEET_API . $SPREADSHEET_ID . ':batchUpdate' ) ); my $response = $ht->request( 'POST', $url, { content => encode_json { requests => [ { updateSpreadsheetProperties => { properties => { title => "Perl から API 叩いて変更したタイトル" }, fields => "title" } } ] } }, ); print $response->{status} . "\n"; print $response->{reason} . "\n";
ちなみに、タイトルを変えても更新日は変わらない模様。
Perl で Slack API を使ってユーザー一覧を取得する(追記あり)
Slack のユーザー一覧を取得せよ!
ってなお仕事が降ってきたものの、Slack のメニューとか探したど見当たらず。
これは API 使えってことなのかなぁ・・・使うんはいいけど、普通の非テックな会社だと厳しいんでは。
もっと高い料金プランだと出てくるのかな?
それとも、非テックでも Slack 使うような会社ならそれくらいいけるんだろうか。
API を使うそのまえに
ってわけで、いつも通り Perl でやります・・・が、やる前に API を利用できるようにする準備から。
こちらの記事が大変役に立つというか、そのまま進んでそのまま OAuth Token を入手することができました。
ありがたい。
WebService::Slack::WebApi
あとは、Perl の便利モジュールがうまいことやってくれます。
#!/usr/bin/env perl use strict; use warnings; binmode STDOUT, ":utf8"; use WebService::Slack::WebApi; my $ACCESS_TOKEN = 'xoxp-hogehogefugafuga'; my $slack = WebService::Slack::WebApi->new( token => $ACCESS_TOKEN ); my $users = $slack->users->list; # $users の中にユーザーの情報が入っているので、 # とりたい情報が気になる人は Data::Dumper で中を覗いてみる for my $member ( @{ $users->{members} } ) { # 削除済みユーザーや、bot も含まれているので除外する next if ( $member->{deleted} == 1 ) || ( $member->{is_bot} == 1 ); print "$member->{name}\t$member->{profile}->{display_name}\n"; }
あっさり。
2020-11-20 追記
依頼主もメンバー一覧とるのに API しかなさそうー、って言ってはいたんですが、slack のワークスペースの管理者であれば、確認方法があることを教えていただきました。
そりゃあるよなぁ!(テノヒラクルー
突然すみません。キーワード見てて見かけました。ワークスペースの管理者であれば、メンバーの管理画面から CSV 形式でユーザー一覧をダウンロードできますよー。あとはエクスポート機能の出力ファイルの中にも users.json というのが含まれます。
— Kazuhiro Sera (瀬良) (@seratch_ja) 2020年11月19日
ありがとうございます・・・って、bio 見たらこの方、slack のAPI SDK 作っておられるのですね・・・助かりました。
画面左上のワークスペース -> 設定と管理 -> メンバーを管理する、から確認することができました。
Perl で Google Drive API をつかって特定のファイルを削除する
ファイルの上書き終わったところでこのシリーズ(シリーズ?)終わらせようと思っておりました。
しかし CRAD:Create, Read, Add, Delete の一角たる Delete やらないっていうのはないんじゃないか?
どうせ後で必要になりそうだし・・・あぁ、でも面倒〜。
ってことでやりました。
うおお、DELETE メソッド初めて使う気がする。そして応答で返ってきた status code 204 も初めてみた。 / Files: delete | Google Drive API | Google Developers https://t.co/YehnNs0o2D
— sironekotoro (@sironekotoro) 2020年11月13日
いざ手をつけるとすぐでしたが、普段は色々なモジュールの裏に隠れている HTTP の DELETE メソッドを明示的に初めて使ったように思います。
情報処理試験の教本や問題集でしか見たことのなかった(そして問題には出そうにない) DELETE メソッド、本当にあって本当に使えるんや・・・という謎の感動がちょっとあります。
そして、削除が成功した時の HTTP ステータスコード 204 これも初見でした。
削除したら応答も何もないよねというのわかる。
はー!これで CRAD 全部揃ったーはずー!!
・・・しかし、なんか my
宣言が並んでてアレな感じのコードですね・・・
#!/usr/bin/env perl use strict; use warnings; binmode STDOUT, ":utf8"; use URI; use HTTP::Tiny; my $ACCESS_TOKEN = ""; my $GOOGLE_DRIVE_API = "https://www.googleapis.com/drive/v3/files/"; my $delete_fileid = "1SNmgEpX2xnFCbeRQWtGg4PdU0ic6h92IO-VuynVGli8"; my $bearer = join ' ', ( 'Bearer', $ACCESS_TOKEN ); my $uri = URI->new( $GOOGLE_DRIVE_API . $delete_fileid ); my $ht = HTTP::Tiny->new( default_headers => { Authorization => $bearer } ); my $response = $ht->delete($uri); print $response->{status} . "\n"; # 204