<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">

  <channel>
    <title>Bot on @johtaniの日記 3rd</title>
    <link>https://blog.johtani.info/tags/bot/</link>
    <description>Recent content in Bot on @johtaniの日記 3rd</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>ja</language>
    <lastBuildDate>Thu, 05 Dec 2024 15:00:00 +0000</lastBuildDate><atom:link href="https://blog.johtani.info/tags/bot/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>AI活用しきれていない話</title>
      <link>https://blog.johtani.info/blog/2024/12/05/with-ai/</link>
      <pubDate>Thu, 05 Dec 2024 15:00:00 +0000</pubDate>
      
      <guid>https://blog.johtani.info/blog/2024/12/05/with-ai/</guid>
      <description>&lt;p&gt;この記事は&lt;a href=&#34;https://adventar.org/calendars/10523&#34;&gt;pyspa アドベントカレンダー 2024&lt;/a&gt;の6日目です。&lt;/p&gt;
&lt;p&gt;公私ともにAIを活用しきれてないけど、こんな感じで使いましたというお話です。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;elasticsearchのハンズオン&#34;&gt;Elasticsearchのハンズオン&lt;/h2&gt;
&lt;p&gt;お客さん向けにElasticsearchのハンズオンを1時間 x 15回という形で提供しました。
新しい人も入ってきたので慣れてもらうために、また、知ってる人もハンズオンすることで普段は触らない・知らない機能を触って刺激になってアイデアが浮かぶかもしれないしという感じのモチベーションです。&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;この記事は&lt;a href=&#34;https://adventar.org/calendars/10523&#34;&gt;pyspa アドベントカレンダー 2024&lt;/a&gt;の6日目です。&lt;/p&gt;
&lt;p&gt;公私ともにAIを活用しきれてないけど、こんな感じで使いましたというお話です。&lt;/p&gt;
&lt;!-- more --&gt;
&lt;h2 id=&#34;elasticsearchのハンズオン&#34;&gt;Elasticsearchのハンズオン&lt;/h2&gt;
&lt;p&gt;お客さん向けにElasticsearchのハンズオンを1時間 x 15回という形で提供しました。
新しい人も入ってきたので慣れてもらうために、また、知ってる人もハンズオンすることで普段は触らない・知らない機能を触って刺激になってアイデアが浮かぶかもしれないしという感じのモチベーションです。&lt;/p&gt;
&lt;p&gt;ただ、資料作成にそれほど時間をかけるのももったいないですよねとお客さんとの会話にも出たので、そういう時にこそAIでしょ！となり、使ってみました。&lt;/p&gt;
&lt;p&gt;こんな感じで。&lt;/p&gt;
&lt;blockquote class=&#34;twitter-tweet&#34;&gt;&lt;p lang=&#34;ja&#34; dir=&#34;ltr&#34;&gt;Google AI StudioのGeminiに壁打ちの相手してもらってるところだけど、賢いなぁ（驚き屋みたいになってるな） &lt;a href=&#34;https://t.co/swyvbTdYxt&#34;&gt;pic.twitter.com/swyvbTdYxt&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jun Ohtani (@johtani) &lt;a href=&#34;https://twitter.com/johtani/status/1785521250955346202?ref_src=twsrc%5Etfw&#34;&gt;May 1, 2024&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&#34;https://platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;


