YAMAGUCHI::weblog

海水パンツとゴーグルで、巨万の富を築きました。カリブの怪物、フリーアルバイター瞳です。

ウィンドウマネージャーをcinnamonからi3にした

はじめに

自宅のラップトップマシンをArch Linuxして、ウィンドウマネージャーをi3に変更した。それと同時に会社のLinux環境もi3に変更して、だいぶシンプルにいろいろとできるようになった。備忘録としてなにをしたか残しておく。

追加でインストールしたパッケージ

  • i3
  • i3lock
  • pulseaudio-control
  • xbacklight
  • playerctl
  • networkmanager

設定ファイル

# This file has been auto-generated by i3-config-wizard(1).
# It will not be overwritten, so edit it as you like.
#
# Should you change your keyboard layout some time, delete
# this file and re-run i3-config-wizard(1).
#

# i3 config file (v4)
#
# Please see https://i3wm.org/docs/userguide.html for a complete reference!

set $mod Mod4

# Font for window titles. Will also be used by the bar unless a different font
# is used in the bar {} block below.
font pango:Inconsolata 10
exec --no-startup-id fcitx
exec --no-startup-id "feh --bg-scale $HOME/.config/i3/wallpaper.jpg"

# This font is widely installed, provides lots of unicode glyphs, right-to-left
# text rendering and scalability on retina/hidpi displays (thanks to pango).
#font pango:DejaVu Sans Mono 8

# Before i3 v4.8, we used to recommend this one as the default:
# font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1
# The font above is very space-efficient, that is, it looks good, sharp and
# clear in small sizes. However, its unicode glyph coverage is limited, the old
# X core fonts rendering does not support right-to-left and this being a bitmap
# font, it doesn’t scale on retina/hidpi displays.

# Use Mouse+$mod to drag floating windows to their wanted position
floating_modifier $mod

# start a terminal
bindsym $mod+Return exec i3-sensible-terminal

# kill focused window
bindsym $mod+Shift+q kill

# start dmenu (a program launcher)
bindsym $mod+d exec dmenu_run
# There also is the (new) i3-dmenu-desktop which only displays applications
# shipping a .desktop file. It is a wrapper around dmenu, so you need that
# installed.
# bindsym $mod+d exec --no-startup-id i3-dmenu-desktop

# change focus
bindsym $mod+j focus left
bindsym $mod+k focus down
bindsym $mod+l focus up
bindsym $mod+semicolon focus right

# alternatively, you can use the cursor keys:
bindsym $mod+Left focus left
bindsym $mod+Down focus down
bindsym $mod+Up focus up
bindsym $mod+Right focus right

# move focused window
bindsym $mod+Shift+j move left
bindsym $mod+Shift+k move down
bindsym $mod+Shift+l move up
bindsym $mod+Shift+semicolon move right

# alternatively, you can use the cursor keys:
bindsym $mod+Shift+Left move left
bindsym $mod+Shift+Down move down
bindsym $mod+Shift+Up move up
bindsym $mod+Shift+Right move right

# split in horizontal orientation
bindsym $mod+h split h

# split in vertical orientation
bindsym $mod+v split v

# enter fullscreen mode for the focused container
bindsym $mod+f fullscreen toggle

# change container layout (stacked, tabbed, toggle split)
bindsym $mod+s layout stacking
bindsym $mod+w layout tabbed
bindsym $mod+e layout toggle split

# toggle tiling / floating
bindsym $mod+Shift+space floating toggle

# change focus between tiling / floating windows
bindsym $mod+space focus mode_toggle

# focus the parent container
bindsym $mod+a focus parent

# focus the child container
#bindsym $mod+d focus child

# switch to workspace
bindsym $mod+1 workspace 1
bindsym $mod+2 workspace 2
bindsym $mod+3 workspace 3
bindsym $mod+4 workspace 4
bindsym $mod+5 workspace 5
bindsym $mod+6 workspace 6
bindsym $mod+7 workspace 7
bindsym $mod+8 workspace 8
bindsym $mod+9 workspace 9
bindsym $mod+0 workspace 10

