そこに仁義はあるのか(仮)

略してそこ仁!

【Electron】アプリを全画面モードでも他のデスクトップに移動しても常に最前面に表示する

Electronでアプリを作りました。

アプリは

  • ワークスペース(デスクトップ)を切り替えても表示される
  • 他のアプリよりも前に表示される
  • 他のアプリを全画面表示した時も前に表示される
  • Keynoteをプレゼンテーションモードにした時も前に表示される

としたかったです。

特に4つ目のKeynoteでのプレゼンテーションモードが一筋縄ではいきませんでしたが、最終的にはこんな感じで実現ができました。

f:id:syobochim:20201116213210g:plain

package.jsonmain で定義している main.js にてウィンドウを開くときに設定を追加していきました。

const { app, BrowserWindow } = require("electron");
const is_mac = process.platform==='darwin'     // macOSかどうかの判定をする

if(is_mac) {     // macOSの時のみこの設定を反映する
  app.dock.hide()          // Dockを非表示にする
}

const MAIN_WINDOWS_WIDTH = 300;
const MAIN_WINDOWS_HEIGHT = 350;
function createClapWindow() {
  // Create the browser window.
  const mainWindow = new BrowserWindow({
    width: MAIN_WINDOWS_WIDTH,
    height: MAIN_WINDOWS_HEIGHT,
    webPreferences: {
      preload: path.join(__dirname, 'preload.js'),
    }
  })
  mainWindow.setAlwaysOnTop(true, "screen-saver")    // 常に最前面に表示する
  mainWindow.setVisibleOnAllWorkspaces(true)      // ワークスペース(デスクトップ)を移動しても表示される
  mainWindow.loadFile('public/reaction.html')
}

app.whenReady().then(() => {
  createClapWindow()
})

app.dock.hide() の設定によって、macOSにて全画面モードで開いた時にその上に表示できるようになります。
Windowsにはこの設定はないので、事前にmacOSであるかどうかを判断してmacOSの時のみ設定を適用しています。

mainWindow.setAlwaysOnTop(true, "screen-saver") の設定をすることで、キーノートのプレゼンテーションモードの時にも最前面に表示されるようになります。
BrowserWindow にも alwaysOnTopのプロパティを持っていますが、これに true を設定してもキーノートのプレゼンテーションモードでは適用されませんでした。(他のアプリの全画面モードでは最前面に表示されたのに。。。)

mainWindow.setVisibleOnAllWorkspaces(true) の設定で、ワークスペース(デスクトップ)を切り替えても一緒に移動して表示されるようになります。

これらの設定で実現できました。

参考

【Electron】アプリの背景を透明にしてマウスイベントも拾わせない

Electronでデスクトップアプリを作りました。 その中で

  • 背景を透明にしたい
  • フレームを消したい
  • サイズを変えてほしくない
  • ウィンドウに影をつけない(背景を透明にしたときに文字の影のような者が出てきていた)
  • マウスでクリックさせたくない(選択などもできないようにする)
  • 開発者モードを開いてほしくない

という設定をしたので、それのメモです。 あまりネットでまとまっているものがなかったので、参考までに。

package.jsonmain で定義している main.js にてウィンドウを開くときに設定を追加していきました。

const { BrowserWindow } = require("electron")

function createClapWindow() {
  // ウィンドウを作る
  const mainWindow = new BrowserWindow({
    transparent: true,   // ウィンドウを透明にする
    frame: false,     // フレームをなくす
    resizable: false,    // 画面の大きさを変えさせない
    hasShadow: false,    // ウィンドウに影をつけない
    webPreferences: {
      devTools: false       // 開発者モードを表示させない
    }
  })
  mainWindow.setIgnoreMouseEvents(true)     // マウスイベントを無視させる
  mainWindow.loadFile('public/index.html')
}

この設定で、Electronのブラウザらしさが無くなって、デスクトップアプリっぽく動いてくれました。

参考リンク BrowserWindow | Electron

GitHubで特定のリポジトリをピン留めする

GitHubのプロフィールに表示されているリポジトリをカスタマイズした時のメモです。
今はかなり昔に作ってスターを付けてもらったリポジトリがトップに表示されていました。
いま力を入れているリポジトリをプロフィールに表示したいと思っていたら設定がありました。


まず、プロフィールページの「Customize your pins」を押します。
f:id:syobochim:20201101155004p:plain


そうするとポップアップでGistsとリポジトリの一覧が表示されるので、それにチェックを付けて「Save pins」ボタンをクリックします。
f:id:syobochim:20201101155428p:plain:h600

これでプロフィールページにPinを付けたリポジトリが表示されます。

オンラインのイベントがさみしくて拍手👏アプリを作りました

