<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Software on kymt.me</title><link>https://kymt.me/projects/</link><description>Recent content in Software on kymt.me</description><generator>Hugo</generator><language>ja</language><lastBuildDate>Tue, 25 Jun 2013 00:00:00 +0000</lastBuildDate><atom:link href="https://kymt.me/projects/index.xml" rel="self" type="application/rss+xml"/><item><title>Gauss</title><link>https://kymt.me/projects/gauss/</link><pubDate>Fri, 29 Apr 2011 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/gauss/</guid><description>&lt;p&gt;画面の明るさを変更するソフトウェアです。&#10;主に FPS ゲーマー向けで、設定を変更すると以下の画像のように暗いところが見やすくなります。&#10;正しくは明るさの変更ではなく、画面の暗い範囲の色だけを明るい色にすることで、モニタの明るさを一切変えずに相対的に暗いところを見えやすくします(これをガンマ変更と呼ぶ)。&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://kymt.me/projects/gauss/sample_gamma1.png" alt="ガンマ変更前のサンプル"&gt;&#10;&lt;img src="https://kymt.me/projects/gauss/sample_gamma2.png" alt="ガンマ変更後のサンプル"&gt;&lt;/p&gt;&#10;&lt;h2 id="特徴"&gt;特徴&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;モニタの明るさ(ガンマ値)を変更出来る&lt;/li&gt;&#10;&lt;li&gt;マルチモニタ環境では&lt;strong&gt;モニタごとに異なる明るさを設定可能&lt;/strong&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;キーボードショートカット&lt;/strong&gt;対応&#10;デフォルトは Ctrl + PageUp で明るさアップ、Ctrl + PageDown で明るさダウン、Ctrl + Reset で初期状態(変更可能)&lt;/li&gt;&#10;&lt;li&gt;デスクトップに現在の明るさ設定を保存したショートカットを作成可能&#10;作成したものをダブルクリックですぐにその設定に変更することが出来ます&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="使い方"&gt;使い方&lt;/h2&gt;&#10;&lt;p&gt;起動するとタスクトレイに常駐(起動しっぱなしで待機)します。アイコンを左クリックすると「全体のガンマ調節」ダイアログが開き、右クリックすると設定メニュー(下の画像)が開きます。&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://kymt.me/projects/gauss/gamma_gui.png" alt="タスクトレイアイコンの右クリックメニュー。モニタ1調節・モニタ2調節・全体調整・リセット・キー設定・終了の6項目が並んでいる"&gt;&lt;/p&gt;&#10;&lt;p&gt;右クリックメニューの各項目は次の通りです。&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;「(モニタ名)調節」&lt;/strong&gt; … マルチモニタ環境でのみ、モニタの台数分表示されます。そのモニタだけのガンマを個別に調整するダイアログを開きます&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;全体調整&lt;/strong&gt; … すべてのモニタに共通のガンマを調整する「全体のガンマ調節」ダイアログを開きます(左クリックと同じ)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;リセット&lt;/strong&gt; … 全体・RGB 別のガンマをすべて初期状態に戻します&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;キー設定&lt;/strong&gt; … キーボードショートカットの割り当てを変更します。「明るくする」「暗くする」「リセット」それぞれの「設定」ボタンを押してから好きなキーを押すと、そのキーが割り当てられます。「デフォルト」でデフォルトの割り当てに戻ります&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;終了&lt;/strong&gt; … アプリケーションを終了します&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;「全体のガンマ調節」ダイアログでは、スライダーまたは数値入力で全体のガンマを、「RGB 別」欄で R/G/B それぞれのガンマを個別に調整できます。「ショートカット作成」ボタンを押すと、そのときの設定を保存したショートカットがデスクトップに作成され、以後はダブルクリックするだけで同じ設定に切り替えられます。&lt;/p&gt;&#10;&lt;p&gt;なお、設定ダイアログはマウスカーソルの近くに表示されるようにしてあり、マウスの移動距離が少しでも減るようにしています。&lt;/p&gt;&#10;&lt;h2 id="ダウンロード"&gt;ダウンロード&lt;/h2&gt;&#10;&lt;p&gt;Windows7(32/64bit):&lt;a href="https://assets.kymt.me/files/Gauss/Gauss_latest.zip"&gt;Gauss_latest.zip&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;※1: 動作確認しているのは上記環境だけですが、もしかしたら他の環境でも動作するかもしれません&lt;/p&gt;&#10;&lt;p&gt;※2: グラフィックボードがガンマ変更に対応していない場合動作しません&lt;/p&gt;</description></item><item><title>retry-handler</title><link>https://kymt.me/projects/retry-handler/</link><pubDate>Mon, 13 Jul 2009 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/retry-handler/</guid><description>&lt;p&gt;「失敗したら N 回リトライする」という処理を簡単に書けるようにする Ruby ライブラリです。&lt;code&gt;Proc&lt;/code&gt; に &lt;code&gt;retry&lt;/code&gt; メソッドを生やして、リトライしたい処理をブロックで包むだけで最大回数・待ち時間・対象例外を指定したリトライができます。&lt;/p&gt;&#10;&lt;p&gt;gem として公開していて、Ruby 1.8/1.9 で動きます。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gem install retry-handler&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="使い方"&gt;使い方&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;Proc&lt;/code&gt; のブロック内で例外が発生すると、指定回数まで自動でリトライします。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# なんかエラーの起こるコード&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:max&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:accept_exception&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;:wait&lt;/code&gt; を指定すると失敗のたびに指定秒だけ待ってから次の試行に入ります。処理の流れを可視化したいときは &lt;code&gt;:logger&lt;/code&gt; を付けます。&lt;code&gt;Proc.new&lt;/code&gt; と書くのがダサいときは lambda でも使えます。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:max&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:wait&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;lambda&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:max&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:wait&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:logger&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;STDERR&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;Method&lt;/code&gt; オブジェクトにも &lt;code&gt;retry&lt;/code&gt; が生えるので、既存メソッドをそのままリトライ対応にできます。&lt;code&gt;File.read&lt;/code&gt; の例です。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:read&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./not_found.txt&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="ss"&gt;:accept_exception&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;StandardError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:logger&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Logger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;STDERR&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="内部設計"&gt;内部設計&lt;/h2&gt;&#10;&lt;p&gt;実体は 1 ファイル(&lt;code&gt;lib/retry-handler.rb&lt;/code&gt;)で、中核は &lt;code&gt;RetryHandler.retry_handler&lt;/code&gt; の 1 メソッドです。&lt;code&gt;Proc#retry&lt;/code&gt;・&lt;code&gt;Method#retry&lt;/code&gt;・&lt;code&gt;Kernel#retry_handler&lt;/code&gt; はどれもそこへ委譲するモンキーパッチになっています。&lt;/p&gt;&#10;&lt;p&gt;&lt;span class="diagram-scroll" role="group" tabindex="0" aria-label="retry-handler の呼び出し構造図"&gt;&lt;img src="https://kymt.me/projects/retry-handler/internals.svg" alt="retry-handler の呼び出し構造図"&gt;&lt;/span&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;オプションの既定値&lt;/strong&gt; — &lt;code&gt;:max&lt;/code&gt; は 3 回、&lt;code&gt;:wait&lt;/code&gt; は 1 秒、&lt;code&gt;:timeout&lt;/code&gt; はなし(無制限)、&lt;code&gt;:accept_exception&lt;/code&gt; は同梱の &lt;code&gt;RetryError&lt;/code&gt;、&lt;code&gt;:logger&lt;/code&gt; は &lt;code&gt;Logger.new(nil)&lt;/code&gt;(何も出力しない)です&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;リトライ対象は &lt;code&gt;:accept_exception&lt;/code&gt; で指定した例外だけ&lt;/strong&gt;です。既定値の &lt;code&gt;RetryError&lt;/code&gt; を自分で投げることは普通ないので、実用上は README の例のように &lt;code&gt;:accept_exception =&amp;gt; StandardError&lt;/code&gt; などを渡す形になります。対象外の例外はそのまま素通しです&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;:timeout&lt;/code&gt; を指定すると &lt;code&gt;timeout()&lt;/code&gt; でブロックを包み&lt;/strong&gt;、時間超過時に &lt;code&gt;:accept_exception&lt;/code&gt; の例外を送出させます。タイムアウトも「捕捉対象の例外」に化けるので、リトライループ側は例外の一種として一元的に扱えます&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;Method#retry&lt;/code&gt;&lt;/strong&gt; は引数リストの末尾が &lt;code&gt;Hash&lt;/code&gt; ならそれをオプションとして取り出し、残りをメソッド本体の引数として渡します&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;リトライループ本体(&lt;code&gt;RetryHandler._retry_handler&lt;/code&gt;)の流れです。&lt;code&gt;begin&lt;/code&gt;/&lt;code&gt;rescue&lt;/code&gt;/&lt;code&gt;retry&lt;/code&gt; の素直な作りで、失敗のたびにカウントを進めて &lt;code&gt;sleep&lt;/code&gt; してやり直します。&lt;/p&gt;</description></item><item><title>ruby-qstat</title><link>https://kymt.me/projects/ruby-qstat/</link><pubDate>Sat, 04 Feb 2012 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/ruby-qstat/</guid><description>&lt;p&gt;ゲームサーバー統計取得ツールの &lt;a href="https://qstat.sourceforge.net/"&gt;QStat&lt;/a&gt; を Ruby から使うためのラッパー gem です。qstat コマンドを起動して、マスターサーバーからのサーバー一覧取得や、個別のゲームサーバーへの状態問い合わせの結果を、Ruby のオブジェクトとして返します。&lt;/p&gt;&#10;&lt;p&gt;README 記載の動作要件は Ruby 1.9 と QStat 2.12 です。&lt;/p&gt;&#10;&lt;h2 id="使い方"&gt;使い方&lt;/h2&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gem install ruby-qstat&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;README にある例です。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# マスターサーバーからサーバー一覧を取得&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;QStat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_serverlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hl2master.steampowered.com:27011&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;stm&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;left4dead2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxping&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 個別サーバーの状態を取得&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;QStat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;xxx.yyy.zzz.qqq&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;a2s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 個別サーバーの状態を取得(サーバー情報のみ)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;QStat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;query_serverinfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;xxx.yyy.zzz.qqq&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;a2s&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="内部設計"&gt;内部設計&lt;/h2&gt;&#10;&lt;p&gt;ライブラリ本体は &lt;code&gt;lib/ruby-qstat.rb&lt;/code&gt; の1ファイルで、API は &lt;code&gt;QStat&lt;/code&gt; クラスのクラスメソッド群として提供します(インスタンス化はできない作りです)。qstat コマンドの起動は共通の &lt;code&gt;exec_cmd&lt;/code&gt; に集約されていて、出力の形式によってパース経路が2系統に分かれます。&lt;/p&gt;&#10;&lt;p&gt;&lt;span class="diagram-scroll" role="group" tabindex="0" aria-label="ruby-qstat のコンポーネント構成図"&gt;&lt;img src="https://kymt.me/projects/ruby-qstat/internals.svg" alt="ruby-qstat のコンポーネント構成図"&gt;&lt;/span&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;exec_cmd&lt;/strong&gt; — &lt;code&gt;Open3.popen3&lt;/code&gt; で qstat を起動し、stdout を読むスレッドと stderr を Logger に流すスレッドの2本で出力を回収します。filter ブロックを渡すと、stdout を1行読むごとに判定して、条件が成立した時点で qstat プロセスを &lt;code&gt;TERM&lt;/code&gt; で打ち切れます。qstat のパスは &lt;code&gt;QStat.qstat_path=&lt;/code&gt;、ロガーは &lt;code&gt;QStat.logger=&lt;/code&gt; で差し替えられます&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;XML 系&lt;/strong&gt;(&lt;code&gt;query_serverlist&lt;/code&gt;/&lt;code&gt;qslist&lt;/code&gt;)— &lt;code&gt;-xml&lt;/code&gt; オプション付きで起動して、出力を Nokogiri でパースします。&lt;code&gt;Response&lt;/code&gt; オブジェクトが XML と DOM を保持し、&lt;code&gt;valid?&lt;/code&gt;(先頭の server 要素の status が UP か)や &lt;code&gt;to_ror&lt;/code&gt;(ActiveSupport の &lt;code&gt;Hash.from_xml&lt;/code&gt; によるハッシュ化)を提供します&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;テキスト系&lt;/strong&gt;(&lt;code&gt;query&lt;/code&gt;/&lt;code&gt;query_serverinfo&lt;/code&gt;)— &lt;code&gt;-P&lt;/code&gt;(プレイヤー情報)・&lt;code&gt;-R&lt;/code&gt;(サーバールール)付きのプレーンテキスト出力を正規表現でパースして &lt;code&gt;ServerInfo&lt;/code&gt; を作ります。「no response」「DOWN」の応答もヘッダ行の正規表現で判定します&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;結果オブジェクト&lt;/strong&gt; — &lt;code&gt;ServerInfo&lt;/code&gt;(アドレス・サーバー名・マップ・人数・ping・status など)、&lt;code&gt;PlayerInfo&lt;/code&gt;(名前・frags・プレイ時間。qstat の「1h 2m 3s」のような時間表記を秒に換算するパーサ入り)、&lt;code&gt;Rule&lt;/code&gt;(protocol・gamedir・secure・version・game_tag などのサーバールール)。&lt;code&gt;ServerInfo#playing_time&lt;/code&gt; は、いちばん長くプレイしているプレイヤーの時間を返します&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;ゲームタイプ推定&lt;/strong&gt; — &lt;code&gt;ServerInfo#suggest_game_type&lt;/code&gt; は、gametype が取れなかったときにサーバールールの game_tag(&lt;code&gt;@&lt;/code&gt; 以降のカンマ区切りタグ)から推定し、それもなければ &lt;code&gt;unknown&lt;/code&gt; を返します&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;文字コード対策&lt;/strong&gt; — qstat には常に &lt;code&gt;-utf8&lt;/code&gt; を付け、さらに &lt;code&gt;String#force_convert_to&lt;/code&gt;(UTF-16BE を経由して不正なバイトを &lt;code&gt;?&lt;/code&gt; に置換してから戻す monkey patch)で、サーバー名などに混ざる壊れたバイト列を無害化します&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;サーバー一覧取得(&lt;code&gt;query_serverlist&lt;/code&gt;)の流れです。maxping を超えるサーバーが現れた時点で qstat を打ち切る作りで、途中で切れた XML はそのまま Nokogiri に渡してパースします。&lt;/p&gt;</description></item><item><title>spraymaker</title><link>https://kymt.me/projects/spraymaker/</link><pubDate>Sat, 18 Jun 2011 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/spraymaker/</guid><description>&lt;p&gt;Valve の Source Engine 向けのスプレー(ゲーム内で壁などに貼る自作画像)を簡単に作成するためのツールです。&#10;画像ファイルをドラッグドロップするだけで変換できます。&lt;/p&gt;&#10;&lt;h2 id="詳細"&gt;詳細&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;256x256 サイズのスプレーを生成します&lt;/li&gt;&#10;&lt;li&gt;透過情報付きのファイルに対応しています&lt;/li&gt;&#10;&lt;li&gt;高品質の圧縮フォーマットで出力します(VXT5)&lt;/li&gt;&#10;&lt;li&gt;高画質設定のための VTF Flags を自動で付加します(NOMOD, NOLOD)&lt;/li&gt;&#10;&lt;li&gt;200以上の画像形式の読み込みに対応しています(ImageMagick で読み込める画像であればなんでも)&lt;/li&gt;&#10;&lt;li&gt;元画像のアスペクト比を維持します、その際空いた場所は透過ピクセルで自動で埋めてくれます&lt;/li&gt;&#10;&lt;li&gt;ディレクトリが指定された場合は再帰的(サブフォルダの中まで全部たどって)にすべてのファイルを変換します&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="実行例"&gt;実行例&lt;/h2&gt;&#10;&lt;p&gt;元画像&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://kymt.me/projects/spraymaker/fujisan.jpg" alt="元画像: 桜の枝ごしに、雪をかぶった富士山と湖が見えている写真"&gt;&lt;/p&gt;&#10;&lt;p&gt;変換後の VTF ファイル&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://kymt.me/projects/spraymaker/fujisan_vtf.jpg" alt="変換後のVTFファイル"&gt;&lt;/p&gt;&#10;&lt;p&gt;(transparent)というのが実際には透明な部分になります。&lt;/p&gt;&#10;&lt;p&gt;ゲーム内&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://kymt.me/projects/spraymaker/fujisan_ingame.jpg" alt="ゲーム内: 部屋の壁に、富士山の写真がスプレーとして貼られている様子"&gt;&lt;/p&gt;&#10;&lt;h2 id="ダウンロード"&gt;ダウンロード&lt;/h2&gt;&#10;&lt;p&gt;Windows7(32/64bit):&lt;a href="https://assets.kymt.me/files/spraymaker/spraymaker_latest.zip"&gt;spraymaker_latest.zip&lt;/a&gt; (&lt;a href="https://assets.kymt.me/files/spraymaker/spraymaker_latest.zip.asc"&gt;PGP&lt;/a&gt;)&lt;/p&gt;&#10;&lt;p class="note"&gt;※1 他の環境ではテストしていませんが、もしかしたら動くかもしれません。&lt;/p&gt;&#10;&#10;&lt;p class="note"&gt;※2 動作には&lt;strong&gt;ImageMagickがインストールされている必要があります&lt;/strong&gt;。&lt;a href="https://legacy.imagemagick.org/script/download.php"&gt;ImageMagick 6 系の配布ページ&lt;/a&gt;から Q16 版をインストールしてください。(ImageMagick は画像処理ソフトウェアです)&lt;/p&gt;</description></item><item><title>vrowser</title><link>https://kymt.me/projects/vrowser/</link><pubDate>Mon, 06 Feb 2012 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/vrowser/</guid><description>&lt;p&gt;Left 4 Dead 2 や Team Fortress 2 など、HalfLife2 系(Source Engine)ゲームのサーバーブラウザを作るための Ruby ライブラリです。ゲームサーバー統計取得ツールの &lt;a href="https://qstat.sourceforge.net/"&gt;QStat&lt;/a&gt; をラップして、サーバー一覧の取得・データベースへの記録・JSON API での配信までを一式でやります。&lt;/p&gt;&#10;&lt;p&gt;gem として公開していて、コマンドラインツールも同梱しています。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gem install vrowser&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vrowser sample &lt;span class="c1"&gt;# サンプル設定ファイルを生成&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vrowser -f config.yml fetch &lt;span class="c1"&gt;# マスターサーバーからサーバー一覧を取得&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vrowser -f config.yml update &lt;span class="c1"&gt;# 登録済みサーバーの状態を更新&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vrowser -f config.yml daemon &lt;span class="c1"&gt;# 常駐デーモンとして起動&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="内部設計"&gt;内部設計&lt;/h2&gt;&#10;&lt;p&gt;コンポーネント構成です。中核の &lt;code&gt;Vrowser&lt;/code&gt; クラスが QStat の呼び出しと Sequel(ORM)経由の DB 記録を束ねていて、CLI・常駐デーモン・Sinatra アプリのどこからでも同じ処理を呼べる作りになっています。&lt;/p&gt;&#10;&lt;p&gt;&lt;span class="diagram-scroll" role="group" tabindex="0" aria-label="vrowser のコンポーネント構成図"&gt;&lt;img src="https://kymt.me/projects/vrowser/internals.svg" alt="vrowser のコンポーネント構成図"&gt;&lt;/span&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;Vrowser&lt;/strong&gt;(&lt;code&gt;lib/vrowser.rb&lt;/code&gt;)— &lt;code&gt;fetch&lt;/code&gt;(マスターサーバーからサーバー一覧を取得して DB に登録)、&lt;code&gt;update&lt;/code&gt;(登録済みサーバーを全件クエリして状態更新)、&lt;code&gt;clear&lt;/code&gt;(DOWN したサーバーの削除)を提供します&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;VrowserModel::Servers&lt;/strong&gt; — Sequel のモデル。スキーマ定義もコード内に持っていて、テーブルがなければ初回接続時に作ります。&lt;code&gt;host&lt;/code&gt; カラムが unique キーです&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Vrowser::HTTPDaemon&lt;/strong&gt;(&lt;code&gt;lib/vrowser/http_daemon.rb&lt;/code&gt;)— WEBrick で JSON API(&lt;code&gt;/api/updated/json&lt;/code&gt;・&lt;code&gt;/api/connected/json&lt;/code&gt;)と &lt;code&gt;public_html/&lt;/code&gt; の一覧 UI(jQuery DataTables 製)を配信しつつ、別スレッドで取得ループを回します&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;plugins/&lt;/strong&gt; — ゲーム別の追加ロジック。同梱の &lt;code&gt;l4d2.rb&lt;/code&gt; は、サーバー情報からゲームタイプが判定できないときにサーバー名の正規表現マッチ(&lt;code&gt;co-?op&lt;/code&gt;、&lt;code&gt;versus&lt;/code&gt;、&lt;code&gt;scavenge&lt;/code&gt; など)でゲームタイプを推定します&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;デーモンの更新サイクルはこうなっています。マスターサーバーへの問い合わせは重いので、一覧の再取得は約30分に1回、その間は登録済みサーバーへの個別クエリ(A2S)を150秒間隔で回す二段構えです。&lt;/p&gt;</description></item><item><title>mwheel++</title><link>https://kymt.me/projects/mwheel-plus/</link><pubDate>Wed, 11 May 2011 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/mwheel-plus/</guid><description>&lt;p&gt;マウスホイールイベントを、アクティブでないウインドウにも送信できるようにします。&#10;シンプルなプログラムですがとても便利です(Mac OSX や Linux の GNOME っぽい挙動になる)&lt;/p&gt;&#10;&lt;h2 id="詳細"&gt;詳細&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;タスクトレイアイコンをダブルクリックすることで一時的に無効化させることも出来ます。(再度ダブルクリックで有効化)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="ダウンロード"&gt;ダウンロード&lt;/h2&gt;&#10;&lt;p&gt;Windows7(32/64bit):&lt;a href="https://assets.kymt.me/files/wheel&amp;#43;&amp;#43;/wheel&amp;#43;&amp;#43;%2820110701%29.zip"&gt;mwheel_plus(20110701).zip&lt;/a&gt; (&lt;a href="https://assets.kymt.me/files/wheel&amp;#43;&amp;#43;/wheel&amp;#43;&amp;#43;%2820110701%29.zip.asc"&gt;PGP&lt;/a&gt;)&lt;/p&gt;&#10;&lt;p&gt;他の環境ではテストしていませんが、もしかしたら動くかもしれません。&lt;/p&gt;</description></item><item><title>GyazoIM</title><link>https://kymt.me/projects/gyazoim/</link><pubDate>Sun, 15 May 2011 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/gyazoim/</guid><description>&lt;p&gt;Windows のタスクトレイに常駐(起動しっぱなしで待機)して、画面のスクリーンショットを撮って &lt;a href="https://gyazo.com/"&gt;gyazo.com&lt;/a&gt; にアップロード（またはローカルで開くだけ）するツールです。素の Gyazo クライアント（当時の gyazowin）に、キーボードショートカットとウィンドウ単位の撮影、それに作り込んだ範囲選択オーバーレイ(画面に重ねて表示する層)を足したもの。&lt;/p&gt;&#10;&lt;h2 id="範囲選択のデモ"&gt;範囲選択のデモ&lt;/h2&gt;&#10;&lt;p&gt;選択 UI は、先に単体のプロトタイプとして作ってから GyazoIM に組み込みました。そのプロトタイプ（&lt;a href="https://github.com/kimoto/selector"&gt;kimoto/selector&lt;/a&gt;）を動かしたときの動画がこれです。全画面に半透明のオーバーレイを敷いて、ドラッグした範囲をリアルタイムにサイズ表示しています。&lt;/p&gt;&#10;&lt;div class="video-embed"&gt;&#10; &lt;iframe src="https://www.youtube-nocookie.com/embed/qGPD4zhf-2I" title="GyazoIM の範囲選択 UI プロトタイプ" loading="lazy" allowfullscreen&gt;&lt;/iframe&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;h2 id="3つの撮り方"&gt;3つの撮り方&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;範囲選択&lt;/strong&gt; — 全モニタにまたがる透明オーバーレイを出してドラッグで範囲を決める。下の一覧のキー操作で、正方形ロック・ウィンドウ端へのスナップ・範囲ごと移動・ホイールでの拡縮ができる&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;ウィンドウ単位（インスペクト）&lt;/strong&gt; — カーソルの下にあるウィンドウをハイライトし、クリックでそのウィンドウ全体を自動で範囲取りして撮る&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;ホットキー&lt;/strong&gt; — グローバルホットキーで即撮影。既定は F9 でアクティブウィンドウ、Ctrl+F9 でデスクトップ全体。設定ダイアログで変更でき、&lt;code&gt;config.ini&lt;/code&gt; に保存される&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="範囲選択中のキー操作"&gt;範囲選択中のキー操作&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;操作&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;効果&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;ドラッグ&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;範囲を選ぶ（カーソル脇に幅×高さを表示）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Ctrl&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;正方形にロック（縦を横に合わせる）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Shift&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;ウィンドウの端にスナップ（50px 以内で吸着）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;右ドラッグ&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;選んだ範囲を、サイズを変えずに移動&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;マウスホイール&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;範囲を四方に均等に拡大・縮小&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;何かキー&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;キャンセル&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;範囲を離すと、10×10 より大きければビープを鳴らして撮影します。&lt;/p&gt;&#10;&lt;h2 id="アップロードと画像処理"&gt;アップロードと画像処理&lt;/h2&gt;&#10;&lt;p&gt;出力先はタスクトレイの右クリックメニューで切り替えます。&lt;strong&gt;gyazo.com にアップロード&lt;/strong&gt;すると、返ってきた URL をクリップボードにコピーしてブラウザで開きます（&lt;code&gt;upload.cgi&lt;/code&gt; への multipart POST。認証や SSL もオプションで対応）。もう一方の&lt;strong&gt;ローカルプレビュー&lt;/strong&gt;は、撮った PNG をそのまま開くだけ。画像は &lt;code&gt;BitBlt&lt;/code&gt; で取り込み、最終的には ImageMagick で減色してから保存していました。&lt;/p&gt;&#10;&lt;h2 id="名前について"&gt;名前について&lt;/h2&gt;&#10;&lt;p&gt;「IM」は、当時の PeerCast クライアント「PeerCastIM」の名前にならったもので、「ちょっと手を加えた版」というくらいの意味です（ただし中身は 1 から書いています）。当時のサイトには「完成しているけれどバグ取りが面倒で放置中」とも書いていて、配布は GitHub のソースのみ、コンパイル済みの実行ファイルは配っていませんでした。&lt;/p&gt;</description></item><item><title>matchdata-highlight</title><link>https://kymt.me/projects/matchdata-highlight/</link><pubDate>Thu, 24 Nov 2011 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/matchdata-highlight/</guid><description>&lt;p&gt;正規表現のマッチ結果(Ruby の &lt;code&gt;MatchData&lt;/code&gt;)に &lt;code&gt;highlight&lt;/code&gt; メソッドを生やす小さな gem です。マッチした箇所の前後にマーカー文字列を挿し込んだ文字列を返します。マーカーを省略すると &lt;a href="https://github.com/flori/term-ansicolor"&gt;term/ansicolor&lt;/a&gt; の ANSI エスケープシーケンスが入り、ターミナル上でマッチ箇所が赤く表示されます。&lt;/p&gt;&#10;&lt;p&gt;ライブラリ本体は 1 ファイル約 40 行で、ソース末尾に呼び出し例がコメントで残っています。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;foobar foobar BAZ&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/(foobar)/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;highlight&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="sr"&gt;/foobar/&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;foobar foobar BAZ&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;lt;B&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;/B&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="sr"&gt;/foobar/&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;foobar foobar BAZ&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="sr"&gt;/foobar/&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;foobar foobar BAZ&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:begin&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;B&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:end&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;/B&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="sr"&gt;/foobar/&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;highlight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;foobar foobar BAZ&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;B&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;lt;/B&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="内部設計"&gt;内部設計&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;MatchData&lt;/code&gt; と &lt;code&gt;Regexp&lt;/code&gt; の 2 クラスへのモンキーパッチだけでできています。処理の流れです。&lt;/p&gt;&#10;&lt;p&gt;&lt;span class="diagram-scroll" role="group" tabindex="0" aria-label="matchdata-highlight の処理フロー図"&gt;&lt;img src="https://kymt.me/projects/matchdata-highlight/internals.svg" alt="matchdata-highlight の処理フロー図"&gt;&lt;/span&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;MatchData#highlight&lt;/strong&gt; — 引数の形だけを判定する入口です。引数なしか Hash ならそのまま、位置引数 2 つなら &lt;code&gt;{:begin =&amp;gt; args[0], :end =&amp;gt; args[1]}&lt;/code&gt; に詰め替えて &lt;code&gt;_highlight&lt;/code&gt; に渡します&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;MatchData#_highlight&lt;/strong&gt; — 実体です。マーカーのデフォルトは &lt;code&gt;:begin&lt;/code&gt; が &lt;code&gt;Term::ANSIColor.red&lt;/code&gt;、&lt;code&gt;:end&lt;/code&gt; が &lt;code&gt;Term::ANSIColor.clear&lt;/code&gt; で、渡されたオプションを &lt;code&gt;merge&lt;/code&gt; して上書きします&lt;/li&gt;&#10;&lt;li&gt;マーカーの挿入は、対象文字列(&lt;code&gt;MatchData#string&lt;/code&gt;)を &lt;code&gt;dup&lt;/code&gt; した上で、&lt;code&gt;buf[self.begin(0), 0] = marker_begin&lt;/code&gt; とマッチ開始位置への幅ゼロ代入で行います。終了マーカーの挿入位置は &lt;code&gt;self.end(0) + marker_begin.size&lt;/code&gt; で、先に挿し込んだ開始マーカーの分だけ後ろにずらしています&lt;/li&gt;&#10;&lt;li&gt;位置は &lt;code&gt;begin(0)&lt;/code&gt;/&lt;code&gt;end(0)&lt;/code&gt;、つまりマッチ全体だけを対象にしていて、キャプチャグループ単位のハイライトや複数マッチの一括ハイライトはしません&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Regexp#highlight(target, *args)&lt;/strong&gt; — &lt;code&gt;self.match(target)&lt;/code&gt; を試して、マッチしたら &lt;code&gt;MatchData#highlight&lt;/code&gt; に委譲、マッチしなければ &lt;code&gt;target&lt;/code&gt; をそのまま返します。マッチの有無を気にせず文字列が返ってくる形です&lt;/li&gt;&#10;&lt;li&gt;gem の骨組みは jeweler 生成の雛形です(gemspec は Rakefile から生成、バージョン 0.1.0)。README は雛形のままで、テストも shoulda の雛形が残っています&lt;/li&gt;&#10;&lt;/ul&gt;</description></item><item><title>l4d2.kymt.me</title><link>https://kymt.me/projects/l4d2/</link><pubDate>Fri, 18 Nov 2011 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/l4d2/</guid><description>&lt;p&gt;HalfLife2 エンジン互換のサーバーブラウザ（L4D2 版）。&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://assets.kymt.me/projects/l4d2-serverbrowser-qm-live.png" alt="L4D2 ServerBrowser - QuickMatch"&gt;&lt;/p&gt;&#10;&lt;p&gt;同じアプリは今も &lt;a href="https://game.kymt.me/qm/"&gt;game.kymt.me/qm&lt;/a&gt; に残っていて、上はそのスクリーンショット(サーバー一覧の表示データは2017年で凍結)。入りたいサーバーの行をダブルクリックするとそのままログインできる、というのがページ冒頭の説明にある通りの使い方。&lt;/p&gt;&#10;&lt;p&gt;→ &lt;a href="https://l4d2.kymt.me/"&gt;https://l4d2.kymt.me/&lt;/a&gt;&lt;/p&gt;</description></item><item><title>GSD_crosshair</title><link>https://kymt.me/projects/gsd-crosshair/</link><pubDate>Fri, 29 Apr 2011 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/gsd-crosshair/</guid><description>&lt;p&gt;ゲーム画面の中央に、好きな画像のクロスヘア(照準マーク)を常に表示するソフトです。DirectX(32bit)を利用しているソフトウェアで動作します。DirectX フック(他プログラムの動作に割り込む仕掛け)ライブラリの GSD を利用しています。&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://kymt.me/projects/gsd-crosshair/crosshair_sample1.png" alt="クロスヘアのサンプル1"&gt;&#10;&lt;img src="https://kymt.me/projects/gsd-crosshair/crosshair_sample2.png" alt="クロスヘアのサンプル2"&gt;&#10;&lt;img src="https://kymt.me/projects/gsd-crosshair/crosshair_sample3.png" alt="クロスヘアのサンプル3"&gt;&lt;/p&gt;&#10;&lt;h2 id="ダウンロード"&gt;ダウンロード&lt;/h2&gt;&#10;&lt;p&gt;Windows7(32/64bit):&lt;a href="https://assets.kymt.me/files/GSD_crosshair/GSD_crosshair%2820110701%29.zip"&gt;GSD_crosshair(20110701).zip&lt;/a&gt; (&lt;a href="https://assets.kymt.me/files/GSD_crosshair/GSD_crosshair%2820110701%29.zip.asc"&gt;PGP&lt;/a&gt;)&lt;/p&gt;&#10;&lt;p&gt;DirectX10,11または64bit のソフトウェアには描画できないっぽいです。&#10;他の環境ではテストしていませんが、もしかしたら動くかもしれません。&lt;/p&gt;&#10;&lt;h2 id="カスタマイズ"&gt;カスタマイズ&lt;/h2&gt;&#10;&lt;p&gt;crosshair.png が中央に描画されますので、好みの画像に差し替えることもできます。&#10;アルファチャンネルを適切に設定すれば指定の領域を透過させることもできます。&#10;初期状態の crosshair.png は Poohaltea さん作のものをサイズ調整して同梱しています。&lt;/p&gt;&#10;&lt;h2 id="vacチート検出との関係"&gt;VAC(チート検出)との関係&lt;/h2&gt;&#10;&lt;p&gt;詳しくは&lt;a href="https://kymt.me/gaming/gsd_report/"&gt;GSD の仕組みについてのレポート&lt;/a&gt;を参照してください。&lt;/p&gt;</description></item><item><title>vpk</title><link>https://kymt.me/projects/vpk/</link><pubDate>Fri, 13 Jul 2012 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/vpk/</guid><description>&lt;p&gt;VPK(&amp;quot;Valve Pak&amp;quot;)は、Source Engine(Valve のゲームエンジン)で使われるアーカイブ形式です(&lt;a href="https://developer.valvesoftware.com/wiki/VPK"&gt;Valve Developer Wiki の解説&lt;/a&gt;)。この vpk は、その VPK ファイルを Ruby で読み書きするライブラリで、VPK ファイルの展開と、ディレクトリを VPK にまとめる圧縮の両方ができます。&lt;/p&gt;&#10;&lt;p&gt;gem として公開していて、コマンドラインツールも同梱しています。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gem install vpk&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;vpk&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# VPK ファイルを展開する&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;VPK&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;VPKFile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./path_to.vpk&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;extract_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ディレクトリを VPK にまとめる&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;VPK&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;VPKFile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;archive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./path_to_dir&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./archive.vpk&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;コマンドラインからはこう使います。&lt;code&gt;-c&lt;/code&gt; はディレクトリと同名の &lt;code&gt;.vpk&lt;/code&gt; を作り、&lt;code&gt;-x&lt;/code&gt; はカレントディレクトリに展開します。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vpk -c some_dir &lt;span class="c1"&gt;# some_dir を some_dir.vpk に圧縮&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;vpk -x some_file.vpk &lt;span class="c1"&gt;# カレントディレクトリに展開&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="内部設計"&gt;内部設計&lt;/h2&gt;&#10;&lt;p&gt;実装しているのは VPK フォーマットのバージョン 1 です。ファイルは「12 バイトのヘッダ」「ファイル一覧を持つディレクトリ部」「中身を連結したデータ部」の三層でできています。&lt;/p&gt;&#10;&lt;p&gt;&lt;span class="diagram-scroll" role="group" tabindex="0" aria-label="ヘッダ(12 バイト)のバイトレイアウト"&gt;&lt;img src="https://kymt.me/projects/vpk/format-header.svg" alt="ヘッダ(12 バイト)のバイトレイアウト"&gt;&lt;/span&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;ディレクトリ部は「拡張子 → パス → ファイル名」の 3 段ツリーです。各要素は NUL 終端文字列で、空文字列がその段の終端。ルート直下のファイルはパスを半角スペース 1 文字で表します&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ディレクトリ部の並び(例):&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;vtf&amp;#34; ← 第 1 段: 拡張子&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;materials/vgui&amp;#34; ← 第 2 段: パス&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;logo&amp;#34; + エントリ情報(18 バイト) ← 第 3 段: ファイル名&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;&amp;#34; ← ファイル名の終端&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;&amp;#34; ← パスの終端&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;#34;&amp;#34; ← 拡張子の終端 = ディレクトリ部おわり&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#10;&lt;li&gt;ファイルごとのエントリ情報は 18 バイト固定で、CRC・オフセット・長さなどを持ちます。データ部内の位置は &lt;code&gt;entry_offset&lt;/code&gt;(ディレクトリ部の終端からの相対位置)で参照します。データ部はその名の通り、各ファイルのペイロードをそのまま連結したものです&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;&lt;span class="diagram-scroll" role="group" tabindex="0" aria-label="ディレクトリエントリ(18 バイト)のバイトレイアウト"&gt;&lt;img src="https://kymt.me/projects/vpk/format-entry.svg" alt="ディレクトリエントリ(18 バイト)のバイトレイアウト"&gt;&lt;/span&gt;&lt;/p&gt;</description></item><item><title>SQCN</title><link>https://kymt.me/projects/sqcn/</link><pubDate>Mon, 06 Jun 2011 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/sqcn/</guid><description>&lt;p&gt;Steam のユーザー名(表示名)をワンクリックで切り替えるためのツールです(&lt;strong&gt;S&lt;/strong&gt;team &lt;strong&gt;Q&lt;/strong&gt;uick &lt;strong&gt;C&lt;/strong&gt;hange &lt;strong&gt;N&lt;/strong&gt;ame の略)。よく使う名前を登録しておいて一覧から選んで適用できます。C#/.NET Framework 製。&lt;/p&gt;&#10;&lt;h2 id="機能概略"&gt;機能概略&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;適用: 選択された名前に変更します&lt;/li&gt;&#10;&lt;li&gt;すべて適用: 配信の視聴者参加用(普段の名前の履歴を見せないため)。設定された名前すべてに一度変更してペルソナ履歴を消します(10個あれば履歴完全に消えます)&lt;/li&gt;&#10;&lt;li&gt;履歴消去: 過去に使った名前を他人から見えないようにするため、ペルソナ履歴を1から10の数字で上書きします(「すべて適用」と同じ目的)&lt;/li&gt;&#10;&lt;li&gt;通常オフラインだと名前変更できないが、このツールだとオフラインのまま名前変更できる(ゲームに入る前に名前を決めておくため)&lt;/li&gt;&#10;&lt;li&gt;標準の UI からだと1文字の名前に変更できない(最低2文字必須)が、このツールだと可能&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;いずれも Steam の表示名を自分で設定し直すだけの機能で、変更できるのは自分のアカウントの表示名だけです。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Steam を起動している状態で実行する必要があります。&lt;/strong&gt; なお、なんか Steam のアップデートでうまく動かなくなったので放置中です。&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://assets.kymt.me/gaming/misc/SQCN.png" alt="SQCN のスクリーンショット"&gt;&lt;/p&gt;&#10;&lt;h2 id="ダウンロード"&gt;ダウンロード&lt;/h2&gt;&#10;&lt;p&gt;&lt;a href="https://assets.kymt.me/gaming/misc/SQCN_20110606_2.zip"&gt;SQCN(2011/06/06)&lt;/a&gt;&lt;/p&gt;</description></item><item><title>gtasks</title><link>https://kymt.me/projects/gtasks/</link><pubDate>Fri, 03 Feb 2012 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/gtasks/</guid><description>&lt;p&gt;Google Tasks をターミナルから操作するコマンドラインクライアント(Ruby gem)です。タスクの一覧・追加・完了・削除・完了済みの一括クリアができて、完了と削除は番号指定のほかタスク名の正規表現マッチによる一括処理にも対応しています。&lt;/p&gt;&#10;&lt;p&gt;Google の OAuth2 認証もコマンド内で完結する作りで、初回はローカルに小さな WEBrick サーバーを立ててコールバックを受け取り、取得したトークンをファイルに保存して使い回します。&lt;/p&gt;&#10;&lt;h2 id="内部設計"&gt;内部設計&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;lib/gtasks.rb&lt;/code&gt; 1ファイルに3つのクラスが層になって入っています。REST 呼び出しと認証を受け持つ &lt;code&gt;GoogleTaskAPI&lt;/code&gt;、それを使いやすいメソッドにまとめた &lt;code&gt;GoogleTask&lt;/code&gt;、ターミナル表示に整形する &lt;code&gt;GoogleTaskCLI&lt;/code&gt; です。&lt;/p&gt;&#10;&lt;p&gt;&lt;span class="diagram-scroll" role="group" tabindex="0" aria-label="gtasks のコンポーネント構成図"&gt;&lt;img src="https://kymt.me/projects/gtasks/internals.svg" alt="gtasks のコンポーネント構成図"&gt;&lt;/span&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;bin/gtasks&lt;/strong&gt; — サブコマンドは &lt;code&gt;list&lt;/code&gt;/&lt;code&gt;lists&lt;/code&gt;/&lt;code&gt;add&lt;/code&gt;/&lt;code&gt;clear&lt;/code&gt;/&lt;code&gt;done&lt;/code&gt;/&lt;code&gt;delete&lt;/code&gt;/&lt;code&gt;choice&lt;/code&gt;(引数なしなら &lt;code&gt;list&lt;/code&gt;)。&lt;code&gt;done&lt;/code&gt; と &lt;code&gt;delete&lt;/code&gt; は引数が数字なら一覧の番号指定、それ以外は正規表現として解釈し、タスク名がマッチしたものをまとめて処理します。API の client_id/client_secret は &lt;a href="https://github.com/cho45/pit"&gt;pit&lt;/a&gt; gem 経由で取得していて、コードには書かない作りです&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;GoogleTaskCLI&lt;/strong&gt; — 表示用の層。タスクを &lt;code&gt;[0] ☐ タイトル&lt;/code&gt; の形式で番号付き表示し、完了済みは ☑ にします。&lt;code&gt;choice&lt;/code&gt; はタスク一覧からランダムに1件選んで表示します&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;GoogleTask&lt;/strong&gt; — 番号指定の &lt;code&gt;done&lt;/code&gt;/&lt;code&gt;delete&lt;/code&gt; と、タイトルの正規表現マッチで一括処理する &lt;code&gt;done_if&lt;/code&gt;/&lt;code&gt;delete_if&lt;/code&gt; を持つ中間層。「完了」はタスクの &lt;code&gt;status&lt;/code&gt; を &lt;code&gt;completed&lt;/code&gt; に更新することで実現しています&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;GoogleTaskAPI&lt;/strong&gt; — Google Tasks API v1 の薄いラッパー。タスクとタスクリストそれぞれの取得・作成・更新・削除、完了済みタスクの一括クリアをひととおり持ち、HTTP は &lt;a href="https://rubygems.org/gems/oauth2"&gt;oauth2&lt;/a&gt; gem のアクセストークン経由で呼びます&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;認証まわりはこうなっています。初回は認可 URL を表示してユーザーにブラウザで開いてもらい、ローカルの WEBrick サーバー(既定は 9999 番ポートの &lt;code&gt;/oauth_callback&lt;/code&gt;)で認可コードを受け取ってトークンに交換、&lt;code&gt;~/.google_tasks.yml&lt;/code&gt; に YAML で保存します。&lt;code&gt;access_type=offline&lt;/code&gt; でリフレッシュトークンも受け取っているので、2回目以降はファイルから読み込んでリフレッシュするだけで、ブラウザは開きません。&lt;/p&gt;</description></item><item><title>GetFriendAvators</title><link>https://kymt.me/projects/getfriendavators/</link><pubDate>Tue, 24 May 2011 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/getfriendavators/</guid><description>&lt;p&gt;Steam のすべてのフレンドのプロフィールアイコン(大きい方)を取得して、まとめて保存するツールです(デフォルトの ? アイコンの人がいればそれも取得可能)。&lt;/p&gt;&#10;&lt;p&gt;Open Steamworks + Windows .NET の習作として作りました。Steam を起動している状態で実行してないとエラーで落ちます。&lt;/p&gt;&#10;&lt;h2 id="ダウンロード"&gt;ダウンロード&lt;/h2&gt;&#10;&lt;p&gt;&lt;a href="https://assets.kymt.me/gaming/misc/GetFriendAvators_20110524.zip"&gt;GetFriendAvators(2011/05/24)&lt;/a&gt; (&lt;a href="https://assets.kymt.me/gaming/misc/getfriendavators_ss.png"&gt;スクリーンショット&lt;/a&gt;)&lt;/p&gt;&#10;&lt;p&gt;→ &lt;a href="https://gist.github.com/986866"&gt;ソースコード (gist)&lt;/a&gt;&lt;/p&gt;</description></item><item><title>yp.kymt.me</title><link>https://kymt.me/projects/yp/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/yp/</guid><description>&lt;p&gt;Twitch や Livetube などの配信サイトの番組一覧を、PeerCast 用クライアント(pcyplite や PecaRecorder)に登録できる Yellow Page 形式で配信するサービスです。5分間隔で更新。&lt;/p&gt;&#10;&lt;p&gt;PeerCast のツールで、PeerCast 以外の配信サイトの番組もまとめてチェックできるようにする、という発想でした。&lt;/p&gt;&#10;&lt;h2 id="リスト"&gt;リスト&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;Twitch&lt;/strong&gt; — twitch.tv の番組一覧 YP(LoL, HoN, SC2 とか CS1.6、Mario64 の TA とか色々)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Own3d&lt;/strong&gt; — own3d.tv の番組一覧 YP(ほぼ LoL)※2013/01/31 でサービス終了した&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Livetube&lt;/strong&gt; — livetube.cc の番組一覧 YP(2021/05/02 に停止)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="変更履歴原文ママ"&gt;変更履歴(原文ママ)&lt;/h2&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2021/05/02: Fix twitch cralwer and stop livetube YP&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2015/06/24: livetubeのcrawlに失敗していたので直した&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2014/10/06: /livetubeindex.txtってところへのアクセスが結構あるので対応してみた&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2014/06/20: 関連サイトのニコ生YPさんがDyndnsの問題で参照できなくなってたのでdeleteした(HTMLタグ的な意味で)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2013/02/03: own3d.tvがサービス終了したのでown3d.tvのYPは更新停止することに&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2012/11/17: Twitch.tvのサイト変更で動作しなくなっていたのをDeveloper JSON APIを利用するように変更してfix&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2012/07/12: livetube.ccサイト変更でうまく取得できてなかったのを修正&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2012/07/12: twitch.tvサイトリニューアルでうまく取得できてなかったのを修正&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2012/07/12: own3dサイトリニューアルでうまく取得できてなかったのを修正&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2011/未明: 公開&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;対象サイトのリニューアルのたびにクローラーを直し続けた、10年間の記録でもあります。&lt;/p&gt;&#10;&lt;p&gt;→ &lt;a href="https://yp.kymt.me/"&gt;yp.kymt.me&lt;/a&gt;(当時のページがそのまま残っています)&lt;/p&gt;</description></item><item><title>kaiwa</title><link>https://kymt.me/projects/kaiwa/</link><pubDate>Tue, 25 Jun 2013 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/kaiwa/</guid><description>&lt;p&gt;飲み会とかの会話生成アプリ。&lt;/p&gt;&#10;&lt;p&gt;→ &lt;a href="https://kymt.me/app/kaiwa"&gt;https://kymt.me/app/kaiwa&lt;/a&gt;&lt;/p&gt;</description></item><item><title>mizuho_bank</title><link>https://kymt.me/projects/mizuho_bank/</link><pubDate>Fri, 17 Jun 2011 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/mizuho_bank/</guid><description>&lt;p&gt;みずほダイレクト(みずほ銀行のインターネットバンキング)に Ruby からアクセスするためのライブラリです。&lt;a href="https://github.com/sparklemotion/mechanize"&gt;Mechanize&lt;/a&gt; でログイン画面の遷移(お客さま番号 → 合言葉 → パスワード)を自動化し、口座残高や入出金明細を Ruby のオブジェクトとして取り出せます。&lt;/p&gt;&#10;&lt;p&gt;当時 gem として公開していました。API のない時代に、自分の口座情報をプログラムから扱うためのスクレイパーです。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gem install mizuho_bank&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使い方はこんな感じです(README より)。認証情報はコードに直書きせず、アカウント情報管理の &lt;a href="https://github.com/cho45/pit"&gt;Pit&lt;/a&gt; から読み込む前提の作りです。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;mizuho_bank&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;pit&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Pit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;MizuhoBank&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:require&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;keiyaku_no&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;keiyaku_no&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;password&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;password&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 合言葉(秘密の質問)3組の question/answer も同様に登録しておく&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;aikotoba_dict&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pit&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;aikotoba1_question&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pit&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;aikotoba1_answer&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pit&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;aikotoba2_question&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pit&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;aikotoba2_answer&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pit&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;aikotoba3_question&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pit&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;aikotoba3_answer&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;MizuhoBank&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pit&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;keiyaku_no&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pit&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;password&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;aikotoba_dict&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main_account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;money&lt;/span&gt; &lt;span class="c1"&gt;# 口座残高&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;p&lt;/span&gt; &lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;main_account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache_flows&lt;/span&gt; &lt;span class="c1"&gt;# 最近の取引履歴&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="内部設計"&gt;内部設計&lt;/h2&gt;&#10;&lt;p&gt;コンポーネント構成です。中核の &lt;code&gt;MizuhoBank&lt;/code&gt; クラスがログイン処理とページのパースを束ねていて、結果は &lt;code&gt;MizuhoDirectInfo&lt;/code&gt;/&lt;code&gt;MizuhoAccount&lt;/code&gt;/&lt;code&gt;MizuhoCacheFlow&lt;/code&gt; というプレーンなデータクラスに詰めて返します。&lt;/p&gt;&#10;&lt;p&gt;&lt;span class="diagram-scroll" role="group" tabindex="0" aria-label="mizuho_bank のコンポーネント構成図"&gt;&lt;img src="https://kymt.me/projects/mizuho_bank/internals.svg" alt="mizuho_bank のコンポーネント構成図"&gt;&lt;/span&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;ログインはステートマシン&lt;/strong&gt;(&lt;code&gt;lib/mizuho_bank.rb&lt;/code&gt;)—「次にどのページが来るか」を決め打ちにせず、&lt;code&gt;what_is_this_page?&lt;/code&gt; が現在のページ本文に含まれる文言(「お客さま番号を入力し…」「合言葉確認」など)からページ種別を判定し、ループで対応する入力処理に振り分けます。銀行側が認証ステップを出したり出さなかったりしても追従できる作りです&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;合言葉(秘密の質問)対応&lt;/strong&gt; — 合言葉ページでは質問文を Nokogiri で抜き出し、コンストラクタに渡した「質問文 → 答え」の辞書(&lt;code&gt;aikotoba_dict&lt;/code&gt;)を引いて回答します。3組の質問のどれが出ても対応できます&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;リトライ&lt;/strong&gt; — ログイン処理全体を自作 gem の &lt;a href="https://github.com/kimoto/retry-handler"&gt;retry-handler&lt;/a&gt; で包んでいて、通信エラーなど一時的な失敗のときに 5 秒間隔で最大 5 回やり直します(認証情報の誤りを試行し直すためのものではありません)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;サービス時間外の検知&lt;/strong&gt; — みずほダイレクトには定期メンテナンスの時間帯があり、公式の時間外案内ページをスクレイピングして「毎週土曜深夜」「第1・第4土曜」のパターンを &lt;code&gt;RepeatDateRange&lt;/code&gt; に落とし、現在時刻がそこに入っていればログイン自体を止めます。「1st saturday in this month」のような英語表現を &lt;a href="https://github.com/mojombo/chronic"&gt;Chronic&lt;/a&gt; に渡して実時刻に解決しています&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;パース&lt;/strong&gt; — ログイン後のトップページから利用者名・最終ログイン日時・代表口座(店名・取引種類・口座番号・残高・引き出し可能残高)と直近 10 件の入出金を、明細照会ページから月単位の入出金明細を、それぞれテーブルの位置指定(&lt;code&gt;nth-child&lt;/code&gt; や添字)で抜き出します。サイトは Shift_JIS なので、入力値は &lt;code&gt;tosjis&lt;/code&gt;、取得ページは &lt;code&gt;toutf8&lt;/code&gt; で変換しています&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;ログインから明細取得までの流れです。&lt;/p&gt;</description></item><item><title>MouseEPS</title><link>https://kymt.me/projects/mouseeps/</link><pubDate>Wed, 20 Apr 2011 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/mouseeps/</guid><description>&lt;p&gt;アプリケーションが WM_MOUSEMOVE 命令を1秒間にどれだけ受信できるかを表示するソフトです。&lt;/p&gt;&#10;&lt;p&gt;通常は127くらいだけど、USB ドライバの更新頻度(Hz)を修正するとその分だけアップさせることが出来ます。120Hz で127、500Hz で507ぐらい。どういう原理で Hz よりも +7 増えてるのかはよくわからない(プログラム的なバグはなさそうだけど謎)。&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://kymt.me/images/projects/mouseeps-about-recon.png" alt="MouseEPS のバージョン情報ダイアログ(配布 exe のリソース定義から再構成)"&gt;&lt;/p&gt;&#10;&lt;p&gt;当時のスクリーンショットは残っていません。上の画像は配布 exe に埋め込まれていたダイアログ定義から再構成したバージョン情報画面です。メイン画面は受信レートの数値をウインドウに直接描画するだけの作りで、画面定義がリソースに残っていないため再構成していません。&lt;/p&gt;&#10;&lt;h2 id="ダウンロード"&gt;ダウンロード&lt;/h2&gt;&#10;&lt;p&gt;&lt;a href="https://assets.kymt.me/gaming/misc/MouseEPS.zip"&gt;MouseEPS.zip&lt;/a&gt;&lt;/p&gt;</description></item><item><title>dice</title><link>https://kymt.me/projects/dice/</link><pubDate>Tue, 25 Jun 2013 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/dice/</guid><description>&lt;p&gt;サイコロアプリ。&lt;/p&gt;&#10;&lt;p&gt;→ &lt;a href="https://kymt.me/app/dice"&gt;https://kymt.me/app/dice&lt;/a&gt;&lt;/p&gt;</description></item><item><title>uc_card</title><link>https://kymt.me/projects/uc_card/</link><pubDate>Fri, 10 Feb 2012 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/uc_card/</guid><description>&lt;p&gt;UC カード(&lt;a href="https://www.uccard.co.jp"&gt;www.uccard.co.jp&lt;/a&gt;)の会員サイトから、未払いのクレジットカード取引履歴(利用明細)を取得する Ruby ライブラリです。Mechanize で会員サイトにログインして明細ページを開き、Nokogiri で HTML をパースして Ruby のオブジェクトとして返します。&lt;/p&gt;&#10;&lt;p&gt;gem として公開していて、同名のコマンドラインツールも同梱しています。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gem install uc_card&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;uc_card &lt;span class="c1"&gt;# 未払いの取引を一覧表示(オプションは一切ない)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;README にある出力例です(利用先の名前は伏せてあります)。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2012/01/28: ****** 500 １回払い 1回&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2012/01/26: ****** 5940 １回払い 1回&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2012/01/22: ****** 3357 １回払い 1回&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Total: 11722&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;ライブラリとして使うときはこう書きます。ID・パスワードはコードに直書きせず、認証情報管理ライブラリの &lt;a href="https://rubygems.org/gems/pit"&gt;pit&lt;/a&gt; から読み込む作りです。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;uc_card&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;UCCard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_with_pit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;uccard.co.jp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="内部設計"&gt;内部設計&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;UCCard.start&lt;/code&gt; がログインから明細取得・ログアウトまでのライフサイクルを丸ごと面倒を見て、ブロックにはパース済みのインスタンスが渡ってきます。&lt;/p&gt;&#10;&lt;p&gt;&lt;span class="diagram-scroll" role="group" tabindex="0" aria-label="uc_card の処理フローのシーケンス図"&gt;&lt;img src="https://kymt.me/projects/uc_card/login-flow.svg" alt="uc_card の処理フローのシーケンス図"&gt;&lt;/span&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;UCCard.start&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;login&lt;/code&gt; → &lt;code&gt;go_recent&lt;/code&gt;(明細取得)→ ブロック実行、と進み、&lt;code&gt;ensure&lt;/code&gt; で必ず &lt;code&gt;logout&lt;/code&gt; します。途中で例外が出てもログアウトだけは走る作りです&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;UCCard.start_with_pit&lt;/code&gt;&lt;/strong&gt; — 認証情報を pit から取得して &lt;code&gt;start&lt;/code&gt; に渡すショートカット。同梱の &lt;code&gt;bin/uc_card&lt;/code&gt; コマンドはこれを呼んで &lt;code&gt;puts card&lt;/code&gt; するだけの 5 行です&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;login&lt;/code&gt;&lt;/strong&gt; — Mechanize(User-Agent は &amp;quot;Windows IE 7&amp;quot; を名乗ります)でログインページを開き、フォームの ID・パスワード欄を埋めて送信します&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;go_recent&lt;/code&gt;&lt;/strong&gt; —「最近のご利用明細」ページを取得し、Nokogiri で明細テーブルの行を &lt;code&gt;ShoppingData&lt;/code&gt;(date・klass・name・value・usecase・times・note・payment の 8 項目)の配列にパースします。README の出力例でいうと「１回払い」が usecase、「1回」が times です&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;ShoppingData#money&lt;/code&gt;&lt;/strong&gt; — 金額文字列から数字以外を取り除いて整数化します。&lt;code&gt;reserved_payment&lt;/code&gt; はその合計で、これが &lt;code&gt;Total:&lt;/code&gt; 行になります(&lt;code&gt;reserved_payment_this_month&lt;/code&gt; というメソッドも生えていますが空実装のままです)&lt;/li&gt;&#10;&lt;li&gt;依存 gem は mechanize・nokogiri・pit の 3 つです&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;README には、SSL 証明書のエラーが出る場合は &lt;code&gt;ruby -ropenssl -e &amp;quot;p OpenSSL::X509::DEFAULT_CERT_FILE&amp;quot;&lt;/code&gt; で証明書のパスを探して適切な証明書を入れるように、という注意書きがあります。&lt;/p&gt;</description></item><item><title>SoundTest</title><link>https://kymt.me/projects/soundtest/</link><pubDate>Sat, 16 Apr 2011 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/soundtest/</guid><description>&lt;p&gt;音の定位を確認するためのソフトです。OpenAL 使用。&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://kymt.me/images/projects/soundtest-window-recon.png" alt="SoundTest のウインドウ(配布 exe のリソース定義から再構成)"&gt;&lt;/p&gt;&#10;&lt;p&gt;中央に人間が居ると仮定して、押したボタンの位置に音を発生させ、前後左右斜めの8方向の定位を診断します(説明文はウインドウ内のものが当時の原文)。&lt;/p&gt;&#10;&lt;p&gt;当時のスクリーンショットは残っていなかったため、この画像は配布 exe に埋め込まれていたダイアログ定義(ボタン・文言・座標)をそのまま描画し直した復元です。&lt;/p&gt;&#10;&lt;h2 id="ダウンロード"&gt;ダウンロード&lt;/h2&gt;&#10;&lt;p&gt;&lt;a href="https://assets.kymt.me/gaming/misc/SoundTest.zip"&gt;SoundTest.zip&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Rotate Sushi</title><link>https://kymt.me/projects/sushi/</link><pubDate>Thu, 02 Jun 2011 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/sushi/</guid><description>&lt;p&gt;巻き寿司の 3D モデルを、青い画面の中で回して見せるだけの Windows プログラムです。DirectX 9 のサンプル(DirectX SDK のチュートリアル「Meshes」)と同じ作りで、モデルだけを自分で用意した寿司に差し替えています。&lt;/p&gt;&#10;&lt;h2 id="再現イメージ"&gt;再現イメージ&lt;/h2&gt;&#10;&lt;p&gt;当時の実際の画面は &lt;a href="https://www.youtube.com/watch?v=WOhSp0lfptQ"&gt;YouTube の動画「Sushi.exeの実行例」&lt;/a&gt; に残っています。下のアニメーションはそれとは別に、配布していた exe とモデルから描画の条件を読み取り、&lt;strong&gt;AI が描き直した創作イメージ&lt;/strong&gt;です(実際の画面ではありません)。&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://assets.kymt.me/projects/sushi-recreation-anim30.png" alt="巻き寿司が青い背景の中で円を描くように回る、AI による再現アニメーション"&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;モデルは原点からずれた位置に置かれているので、寿司はその場で回るのではなく、画面の中を大きく回る&lt;/li&gt;&#10;&lt;li&gt;1 周は約 6.3 秒(毎秒 1 ラジアン)&lt;/li&gt;&#10;&lt;li&gt;光は環境光だけなので陰影は無く、テクスチャの色がそのまま出る&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="ノートンに消された話"&gt;ノートンに消された話&lt;/h2&gt;&#10;&lt;p&gt;配り始めたその日、この寿司を回すだけの 10KB のプログラムが、ノートン アンチウイルスにウイルスとして消されました。もちろん誤検知です。おもしろいのは、&lt;strong&gt;中身を 1 バイトも変えずに、ファイル名を変えただけで判定が変わった&lt;/strong&gt;ことでした。いまはこのプログラムを配っていないので、ここにあるのは当時の記録です。&lt;/p&gt;&#10;&lt;p&gt;ノートンは、まずダウンロードしたときに警告を出しました。理由として並んでいたのは「ごく少数のユーザー」「ごく新しい」「未確認」の 3 つです。&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://kymt.me/images/projects/sushi-norton-download-insight-live.png" alt="Norton のダウンロードインサイト。sushi.exe について「あなたはこのファイルをごく初期にダウンロードした Norton ユーザーです」と警告し、理由として、ごく少数のユーザー・ごく新しい・未確認の 3 つを挙げている"&gt;&lt;/p&gt;&#10;&lt;p&gt;実行すると「疑わしい動作をしていました」として止められ、削除されました。&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://kymt.me/images/projects/sushi-norton-file-insight-live.png" alt="Norton のファイルインサイト。sushi.exe が「プログラムがコンピュータ上で疑わしい動作をしていました。このプログラムは遮断され削除されました」と表示され、詳細にはごく少数のユーザー・リスク高とある"&gt;&lt;/p&gt;&#10;&lt;p&gt;そこで、次の順に確かめました。&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;メッセージボックスを出すだけのプログラムを &lt;code&gt;Sushi.exe&lt;/code&gt; という名前で作ると、これも疑わしいファイルとして検知された&lt;/li&gt;&#10;&lt;li&gt;本物の寿司のプログラムを &lt;code&gt;DxTest4.exe&lt;/code&gt; という名前でコンパイルすると、検知されない&lt;/li&gt;&#10;&lt;li&gt;それをコピーして &lt;code&gt;Sushi.exe&lt;/code&gt; に名前を変えると、ウイルスとして検知された&lt;/li&gt;&#10;&lt;li&gt;2 と 3 のファイルは、MD5(ファイルの中身から計算する指紋のような値)まで一致していた&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;&lt;img src="https://kymt.me/images/projects/sushi-norton-md5-live.png" alt="DxTest4.exe と Sushi.exe を並べた画面。どちらもサイズ 10KB、CRC 72E1A964、MD5 048A66DDB8F2807C838603D37ACF2296 で、ツールが「重複ファイル検出」と表示している"&gt;&lt;/p&gt;&#10;&lt;p&gt;当時は、いくつかの「怪しい」条件が重なったのだろうと考えていました。ダウンロード元が知られていない、使っている人が世界に少ない、コンパイルしたときと名前が違う、昔 &lt;code&gt;sushi.exe&lt;/code&gt; という名前のウイルスがあったのかもしれない、といった条件です(最後のものは当時も推測で、今回調べても確かめられませんでした)。同じファイルを試したほかのアンチウイルス 3 社は、何も言いませんでした。&lt;/p&gt;</description></item><item><title>net-irc-mala</title><link>https://kymt.me/projects/net-irc-mala/</link><pubDate>Fri, 10 Feb 2012 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/net-irc-mala/</guid><description>&lt;p&gt;mala のタイムラインを IRC で読むためのゲートウェイです。手元で小さな IRC サーバーとして動き、IRC クライアントで接続すると &lt;code&gt;#malastream&lt;/code&gt; チャンネルに発言がリアルタイムで流れてきます。&lt;/p&gt;&#10;&lt;p&gt;コードから読み取れる範囲では、接続先は &lt;code&gt;http://api.ma.la/home_timeline/stream&lt;/code&gt; というストリーミング API で、本文(&lt;code&gt;text&lt;/code&gt;)と発言者(&lt;code&gt;user.screen_name&lt;/code&gt;)を持つ JSON が届く、マイクロブログ的なタイムラインです。&lt;/p&gt;&#10;&lt;h2 id="内部設計"&gt;内部設計&lt;/h2&gt;&#10;&lt;p&gt;IRC クライアントとストリーミング API の間の流れです。&lt;/p&gt;&#10;&lt;p&gt;&lt;span class="diagram-scroll" role="group" tabindex="0" aria-label="net-irc-mala のメッセージフローのシーケンス図"&gt;&lt;img src="https://kymt.me/projects/net-irc-mala/stream-flow.svg" alt="net-irc-mala のメッセージフローのシーケンス図"&gt;&lt;/span&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;mig.rb&lt;/strong&gt;(&lt;code&gt;bin/mig.rb&lt;/code&gt;)— 同梱の実行コマンド。&lt;code&gt;Net::IRC::CLI.run(Net::IRC::Mala, ARGV)&lt;/code&gt; の 1 行だけで、起動処理はすべて CLI 側に任せています&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Net::IRC::CLI&lt;/strong&gt;(&lt;code&gt;lib/net/irc/cli.rb&lt;/code&gt;)— OptionParser でオプション(&lt;code&gt;-p&lt;/code&gt; ポート、&lt;code&gt;-h&lt;/code&gt; ホスト、&lt;code&gt;-l&lt;/code&gt; ログファイル、&lt;code&gt;--debug&lt;/code&gt;、&lt;code&gt;-f&lt;/code&gt; フォアグラウンド)を解釈し、&lt;a href="https://rubygems.org/gems/net-irc"&gt;net-irc&lt;/a&gt; gem の &lt;code&gt;Net::IRC::Server&lt;/code&gt; を起動します。デフォルトは &lt;code&gt;localhost:16701&lt;/code&gt; で待ち受け、fork + &lt;code&gt;setsid&lt;/code&gt; でデーモン化します(&lt;code&gt;--debug&lt;/code&gt;/&lt;code&gt;-f&lt;/code&gt; 時はフォアグラウンド)。ログは Logger の日次ローテーションです&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Net::IRC::Mala&lt;/strong&gt;(&lt;code&gt;lib/net/irc/mala.rb&lt;/code&gt;)— net-irc の &lt;code&gt;Net::IRC::Server::Session&lt;/code&gt; を継承したセッション本体。クライアントが USER 登録すると、&lt;code&gt;#malastream&lt;/code&gt; に JOIN させてチャンネルモード(&lt;code&gt;+mto&lt;/code&gt; と自分への &lt;code&gt;+o&lt;/code&gt;/&lt;code&gt;+q&lt;/code&gt;)を設定し、ストリーミング用のスレッドを起動します&lt;/li&gt;&#10;&lt;li&gt;ストリーミングスレッドは EventMachine + &lt;a href="https://rubygems.org/gems/em-eventsource"&gt;em-eventsource&lt;/a&gt; で API に Server-Sent Events 接続し、届いたメッセージを JSON としてパースして、&lt;code&gt;screen_name&lt;/code&gt; を送信元にした PRIVMSG として &lt;code&gt;#malastream&lt;/code&gt; に流します。パースに失敗したメッセージは STDERR にエラーを出して読み飛ばします&lt;/li&gt;&#10;&lt;li&gt;クライアントが切断するとストリーミングスレッドを kill します&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;サーバー名・チャンネル名などは &lt;code&gt;Net::IRC::Mala&lt;/code&gt; にべた書きで、設定ファイルはありません。gem のバージョンは 0.0.1 です。&lt;/p&gt;</description></item><item><title>kimotter</title><link>https://kymt.me/projects/kimotter/</link><pubDate>Fri, 20 Jan 2012 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/kimotter/</guid><description>&lt;p&gt;コマンドラインからツイートを投稿する Ruby 製クライアントです。Twitter API を直接は使わず、mala API(&lt;code&gt;http://api.ma.la/twitter/&lt;/code&gt; にあった Web の投稿フォーム)を &lt;a href="https://github.com/sparklemotion/mechanize"&gt;Mechanize&lt;/a&gt; で操作して投稿します。gem として公開していました。&lt;/p&gt;&#10;&lt;h2 id="使い方"&gt;使い方&lt;/h2&gt;&#10;&lt;p&gt;Ruby 1.9 と nokogiri・mechanize が必要です(README 由来)。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;gem install kimotter&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kimotter &lt;span class="s2"&gt;&amp;#34;ツイート本文&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;投稿の前に、ページから取り出した確認メッセージへの同意を求められます。README の実行例です。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;gt; kimotter &amp;#34;EventSource知らんかった、普通に便利そう&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;嘘偽りなく自分がキモトであると誓います[y/N] y&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hi kimoto. try to tweet your cool message&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;done!&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;オプションは &lt;code&gt;-d, --debug&lt;/code&gt;(デバッグモード)と &lt;code&gt;-f, --force&lt;/code&gt;(投稿前の API 制限チェックを飛ばして実行)の2つです。制限そのものはエンドポイント側で管理されているもので、&lt;code&gt;--force&lt;/code&gt; が飛ばすのは自前の事前チェックだけです。&lt;/p&gt;&#10;&lt;h2 id="内部設計"&gt;内部設計&lt;/h2&gt;&#10;&lt;p&gt;コンポーネント構成です。全体で約170行の小さな gem で、フォーム操作をやる &lt;code&gt;Kimotter&lt;/code&gt; クラスと、対話まわりを受け持つ &lt;code&gt;KimotterCLI&lt;/code&gt; の2層でできています。&lt;/p&gt;&#10;&lt;p&gt;&lt;span class="diagram-scroll" role="group" tabindex="0" aria-label="kimotter のコンポーネント構成図"&gt;&lt;img src="https://kymt.me/projects/kimotter/internals.svg" alt="kimotter のコンポーネント構成図"&gt;&lt;/span&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;Kimotter&lt;/strong&gt;(&lt;code&gt;lib/kimotter.rb&lt;/code&gt;)— &lt;code&gt;fetch_api_limit&lt;/code&gt;(エンドポイントを GET して API 制限値と確認メッセージをパース)、&lt;code&gt;tweet&lt;/code&gt;(投稿フォームの &lt;code&gt;status&lt;/code&gt; に本文を入れ、&lt;code&gt;is_kimoto&lt;/code&gt; チェックボックスにチェックして submit)、&lt;code&gt;api_limit?&lt;/code&gt;(使用数が上限に達したかの判定)を提供します&lt;/li&gt;&#10;&lt;li&gt;HTML のパース(&lt;code&gt;load_from_mala_html&lt;/code&gt;)は Nokogiri でやります。body テキストの &lt;code&gt;API Limit: 使用数 / 上限&lt;/code&gt; を正規表現で読み、フォームのチェックボックスの隣にあるテキストを確認メッセージとして取り出します&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;filter&lt;/code&gt; メソッドは投稿前に本文を加工するためのフックで、デフォルトは素通しです(コードのコメントに「必要ならオーバーライドして」とあります)&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;KimotterCLI&lt;/strong&gt;(&lt;code&gt;lib/kimotter_cli.rb&lt;/code&gt;)— オプション解析(&lt;code&gt;-d&lt;/code&gt;/&lt;code&gt;-f&lt;/code&gt;/&lt;code&gt;-a&lt;/code&gt;(エンドポイント URL の差し替え))、本文の受け取り、API 制限チェック、y/N 確認、終了コードの出し分けをやります。引数に本文がないときは &lt;code&gt;$EDITOR&lt;/code&gt; を一時ファイル相手に起動して書かせ、改行はスペースに置換します&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;bin/kimotter / bin/koiketter&lt;/strong&gt; — どちらも中身は &lt;code&gt;KimotterCLI.execute&lt;/code&gt; の呼び出しだけで、koiketter はエンドポイントを &lt;code&gt;http://kimoto.ssig33.com/api&lt;/code&gt; に向けた別コマンドです&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;投稿1回の流れはこうなっています。&lt;/p&gt;</description></item><item><title>pi.kymt.me</title><link>https://kymt.me/projects/pi/</link><pubDate>Tue, 25 Jun 2013 00:00:00 +0000</pubDate><guid>https://kymt.me/projects/pi/</guid><description>&lt;p&gt;Amazon 画像版 jpg.to。&lt;/p&gt;&#10;&lt;!-- .text-shot-scroll: このスクショは埋め込みテキストが本体(900x36px)。&#10; main img の max-width:100% で縮小するとスマホ幅では文字が判読不能になるため、&#10; 原寸を保って横スクロールさせる(#212) --&gt;&#10;&lt;div class="text-shot-scroll"&gt;&#10; &lt;img src="https://kymt.me/images/projects/pi-kymt-me-2012-rerender.png" alt="pi.kymt.me のトップページ(2012年12月)" width="900" height="36"&gt;&#10;&lt;/div&gt;&#10;&lt;p&gt;トップページは使い方の1行だけ、という作り(Wayback の 2012年12月スナップショットより)。&lt;code&gt;pi.kymt.me/○○.jpg&lt;/code&gt; の形式で URL を作ると、○○の部分で Amazon を検索してマッチした商品の巨大な画像が返ってくる。&lt;/p&gt;</description></item></channel></rss>