&lt;p&gt;このツイートではGoogleのAI Studioを使っていますが、ChatGPTでも同じように作ってもらって、最終的にはChatGPTで最後の回までを作ってもらいました。
プロンプトを工夫したりはできていないので、まだまだ使い切れてる感じはしないですが。。。&lt;/p&gt;
&lt;p&gt;流れとしては、次のような感じで作ってもらいました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;15回で初心者向けのハンズオンの流れを考えてもらう
&lt;ul&gt;
&lt;li&gt;「Elasticsearchのハンズオンを15回程度で考えているところです。全文検索のユースケースを想定しています。カリキュラムを考えてみてください」&lt;/li&gt;
&lt;li&gt;戻ってきた答えにはロギングなどのユースケースも混ざっちゃってたので何度かやり取りして直してもらいました&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;大体出来上がったらそれぞれの回のハンズオンについて詳しく書き下してもらいます
&lt;ul&gt;
&lt;li&gt;最初の流れでは、1行程度の説明しかなかったので、ここでそれぞれのハンズオンでの流れが出来上がります&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;あとは、個別にさらにいくつか会話をしていって、仕上げていきました
&lt;ul&gt;
&lt;li&gt;「図を用いて説明して」「手順を書いて」「Dockerを使う形のインストールだと？」&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;題材がElasticsearchということもあり、世の中に資料がたくさんあって学習されているようで、REST APIやクエリもなんとなく作ってくれます。
おかげでハンズオン資料もそれなりの形にできたのではかどりました（出てきたクエリなどは個別に確認しますが、ざっくりした項目は上げてあるのであとは説明すればいい）。&lt;/p&gt;
&lt;p&gt;ただ、Elasticsearchはバージョンアップも頻繁なので、「資料があふれている＝古いバージョンの書き方もある」という状況がよく起こります。
私はある程度知っている内容なので、「ここがおかしい」「これは古い」という形で自分ですぐに修正が入れられるので良いのですが、知らない分野だとそれぞれの調査で時間がかかるだろうなぁという感じです。&lt;/p&gt;
&lt;p&gt;慣れてる内容を再度まとめたり、文章を書いてもらってちょっと修正などは作業がすごく楽になって助かりました。&lt;/p&gt;
&lt;h2 id=&#34;goでslackのボット作成第2弾&#34;&gt;GoでSlackのボット作成（第2弾）&lt;/h2&gt;
&lt;p&gt;もう一つの使い方としてプログラムも書いてもらいました。
こっちはうまく指示が出せずに失敗という感じになってしまいましたが。。。&lt;/p&gt;
&lt;p&gt;3月に&lt;a href=&#34;https://blog.johtani.info/blog/2024/03/22/daily-routine/&#34;&gt;「続ける思考」を読んだ&lt;/a&gt;記事を書きました。
で、本をちょっとずつ読んだり、ガンプラをちょっとずつ作ったりしています。
それらの記録をつけるのに、Slackのボットを作ってみるか→じゃあ、AIに手伝ってもらいながらコーディング？という流れで、壁打ちしながらコードも書いてもらってみたのですが。。。&lt;/p&gt;
&lt;blockquote class=&#34;twitter-tweet&#34;&gt;&lt;p lang=&#34;ja&#34; dir=&#34;ltr&#34;&gt;ペアプロってこんな感じなのだろうか？ &lt;a href=&#34;https://t.co/KWxYERNgX3&#34;&gt;pic.twitter.com/KWxYERNgX3&lt;/a&gt;&lt;/p&gt;&amp;mdash; Jun Ohtani (@johtani) &lt;a href=&#34;https://twitter.com/johtani/status/1785487886374285768?ref_src=twsrc%5Etfw&#34;&gt;May 1, 2024&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&#34;https://platform.twitter.com/widgets.js&#34; charset=&#34;utf-8&#34;&gt;&lt;/script&gt;


