時間が経ってしまったが 、8/15 夏休みの記録。塩原を経由して、南会津〜会津若松まで向かい、そのまま一泊した。
走行距離 116km 獲得標高 1150m
お天気がイマイチで、ピっといい写真を撮れなかったな。
続きを読む夏休みの記録。8/11 に猪苗代湖〜磐梯吾妻スカイライン(途中で引き返し) 〜福島市 を走った記録
Garmin を途中で間違って止めてしまったので記録が二つに分かれている
走行距離 179km 獲得標高 1900m でお腹いっぱいライド
続きを読む8/7 (日) 4号線をずっと南下して東京駅まで走った。帰りは新幹線。
走行距離 154km 獲得標高 345m 走行時間 6時間11分 ( 経過時間 8時間44分 )
四号線を使って東京まで走ったのは 2回目。下は以前の記録
今回は写真を全然撮らずに走って、最後に一枚だけ。
帰りは新幹線なすのに乗って帰宅。日曜夕方 東京発の なすのはガラガラで、客席に自転車を持ち込んでも迷惑にならない。
日曜日だと物流の大型車が少なめでよかった。
宇都宮〜古河間はリラックスして走りやすい。
飛ばしたい車や大型車は新四号を使うっぽいので、(旧) 四号線はのんびり流れている。路肩もそんなに狭くなくて安全。
狭い + 路面の状態も悪くて凸凹も多い。しんどい!
軽〜い向かい風がずっと吹いていて、風の恩恵は受けられなかったなー Garmin Edge だと 平均 25.2km 時で記録されていて Strava だと 平均 24.9km/時 の記録だった
涼しい時だと もう少し速く走れんかな
slack で bot を動かしていて 「 別の bot のメッセージに反応して何かする bot 」 を書くユースケースがあります *1
例えば こんな メッセージを出す bot がいるとします *2
上記の メッセージ から UUID ( OpenStack の Server UUID ) を取り出して、別の bot で何か処理をしたい場合を考えましょう
text
を抜き出して正規表現で UUID のマッチを試みるといったアプローチでメッセージを扱うかと思います。一種のスクレイピングですね
非構造化されたデータ を扱うのは何かと面倒で変更にも弱いですね
元のメッセージのフォーマットが変わる ( text
の内容が変わる, attachments や blocks の構造が変わる ) と bot を再実装する必要が出そうです 。人間向けの view
に依存してるコードは実装が複雑になったり、メンテナンス性が下がります。スクレイピングのコードを書いたことのある人はよくわかるかと思います。
bot が bot のメッセージを扱う場合に、構造化されたデータ ( Hash, Array ) で扱えると楽だよなぁと.... 長らく思っていたのでした。
で、改めて Slack のドキュメントを調べてみたら、メッセージに metadata
として Hash 構造のデータを付けられるのを この度 知ったのでした。
今年の4月頃に出ていたのかな? *3
下記のように metadata
に event_type
と event_payload
を入れてメッセージを POST できる!
{ "channel": "C23456", "text": "New teammate @Billy just joined", "metadata": { "event_type": "new_teammate", "event_payload": { "id": "TK-2132", "summary": "New teammate has been added to the channel", "description": "@Billy is a new teammate and needs to be added to the neccesary channels", "priority": "HIGH", "resource_ type": "TASK" } } }
別の bot のメッセージに反応して何かする bot を作る際、metadata を参照すると構造化されたデータで扱いやすそう (ただし メッセージを出す bot で metadata を付与してないといけないけど ... )
を例に作ってみました
↑ のようなメッセージを POST する単純なコードです。metadata も付けています。
#!/usr/bin/env ruby require 'slack-ruby-client' require 'dotenv' Dotenv.load params = { channel: '#dev', username: 'live-migration-notifier', text: ":arrow_forward: live-migrationが開始されました", blocks: [ { type: :section, text: { type: :plain_text, text: ":arrow_forward: live-migrationが開始されました", emoji: true } }, { type: :divider, }, { type: :context, elements: [ { type: :mrkdwn, text: ":desktop_computer: *example.com*\n:pencil2: 7ef9111c-0000-0000-0000-1717950e45cf", verbatim: true } ] }, { type: :context, elements: [ { type: :plain_text, text: ":outbox_tray: host000", emoji: true }, { type: :plain_text, text: ":inbox_tray: host001", emoji: true } ] }, { type: :divider, }, { type: :context, elements: [ { type: :plain_text, text: "req-b23c7b63-0000-0000-0000-00000000", emoji: false }, { type: :plain_text, text: "by instance-migrator", emoji: false } ] } ] } # こんな感じで 構造化したデータを突っ込める metadata = { event_type: "live_migration_started", event_payload: { source_host: "host000", dest_host: "host001", server: { name: "example.com", uuid: "7ef9111c-0000-0000-0000-1717950e45cf", }, } } # metadata には JSON を入れる params[:metadata] = metadata.to_json client = Slack::Web::Client.new(token: ENV['SLACK_API_TOKEN']) client.chat_postMessage(params)
先のメッセージに反応する Python Bolt アプリです。
"""_summary_ """ import os import dotenv import slack_bolt from slack_bolt.adapter.socket_mode import SocketModeHandler dotenv.load_dotenv() app = slack_bolt.App(token=os.environ["SLACK_BOT_TOKEN"]) @app.event("message") def handle_message(event, say): metadata = event.get("metadata") if metadata is None: return if metadata["event_type"] == "live_migration_started": payload = metadata["event_payload"] print(payload) server_name = payload["server"]["name"] server_uuid = payload["server"]["uuid"] say("{0} {1} started live-migration".format(server_name, server_uuid)) if __name__ == "__main__": handler = SocketModeHandler(app, os.environ["SLACK_APP_TOKEN"]) handler.start()
二つの bot が連携して動作している例です
Python Bolt アプリでは、以下のように metadata を受け取っています
$ poetry run python bot.py ⚡️ Bolt app is running! {'source_host': 'host000', 'dest_host': 'host001', 'server': {'name': 'example.com', 'uuid': '7ef9111c-0000-0000-0000-1717950e45cf'}}
意図した通りにデータを扱うことができています。
Proof Of Concent なコードなので、特に利用価値がある実装ではありません。 metadata
を通して依存関係を持っている、ってのがポイントですね。
event_type
の命名はどう扱うのがいいのだろうか?
*1:何らかの理由で 元のメッセージを出す bot の実装を変えたくない/変えられない場合
*2:同僚の id:buty4649 が書いた OpenStackのLiveMigrationの通知するくん のメッセージです github.com
*3: このエントリを書いている時点では open beta の機能です
↑ 同僚のエントリを読んで 早速調べものをしていたのだが、8進数で書かれたフラグを理解するのがちょっと大変。 lsof あたりで人間が読みやすくするようにサポートしてないのかな? ... と調べたらあった!
lsof に +f g
って付ける良い。以下は apache2 の lsof をとった内容
$ sudo lsof +f g -p 5087 | tail apache2 5087 www-data DEL REG 0,5 32893 /dev/zero apache2 5087 www-data 0r CHR LG 1,3 0t0 6 /dev/null apache2 5087 www-data 1w CHR W,LG 1,3 0t0 6 /dev/null apache2 5087 www-data 2w REG W,AP,LG 252,0 739 35389666 /var/log/apache2/error.log apache2 5087 www-data 3u IPv4 RW,ND,0x80000 67450 0t0 TCP *:http (LISTEN) apache2 5087 www-data 4r FIFO 0x80000 0,10 0t0 32892 pipe apache2 5087 www-data 5w FIFO W,0x80000 0,10 0t0 32892 pipe apache2 5087 www-data 6w REG W,AP,LG,0x80000 252,0 0 35389607 /var/log/apache2/other_vhosts_access.log apache2 5087 www-data 7w REG W,AP,LG,0x80000 252,0 0 35389605 /var/log/apache2/access.log apache2 5087 www-data 8u a_inode RW,0x80000 0,11 0 12000 [eventpoll]
フラグは略記されており、それぞれの読み方は man に書いてあります
FILE-FLAG when g or G has been specified to +f, this field contains the contents of the f_flag[s] member of the kernel file structure and the kernel's per-process open file flags (if available); `G' causes them to be displayed in hexadecimal; `g', as short-hand names; two lists may be displayed with entries separated by commas, the lists separated by a semicolon (`;'); the first list may contain short-hand names for f_flag[s] values from the following table: AIO asynchronous I/O (e.g., FAIO) AP append ASYN asynchronous I/O (e.g., FASYNC) BAS block, test, and set in use BKIU block if in use BL use block offsets BSK block seek CA copy avoid CIO concurrent I/O CLON clone CLRD CL read CR create DF defer DFI defer IND DFLU data flush DIR direct DLY delay DOCL do clone DSYN data-only integrity DTY must be a directory EVO event only EX open for exec EXCL exclusive open FSYN synchronous writes GCDF defer during unp_gc() (AIX) GCMK mark during unp_gc() (AIX) GTTY accessed via /dev/tty HUP HUP in progress KERN kernel KIOC kernel-issued ioctl LCK has lock LG large file MBLK stream message block MK mark MNT mount MSYN multiplex synchronization NATM don't update atime NB non-blocking I/O NBDR no BDRM check NBIO SYSV non-blocking I/O NBF n-buffering in effect NC no cache ND no delay NDSY no data synchronization NET network NFLK don't follow links NMFS NM file system NOTO disable background stop NSH no share NTTY no controlling TTY OLRM OLR mirror PAIO POSIX asynchronous I/O PP POSIX pipe R read RC file and record locking cache REV revoked RSH shared read RSYN read synchronization RW read and write access SL shared lock SNAP cooked snapshot SOCK socket SQSH Sequent shared set on open SQSV Sequent SVM set on open SQR Sequent set repair on open SQS1 Sequent full shared open SQS2 Sequent partial shared open STPI stop I/O SWR synchronous read SYN file integrity while writing TCPM avoid TCP collision TR truncate W write WKUP parallel I/O synchronization WTG parallel I/O synchronization VH vhangup pending VTXT virtual text XL exclusive lock this list of names was derived from F* #define's in dialect header files <fcntl.h>, <linux</fs.h>, <sys/fcntl.c>, <sys/fcntlcom.h>, and <sys/file.h>; see the lsof.h header file for a list showing the correspondence between the above short-hand names and the header file definitions; the second list (after the semicolon) may contain short-hand names for kernel per-process open file flags from this table: ALLC allocated BR the file has been read BHUP activity stopped by SIGHUP BW the file has been written CLSG closing CX close-on-exec (see fcntl(F_SETFD)) LCK lock was applied MP memory-mapped OPIP open pending - in progress RSVW reserved wait SHMT UF_FSHMAT set (AIX) USE in use (multi-threaded)
macOS の話題です
Linux カーネルのリポジトリなんかでコミットメッセージをすぐ翻訳して読みたいなと思ったので、以下のように ~/.tigrc
を設定してみる
ショートカットキーは、適当に 9
に bind しています
bind generic 9 @sh -c 'git log --format=%B -n1 %(commit) | pbcopy; open /Applications/DeepL.app; osascript -e \'tell application "System Events" to keystroke "v" using command down\''
さて、 tig でコミットを眺めてる時に 9
をピッと押すと
DeepL が出てきて、コミットログをペーストして翻訳して出てくる
とりあえずは便利に使えそうかな?