最近オンラインでのイベントが増えてきましたね。
オフラインで集まってイベントをしていた頃とは違い、オンラインのイベントだとなかなか参加している人のリアクションが見えにくく、
「参加者の人が楽しんで聞いてくれているか分からない」
「参加者のリアクションがなくて寂しい」
「発表でウケているかわからない」
「イベントをもっと盛り上げたい」
そう感じている方も多いのではないでしょうか。

そこで、参加者からリアクションが送れるアプリ、ClapHandを作りました!
このアプリを使うとこのようにパソコンのデスクトップ画面に絵文字を表示して、参加者の方がリアクションすると数字がリアルタイム にカウントアップされていき、参加者の盛り上がりが可視化されます。

f:id:syobochim:20201031213346g:plain:w600

そして、同じイベントに参加している他の登壇者と盛り上がりを競い合うこともできます!

f:id:syobochim:20201031203759p:plain:w600

参加者はリアクションをしよう!

発表者から共有されたリンクを開くとこのようなページが表示されます。
絵文字をクリックすると発表者にリアクションが届きます。

f:id:syobochim:20201031205243p:plain:w300

右下の「Result」ボタンをクリックすると、同じイベントに参加している発表者のリアクションの数字が見られます。
発表者を応援する気持ちで、たくさんリアクションしましょう!👏👏👏👏

始め方は簡単4ステップ

現在 macOS Catalina / Windows 10での動作を確認しています。

1. アプリのインストール

GitHubのReleaseページにアクセスし、プラットフォームに合わせたアプリをダウンロード・インストールします。
Releases · syobochim/ClapHandApp · GitHub

2. イベントを作る

アプリを起動するとメニュー画面が開くので、「Create Event」ボタンをクリックします。
f:id:syobochim:20201031170111p:plain:w500

3. イベントの設定をします

イベントの設定を入力して「Create Event」ボタンを押します。

  • Event Name (必須) : イベント名を入力します。
    • イベント名をイベントのハッシュタグなどで統一すると他の発表者のカウントもグラフで確認できます。
  • Owner (必須) : 発表者の名前を入力します。
  • Emoji (任意) : 参加者にリアクションしてほしい絵文字を入力します。
    • 省略すると👏が登録されます。

f:id:syobochim:20201031171522p:plain:w500

4. 参加者にシェア!

表示されたURLとQRコードを参加者にシェアします!

f:id:syobochim:20201031172755p:plain:w500

あとはアツイ発表を届けるだけ!

表示する位置を変更できる

MacOSのメニューバーとWindowsのタスクトレイから、デスクトップアプリの表示位置を変更できます。
デフォルトは右下に表示されています。

  • Top Right : 右上
  • Top Left : 左上
  • Bottom Right : 右下(デフォルト)
  • Bottom Left : 左下

f:id:syobochim:20201103223222p:plain:w400

f:id:syobochim:20201103230358p:plain:w400

終わりに

オンラインでのイベントをもっともっと楽しんで・盛り上げていきましょう!
皆さん是非使ってみてください!👏👏👏

これから追加開発も進めていきます。一緒に開発に協力いただける方も募集中!

AsanaのWebhookからタスクの追加を検知するbotを作る

タスク管理サービスのAsanaでタスクを追加した時にslackに通知するbotを作りました。
通常は以下の画像のようにAsanaとIntegrationできるアプリがたくさんあるので1からコードを書く必要はほぼ無いのですが、会社Asanaと会社slackにて追加できるアプリにそれぞれ制限があり、今回わざわざコードを書くことになりました。
Webhookをやろうとした時に、ちゃんとセキュリティを担保したいけれどその辺りの情報が全然ない…!ということで困ったので、今後何かAsanaのWebhookでアプリを作る人のお役に立てば幸いです。

f:id:syobochim:20201022005123p:plain:w500


認証用のPersonal Access Tokenを作成する

最初に認証のためのPersonal Access Tokenを作成します。

まず、AsanaのDeveloper App Consoleにアクセスします。
このURL「 https://app.asana.com/0/developer-console 」にアクセスするか、 Asanaでログインした画面の右上にあるプロフィール画像から「My Profile Settings」をクリックし、ポップアップの「Apps」タブから「Manage Developer Apps」をクリックしても同じ画面に遷移します。

「+ New Access Token」をクリックしToken名を入力すると以下のような形式のトークンを取得できます。プログラムやcurlからのアクセスにはこのTokenを使っていきます。 1/1234567890123456:bive3xuos5Ohcavei4yie7ha0Oovaph8

Webhookを登録する