# move focused container to workspace
bindsym $mod+Shift+1 move container to workspace 1
bindsym $mod+Shift+2 move container to workspace 2
bindsym $mod+Shift+3 move container to workspace 3
bindsym $mod+Shift+4 move container to workspace 4
bindsym $mod+Shift+5 move container to workspace 5
bindsym $mod+Shift+6 move container to workspace 6
bindsym $mod+Shift+7 move container to workspace 7
bindsym $mod+Shift+8 move container to workspace 8
bindsym $mod+Shift+9 move container to workspace 9
bindsym $mod+Shift+0 move container to workspace 10

# reload the configuration file
bindsym $mod+Shift+c reload
# restart i3 inplace (preserves your layout/session, can be used to upgrade i3)
bindsym $mod+Shift+r restart
# exit i3 (logs you out of your X session)
bindsym $mod+Shift+e exec "i3-nagbar -t warning -m 'You pressed the exit shortcut. Do you really want to exit i3? This will end your X session.' -b 'Yes, exit i3' 'i3-msg exit'"
# lock screen (requires 'i3lock' package)
bindsym $mod+Control+l exec i3lock

# audio (requires 'pactl' package)
bindsym XF86AudioRaiseVolume exec --no-startup-id pactl set-sink-volume 0 +5%
bindsym XF86AudioLowerVolume exec --no-startup-id pactl set-sink-volume 0 -5%
bindsym XF86AudioAudioMute exec --no-startup-id pactl set-sink-mute 0 toggle 

# display (requires 'xbacklight' package)
bindsym XF86MonBrightnessUp exec xbacklight -inc 10
bindsym XF86MonBrightnessDown exec xbacklight -dec 10

# music control (requires 'playerctl' package)
bindsym XF86AudioPlay exec playerctl play
bindsym XF86AudioPause exec playerctl pause
bindsym XF86AudioNext exec playerctl next
bindsym XF86AudioPrev exec playerctl previous

# resize window (you can also use the mouse for that)
mode "resize" {
        # These bindings trigger as soon as you enter the resize mode

        # Pressing left will shrink the window’s width.
        # Pressing right will grow the window’s width.
        # Pressing up will shrink the window’s height.
        # Pressing down will grow the window’s height.
        bindsym j resize shrink width 10 px or 10 ppt
        bindsym k resize grow height 10 px or 10 ppt
        bindsym l resize shrink height 10 px or 10 ppt
        bindsym semicolon resize grow width 10 px or 10 ppt

        # same bindings, but for the arrow keys
        bindsym Left resize shrink width 10 px or 10 ppt
        bindsym Down resize grow height 10 px or 10 ppt
        bindsym Up resize shrink height 10 px or 10 ppt
        bindsym Right resize grow width 10 px or 10 ppt

        # back to normal: Enter or Escape
        bindsym Return mode "default"
        bindsym Escape mode "default"
}

bindsym $mod+r mode "resize"

# Start i3bar to display a workspace bar (plus the system information i3status
# finds out, if available)
bar {
        status_command i3status
}

おまけ

FnキーとMultimediaキーの主従が希望と逆(F1-F12が通常だとMultimediaキーとして動作していて、Fnキーを押さないとF1-F12として動作しない)だったので、どうやったらデフォルトの設定を逆にできるんだろうと思っていろいろ調べてたんだけど、なんのことはない、Fn+ESCを押せばいいだけだった。

XPS 13'' (9350)だとBIOS設定(Fn Lock)で挙動を変更できる。

参照

画面外にあるダウンロードボタンを押してファイルを取得する

はじめに

日常的にやってる作業を自動化するためにSeleniumを使っているわけだけど、どうでもいいことで無駄なハマり方をしたのでメモしておく。基本的にChrome Driverしか使ってないので他のブラウザの挙動は知らない。

画面外にあるボタンをクリックする

「要素がクリックできる状態になったらクリックする」というコードを最初適当に書いていたけれど、それだとエラーが出てクリックできなかったので、なぜかなーと思っていた。 たとえばこういう感じでコードを書いて実行すると

button_path = '//path/to/xpath'
WebDriverWait(driver, 10).until(
    expected_conditions.elements_to_be_clickable(
        (By.XPATH, button_path)
    )
)
button = driver.find_element_by_xpath(button_path)
button.click()