&lt;p&gt;G.W.に壁打ちしながらコードは書いてもらったんですが、実際にGoLandにもってきて動かそうとすると、まぁ、うごかないのです。。。
（そもそもmainの関数なのにpackage mainになってないとか。。。）
結局、お蔵入りして8月に骨格をもとにしながら最低限（Google Driveじゃなくてローカルファイルに保存）の実装にして動くようになって活用していますが。&lt;/p&gt;
&lt;p&gt;反省点はこんな感じ&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;複数ファイルにまたがるような形で設計してもらおうとしたのがよくなかった&lt;/li&gt;
&lt;li&gt;そもそも指示の出し方がよくない？（プロンプト書くのが下手？）&lt;/li&gt;
&lt;li&gt;GitHub CopilotのようなIDEで使えるもののほうがよさそう（もってきて動かすのがめんどくさい）&lt;/li&gt;
&lt;li&gt;テストとか書いてもらうのがいいのかなぁ？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;もちろん、メリットもありました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;プロジェクト、プログラムの命名
&lt;ul&gt;
&lt;li&gt;「こんなプログラムを作りたいんだけど名前をいくつか考えて？」「もっと中二っぽい名前がいいな」という感じ会話をして10個くらい名前を考えてもらってそこから採用した&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;変数名、関数名などの命名&lt;/li&gt;
&lt;li&gt;READMEの記述&lt;/li&gt;
&lt;li&gt;REST APIのレスポンスJSONとかをもとに構造体作ってもらったり&lt;/li&gt;
&lt;li&gt;書いてもらったプログラムの個々のパーツは書き方の参考になった&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;あとは、JetBrainsのAIアシスタントに課金はしていないのですが、GoLandの補完が賢くなっており、定型的なエラー処理やログメッセージなどを予測して補完してくれるのがすごく楽ですね。テストケース書く時もいい感じに補完してくれるのかしら？（書きなさいよテストケース）&lt;/p&gt;
&lt;h2 id=&#34;まとめ&#34;&gt;まとめ&lt;/h2&gt;
&lt;p&gt;まだまだうまく使えてない自信があります（なんだそれ）。
プロンプトを工夫したりなどはしていないので、なんとなく会話してもらってAIに壁打ちしてもらう形程度にしか使えてないと思っています。
あとは、プログラミングでの活用も課金してたりはしていないので、試してみないとなぁ。&lt;/p&gt;
&lt;p&gt;世の中的には、プロンプトエンジニアリング、RAG、エージェントRAG、グラフRAG、画像生成などいろいろ流行っていますが、なんとなく概念を知っているかな？くらいになっています。
来年はRAGだとかエージェントRAGだとかをもっと触って使ってみたり作ってみたりしないとですね。
（本も買って積んでるし。。。）&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>Slackボットで音楽検索＆再生</title>
      <link>https://blog.johtani.info/blog/2024/03/14/search-api-owntone/</link>
      <pubDate>Thu, 14 Mar 2024 09:24:04 +0000</pubDate>
      
      <guid>https://blog.johtani.info/blog/2024/03/14/search-api-owntone/</guid>
      <description>&lt;p&gt;年末に、&lt;a href=&#34;https://blog.johtani.info/blog/2023/12/08/smarthome-bot/&#34;&gt;GoでSlackのボットを作った&lt;/a&gt;という記事を書きましたが、少し改良したので話の続きを。&lt;/p&gt;