Webhookを登録するにはTaskが登録されるProjectのIDと、イベントが発生した時に情報を送信するURLが必要です。
ProjectのIDはAsanaのURLから確認できます。タスクを登録するプロジェクトのリストやボードを確認した時のURLに含まれている数字がProject IDを示します。
以下の例であれば 1234567890123456 がProject IDです。
https://app.asana.com/0/1234567890123456/board

先ほど取得したAccess Tokenを{access-token}、Project IDを{project-id}、Webhookイベントの送信先URLを{target-url}に置き換えてcurlコマンドを実行します。
action : added でタスクの追加イベントを検知します。addedの他にもchanged removed deleted undeletedなどが指定できるようです。

$ curl -X POST https://app.asana.com/api/1.0/webhooks \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}' \
  -d '{
  "data": {
    "filters": [
      {
        "action": "added",
        "resource_type": "task"
      }
    ],
    "resource": "{project-id}",
    "target": "https://{target-url}"
  }
}'

Handshakeをする

一番最初にWebhookのイベントが発生すると、Handshake用にX-Hook-SecretをHTTPヘッダーに含めたリクエストがtarget-urlに設定したURLへ送られます。
このリクエストに対して 200 OK もしくは 204 No Content のレスポンスを返すことで登録が完了します。
以下が初期登録時のNode.jsでのAWS Lambdaコードです。2行目でHTTPヘッダーの X-Hook-Secret をログ出力します。
このX-Hook-Secretの値は後ほど使うのでMEMOしておいてください。 また、このコードは初回登録時の1回のみ利用します。

