My tech diary

ソフトウェアエンジニアをやっています。技術的内容を中心に調べたことを書いていきます。

Slack フリープランからメッセージを退避せよ

最近、プライベートで使っている Slack の workspace で、以下のようなメッセージが表示されるようになりました。

slack_free_plan_notification_hidden_messages.png

どうやら、 2022-09-01 から Slack のフリープランの内容が変わって、 90 日以前に投稿されたメッセージについては非表示になる ようです。

現在は、メッセージ数 10,000 件、ストレージ容量 5 GB 以内であるならば、投稿日時に関わらず、閲覧可能となっているので、私のようにプライベートで使っている場合だと、ちょっとしたメモアプリ、ウェブクリップのように使用できていたのですが、フリープランではこれができなくなってしまいました。

なお Slack のお知らせ上では、よりポジティブに、 過去 90 日間のメッセージ履歴とファイルストレージを (メッセージ数、ストレージ容量の制限なく) 無制限に利用できる ようになると書かれています。まぁ、チームでのコミュニケーションに活用する、という本来の用途をフリープランで体験するためには、あるべき形になるのかなというのは、同じ開発者としてよく分かりますが。

兎にも角にも、私のような使い方をしていた場合には、過去分のメッセージを退避させなくてはいけません。

この発表があったのが 2022-07-18 となっていますから、1ヶ月そこらの猶予しかないわけですが、この手のタスクは「期限までにやらなくてはいけない」類のものなので、後回しにしても何もいいことはありません。少しでも余裕があるうちにやってしまいましょう。

Slack フリープラン 保存期間 などで検索すれば、ソリューションがたくさん出てくるので、詳しくはそれらを参照、としてもいいのですが、せっかく対処したので、ここでも記録を残しておきます。


いくつかの、レベルの違う対処法が考えられます。

1. プロプランに変更する

時間がない人は、お金に物を言わせましょう。 今回の発表で、同時にプロプランの値上げも発表されていますが、新しい価格では、月額料金で 1 ユーザーあたり 1,050 円となっています。 (年間契約ではもうちょっと安くなります)

ただ、プライベートで開発に使うものって、基本無料だから使っている、的なものは多いとは思います。これも同じ開発者として、タダ乗りはあまり薦められないですが、この費用負担に耐えれるかどうかは、個々人の価値観と判断に依るかなとは思います。

なお、上記のフリープランの制限を再掲すると、 90 日以前に投稿されたメッセージについては非表示になる であり、削除されるとは書いていないのがポイントです。実際の Slack の FAQ にも 有料プランへアップグレードすれば、いつでもワークスペースのメッセージとファイルの全履歴にアクセスできるようになります。 と書いてあります。

万が一、メッセージの退避を期限内に終えられなくても、一時的にプロプランに変更することで、引き続き過去のメッセージの参照ができるようになる救済策が用意されていると考えられるでしょう。良心的ですね。

2. Slack 公式のデータエクスポート機能を用いる

Slack には、公式のデータエクスポート機能が用意されています。

workspace owner の権限で https://{workspace_name}.slack.com/services/export にアクセスすることで、この機能を利用できます。

ただし、少し制限があって、フリープラン、および、プロプランでは、 public channel のデータエクスポートしか行うことができません。つまり private channel と direct message (DM) のデータエクスポートができないことになります。 (ビジネスプランでは、これらのチャンネルのデータエクスポートも可能)

Slack の仕様として、 private channel を public にすることはできない (逆は可能) ので、ちゃんと抜け穴も塞がれています。

これを許容できるなら最も手っ取り早い対処法かなと思います。

なお、データエクスポートを実際に行ってみると、 {workspace_name} Slack export MMM DD YYYY - MMM DD YYYY.zip (e.g. tearoom6 Slack export Nov 16 2015 - Aug 22 2022.zip) のようなファイル名で zip file がダウンロードでき、それを展開すると、以下のようなディレクトリ構成となっていました。 Slack のデータ構造の一端を覗ける感じがして、興味深いですね。

  • channels.json (チャンネル一覧: public channel のみ)
  • users.json (ユーザ一覧)
  • integration_logs.json (インテグレーションの追加・更新・削除ログ)
  • {channel_name}/ (チャンネル名ごとのディレクトリ)
    • {YYYY-MM-DD}.json (日付ごとのメッセージ一覧)
    • {YYYY-MM-DD}.json
    • ...
  • {channel_name}/ (チャンネル名ごとのディレクトリ)
    • {YYYY-MM-DD}.json (日付ごとのメッセージ一覧)
    • {YYYY-MM-DD}.json
    • ...
  • {channel_name}/ (チャンネル名ごとのディレクトリ)
    • {YYYY-MM-DD}.json (日付ごとのメッセージ一覧)
    • {YYYY-MM-DD}.json
    • ...
  • ...