&lt;p&gt;やってみたいこととして、キーワードを指定して再生する仕組みを上げていました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;キーワードに関連する曲の再生
&lt;ul&gt;
&lt;li&gt;OwnTone自体に検索の仕組みがついているので、キーワードを受け付けてそれに関する曲を流す&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;コマンドの判別の仕組みの変更&#34;&gt;コマンドの判別の仕組みの変更&lt;/h2&gt;
&lt;p&gt;これまでは、単純なコマンドを実行してもらう形で実装していたため、Slackで入力された文字列をそのままコマンド名にマッチするかどうかの判定だけをしていました（レーベンシュタイン距離でタイポ対応はしてましたが）。&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;年末に、&lt;a href=&#34;https://blog.johtani.info/blog/2023/12/08/smarthome-bot/&#34;&gt;GoでSlackのボットを作った&lt;/a&gt;という記事を書きましたが、少し改良したので話の続きを。&lt;/p&gt;
&lt;p&gt;やってみたいこととして、キーワードを指定して再生する仕組みを上げていました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;キーワードに関連する曲の再生
&lt;ul&gt;
&lt;li&gt;OwnTone自体に検索の仕組みがついているので、キーワードを受け付けてそれに関する曲を流す&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;コマンドの判別の仕組みの変更&#34;&gt;コマンドの判別の仕組みの変更&lt;/h2&gt;
&lt;p&gt;これまでは、単純なコマンドを実行してもらう形で実装していたため、Slackで入力された文字列をそのままコマンド名にマッチするかどうかの判定だけをしていました（レーベンシュタイン距離でタイポ対応はしてましたが）。&lt;/p&gt;
&lt;p&gt;この場合、やってみたいことに挙げていた「キーワードを受け付けて」の部分が想定から抜けています。
なので、コマンド名の判別処理を入力文字列そのままではなく、次のような処理にしました（&lt;a href=&#34;https://github.com/johtani/smarthome/pull/43/files&#34;&gt;処理切り替え時のPR&lt;/a&gt;）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;入力された文字列をスペース区切りでSplitして配列に&lt;/li&gt;
&lt;li&gt;登録済みのコマンド名もスペース区切りでSplitして配列に&lt;/li&gt;
&lt;li&gt;入力の配列に登録済みコマンドの配列が含まれていればコマンドにマッチした
&lt;ul&gt;
&lt;li&gt;入力の配列の残り（コマンド部分を抜いたもの）をスペースで連結してからコマンドの引数として渡す&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;また、このタイミングでコマンド名を1つの文字列にするために使っていたハイフンも除去することにしました（コマンドと引数の切れ目をわかりやすくしようと思ってハイフンを入れていたのですが、連結しておく必要もなくなったので）。&lt;/p&gt;
&lt;h2 id=&#34;キーワードを受け取って検索&#34;&gt;キーワードを受け取って検索&lt;/h2&gt;
&lt;p&gt;コマンドごとに引数をどのように利用するかは異なるので、コマンド側で引数の処理を行います。&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://owntone.github.io/owntone-server/json-api/#search-by-search-term&#34;&gt;Owntoneには単語で簡単に検索できるAPI&lt;/a&gt;が用意されています。
とりあえずは、引数の部分の文字列をそのままこのAPIの&lt;code&gt;query&lt;/code&gt;パラメータに渡すだけの簡単な処理を実装しました。
取得件数（&lt;code&gt;limit&lt;/code&gt;）などもありますが、まずは固定で&lt;code&gt;5&lt;/code&gt;として実装しました
（&lt;a href=&#34;https://github.com/johtani/smarthome/pull/45/files&#34;&gt;その時のPRはこちら&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;APIのレスポンスには、アーティスト、アルバム、曲ごとに項目が返ってくるので、それを表示しています。
「検索するだけ(&lt;code&gt;search music&lt;/code&gt;)」のコマンドと「検索した結果を再生する&lt;code&gt;search play&lt;/code&gt;」コマンドの2種類を追加しました。&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Search Results...
# Artists (1)
 Southern All Stars
# Albums (4)
 Paint The Sky With Stars: The Best Of Enya / Enya
 SOUTHERN ALL STARS / サザンオールスターズ
 STARS / Superfly &amp;amp; トータス松本
 江ノ島 (SOUTHERN ALL STARS GOLDEN HITS MEDLEY) / Z団