exports.handler = async (event) => {
    console.log('this is secret header ----->' + event.headers['X-Hook-Secret'])
    const response = {
        statusCode: 200,
        headers: {"X-Hook-Secret": event.headers['X-Hook-Secret']},
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

X-Hook-Secretja0ge6aChaiPhee7ioph7cucoongaht3 のような形式の文字列です。

また、このコードが正常に動作しているかどうかはこちらのサイトを利用すると簡単に確認できます。
https://asana-webhook-tool.herokuapp.com/

Webhookで受け取ったリクエストを検証する

次に、実際にWebhookが送られてきたデータを受け取ります。
先ほど得た X-Hook-Secretを利用することで、target urlへ送られてきたリクエストがAsanaから送られたものであることを確認でき、安全にリクエストを処理できます。
以下はAWS Lambdaのコードです。{X-Hook-Secret} は先ほど得た値に置き換えてください。
AsanaからはX-Hook-Signature ヘッダーを持つリクエストが送られてきます。このX-Hook-Signatureの値はリクエストのbodyをX-Hook-Secretを使ってSHA 256 HMACでHash化したものになります。
Hash化した値とX-Hook-Signatureが同じ値かどうか確認し、同じ値であれば正常なリクエストとして処理します。

var crypto = require("crypto");

exports.handler = async (event) => {
    const signature = event.headers['X-Hook-Signature'];
    const hash = crypto.createHmac('sha256', '{X-Hook-Secret}')
        .update(String(event.body))
        .digest('hex');

    // ヘッダーのシークレットをチェックする
    if (signature != hash) {
        console.error('Calculated digest does not match digest from API. This event is not trusted. : ' + signature);
        return response = {
            statusCode: 401
        };
    }

    // Webhookのデータを処理するコードを書く

    const response = {
        statusCode: 200
    };
    return response;
};

以上で基本的なWebhookの設定は完了です。
ヘッダーのチェックをした後は、HTTPリクエストのbodyを処理していけば安全にWebhookのリクエストを操作できます。

参考リンク

公式ドキュメント

Node.js
Crypto | Node.js v15.0.0 Documentation

Gitの認証がうまくいかなくなった時の対処法

Macを使っていてGitのリポジトリへpushしようとしたら認証エラーが発生しました。
その対処方法のメモです。

remote: HTTP Basic: Access denied
remote: You have 2FA enabled, please use a personal access token for Git over HTTP.
fatal: Authentication failed for 'https://example.com/repo/sample.git/'

パスワードも合っているのに何故…?と思っていたけど、Mac OSX キーチェーンに保存されている認証情報が変な状態になっていることが原因でした。

Gitの設定情報を確認すると、デフォルトで credential.helper が登録されていました。

$ git config --list
credential.helper=osxkeychain
...

GitHubのドキュメントを見ると、以下の条件があるとのこと。確かに当てはまる。

  • osxkeychain 認証情報ヘルパーを利用するには、Git 1.7.10 以降が必要です。
  • Homebrew を使って Git をインストールした場合、osxkeychain helper はインストール済みです。
  • Mac OS X 10.7 以降を実行しており、Apple の Xcode コマンドラインツールで Git をインストールした場合、osxkeychain helper は インストールした Git に含まれています。

Git に GitHub の認証情報をキャッシュする - GitHub Docs

私は特定のリポジトリでこのcredential.helper を上書きしており、そこの設定が osxkeychain に保存されていることで問題が発生していたようでした。

$ git config --list
...
credential.usehttppath=true
credential.helper=!aws codecommit credential-helper $@ 
...

解決方法

解決方法としては、キーチェーンに保存されている情報を削除すれば認証情報が更新されてアクセス可能になりました。
参考 : OSX キーチェーンから認証情報を更新する - GitHub Docs

コマンドラインで削除する方法もありますが、今回はGUIの操作を紹介します。(どちらの方法も上記のGitHubドキュメント見ればわかります。)

まずはキーチェーンアクセスを開きます。Spotlight検索からすぐに開きます。
f:id:syobochim:20200909192334p:plain:w500

右上の検索ボックスで git と検索すると対象のデータが表示されます。
f:id:syobochim:20200909192721p:plain

右クリックして「情報を見る」を選択します。
f:id:syobochim:20200909192853p:plain:w500

「アクセス制御」をクリックし、対象の git-credential-osxkeychain を選択したら下の「ー」をクリックすれば削除が完了します。
f:id:syobochim:20200909193015p:plain:w500

これで再びGitリポジトリへアクセスしたら事象が解決しました。👏

特定のディレクトリ配下のリポジトリに別のGitの設定を適用する

Gitの設定(コミット時に利用する user.name や user.email など)は基本的に git config コマンドを利用して設定をしていきます。
git config にはいくつかのオプションがあり、基本的には --global を使ってPCのユーザーとしての設定をし、個別に設定を変更したいリポジトリについては --local を利用して設定してきました。
しかし、この方法だと毎回設定の手間がかかったり、設定するのを忘れてしまって私用のユーザー名やメールアドレスで会社のリポジトリにコミットしてしまったりと、課題がでてきます。

git config --global コマンドを実行すると ~/.gitconfig に設定が保存されていきますが、Gitにはディレクトリ配下に対して別の config ファイルを適用できます。
今回はそれの備忘MEMOです。

例として、以下のようなディレクトリ構成とします。ここでは ~/dev/project ディレクトリに会社のユーザーを利用したいと想定します。

~/dev
├── study
├── github
└── project
           ├── projectA
           ├── projectB
           └── projectC

まず、 ~/.gitconfig に以下のような設定を追加します。

[user]
    name = syobochim
    email = syobochim@email.com
+ [includeIf "gitdir:~/dev/project/"]
+         path = ~/.gitconfig-project

includeIf に、設定を変更したいディレクトリのパスを書き、 path に適用させたい設定ファイルのパスを書きます。
~/.gitconfig-project は会社用の設定として以下のように記載をします。
ここではuser設定のみ追加していますが、 認証情報の保存をする credential.helper などを追記する場合も多いかと思います。

[user]
    name = syobochim-office
    email = syobochim-office@email.com

そうすると、 ~/dev/project ディレクトリ配下でgitリポジトリを作成したときに、~/.gitconfig-project の設定が適用されます。
git config --list コマンドにて設定を確認してみると、 ~/dev/project ディレクトリ配下のGitリポジトリでは以下のように出力されました。
user.name=syobochim-officeuser.email=syobochim-office@email.com にて設定が反映されているのがわかります。

$ git config --list
credential.helper=osxkeychain
user.name=syobochim
user.email=syobochim@email.com
includeif.gitdir:~/dev/project/.path=~/.gitconfig-project
user.name=syobochim-office
user.email=syobochim-office@email.com
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
core.precomposeunicode=true

実際にコミットしてみても、会社用のユーザーでコミットされていますね。

$ git log
commit 634ab266a2beee348c1de123569f29c78aff7fde
Author: syobochim-office <syobochim-office@email.com>
Date:   Tue Aug 25 09:09:44 2020 +0900

    test commit

しかし、~/dev/project ディレクトリ以外のGitリポジトリを作成して設定やコミットを実行しても、以下の通り設定は反映されませんでした。

$ git config --list
credential.helper=osxkeychain
user.name=syobochim
user.email=syobochim@email.com
includeif.gitdir:~/dev/project/.path=~/.gitconfig-project
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true
core.ignorecase=true
core.precomposeunicode=true
$ git log
commit a7bbb75e5fabd46afb362401d1ab8ace7393f2ae
Author: syobochim <syobochim@email.com>
Date:   Tue Aug 25 09:13:06 2020 +0900

    test commit

ということで、 [includeIf "gitdir:<<path>>"]~/.gitconfig に記載すれば、特定のディレクトリ配下のGit設定を一括で変更できました。

参考
Git - git-config Documentation