3. Slack API でメッセージ取得

やはり private channel や DM のメッセージも保存したい! ということになると、メッセージ数が多い場合には、一個一個手動でコピーするのは修行になるかと思うので、 Slack API を使うしか無いかなと思います。

Slack API でメッセージ一覧取得 - My tech diary に記載したやり方を流用してやってみます。 2 年前なので、画面などがちょいちょい変わっていますが、記憶を思い出しながら、 Slack App の作成から token を取得までの流れをやっておきます。

こうして得た user token と、 Slack 上でチャンネルの右クリックメニューから実行できる "Copy link" で取得できる、各チャンネルの URL に含まれている channel ID を用いて、以下のようなスクリプトを作成し、実行することで、とりあえず private channel なども含めたメッセージ一覧を抜き出すことができます。

require 'json'
require 'net/http'

BASE_URL = 'https://slack.com/api/'
USER_TOKEN = 'xoxp-...'  # REPLACE IT!!
CHANNEL_ID = 'G0HEFHV61' # REPLACE IT!! You can get it by "Copy link" operation to the channel.

# Get conversation history.
cursor = nil
while true do
  uri = URI("#{BASE_URL}/conversations.history")
  params = {
    channel: CHANNEL_ID,
    cursor: cursor,
    limit: 100,
  }
  uri.query = URI.encode_www_form(params)
  req = Net::HTTP::Get.new(uri)
  req[:Authorization] = "Bearer #{USER_TOKEN}"

  res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
    http.request(req)
  end
  res_body = JSON.parse(res.body)

  res_body['messages'].each do |message|
    puts message['text']
    puts '--------------------------------'
    puts ''
  end

  break unless res_body['has_more']
  cursor = res_body['response_metadata']['next_cursor']
end

チャンネルを一覧指定できるようにしたり、投稿ユーザ名や投稿日時も出力するようにするなど、改良してみても良さそうですね。

4. 世の人が作ってくれているツールを使う

Slack API を使う形式の派生系と言えますが、世の人が使いやすい形で公開してくれているツールもいくつかあるみたいですので、それらを使ってみるのもいいかと思います。

References

Docker Compose V2 を理解する

この記事は 🌊 UMITRON 🐟 Advent Calendar 2021 20日 の記事です。 Advent Calendar も最終週ですね!

この記事は、以前から気になっていた Docker Compose V2 の経緯・現在地点について、自分なりに調べて、まとめておくものです。 あまり深堀り調査していないので、もし誤りがあれば、ご指摘ください。


(2022-03-10 一部追記しました)


結構以前だと思うのですが、いつものように docker-compose を使っていると、

Docker Compose is now in the Docker CLI, try docker compose up

みたいなメッセージが表示されるようになりました。

この時点では、そうか、これからは docker compose と打てばいいのか (後で調べよう...)、くらいの理解でとりあえず使っていました。

ところで、最近、 docker-compose を使っても、またこのメッセージ出現しなくなっております。 (後述の Use Docker Compose V2 のチェックを外したとしても)

この間、私を含む利用者はじわじわと、 Docker Compose の V1 から V2 と呼ばれる実装へ誘導されていたんですが、そもそも Compose V2 とはなんぞ、というところを先に書きます。

Docker Compose V1 vs V2

もともとの docker-compose の実装、つまり Compose V1 (と便宜的に呼びます) は Docker client とは全く別のツールとして Python で書かれていました。

https://github.com/docker/compose/tree/v1.25.2

そのため、従来 Linux において Docker Compose CLI は Docker CLI に追加して、別途インストールする必要がありました。 (現時点では、まだそうなってます)

Docker Compose を Go で書かれている Docker CLI に組み込むには、 Compose V1 が Python で書かれていることがネックになっており、実験的なプロジェクトとして、 Go 版の Docker Compose (Compose CLI) の開発が docker/compose-cliリポジトリで進められたようです。

