<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Retired on kymt.me</title><link>https://kymt.me/tags/retired/</link><description>Recent content in Retired 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/tags/retired/index.xml" rel="self" type="application/rss+xml"/><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>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>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>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>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>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>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>Fake sniper spray</title><link>https://kymt.me/gaming/fake_sniper_spray/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://kymt.me/gaming/fake_sniper_spray/</guid><description>&lt;p&gt;TF2 用のネタスプレー。壁の角や窓のふちに貼ると、スナイパーが物陰から覗いては引っ込む動きをしているように見える、スナイパーの擬態(fake)スプレーです。&lt;/p&gt;&#10;&lt;p&gt;仕様変更でアニメーションスプレーは現在の TF2 では使えなくなりました。&lt;/p&gt;&#10;&lt;h2 id="復元アニメーション"&gt;復元アニメーション&lt;/h2&gt;&#10;&lt;p&gt;当時のスクリーンショットや動画は残っていなかったため、配布していた VTF(Source のテクスチャ形式)をパースしてアニメーション PNG に復元しました。&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://assets.kymt.me/gaming/misc/fake_sniper_spray_restored.png" alt="Fake sniper spray の復元アニメーション"&gt;&lt;/p&gt;&#10;&lt;p&gt;RED スナイパーが横にスライドして画面外へ消え、また戻ってくる 11 フレームのループになっています。ゲーム内の実際の再生速度の記録は残っていないため、この APNG のフレーム間隔(200ms)は推定です。&lt;/p&gt;&#10;&lt;h2 id="ダウンロード"&gt;ダウンロード&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://assets.kymt.me/gaming/misc/fake_sniper_spray.vtf"&gt;fake_sniper_spray.vtf&lt;/a&gt; - 当時配布していた原本&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="技術メモ"&gt;技術メモ&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;VTF 7.2、256×256、DXT5(アルファ付き)、11 フレーム、mipmap 1 枚&lt;/li&gt;&#10;&lt;li&gt;フラグは No Mipmap/No LOD/Eight Bit Alpha。&lt;a href="https://kymt.me/gaming/vtf_vmt_notes/"&gt;VTF / VMT についての備忘録&lt;/a&gt;に書いた「キレイなスプレーの作り方」そのままの構成&lt;/li&gt;&#10;&lt;li&gt;アニメーション VTF はプレイ環境の FPS に依らず一定速度で再生される(同備忘録参照)&lt;/li&gt;&#10;&lt;li&gt;復元は VTF ヘッダをパースして DXT5 の各フレームをデコードし、APNG に合成したもの(2026-07)&lt;/li&gt;&#10;&lt;/ul&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>