ミスミメンテbotにカウントダウンを実装した
カウントダウン機能をつけたくなった
ミスミメンテbotは メンテナンスが予告されている間毎日ツイートする仕様。
毎日まったく同じツイートをするのはつまらない。 そこで、カウントダウン機能をつけることにした。
#ミスミメンテ
— ミスミメンテおしらせbot《非公式》 (@misumi_mainte) 2020年1月23日
Start: 1/26 11:00
Goal: 1/27 7:30
メンテまであと 2日と4時間
https://t.co/C2VY3rgPcZ
#ミスミ pic.twitter.com/8BQiSVAp3k
実装
以下のようなカウントダウンの関数を作った。
いままでのプログラムではツイートしかしなかった為、 ミスミサイトから取得した日時を数字に変換せず文字列として扱っていた。 また、開始終了の日時をまとめて使いやすくする為、配列に格納している。
そこで、ひとまず文字列の配列として日時を受け取り、関数内で数字への変換機能を実装することにした。
# countdownを文字列で返す # 入力は文字列の配列 def count_down(date, time):
メンテ日時の文字列を数字に変換
先述したように、今までは日時を文字列として扱っていた。
今回は残り日時の計算に使いたいので、数字に変換する。
# 取得日時を切り出してintにする str = re.sub(r'\D', ' ', date[0]) sdate = str.split() smonth = int(sdate[0]) # 月 sday = int(sdate[1]) # 日 str = re.sub(r'\D', ' ', time[0]) stime = str.split() shour = int(stime[0]) # 時
現在日時の取得
現在日時はpythonの機能で取得できる。
気をつけなければいけないのはタイムゾーン。
ミスミサイトの表示はJST。 筆者は日本に住んでいて、JST設定のPCを使っている。 そのため、ローカルで日時差分をとると何も考えなくても問題なく動く。
しかし、定期実行に使っているherokuはデフォルトでJSTではない為、 明示的にタイムゾーンをJSTに指定する必要がある。
# 現在日時 # タイムゾーンの生成 JST = timezone(timedelta(hours=+9), 'JST') # JSTで現在時刻 time_info=datetime.now(JST) # time_info=datetime.today() print('time_info:{}'.format(time_info)) year_now = time_info.year
メンテ日時を現在日時と同じ形式に整える
ところで、ミスミサイトのメンテ日時案内には年が表示されたりされなかったりする。 また、ツイート本文に年をわざわざ書く必要も感じない。
このため、スクレイピングでは年情報を取得していない。
しかし、 datetimeオブジェクトを使いたいので、 メンテ日時にも年情報が必要となる。
datetime --- 基本的な日付型および時間型 — Python 3.8.1 ドキュメント
メンテナンスが年末年始に行われることはないだろうと考え、 現在の年=メンテ時の年 と指定する。
前章で用意した、
year_now = time_info.year
を利用する。
# 開始日時 # メンテは年をまたがないと仮定 # time_start = datetime(year_now,1,2,3) time_start = datetime(year_now,smonth,sday,shour, tzinfo=JST) print('time_start:{}'.format(time_start))
フォーマッティングして文字列として返す
引き算で差分を計算。
秒で出るので時間に変換。
フォーマットを使うときれいな表示になる。
# 差分 time_diff = time_start-time_info # 文字列を返す cd_s = "{0}日と{1}時間".format(time_diff.days, time_diff.seconds // 3600)
作成したコード
参考文献
django - How do I convert datetime.timedelta to minutes, hours in Python? - Stack Overflow
Mint60の一体型ケースを自作した
ずいぶん前(2019GW)につくったものだけどやっと記事化.
対象キーボード
自作キーボードキットのMint60
製作背景
キーボードのケースを作ったものの,以下のような点がつかいにくいことに気づいた.
- キーボードよりひとまわりかさばってしまう
- 使用中の箱が置き場所を取るので少々邪魔
- 緩衝材に使ったマットが重い
これを改善する為,一体型の専用ケースを作ることにした.
つくったもの
使い方
- 持ちて近くのスナップをはずす
- 開く
- ベルトを抜いてケース部分を外せば使用可能
材料・道具
- 合成皮革(「マドンナ」赤・茶)
- ベルト用金具
- スナップボタン(ベルト金具と合わせてアンティークゴールドのものを購入)
- 接着剤(コニシ ボンド Gクリヤー(箱) 20ml #14321 コニシ(Konishi) Amazon CAPTCHA )
- 3Dプリンタ(PLAを積層できるタイプ)
- ミシン
作り方
骨格のモデリング
骨格作成
装飾作成
布は接着剤で貼っている.
持ちて部分はミシンで縫い付けている.
角の茶色の布は接着剤で貼った後でミシンで縫うことで雰囲気が出る.
感想
Mint60の付属ねじを無理やりねじこんで固定したので固定があやしい. インサートナットを使用してちゃんと固定できるようにしたい.
布は革と違って端面が白くで絵にならないのできれいにつくるのが難しい.
(おまけ)天下一わいわいキーボード行ってきました
これ持って行ってきた. いろんな切り口で製作・改造しているものをたくさん見られたので良かった.
キーキャップ自作をしている方がたくさんいて楽しそうだったのでやってみたい.
xpathを学んでミスミメンテbotのメンテ情報取得を改善した
困っていたこと
ミスミメンテbotが時々うまく動かないバグに困っていた.
動かなくなる状況を確認してみると, ミスミサイトでトラブルが発生し,ページ上部に緊急お知らせが出されるときに動かなくなる.
緊急お知らせがでてきた様子は こんな感じ.
赤枠の欄が緊急時のみ出現する. 赤枠のせいでレイアウトがずれるのが原因らしい.
もともとの取得方法
レイアウトがずれただけで動かなくなってしまうのは以下のようなやり方をしていたのが原因.
もともとは以下のようにメンテ情報を取得していた.
- chromeのデベロッパーツールでメンテお知らせ欄(青枠)のpathを探し出す
- 右クリックで出てくる画像赤枠の部分からxpathをコピー
- 青枠内の何番目にメンテ情報が記載されるか不明の為,青枠内を十分回数のforループで順番に取得
- "メンテナンス"という文字がある行の日時を取得してツイートに反映
このやり方だと,xpathは要素の位置の形式を使うことになる. 当然レイアウトがずれるとこの記述もかわるので正しく取得できないということになる.
実際のコードだと以下の行.
# メッセージを順番に取得 mescnt = 0 i = 1 for i in range(1,cnt): headings = html.xpath("string(/html/body/div[1]/div[3]/div[1]/div[1]/div/ul/li[" + str(i) + "])") if [headings]!=['']: message += [headings] mescnt += 1
改善した
「attention--infoというクラスで囲まれているので 順序ではなくタグとclass名でXPath指定すると結構ロバストになるよ(要約)」
というアドバイスをもらったので調べました. (xpathあんまりわからずに使っていた...)
調べたこと
以下のページがとても分かりやすかった.
xpathではXML文章をツリー構造としてとらえていて, (開発者が書いていれば)class属性がラベルのようにくっついている.
で,そのラベルを使った指定もできるらしい.できないと存在意味ない気がするのでそれはそうだけどとても便利.
上のページを見ると,コンテンツの文字列で検索しての指定や,指定要素の兄弟要素(○○の行の一個上とかが取得できそう)という指定方法もあるらしい.
書いてみたコード
これを読んで改善したコードを最後に貼る.
attention--infoというclass属性を指定するやり方で書いてみた. ちなみに,緊急お知らせ欄(赤枠)の属性はattention--notice.
xpath側で"メンテナンス"で検索をかけるようにするとプログラムがとても短くなりそう. ただ,ミスミサイト内の予想外の場所に"メンテナンス"という文字列が現れる可能性があるので,青枠内を取得する方針は維持することにした.
# メッセージを順番に取得 mescnt = 0 i = 1 for i in range(1,cnt): # メンテが予告される青枠部分を指定して取得 headings = html.xpath("string(//ul[@class='attention--info']/li[" + str(i) + "])") if [headings]!=['']: message += [headings] mescnt += 1 # print(mescnt) # メッセージ数 print(message)
備考
貼っている画像はすべて MISUMI-VONA | ミスミの総合Webカタログ のスクリーンショット.
3Dプリンタパーツへのインサートナット圧入で微妙に失敗したのを修復した
3Dプリンタパーツへのインサートナット圧入をやってみた
3Dプリンタパーツ(ここではABSやPLAのFDM式を想定)へめねじを切る簡単なやり方として,インサートナットをはんだごてを用いて圧入する方法がある.
適切な径と深さの下穴をモデリングして造形して,あとから入れるだけなので手軽.
今作っているものにめねじを切る必要があった為,ためしてみた.
今回使ったのはこれ↓(M3,長さ4mm).
※下記サイトを参照すると 本来これは熱圧入するものではなさそうだが. コンパクトであるのと,試してみて問題がなかった為これを使った.
発生した失敗
適当なサイズの止まり穴を造形し,圧入した.
実際にやってみるとこんな感じになった.
めねじ部分に樹脂がつまっている.
止まり穴なので,つまりの除去が困難. 千枚通し等で掻き出す方法ではうまくとれなかった.
比較対象としてうまくいったものが以下.
原因として考えられること:
- 下穴の径・深さが適切でなく樹脂が逃げ場を失って上がってきた
- 使用したナットに対してはんだごての先端が長すぎ,溶けた樹脂はんだごてを介してがナットの内側に付着した
(本来下穴や使うはんだごてを試行錯誤したうえで本番パーツを作るべきという話ではあるのだが,) めねじのつまりさえなくせれば使えそうだった為,修復を試みた.
対処
用意するもの:
- アクリサンデー
- 予備のねじ(ねじが汚れるので組立時に使うものとは別に用意した方が良い)
- 綿棒(なければティッシュとか)
- (つまようじ)
やりかた:
- ねじ穴にアクリサンデーを入れる
- アクリサンデーに付属の針でめねじが浸る程度入れる
- あふれてしまった場合は周りが溶けるので早めにふき取る
- とれにくい樹脂の塊はこの段階でつまようじで除去する
- 下の画像では見づらいのだが,この時点で目詰まりがわりと消える
- 予備のねじを入れる
- 止まり穴の内部がやわらかくなっているので,樹脂を押しのける感覚で完成時に入れたい深さまで入れる
- アクリサンデーが溢れてくるので綿棒かティッシュで吸っておく
完成したねじ穴が以下.
アクリサンデーが蒸発するまで放置して完成.
補足
今回のパーツはABS製. たぶんPLAでも可能だが試していない.
アクリサンデーの正しくない使い方なので責任は負えません...
3Dプリンタパーツの接着や修復(線状に割れた,など)にアクリサンデーを使うときれいに仕上がるので,1本持っておくと便利(これが本来の用途).
ミスミメンテbotを改良した~pythonで直接ツイート~
前作ったこれを改良しました
- 上記記事でiftttというサービスを利用して簡単なbotを作った
- おかげ様でわりと見てもらってるぽい(うれしい)
- 調整作業をしたり,意見をもらう中で改善点がたまってきたので改良することにした
今回作ったもの
メンテがあるときつぶやくbot twitter.com
メンテがないときつぶやくbot twitter.com
実際のツイート
#ミスミメンテ
— ミスミメンテおしらせbot《非公式》 (@misumi_mainte) July 6, 2019
Start:7/7 10:00
Goal:7/8 8:00
https://t.co/C2VY3rgPcZ
#ミスミ pic.twitter.com/wh2rNLfDv4
メンテ中だよ
— ミスミメンテないよbot《非公式》 (@misumi_nomainte) July 7, 2019
https://t.co/Mubq0qT9Me
#ミスミ #ミスミメンテ pic.twitter.com/c2S5azhx4o
前回からの改善点
- メンテがあるときとないときのアカウントを分けた
- ツイートのpush通知をONにして使ってもうるさくない(利用者にうれしい)
- ツイート内容のカスタマイズが楽になった(私がうれしい)
- プログラム上で直接ツイート内容を記述・投稿できるようにした
- これに伴いツイート内容に渡せる引数の数の制限もなくなった
なぜわざわざ作り直したのか・今回の目標
- 2019年3月から運用を続けた結果,以下のように改善点・不満点がたくさんでてきた.
つまり今回やりたいこと:iftttを使わずに複数アカウントからツイートできるようにする
Step1. システムのしくみ
- iftttを利用しないしくみに変えた.
- 同時にメンテの有無によってアカウントを分けるように変更した.
以前のシステム
新しいシステム
Step2. プログラムからのツイート
Twitter appの作成とアクセストークンの取得
- ↓にbotにしたいTwitterアカウントごとに登録する
- ぐぐると↓みたいな解説ページが出てくるのでそのとおりにやるのが楽.
- 【2019年1月最新版】新しくなったTwitterのAPIの登録方法と使い方。Developer登録をしてAPI keyを取得するまでの手順を公開!RailsもPythonも対応可能!|Data Science Navi
- 頻繁に操作画面が変わるようなので最新の記事を参照することに注意.
- 公式ドキュメントにちゃんとした説明がのっている↓
Consumer Key Consumer Secret Access Token Accesss Token Secert
の4つの英数字列がアクセストークンで,これをあとで使う.
python3からのツイート
- 前記事のpython3で書いたメンテ判定のプログラムを使いたい為,python3を利用する前提で作成.
基本的なしくみ
これ↓を参考にした.
上で紹介されているコードで使われているライブラリが↓.
requests-oauthlib.readthedocs.io
Requests-OAuthlib uses the Python Requests and OAuthlib libraries to provide an easy-to-use Python interface for building OAuth1 and OAuth2 clients.
というものらしく, pythonで画像付きツイートをしたい人はだいたいこれを使っていた.
複数アカウント対応
一つのコードから複数のアカウントを利用するので,アカウント切り替えがしやすいように整備.
アクセストークンの登録
入れ子にした辞書型を使うと便利.
以下のように別ファイルsecret_cactus.py
に記述.
twDict = {'mainte':{ 'consumer_key' : 'xxxxxxxxxxxxxxxx', 'consumer_secret' : 'xxxxxxxxxxxxxxxx', 'access_token_key' : 'xxxxxxxxxxxxxxxx', 'access_token_secret' : 'xxxxxxxxxxxxxxxx' }, 'nomainte':{ 'consumer_key' : 'xxxxxxxxxxxxxxxx', 'consumer_secret' : 'xxxxxxxxxxxxxxxx', 'access_token_key' : 'xxxxxxxxxxxxxxxx', 'access_token_secret' : 'xxxxxxxxxxxxxxxx' } }
これを以下のように取り込む.
(別ファイルimportだけして直接右辺の形で使ってもよいのだが,長くなるので以下のようにした)
import secret_cactus pw = {} pw['mainte'] = secret_cactus.twDict['mainte'] pw['nomainte'] = secret_cactus.twDict['nomainte']
ツイート関数
先述の記事を参考にしてアカウント切り替えがしやすい関数を作成した.
以下のように添付画像pic
,投稿文字列message
に加えアカウント名tw_ac
を引数にとる関数を作成.引数で指定したアカウントのトークンを辞書から探してきてOAuth認証する.
def tweet_with_pic(tw_ac, pic, message): # 文字列,文字列,文字列 # OAuth認証 セッションを開始 tweet_ac = OAuth1Session(pw[tw_ac]['consumer_key'], pw[tw_ac]['consumer_secret'], pw[tw_ac]['access_token_key'], pw[tw_ac]['access_token_secret']) # 画像投稿 files = {"media" : open(pic, 'rb')} req_media = tweet_ac.post(url_media, files = files) # レスポンスを確認 if req_media.status_code != 200: print ("画像アップデート失敗: %s", req_media.text) exit() # Media ID を取得 media_id = json.loads(req_media.text)['media_id'] print ("Media ID: %d" % media_id) # Media ID を付加してテキストを投稿 params = {'status': message, "media_ids": [media_id]} req_media = tweet_ac.post(url_text, params = params) # 再びレスポンスを確認 if req_media.status_code != 200: print ("テキストアップデート失敗: %s", req_text.text) exit() print ("OK")
プログラムの定期自動実行
と同じくherokuを利用.
利用するライブラリが増えたら忘れずにrequirements.txtに書き足すことに注意.
Step3. できたコード全体
以前のプログラムをベースに作成したもの. メンテ予告メッセージ取得や状況に応じた投稿内容はほぼそのまま.
- 本体
- requirements.txt
django gunicorn django-heroku lxml requests requests_oauthlib
参考文献
OAuthのイメージがわかりやすかった記事 https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be
AndroidStdioの教材を探して勉強してみる
アプリを作りたくなったのでandroidstudioを勉強しはじめた.
勉強方法をしらべつつ試行錯誤しているのでそのメモ.
この記事で書きたいこと
- android学習方法紹介
- やったことの時系列記録
対象読者
- androidを勉強し始めたい人
- 新しい何かを始めたくなった未来の自分
開始時筆者ステータス
- javaは名前しか知らない
- WSL等のターミナルは触る
環境構築
- 公式サイトに行ってダウンロードした.
- 開けたので問題なさそうと判断し,次に進んだ.
とりあえずhelloworld
- ダウンロード後公式サイトを眺めていたところ,以下のぺージを見つけたのでやってみた.
- 日本語で解説されていてhelloworldまで詰まらずに行けた.うれしい.
- シミュレータは使わず,手持ちのandroidスマホを開発者モードにして接続してテストした.
- スマホにアプリが入った.うれしい.
方法の調査と選定
この記事の本題. helloworldの次にやるべきものがすぐに見つからず,しばらく人に聞いたり調べたりした.
調査で見つけた勉強方法とその感触
本
- amazonで調べたり,本屋さんで立ち読みしたりした.
- アプリの本
- Amazon CAPTCHA
- 環境構築から丁寧に解説しているものや仕組みの解説が多いものなど様々
- 手元のandroidstdioバージョンと合わせようとすると選択肢が限られてしまう
ブログ、wiki、動画
google公式資料
↓のページに求めていたものを見つけた.
このページを下まで読んでいくと,
- 左から順に以下の3つが紹介されている.
- チュートリアル形式のもの↓
- codelabs.developers.google.com
- 動画形式のもの↓
- www.udacity.com
- wiki形式のもの↓
- developers.google.com
- wiki形式のものの中にはスライド形式の教材が入ったドライブやサンプルコードのgithubもまとめられていた.
方法選定
- 結論から言うと,↓を採用した.
codelabs.developers.google.com
上記を選定した理由は以下.本を買うのと迷った.
- ひとまずなにが作れるか知りたいので作例がたくさんほしい
- 画面操作手順・コード両方を解説してほしい
- 手元の環境とバージョンが同じか極力近い解説が見たい
- 環境構築はやったらできてしまったので解説なくてよい(本だとここにページを割いているものがわりとある)
- 動画のように焦らなくてよい,かつ指示通り作業するだけというハードルの低さ(チュートリアル形式の利点)
公式チュートリアルという信頼感
- まとまった量を順序立てて書かれていそう
- 英語であることを除けば本と同等のものが無料
- というのも大きな魅力.
チュートリアル開始
- 早速選んだチュートリアルを始めた.
画面の通りにすすめるだけ
英語読む気分になれないときは↓のChrome拡張がおすすめ.
- chrome.google.com
- アイコンをクリックするとページ全体が翻訳されて楽.
- 訳された日本語がおかしいときは読みたい文にカーソルを合わせると良い
- ハイライトされて原文が表示される
おまけ:javaの本を読み始めた
チュートリアルは楽しく進めていて,チュートリアルの解説を読めばコードの事前知識はなくても動かせてしまう.
- でも,ちょっとすっきりしない
- 「(知らない単語)が(知らない単語)だから(知らない単語)になります.」という状況
平易な例で概念を丁寧に説明してある.オブジェクト指向が(自分の中では過去最も)わかった気分になった(わかったとは言っていない...).
- 実用に振ったgoogleのチュートリアルと組み合わせて使うのにちょうど良いように感じた.
まとめ
困っていること
- ここまで書いたけど,「Android Developer Fundamentals Course」の次にやるとよさそうな教材が見つけられていない.
- いいのあったら教えてください
今後やりたいこと
- さぼてんの観察日記アプリでもつくりたいなあと思っている.
ミスミメンテbotをつくった(簡易版)
つくったもの
- メンテ予告をつぶやいてくれるtweetbotを作った.
- 今のところ,メンテの有無にかかわらず,1日1回つぶやく仕様.
ミスミメンテとは
https://jp.misumi-ec.com/jp.misumi-ec.com
- 機械部品とかが買えるサイト.何かとお世話になる機会が多い.
- 時々メンテをしていて,作業しようと思ってメンテだと悲しくなる.
- トップページの上部で予告はされるので確認すればよいのだが,忘れがち.
仕様・しくみ
使用言語とか
- python3
- ちなみにpython2で作り始めたところ,文字列まわりの扱いが悲しいことになった(使いたいものが使えず文字化けが解決できなかった).
メンテ判定
- トップページ上部のお知らせ欄にメンテナンスの文字があるかを調べ,その行の日時の文字列(=予定日時)を抽出.
- お知らせ欄のXPathはchromeのデベロッパーツールから調べた.
- お知らせ欄全体ではなくメンテナンスの行の日時を抽出しているのは災害等による輸送遅延なども書かれる場合がある為
ツイート
- 使い慣れているiftttのwebhookを使った.
- プログラム側から引数を与えることはできるが,ツイート内容の細かい設定はifttt側で行う.
- 利点
- プログラム上では1行でツイートできる
- 欠点
- ソースコードとappletの両方を管理しないといけないのが面倒.辻褄があっていないとバグる.
- ツイートの種類を増やそうとするとappletが増えていくのが面倒
- ツイート内容の自由度が低い
- ただただ気楽に使えるのが良さ.
定期実行
- 「アプリの構築、提供、監視、スケールに役立つクラウドプラットフォーム」らしい
- 今回初めて触った,詳しくない...
- tweetbotの作り方を調べたり聞いたりするとわりとこれが使われている.
- 基本無料
- スケジューラのアドオンを使うと無料で定期実行できる
- アドオンを使うにはクレジットカード(デビットカードでもいけた)の登録が必要ぽい
ソースコード
- よい書き方じゃないかもなので注意
url.py gist.github.com
runtime.txt
- python-3.x.xの数字は公式ドキュメントに書いてあるものにする
- 2系か3系かだけそろえれば以降の数字は手元のバージョンではなく公式ドキュメントの方に合わせる
- 今回は3.7.2
python-3.7.2
- requirements.txt
- Getting Started on Heroku with Python | Heroku Dev Centerをベースにした
- サンプルに最後2行を書き足したら動いた
django gunicorn django-heroku lxml requests
- Procfile
web: gunicorn misumi-tweet.url --log-file -
今後改善したいこと
- ifttt使わず直接tweet
- 設定を一元管理したい
- ツイート内容のバリエーション増やしたい
- メンテ中のツイートにメンテ終了時刻を表示
- 当然メンテ中はアクセスできない(=時刻を取得できない)のでメンテ前にメモっておく仕組みが必要そう