# Tracks (20)
 All Of The Stars / Ed Sheeran
 ...
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id=&#34;簡易版のクエリパーサーを追加&#34;&gt;簡易版のクエリパーサーを追加&lt;/h2&gt;
&lt;p&gt;ちょっと使ってみると、ほかにもできそうなことが出てきます。&lt;/p&gt;
&lt;p&gt;検索結果を見るとわかりますが、情報のタイプごとに結果が返ってきています。
検索のAPIの仕様に、このタイプを絞り込む条件も渡すことができるようになっています。&lt;/p&gt;
&lt;p&gt;いろんな曲が混ざるのもいいのですが、アルバム名で指定したかったり、アーティストで指定して曲をかけたくなります。
また、取得件数も指定できるのがいいかも？ということで、引数にちょっとだけパース処理を入れてみました（&lt;a href=&#34;https://github.com/johtani/smarthome/blob/69373b9b38c4a92f8693cd6be18a2f0508b27658/subcommand/action/owntone/search.go#L118&#34;&gt;Parse関数&lt;/a&gt;）。&lt;/p&gt;
&lt;p&gt;パーサー用のライブラリなども調べてみましたが、そこまで凝った条件を書くわけでもないので、スペース区切りで分割した後に、
「limit:数値」や「type:artist」など特殊なキーが単語の先頭についているかどうかの判断をする処理でお茶を濁しました。。。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;search music stars type:artist&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;と入れた場合は、「stars」を含むアーティストだけが結果としてかえるようになります。&lt;/p&gt;
&lt;h2 id=&#34;そして新たな課題&#34;&gt;そして新たな課題&lt;/h2&gt;
&lt;p&gt;とりあえずなんとなく検索して再生できるようになりました。
ただ、OwntoneはSQLiteに入っているデータに対して、入力された単語で部分一致検索をしているだけのようです。
また、曲ごとに異なるアーティスト名になっている場合にアルバムのアーティストがVarious Artistsのようになっており、
いい感じに検索できないこともあります（トラックごとのアーティストにも検索してほしい）。&lt;/p&gt;
&lt;p&gt;また、私の問題なのですが、海外の歌手名のつづりを覚えていなかったりもするので、もうちょっといい感じに検索できる仕組みが欲しくなってきています。&lt;/p&gt;
&lt;p&gt;ということで、今後は類義語（英語名の歌手のカタカナ名とか）や細かなアーティスト名やコンポーザーなどでも検索できるようになにかしらの検索エンジンにデータをいれて、検索はそちらでやりつつ、音楽を流せるような仕組みを作りたくなってきました。&lt;/p&gt;
&lt;p&gt;曲のメタデータを増やしたりどうやってやるのがいいかなぁ？&lt;/p&gt;
</content:encoded>
    </item>
    
    <item>
      <title>GoでSlackのボットを作った話</title>
      <link>https://blog.johtani.info/blog/2023/12/08/smarthome-bot/</link>
      <pubDate>Fri, 08 Dec 2023 00:00:00 +0900</pubDate>
      
      <guid>https://blog.johtani.info/blog/2023/12/08/smarthome-bot/</guid>
      <description>&lt;p&gt;PySpaアドベントカレンダーの12/08のエントリーです。昨日は&lt;a href=&#34;https://gist.github.com/shibukawa/af62f192033cb6d500017505f503ca18&#34;&gt;渋川さんのアンラーニングで失敗した話&lt;/a&gt;でした。&lt;/p&gt;
&lt;p&gt;私は、今年は&lt;a href=&#34;https://blog.johtani.info/blog/2022/12/15/music-server-on-mac-mini/&#34;&gt;昨年の続き&lt;/a&gt;っぽい話を。&lt;/p&gt;
&lt;h2 id=&#34;音楽の再生とか停止とか&#34;&gt;音楽の再生とか停止とか&lt;/h2&gt;
&lt;p&gt;今年もリモートで仕事を継続しています。
昨年、OwnToneという音楽サーバーを導入しましたが、ミーティング開始時や終了後に音楽の停止や再生をするのに、ブラウザで操作するのはちょっと面倒です。
加えて、ミーティングする際には実際には&lt;/p&gt;</description>
      <content:encoded>&lt;p&gt;PySpaアドベントカレンダーの12/08のエントリーです。昨日は&lt;a href=&#34;https://gist.github.com/shibukawa/af62f192033cb6d500017505f503ca18&#34;&gt;渋川さんのアンラーニングで失敗した話&lt;/a&gt;でした。&lt;/p&gt;
&lt;p&gt;私は、今年は&lt;a href=&#34;https://blog.johtani.info/blog/2022/12/15/music-server-on-mac-mini/&#34;&gt;昨年の続き&lt;/a&gt;っぽい話を。&lt;/p&gt;
&lt;h2 id=&#34;音楽の再生とか停止とか&#34;&gt;音楽の再生とか停止とか&lt;/h2&gt;
&lt;p&gt;今年もリモートで仕事を継続しています。
昨年、OwnToneという音楽サーバーを導入しましたが、ミーティング開始時や終了後に音楽の停止や再生をするのに、ブラウザで操作するのはちょっと面倒です。
加えて、ミーティングする際には実際には&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;音楽を止める&lt;/li&gt;
&lt;li&gt;電気をつける&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;といったことをっやります。
音楽再生についても&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;音楽を再生する&lt;/li&gt;
&lt;li&gt;アンプの音は音楽再生用の音量にする&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;といった複数のことを行います。&lt;/p&gt;
&lt;p&gt;基本的にPCの前にいるし、Slackを常に開いているからSlackのBotを作ると便利になるのでは？
ということで、自分専用のSlack Botを作ってみました。&lt;/p&gt;