次のようなエラーが出ていた。

Traceback (most recent call last):
...
selenium.common.exceptions.WebDriverException: Message: unknown error: Element is not clickable at point (61, 6703)
  (Session info: chrome=61.0.3163.100)
  (Driver info: chromedriver=2.30.477690 (c53f4ad87510ee97b5c3425a14c0e79780cdf262),platform=Mac OS X 10.12.6 x86_64)

viewport外に要素がある場合にこういう挙動になる模様。ActionChainsで対応できるかなと思って次のようなコードに変更。

button = driver.find_element_by_xpath(button_path)
actions = ActionChains(driver)
actions.move_to_element(button)
actions.click()
actions.perform()

エラーは出なくなったけどダウンロード部分がうまくいかない。

ダウンロード用の設定と画面の調整

Chromeの起動オプションの変更

SeleniumでのChrome起動時にオプションを追加してダウンロードの際のプロンプトなどを滅殺する。

options = selenium.webdriver.ChromeOptions()
download_dir = os.path.dirname(__file__)
options.add_experimental_option("prefs", {
    "download.default_directory": download_dir,
    "download.prompt_for_download": False,
    "download.directory_upgrade": True
})
driver = selenium.webdriver.Chrome(chrome_options=options)

画面を強制的にスクロールする

ActionChains#move_to_element はマウスをそこまで持ってってくれるだけで画面のスクロールをしてくれないので(どうもviewport内のクリックでしかダウンロードイベントが発生しない)、仕方なくJavaScriptを無理やり実行して動かす。

driver.execute_script("arguments[0].scrollIntoView()", button)

Selenium for Pythonでタブの切替をする

はじめに

ここ数年はMoney Forwardとかのおかげでお金の管理はほぼ自動でいい感じにできてるんだけど、それでもまだ政治的な理由とかサービス側のセキュリティの問題で、利用している全サービスの自動管理とかできてない。というわけでエンジニアならその辺自動化するでしょ、とSeleniumでコツコツ始めたわけです。

で、ナビゲーションをしてると <a target="_blank"...> になってるaタグとかあって、ちょっとはまったのでメモ。

タブの切替方法

SeleniumはChromeDriverを使ってChromeを動かしている。タブを切り替える作業はいくつか方法があって

  1. WebDriver.send_keys() を使って手動で切り替えるのと同じキーを送る(例: Ctrl+Tabなど)
  2. WebDriver.ActionsChain() を使って 1 と同様にする
  3. WebDriverが持ってるwindowの一覧のなかから特定のwindowに WebDriver.switch_to.window() で切り替える

1と2は動かす環境(OS)が限定されているのであれば、手書きの作業書をそのままコードに書き直すだけなので簡単。ただ今回はOSをまたいで(MacLinux)実行する予定なので3を採用した。

スニペット

main_window = driver.current_window_handle  # 移動前のwindowを取っておく

for window in driver.window_handles:
    driver.switch_to.window(window)
    if driver.title == 'target title text':  # ページの <title> の内容で目的のタブと判定する
        break

do_some_process()
...
driver.switch_to.window(main_window)  # 処理が終わったら戻る

非常に泥臭いコードだけれども、新しいタブが開かれる前は window handle のインスタンスが無いので、結局こうやって処理するしかないと思う。もっといい方法があったら知りたい。

api.aiのdefault fallback intentをFirebase Functionsで受ける

はじめに

こんにちは、Slack Bot界のアラーキーです。今日はAlphaGoが柯潔との3局対決2勝目で中押し勝ちで2勝目を挙げましたね。Machine Learningの発展はすごいなあという素人の感想ですが、そんなMachine Learningの力をBot開発にも簡単にもたらしてくれるサービスの一つがapi.aiです。api.ai自体の説明は公式サイトや他のサイトに譲るとして、今日はapi.aiのdefault fallback intentをFirebase Functionsで実装したFulfillmentで受けるという簡単なデモを試したので、その作業ログを残します。

チャットボット AIとロボットの進化が変革する未来

チャットボット AIとロボットの進化が変革する未来

前提