やがて、こちらのリポジトリは Docker Compose "Cloud Integrations" として、 Docker Compose のクラウド連携機能に特化するようになり、 Compose V2 (従来の docker-compose が提供する機能の代替) 部分は、もともとの Compose V1 のリポジトリ (https://github.com/docker/compose) に移管されることになったようです。

https://github.com/docker/compose/tree/v2.2.2

Compose V2 は Go でフルスクラッチで書き直されていることが分かりますね。

Compose V1 と V2 は概ね互換性はあるものの、完全ではないようで、一部のコマンドについては差異があると言及されています。 実装の違いによる細かい動作の違いや、画面上の表示の変化については、自分も使っている中でいくつか感じられます。

(スムーズな移行のために、 docker-compose を実行したときに、内部的に docker compose (Compose V2) のコマンドに変換して実行してくれる Compose Switch というツールも提供されているようです)

さて、 Compose V2 は Compose V1 の後継ではあるのですが、もちろん機能を引き継いだだけではなく、いくつかの改良・新機能が含まれています。

また、 Compose V2 の開発にあたっては、 Compose V1 の挙動を元にした Compose Specification が platform 非依存な標準仕様として策定され、それに準拠する実装として Compose V2 を開発する、という体制が整えられました。

Docker Compose V2 への移行状況

およそここ1年間、 Docker Compose V2 への移行がどのように進められたかを、 macOS 版 Docker Desktop のみを対象に、漁ってみました。 (リアルタイムで追ってないので、詳しい状況はわからないけど)

  • Docker Desktop 3.0.0 (2020-12-10)
    • Go 版の Compose CLI が内包され、 docker のサブコマンド docker compose で実行できるようになった
  • Docker Desktop 3.4.0 (2021-06-09)
    • docker compose が Compose V2 beta として提供されるようになった
    • docker-compose を呼んだときに Compose V2 を使うかどうかを docker-compose disable-v2, docker-compose enable-v2 によって切り替えられるようになった
  • Docker Desktop 4.0.0 (2021-08-31)
    • (大企業向けに有料化)
  • Docker Desktop 4.0.1 (2021-09-13)
    • Compose V2 が github.com/docker/compose でホストされるように変更された
  • Docker Desktop 4.1.0 (2021-09-30)
    • docker-compose がデフォルトで Compose V2 を向くようになった
    • General settings (Use Docker Compose V2 という項目) で docker-compose を呼んだときに Compose V2 を使うかどうか選択できるようになった
  • Docker Desktop 4.3.1 (2021-12-11)
  • Docker Desktop 4.3.2 (2021-12-21)
  • Docker Desktop 4.4.2 (2022-01-13)
  • Docker Desktop 4.5.0 (2022-02-10)
    • (今ここ)

ということで、 macOS 版 (たぶん Windows 版も) Docker Desktop をきちんと最新版にアップデートしている場合、明示的に OFF しない限りは、既に Compose V2 への移行が完了しているということですね。

(明示的に Use Docker Compose V2 のチェックを外せば docker-compose コマンドで引き続き Compose V1 を利用することはできます)

(2022-03-10 追記: もしくは docker-compose-v1 コマンドを使うことで、引き続き Compose V1 を利用することができるようです)

Linux 版の状況もあって Compose V2 は 2021-12-20 現在ではまだ GA (Generally Available) とはなっていませんが、もう GA になるのは時間の問題と思われます。

試しに、今、手元に入っている macOS 版 Docker Desktop 4.3.1 で実験してみます。

Use Docker Compose V2 のチェックを外した状態では、 docker-compose では Compose V1, docker compose では Compose V2 がそれぞれ呼ばれます。

$ docker-compose version
docker-compose version 1.29.2, build 5becea4c
docker-py version: 5.0.0
CPython version: 3.9.0
OpenSSL version: OpenSSL 1.1.1h  22 Sep 2020

$ docker compose version
Docker Compose version v2.2.1

Use Docker Compose V2 のチェックを付けた状態では、いずれも Compose V2 が呼ばれます。

$ docker-compose version
Docker Compose version v2.2.1

$ docker compose version
Docker Compose version v2.2.1

Compose V2 のクラウド連携機能

ちょっと本筋と離れますが、もともとの "Compose CLI" であった、クラウド連携機能についても、個人的整理をしておきます。

Compose V2 のクラウド連携機能は docker-compose.yml で定義したマルチコンテナ環境を、簡単にクラウド環境にデプロイできるようにするもので、現時点で、以下のプラットフォームがサポートされています。

  • Amazon Elastic Container Service (ECS)
  • Microsoft Azure Container Instances (ACI)

Kubernetes についても対応が進められています。

こちらの機能については、元々 2020-07-09 に docker ecs というサブコマンドを追加する CLI Plugin がベータ版が公開されたものの流れを組むようです。 2020-11 には既に、今の "Compose CLI"、つまり docker compose として使えるものを公開したというアナウンスが出されています。

一方、上と同日の 2020-07-09 には AWS Copilot という、これまたコンテナ環境を ECS 環境にデプロイするためのツールが公開されていて、一体どっちを使えってこと?って混乱したのを覚えています。こちらのツールは元々は Amazon ECS CLI (ecs-cli) の流れを組むもののようです。

Compose V2 のクラウド連携機能は、当然 docker-compose.yml を中心に据えて、そこで定義したコンテナの集合体をクラウドで構築することを目標としているのに対し、 AWS Copilot の方は、個々の Dockerfile (コンテナ) をそれぞれ役割に応じて AWS のサービスにデプロイして、連携して動かすというアプローチの違いがあるようです。

ただ、似たツールなのは間違いないないですね。。 汎用 IaC ツールとして CDK や Terraform もありますし、正直、この手のツールは、今、乱立している感じはありますね。どれが覇権を取るのでしょう。。

さて、調査だけだと退屈なので、最後に簡単に動かしてみます。

コードはこちらに置いてあります。 (🐟漢字クイズです)

https://github.com/tearoom6/docker-ecs-tools-trials

Compose V2 と AWS Copilot を用いて AWS 環境にデプロイするのを試してみました。

まず Compose V2 の方を試してみます。

既存の docker-compose.yml がそのまま使えるかというと、全然そんなことはなく、いくつかの制約があるようです。 例えば、以下のようなエラーに遭遇しました。

  • published port can't be set to a distinct value than container port: incompatible attribute (ports の mapping はコンテナ内外で同じにしなくてはいけない)
  • ECS Fargate does not support bind mounts from host: incompatible attribute (Fargate の場合 volumes で host からのマウントができない)
  • services.build: unsupported attribute (Dockerfile を指定したイメージのビルドに対応していない)

image は ECR や DockerHub などから、 public で公開されているものか、カスタムビルドして push しておいたものを引っ張ってくるしかないようです。

今回はまず ECR で repository を作っておいて Dockerfile から作った Docker image を push します。

aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/a9b0d6v6
docker build -t fish_quiz .
docker tag fish_quiz:latest public.ecr.aws/a9b0d6v6/fish_quiz:latest
docker push public.ecr.aws/a9b0d6v6/fish_quiz:latest

その上で docker-compose.yml を用意しておけば、以下のようにしてデプロイ可能です。 デプロイの裏側では CloudFormation が動いています。

# Configure AWS context by answering questions.
docker context create ecs tearoom6
# ? Create a Docker context using: An existing AWS profile
# ? Select AWS Profile default
# Successfully created ecs context "tearoom6"

# Switch context to use AWS resources.
# `--context tearoom6` flag also can be used.
docker context use tearoom6

# Generate and review CFn template.
docker compose convert

# Deploy to AWS env.
docker compose up

# Check created LoadBalancer's DNS name to find endpoint.

# Delete all resources.
docker compose down

# Reset context to default!!
docker context use default

AWS Copilot の場合だと、 Dockerfile とソースコードを用意した上で、以下のようにして、とりあえずデプロイ可能です。 こちらも、デプロイの裏側では CloudFormation が動いています。

# Install copilot-cli.
brew install aws/tap/copilot-cli

# Create project and deploy by answering questions.
copilot init
# Application name: fish-quiz
# Workload type: Load Balanced Web Service
# Service name: front-end
# Dockerfile: ./Dockerfile
# Ok great, we'll set up a Load Balanced Web Service named front-end in application fish-quiz listening on port 80.
# ...
# All right, you're all set for local development.
# Deploy: Yes
# ...
# ✔ Deployed service front-end.
# Recommended follow-up action:
#   - You can access your service at http://fish-Publi-1QFXMQVITL58L-571977271.ap-northeast-1.elb.amazonaws.com over the internet.

# Delete all resources.
copilot app delete

References

Compose V2 での pts の stdout/stderr の向き先 (2022-03-10 追記)

V1 と V2 の動作の違いで、結構大きな違いを見つけたので、追記します。

Docker Compose V1 では、以下のように exec コマンドでコンテナに接続して、その中で標準出力に出力したメッセージを、リダイレクトによってホスト側のファイルに書き込むことができていました。

# Docker Compose V1
$ docker-compose-v1 exec app echo Hello > /tmp/test
$ cat /tmp/test
Hello

ところが、 Docker Compose V2 では、同様にしても、コマンド実行時にメッセージが表示され、ファイルには何も書き込まれません。

# Docker Compose V2
$ docker compose exec app echo Hello > /tmp/test
Hello
$ cat /tmp/test

ただし、 -T (--no-TTY) option をつけて実行をすることで従来どおり、ホスト側ファイルへリダイレクトすることは可能です。 このオプションは、 pts (pseudo-TTY, pseudo-terminal slave, 擬似端末) の割当を無効にするためのものとのことです。

# Docker Compose V2
$ docker compose exec -T app echo Hello > /tmp/test
$ cat /tmp/test
Hello

Docker Compose V1 の場合でも -T option をつけても同様の結果が得られました。

# Docker Compose V1
$ docker-compose-v1 exec -T app echo Hello > /tmp/test
$ cat /tmp/test
Hello

どういう理屈で、このような挙動になっているのか、という点については、私の Linux への理解がなさすぎて、下手に憶測を書いても不正確になりそうなので、控えます。 もし親切に教えていただける神の方がいたら嬉しいです。

Docker 雰囲気で扱えるくらいにはなっているけど、一歩奥に入れば分からないことだらけだなと思い知った次第です。

References

AWS SDK Ruby で credentials の読み込みに失敗する場合

この記事は 🌊 UMITRON 🐟 Advent Calendar 2021 13日目 の記事です。 Advent Calendar も折り返し地点です 🏃


お魚食べてますか? 🐟

現在は UMITRON 株式会社で、水産養殖 🐟🐟🐟 の持つ潜在的可能性を IT を使って引き出すことに取り組んでいます。

当初予定していた内容がちょっと間に合わなかったので、以前遭遇したエラーについての小ネタを書きたいと思います。


AWS SDK for Ruby環境変数 AWS_CONFIG_FILE, AWS_SHARED_CREDENTIALS_FILE を指定しつつ、使おうとしたときに Aws::Errors::MissingCredentialsError のようなエラーが起きました。これらの環境変数で指定している設定ファイルは確実に存在している状態で、 AWS CLI などは正常に使えている状態だったので、原因を突き止めるのに難儀しました。

結論から言えば、これらの設定ファイルの内容にインデントが含まれていたのが原因でした。

設定ファイル中では、名前付きプロファイルを用いており、名前付きプロファイル - AWS Command Line Interface に書かれている例を使わせてもらうと、もともと私は以下のように記載していました。

[default]
    region=us-west-2
    output=json

[profile user1]
    region=us-east-1
    output=text

AWS の config, credentials 設定ファイルでは、 INI ファイル形式が使われていますが、名前付きプロファイルの数が多い場合には、上のようにインデントを付けることで、視認性はよくなるため、そのようにしていました。 INI ファイルの公式仕様のような存在を私は知らないのですが、なんとなく慣習的にインデントを付けるのは OK なのかなという理解でいました。実際、 AWS CLI や Boto3 (AWS SDK for Python) では、インデントが含まれた状態でも読み込んでくれていました。

しかし、下のファイルが AWS SDK for Ruby で INI ファイルを読み込む実装になっているのですが、正規表現の部分がインデントに対応していないことがわかります。

https://github.com/aws/aws-sdk-ruby/blob/a8332558e8c8a4889dfce17dd54742172500dffb/gems/aws-sdk-core/lib/aws-sdk-core/ini_parser.rb

改めて INI ファイルについて説明しているネット上の情報を見てみると、確かに、敢えてインデントに言及しているものはあまりなさそうです。 (WikipediaLeading and trailing whitespaces around the outside of the property name are ignored. と書いてあるので、それだとインデントも許容されそうではあるんですが)

ともかくも AWS SDK for Ruby は対応していないので、それに従う他ありません。以下のように修正することでエラーは解消しました。

[default]
region=us-west-2
output=json

[profile user1]
region=us-east-1
output=text

INI の仕様が決まっていて、インデントを許容すると明示してあるなら、修正の Pull Request を送るのですが、これはむしろこちらの指定ミスの可能性があるので、微妙なところですね。。 とはいえ、踏んでしまうと原因がわかりにくいので、嫌なエラーでした。

Advent calendar driven development

この記事は UMITRON Advent Calendar 2021 6日目 の記事です。

朝起きるまでは断固として 6日目 です🌅


お魚食べてますか?

改めまして tearoom6 です。 現在は UMITRON 株式会社で、養殖の海洋生物 🐟 の持つ潜在的可能性を IT を使って引き出すことに取り組んでいます。

さて、日々開発を行っていると、どうしても優先度付けで後回しになるタスクはあるかと思います。ユーザに直接価値を提供しないけど、細かいところで不便を感じる、あったら便利なんだけどなぁ、、という類のものですね。ほんとにすぐできるものはさっと作っちゃうほうが良いかと思いますけど、実際にほんとにすぐできちゃうものは実はそんなに多くないかと思います。

そういう場合、まずは既に世の中にあるもので使えそうなものがないか探しますが、ぱっと探して、良いものが無かった場合は、自分で作るか、と思いつつ、塩漬けになりがちです。

ただ、こういう開発支援的なタスクは、制約が強くなくて、比較的自由にできるので、技術的ノウハウが溜まりやすいと感じているのもあって、できるなら並行して進めたい気持ちがあります。 (何よりもエンジニアは何でも自動化したがるのが習性ですし・・・)

そこで、表題の "Advent calendar driven development" です。 Advent calendar で宣言して、その日 (6日目 です) までに必ずやるぞとプレッシャーを与えることで、その手のタスクをこなそうとするものです。

本題 - 実現したい機能

前置き長くなりましたが、以下、本題です。

私の関わっているプロダクトでは、画面を見ていて UTC で記録している時刻が、現地のタイムゾーンでは何時になるのだろう、というのを知りたい場面に出くわすことが多くありました。

もちろん画面上で表示するよう実装しちゃってもいいのですが、もし、ブラウザの拡張機能で実現することができるなら、再利用性もあり、アプリケーションコードもシンプルに保てるので、実現したい機能に対して親和性が高いのではと思いました。

まず、既存の拡張機能を探すところから始めたのですが、公開されている Chrome拡張機能だと、以下の 2 系統が多く、目的のものがなかなか見つかりませんでした。

  • 使用しているブラウザの timezone の設定を手軽に変更する
  • 現在時刻を複数 timezone の時刻で表示 (世界時計的な)

一番近かったのが Utime - Chrome Web Store という拡張機能でした。

billdami/utime: A Google Chrome extension that converts UNIX timestamps to dates (and vice versa)

この拡張機能は、テキストを選択した状態で context menu を押すと、そのテキストが示す日時を、事前に設定したタイムゾーンでの日時に変換した上で、 Notification で表示する、という機能などを提供していました。

chrome_extension_utime_notification.png

しかし、実現したいのは、選択したときに変換対象のタイムゾーンを指定したい、もしくは、複数タイムゾーンでの時刻を表示したい、、という感じだったので、惜しかったのですが、ちょっと違う感じでした。なので、チャンスがあれば自作しようかなと思っていました。

前提条件

実装前の状況は、以下のような感じです。

  • Chrome拡張機能での開発経験はあり
  • どのような API を使えば、どのような機能が実現できる、というような知識・調査を行わない状態で、とりあえず実現したい機能だけある状態で開始
  • Content scripts を使えば、割とどんなことでもできるイメージはあったが、この機能はどんなウェブサイトでも使えるようにしたかったため Context Menus を使うことにした
  • 当初のイメージでは、以下のどちらかで実現できれば非常に嬉しいなという期待を持っていたが、他の拡張機能を使っている感覚からすると、そういう機能は実現できないのかなという気もしていた

なお、今回は、普段遣いの Chrome のみを対象にすることにしました。

私は作ったことはないですが、他の主要モダンブラウザについても、拡張機能の仕組みは用意されています。

ただ、 API 標準化は現時点では道半ばで、互換性のないところは相当あると思われます。 (つまり、なかなか同一コードでは書くのは難しそうという感じがします)

実装方針

全体としては、以下の方針を持っていました。

  • TypeScript で実装しておく
  • 画面が必要なら React を用いる (結果的には画面は作っていない)

また、時間のない中作るので、とりあえず荒削りでも OK ということにしました。

また、 MV3 (Manifest V3 for Chrome Extensions) と呼ばれる Chrome Extensions API の新しいバージョンが使えるので、チャレンジングな要素として、そちらで実装することにしました。

MV2 と MV3 は、例えば以下のような違いがあります。

  • Browser Action API と Page Action API が Action API に統合された
  • background pages と呼ばれていた headless で動く部分は service workers で実装しなければならない
  • Remotely hosted code が実行できなくなった

Chrome Extension Tutorial: Migrating to Manifest V3 from V2

機能的に向上したというよりは、セキュリティやパフォーマンス面に配慮したアップデートが中心な気がします。ただ、今 MV2 で動いている拡張機能は 2022 年中の MV3 へのアップデートが求められているので、拡張機能開発者は対応する必要があります。

Manifest V2 support timeline - Chrome Developers

※ 余談ですが、一昔前は Chrome Web Store への拡張機能の公開は、割とハードルが低かったのですが、今はレビューも厳格で、結構厳しくなっている気がしました。まぁ、拡張機能って結構セキュリティ的には厳格にしないと危ないよね、っていう気はしてましたが。。

実装結果と知見

こちらがとりあえず作ったコードです。

https://github.com/tearoom6/TimezoneTraveler

chrome_extension_timezone_traveler_usage.png

結果的に出来上がったものは、 "対象の時刻文字列を選択した状態でコンテキストメニューを選択すれば、再度コンテキストメニューを開いた際に、各タイムゾーンでの時刻が表示される" というもので、これだと、目的の各タイムゾーンでの日時を表示するのに 2 クリック (+ カーソルの移動 = これはショートカットキーを設定すれば、回避できるかも) を要するので、使い勝手としてはかなり悪く、さらなる改良は必要そうです。

ブラウザの拡張機能は、セキュリティ的な要件から制約が強いので、どうしても実際に作りながら、実現できそうなラインを探っていく感じになっちゃいますね。。 それも数をこなせば、大体勘所は掴めてくるんでしょうけど・・・。

実装上の苦労したポイントは以下のとおりです。

service worker に起因する問題

service worker は必要なときにだけロードされて、動作するという特性を持つため、いくつかのルールを守って実装する必要があります。

  • event listeners の登録は top level で行う (非同期 callback の中などで行わない)
  • global variable の状態は、すぐにリセットされてしまう可能性があるため、必要なものは storage に永続化を行う
  • setTimeout, setInterval の代わりに Alarms API を top level で用いる

chrome.contextMenus

こちらも割とクセのある API でした。

  • context menu item は親子関係にしてネストすることができるが、親にできるのは ItemTypenormal のもののみ
    • checkbox, radio, separator は親にすることができない
  • 親になった menu item へのクリックは OnClick event で捕捉できない
  • 同一 id の menu item は登録できない
  • 存在しない parentId を指定した menu item は登録できない
  • テキスト選択時の context menu に item を表示したい場合は、全ての対象 item の contextsall or selection を含める必要がある
    • 親 item で指定していたからといって、子 item で指定していなければ、子 item の方は表示されない

browserAction.openPopup()

当初の野望にあった、コンテキストメニュー押下で、拡張機能の popup を表示、という機能が実現できなかった点です。

Mozilla の出している WebExtensions API のドキュメントを見てみると browser.browserAction.openPopup() というのが実はあるので、 Chrome でも popup をプログラムから開けるのではないかという期待が生まれたのですが、どうも Security の問題から Chrome では chrome.browserAction.openPopup() を一般公開はしていないようです。詳細は把握していないですが、 Security 周りはかなり泥臭い実装が行われているのかもしれないです。

https://chromium.googlesource.com/chromium/src/+/0ab916c3ef2fd0674d179e2c29b3e6b937231138/chrome/common/extensions/api/_api_features.json#167

ただし、拡張機能の画面を popup ではなく、新規タブとして開くことは (Permission が許せば) 可能なようなので、それを使えば、もっと使い勝手を良くすることができるかもしれないです。

Question How to programmatically open a chrome extension popup window from background.html

References


(追記) レビュー通って Chrome Web Store からインストール可能になりました。 もしご興味持った方がいれば改善にご協力お願いします🙏

https://chrome.google.com/webstore/detail/timezone-traveler/gndkkoonfiibdihdaklkhfiikfkbhdik


以上、6日目が誕生日の tearoom6 の記事でした 🎉

Vim E1208: -complete used without allowing arguments エラー

Vim のバージョンを 8.2.3550 に上げた後、 Vim を開くと、以下のようなエラーが発生しました。

hint: Waiting for your editor to close the file... Error detected while processing /Users/t-murota/dotfiles/.vim/dein/.cache/.vimrc/.dein/plugin/fugitive.vim:
line  410: E1208: -complete used without allowing arguments
line  411: E1208: -complete used without allowing arguments

これは Vim 本体の patch v8.2.3141 にて加わった VimAPI 変更の影響を、プラグインである fugitive.vim が受けたことによるエラーメッセージになります。

この問題は fugitive.vim v3.4 にて修正されています。

私の場合、 Vimプラグイン管理に dein.vim を使っているので、以下のコマンドを叩いて、プラグインのアップデートを行い、 Vim を開き直すことで解消しました。

:call dein#update()

References

年末調整のためQR付住宅ローン控除証明書を取得

年末調整のためQR付住宅ローン控除証明書を取得

なんか割とこれ系のシステムも更新されているようですが、それだけに情報が錯綜していて、結構ヒャッホーだったので、またメモしておきます。

前提

https://tearoom6.hateblo.jp/entry/2021/05/07/003039 で書いたんですが、昨年分は住宅ローン控除を受けるために確定申告を行いました。 カードリーダーの代わりにスマホマイナンバーカードの読み取りに使えるようになったので、 e-Tax で行いました。

その時に 「控除証明書について、電子情報処理組織(e-Tax)による交付を希望します。」という欄があったので、悲しいかなエンジニアの性でついついチェックを入れてしまったのです。

ここにチェックを入れなければ、今年の 10 月頃に 9 年分とか 12 年分の住宅ローン特別控除証明書が税務署から郵送されてきていたはずなんですが、ここにチェックを入れてしまっていたがために送られてこない・・・。通知も来ないので、その時が来るまで気づかない。 (日常的にマイナポータルにログインしている人なら気がつくかもですが)

確定申告で控除申請するなら、年末調整はスキップして e-Tax で確定申告する際に提出してもいいんですが、年末調整で処理したくて、年末調整で控除証明書の提出を紙で求められている場合は、なんとかして紙版を手に入れなければなりません・・・。

(ただし、年末調整も電子化が始まっているようで、会社側がもし対応していれば、 e-Tax で取得した控除証明書を用いることもできるようです)

References

手順

調べてみると、こういうケースでは、「QRコード付証明書」というのを作成するのが最も手軽で良さそうなことがわかりました。

まず、以下のものを用意します。 (macOS や他のブラウザでもできるようになっているかもしれませんが、できるだけレガシーなソフトを用いることが国のシステムで罠を踏まない基本ですね!)

  • 確定申告で使ったのと同じ Windows OS マシン (もちろん OS 言語設定は日本語にしておきます!!)
  • Microsoft Edge (確定申告の際は Internet Explorer を使いましたが、今回は Edge で最後までいけました🎉)

まず、下のページのリンク中から e-Taxソフト(WEB版) を開きます。

メッセージボックスの確認 | 【e-Tax】国税電子申告・納税システム(イータックス)

ログインには、2次元バーコード認証を用います。

ブラウザ上に QR コードが表示されるので、これを Android スマホにインストールした "マイナポータル" アプリ (以前は "マイナポータルAP" だったような) でスキャンした後、マイナンバーカードをスマホにかざすことでログインできます。

"マイナポータル" アプリ上の2次元バーコード読み取りボタンのデザインが以前と変わっているので、お気をつけください。 (画面右下のフローティングボタンみたいなところに移動しています)

メインページが開いたら [送信結果・お知らせ] > [通知書等一覧] と遷移し、 "住宅借入金等特別控除証明書" を選んで "切替" ボタンを押すことで、データをダウンロードできるようになります。

ただし、ここでダウンロードできるのは、 XML データであり、 PDF データではないことに注意が必要です。

PDF を手に入れるには、今度は以下のページを開いて操作する必要があります。

QRコード付証明書等作成システム

ページを操作する前に、 "事前準備セットアップ" に書かれた手順を実施しておく必要があります。

今回の場合 Edge に拡張機能である e-Tax AP - Chrome ウェブストア をインストールし、サイトからダウンロードした exe ファイル (QR_IEsetup.exe) を実行して、セットアップを済ませておきます。 IE とファイル名についていますが、 Edge と Chrome にも対応しているので、ご安心ください。

その後、 "QRコード付証明書等作成システムを利用する" ボタンを押して、先ほどダウンロードした XML ファイルをアップロードすることで、目的の PDF ファイルが閲覧・ダウンロードできます。

e-Tax もそうですが、土日は基本やってないので、注意してください。 (月の最終の土曜日はやっている)

e-Taxの利用可能時間 | 【e-Tax】国税電子申告・納税システム(イータックス)

References

動画ダウンロードと加工

ちょっと自分のメモ代わりの記事です。

念の為記載しますが、この記事の内容は、使い方によっては法令に触れますので、あくまで自己責任で、個人的な研究目的でご参照ください。

研究用の動画素材をちょこっと YouTube から拝借する。

ytdl-org/youtube-dl (元々は https://github.com/rg3/youtube-dl だった) を用いる。

# Install youtube-dl.
brew install youtube-dl

# Download video.
youtube-dl "https://www.youtube.com/watch?v=xxxxxxxxxxx"

先頭から 30 秒までを別動画ファイルに切り出す。

ffmpeg -i sample.mkv -t 30 -c copy sample_001.mkv

先頭から 90 秒を開始地点として、その後 60 秒までを別動画ファイルに切り出す。

ffmpeg -i sample.mkv -ss 90 -t 60 -c copy sample_002.mkv

References