&lt;link rel=&#34;stylesheet&#34; href=&#34;https://blog.johtani.info/css/hugo-easy-gallery.min.css&#34; /&gt;
&lt;div class=&#34;box&#34; style=&#34;max-width:400&#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope
    itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34; &gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://blog.johtani.info/images/entries/20231208/slack-bot-start-music.png&#34;  /&gt;
    &lt;/div&gt;
    &lt;a href=&#34;https://blog.johtani.info/images/entries/20231208/slack-bot-start-music.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;h3 id=&#34;githubリポジトリ&#34;&gt;GitHubリポジトリ&lt;/h3&gt;
&lt;p&gt;全く誰の役にも立たないけど、公開しています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/johtani/smarthome&#34;&gt;https://github.com/johtani/smarthome&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;goで実装&#34;&gt;Goで実装&lt;/h3&gt;
&lt;p&gt;知り合いに勧められたということもあり、Goをあまり触ったことがなかったので、Goでやってみました。時々新しい言語を触ってみたくなるので。。。&lt;/p&gt;
&lt;p&gt;コマンドパターンのような作りで、コマンド（操作）を増やしていく感じで実装してはみたのですが、この書き方がいいのかはまだよくわかっていないです。&lt;/p&gt;
&lt;p&gt;Actionという単体の操作(例：&lt;a href=&#34;https://github.com/johtani/smarthome/blob/master/subcommand/action/owntone/pause.go&#34;&gt;OwnToneの一時停止&lt;/a&gt;)を、操作したい機器の操作ごとに作成しています。
インターフェースとしてはRunだけを定義しています。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Action&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;interface&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Run&lt;/span&gt;() (&lt;span style=&#34;color:#66d9ef&#34;&gt;string&lt;/span&gt;, &lt;span style=&#34;color:#66d9ef&#34;&gt;error&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;それらの操作（Action）をひとまとめにしたものをSubcommandという形で定義して、SlackのBotにはSubcommandの名前を送って処理を実行する形にしています。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;type&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Subcommand&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;struct&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;Definition&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;actions&lt;/span&gt;     []&lt;span style=&#34;color:#a6e22e&#34;&gt;action&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Action&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;	&lt;span style=&#34;color:#a6e22e&#34;&gt;ignoreError&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;bool&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;やりたいことを追加するタイミングで時々リファクタしたりもしていますが、基本はこんな感じです。
細かな実装はさておき、今回ボットを作るときの観点をいくつか。&lt;/p&gt;
&lt;h3 id=&#34;宅内ネットワーク&#34;&gt;宅内ネットワーク&lt;/h3&gt;
&lt;p&gt;音楽サーバーのOwnToneもですが、YAMAHAのアンプのAPIも見つけたのでこれらの操作をボットから行っています。どちらも自宅のネットワークにあるので、今回作成したボットも同じネットワークで動作します。&lt;/p&gt;
&lt;p&gt;外部に公開されたHTTPのエンドポイントがあればよいのですが、今回は用意していません。
SlackのAPIではこれに対応したアプリを作るときのために&lt;a href=&#34;https://api.slack.com/apis/connections/socket&#34;&gt;Socket Mode&lt;/a&gt;を用意してくれています。&lt;/p&gt;
&lt;p&gt;ドキュメントがしっかりしているのもあり、チュートリアルなどを見つつ特に迷うことなく構築できました。&lt;/p&gt;
&lt;p&gt;基本的には「特定のチャンネルでボットがメンションされたら動作する」という挙動だけしか実装していないので、特に今回はこれで困ることもありませんでした（世の中の人たちはもっとすごいSlackの使い方をしていそうだけど。。。）。&lt;/p&gt;
&lt;h3 id=&#34;対象となるものたち&#34;&gt;対象となるものたち&lt;/h3&gt;
&lt;p&gt;ボットから操作しているのは今のところ3種類です。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SwitchBot&lt;/li&gt;
&lt;li&gt;OwnTone&lt;/li&gt;
&lt;li&gt;YAMAHAのアンプ&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&#34;switchbot&#34;&gt;SwitchBot&lt;/h4&gt;
&lt;p&gt;SwitchBotについては、&lt;a href=&#34;https://github.com/nasa9084/go-switchbot&#34;&gt;go-siwtchbot&lt;/a&gt;を利用しています。
電気、エアコン、空気清浄機、サーキュレーターのオンオフをSwitchBot経由でやっています。&lt;/p&gt;
&lt;p&gt;室内灯の明るさのために、SwitchBot側でシーンを設定している（一番暗い明るさで電気をつける）ので、そこが少しと特殊でしょうか？&lt;/p&gt;
&lt;p&gt;あとは、SwitchBotの温湿度計を3つ（室内2つ、室外1つ）おいてあるので、これらの温度を表示するコマンドも用意しました。&lt;/p&gt;


&lt;div class=&#34;box&#34; style=&#34;max-width:400&#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope
    itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34; &gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://blog.johtani.info/images/entries/20231208/display-temperature.png&#34;  /&gt;
    &lt;/div&gt;
    &lt;a href=&#34;https://blog.johtani.info/images/entries/20231208/display-temperature.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;それぞれの機器の電池残量（電池の絵文字の右側）を表示してます（書斎が2つあるように見えますが、温湿度計の名前なので、同じ部屋においてあります。。。温度が高いほうがデスクトップPCの近くにある）。
防水の温湿度計があるので、外の気温もわかるのは便利です。&lt;/p&gt;
&lt;h4 id=&#34;owntone&#34;&gt;OwnTone&lt;/h4&gt;
&lt;p&gt;&lt;a href=&#34;https://owntone.github.io/owntone-server/json-api/&#34;&gt;JSONのAPIが用意されています&lt;/a&gt;が、特にクライアントライブラリなどは用意されていません。
今回は、用意されているAPIのごく一部だけを利用するので、net/httpのClientでJSONを投げています。
必要になったらowntone/clientにメソッドを追加していく感じです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/johtani/smarthome/blob/master/subcommand/action/owntone/client.go#L45&#34;&gt;https://github.com/johtani/smarthome/blob/master/subcommand/action/owntone/client.go#L45&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;再生、一時停止、プレイリストの取得、音量指定あたりを実装しています。
音の出力先はAirPlay（次に出てくるYamahaのアンプ）になっています。&lt;/p&gt;
&lt;p&gt;あと、JSON APIのサンプルをChatGPTに渡して、「このJSONをレスポンスに受け取る処理をGoで書いてください」と聞いて処理を書いたりしました（細かいのはどう聞いたか覚えてないけど）。&lt;/p&gt;
&lt;h4 id=&#34;yamahaのアンプ&#34;&gt;YAMAHAのアンプ&lt;/h4&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/rsc-dev/pyamaha/blob/master/doc/YXC_API_Spec_Basic_v2.0.pdf&#34;&gt;ググって探してきましたが&lt;/a&gt;、利用しているYAMAHAのアンプにYXC（Yamaha Extented Control）と呼ばれるAPIが用意されていました。
PDFでAPIの仕様が公開されていたので、この資料とにらめっこしながら、必要そうなAPIを実行するクライアントを実装しました。
こちらは、OwnToneのAPIの処理をすでに書いた後だったので、それをもとに必要な実装を追加しただけです。&lt;/p&gt;
&lt;p&gt;ちなみに、アンプの電源をONにするAPIは実装していません。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;OwnToneで音楽を再生する(AirPlayで信号が送られるみたい)&lt;/li&gt;
&lt;li&gt;シーン選択（Nintendo SwitchやPS5のシーンを用意している）のAPIを呼び出す&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;という処理をすると自動で電源がONになりました。スタンバイしている状態でもAPIはリクエストを受け付ける状態になっているようです。
また、PS5のシーンを呼び出したら、PS5自体も起動するのが便利だったりします。HDMI経由で信号が飛んでるのかな？
「ミーティング開始」や「音楽停止」コマンドではアンプの電源をオフするAPIを呼び出すようにしています。&lt;/p&gt;
&lt;h3 id=&#34;タイポ対応&#34;&gt;タイポ対応&lt;/h3&gt;
&lt;p&gt;「入力間違いしちゃうんです、人間だもの。。。」&lt;/p&gt;
&lt;p&gt;ということで、&lt;a href=&#34;https://github.com/johtani/smarthome/pull/34&#34;&gt;10月にタイポ対応の処理&lt;/a&gt;を入れてみました。
タイポ対応前の実装では、入力された文字列がコマンドの文字列と完全一致した場合にだけ、コマンドが実行される形になっていました。
結構タイポしちゃうので、完全一致で見つからない場合の処理として、入力された文字列とコマンド名のレーベンシュタイン距離が&lt;strong&gt;2&lt;/strong&gt;以下のものを探し、その中で距離が一番小さな値のコマンドを実行する形にしてみました。
コマンド数はたかが知れているのでコマンドのリストを全部チェックする形にしています。
タイポするとこんな感じ。&lt;/p&gt;


&lt;div class=&#34;box&#34; style=&#34;max-width:400&#34; &gt;
  &lt;figure  itemprop=&#34;associatedMedia&#34; itemscope
    itemtype=&#34;http://schema.org/ImageObject&#34;&gt;
    &lt;div class=&#34;img&#34; &gt;
      &lt;img itemprop=&#34;thumbnail&#34; src=&#34;https://blog.johtani.info/images/entries/20231208/did-you-mean.png&#34;  /&gt;
    &lt;/div&gt;
    &lt;a href=&#34;https://blog.johtani.info/images/entries/20231208/did-you-mean.png&#34; itemprop=&#34;contentUrl&#34;&gt;&lt;/a&gt;
  &lt;/figure&gt;
&lt;/div&gt;

&lt;p&gt;レーベンシュタイン距離の計算には&lt;a href=&#34;https://pkg.go.dev/github.com/hbollon/go-edlib#section-readme&#34;&gt;Go-edlib&lt;/a&gt;を利用しています。OSS便利ですね。&lt;/p&gt;
&lt;h2 id=&#34;やってよかったこと&#34;&gt;やってよかったこと&lt;/h2&gt;
&lt;p&gt;ミーティング開始時などのいくつかの手順が簡素化できたので満足しています。
SwitchBotの操作もスマホをわざわざ操作しなくてもいいですし（仕事してるとキーボードとSlackは常に使ってるし）。
また、副次的な効果として、部屋の外から電源を切り忘れていたり、エアコンの切り忘れなどの場合に、スマホから遠隔でまとめた処理ができるのが便利です。&lt;/p&gt;
&lt;h2 id=&#34;今後やってみたいこと&#34;&gt;今後やってみたいこと&lt;/h2&gt;
&lt;p&gt;こういうのがあるともうちょっと便利かもなぁ？というのもあるので今後も気が向いたらコマンドなどを追加していく予定です。
今のところこんなことやりたいなというのがいくつかあります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;キーワードに関連する曲の再生
&lt;ul&gt;
&lt;li&gt;OwnTone自体に検索の仕組みがついているので、キーワードを受け付けてそれに関する曲を流す&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://blog.johtani.info/blog/2020/09/08/update-working-facility/#%E3%83%87%E3%82%B9%E3%82%AF&#34;&gt;Flexispotの操作&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;ミーティングの時は立ち上がっているので、机をスタンディングの状態にしたい&lt;/li&gt;
&lt;li&gt;電子工作しないとだめかも？&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://amzn.to/3GxIw94&#34;&gt;Webカメラ用ライト&lt;/a&gt;の点灯
&lt;ul&gt;
&lt;li&gt;ミーティング時に明かりが必要なのですが、物理スイッチのOn/Off&lt;/li&gt;
&lt;li&gt;これも電子工作かな？&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;定期的に温度をBotに表示させるとと面白いかも？&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;まとめ&#34;&gt;まとめ&lt;/h2&gt;
&lt;p&gt;ということで、新しいプログラミング言語にも触れたし、作業環境も便利になって一石二鳥でした。
新しいプログラミング言語触るのはやっぱたのしいですね。&lt;/p&gt;
</content:encoded>
    </item>
    
  </channel>
</rss>