まずは用語の説明から。

  • api.ai: 様々なチャットシステム用Bot(Actions on Google、Slack、LINE、Facebook Messanger)などに対応しているチャットボットミドルウェア
    • Agent: あるひとまとまりの応答をするNLU(Natural Language Understanding、自然言語理解)モジュール。複数のチャットアプリケーションと同時に連携が可能。
    • Intent: ユーザーからの特定のクエリに対する応答のマッピング。このintentをいかに多くの応答に対して作成できるかがapi.aiの肝。
    • Fulfillment: api.aiの先に用意する自前の応答システム。通常のbotシステムであればチャットシステムから直接メッセージデータを受け取るが、api.aiを利用する場合は前段となるapi.aiで捌けなかったものや複雑なトランザクションが発生するものをここで受ける。

api.ai自体を利用する場合は他にもEntityやContextといったものとapi.aiで内蔵している自然言語理解のトレーニング機構を使っていかに効率よくルールを作成していくかが肝心なのですが、今日は対応するIntentがなにもない場合のフォールバック先であるDefault Fallback Intentを自分で用意したFirebase Functionsのエンドポイントで受ける、ということをまずやってみます。

Firebase Functionsでのエンドポイントの作成

Firebaseプロジェクトの作成

Firebase FunctionsのGetting Startedのドキュメントを読めば簡単にできると思いますが、簡単にコマンドログだけ書くと

$ npm install -g firebase-tools
$ firebase login
$ firebase init functions
$ cd functions
$ npm install --save firebase-functions
$ npm install --save firebase-admin@4.2.1
$ tree -a -L 2 .
.
├── .firebaserc
├── firebase.json
└── functions
    ├── index.js
    ├── node_modules
    └── package.json

作成された firebase.json は空のオブジェクトが書いてあるだけですが、作成するFirebase Functionsのファイルを functions 以下に置くのであればそのままで大丈夫。functionsディレクトリの中のindex.jsに実際のエンドポイントの処理を書いていきます。

index.jsの作成

index.js をこんな感じで雑に作ります。Slackのみをつなげているため、dataの中にslackしかありませんが、つなげているアプリケーションが複数あればそれに応じてassistantfacebookなどのメッセージも追加することで対応できます。api.aiから飛んでくるリクエストと、そこに返すべきレスポンスのJSONスキーマに関しては api.ai のWebhookのドキュメントに書いてある。

const functions = require('firebase-functions');
const admin = require('firebase-admin');

admin.initializeApp(functions.config().firebase);

const slack_message = {
    "text": "hello slack from firebase"
}

exports.sampleBotAgent = functions.https.onRequest((req, res) => {
    console.log(req);
    res.status(200).send({
        "speech": "hello from firebase",
        "displayText": "hello from firebase",
        "data": {"slack": slack_message},
        "source": "foo"
    })
});

デプロイ

これで完了です。デプロイします。プロジェクトルート直下で firebase deploy コマンドを実行します。

$ firebase deploy --only functions
=== Deploying to 'sample-bot-agent'...

i  deploying functions
i  functions: ensuring necessary APIs are enabled...
i  runtimeconfig: ensuring necessary APIs are enabled...
✔  functions: all necessary APIs are enabled
✔  runtimeconfig: all necessary APIs are enabled
i  functions: preparing functions directory for uploading...
i  functions: packaged functions (1.55 KB) for uploading
✔  functions: functions folder uploaded successfully
i  starting release process (may take several minutes)...
i  functions: updating function sampleBotAgent...
✔  functions[sampleBotAgent]: Successful update operation.
✔  functions: all functions deployed successfully!

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/samplebot-agent/overview
Function URL (sampleBotAgent): https://us-central1-samplebot-agent.cloudfunctions.net/sampleBotAgent

ログの最後のFunction URLというのが作成されたエンドポイントのURLなのでこれをコピーします。

api.aiの設定

Agentの作成

api.aiに初めてログインするとすぐにAgentの作成画面に飛ばされると思います。すでにAgentがあるけれど新規に作る場合には、左ペインのエージェント名の右側にあるメニューボタンを押すと “Create new agent” というメニューがでてくるのでそこで作成。適当にAgentの名前やdescriptionを書きます。Sample Dataは無いと思うので無視。言語は英語が一番ちゃんと動きますが、日本語もちょっとは使えます。目下改良中の模様。今回はどうせDefault Fallback Intentしか作らないのであまり関係ないです。Google Projectはデフォルトで作ってくれるものでいいと思うので無視。

f:id:ymotongpoo:20170526002606p:plain

Slackとの連携

Agentは複数のチャットアプリケーションと連携できますが、今回はSlackのみと連携します。左ペインのメニューのIntegrationsを押して、Slackのトグルスイッチをオンにします。 f:id:ymotongpoo:20170526004756p:plain

するとSlackの各種トークンを設定するダイアログが出てくるので各々設定します。Slack側でもBotの設定をする必要がありますが、そのあたりは次のドキュメントにスクリーンショット付きで書いてあるのでそちらに譲ります。 先のダイアログ内のOAuth URLとEvent Request URLはSlack側のBotの設定で必要となります。その設定手順も次のドキュメントに書いてあります。

Fulfillmentの設定

AgentができてIntegrationができたのでFulfillmentを設定します。左ペインのメニュー内のFulfillmentを選択して設定画面にいきます。 f:id:ymotongpoo:20170526003017p:plain

WebhookのEnabledをオンにするといくつかフォームが出てきますが、ここのURLに先ほどコピーしたFirebase FunctionsのURLを貼ります。あとは設定しなくてOKです。 f:id:ymotongpoo:20170526003156p:plain

default fallback intentの設定

Fulfillmentの設定が終わったのでいよいよdefault fallback intentの設定です。初期値ではDefault Fallback IntentとDefault Welcome Intentがあると思います。 f:id:ymotongpoo:20170526003813p:plain

このDefault Fallback Intentを開くといくつかText Responseが設定されていることと思います。 f:id:ymotongpoo:20170526003953p:plain

このText Responseをまず全部消します。全消しです。Actionはそのまま “input.unknown” にしておいてください。その後、画面下にある Fulfillment という文字を押します。ここがわかりにくいですがこの Fulfillment の文字を押すと “Use webhook” のチェックボックスが出てくるのでチェックします。 f:id:ymotongpoo:20170526004257p:plain

これで設定完了です。

Slackで試す

適当なチャンネルに先ほど Agent と連携させた Bot を招待して、適当に書き込んでみます。 f:id:ymotongpoo:20170526005711p:plain

無事に動作しました!あとはapi.ai側のIntentを拡充させて、Fallback側での動作を諸々と考えるだけですね!楽しくなってきました!

おわりに

チャットシステムが普及し、様々な場面で文字・音声チャットが使われることが増えてきました。自分用のBotもより柔軟なものにできるように自分もいろいろ試そうと思います。

参照

macOSでパスワード付きzipを作成する

はじめに

いつから行われているのかわからないけれど、IT業界では「パスワード ハ オッテ ゴレンラクイタシマス」という呪文を唱え、パスワード付きzipとそのパスワードを2通のメールで送る慣習がある。普段は自分からそういうzipファイルを送ることはないのだけれども、今日たまたまどうしてもそうする必要があった。

zipcloak

で、調べてみたら、macOSでもzipcloakコマンドというのがあって、それを使えばパスワード付きzipを作れるということを今日知った。情弱乙。

$ zip -r path-to-dir/ archive.zip
$ zipcloak archive.zip
Enter password:
Verify password:

zipコマンドでまず普通のzipファイルを作成して、zipcloakコマンドでそれをキーフレーズで暗号化するという流れ。地味に便利だった。

zip -e

記事を公開してから力武さんにzipコマンド自体のオプションを教えてもらった。

$ man zip
...
       -e
       --encrypt
              Encrypt the contents of the zip archive using a password which is entered on the terminal in response to a prompt (this will not be echoed; if standard error is not a  tty,  zip  will  exit
              with an error).  The password prompt is repeated to save the user from typing errors.

ほんとだ、情弱すぎた。というかmanを読んでないのが悪い。

$ zip -e -r path-to-dir/ archive.zip
Enter password:
Verify password:

できた。