Archive - XboxInfoTwit

ver.1.3.0.7

CoD4やMW2、L4Dなんかで顕著に見られるようですが、オンライン対戦時の他プレイヤーが「参加可能」時にデータの取得に失敗して、ゲーム名に参加可能が付いていたり実績が0/0になっていたりするような件を修正しました。実のところ、これver1.3.0.1の時に修正したはずだったんですが、いつのまにやらその時対策したはずのコードが元に戻ってました。あらららら……。

といったように、非常にいい加減な開発姿勢なので今回のリリースでも更にバグ埋め込んだりする可能性大です。バグ見つけたら怒ってやってください。Twitter検索でキーワード「XboxInfoTwit」を始終チェックしてますので、もし不具合があったらTwitterでの投稿時に「XboxInfoTwit」と文中に混ぜておけば、例えば「XboxInfoTwitクソ、***で動かねえ」とか言ってくれれば私の方で巡回して気づくと思いますので、気楽に苦情文句要望バグ報告してやってください。

そういえばというわけでもないのですけど、利用ユーザー数が500超えました。いやー、ビックリですね。当初は2桁台に行けばいいなあ、とか言ってたぐらいだったり、実際致命的な不具合があったのに3ヶ月放置してたり(誰も使わないので気づかなかった!)などだったはずが。嬉しいです。が、現在のコード品質は相当アレなので、なるべく早く、せめて今年中には全面的に書き換えたver2.0を出せるといいなあ、なんて思っています。

ちなみに、amazonアサマシゲイツポイントの購入者数はゼロに近かったりします(買ってくれた人は本当に本当にありがとうございます)。いやまあ、別にネタなのでいいんですけどね。はは。

C#でTwitterのStreaming APIを使ってリスト自動追加

XboxInfoTwitの認証数は現在450を越えて、近いうちに500には届きそうです。現在の実装はIEを裏で動かすという、しょーもないものになっていて、それに起因する不具合や、どうしょうもない点が幾つかあるため、クローラー部分は全面的に書き変えようと思っています。あと、エラーメッセージがド不親切とか至らない点だらけでした、すみません。そんな次期バージョンの作業は全然捗ってないのですが、せめて年内ぐらいには何とかしたいです。

@のお話

ゲーム名に@が含まれるものをポストする(例えばTHE IDOLM@STER)と、STERさんに@が飛んで迷惑。というお話を見たので検証してみました。@は行頭かスペース + @ + 数字/アルファベットのものがあると飛びます。つまり、@の前にアルファベットがあれば@は飛びません。なので、別にIDOLM@STERだからってSTERさんに@が飛びまくる、なんてことはありません。正規表現で表すと「(?<=^| )@[a-zA-Z0-9_]+」になります。ついでに、ハッシュタグのほうも軽く検証してみました。基本的には@と同じですが、英単語以外にもリンクが張られるようなので、正規表現は「(?<=^| )#[^ ]+」になるようです。

List

Twitterにリストが実装されました。そこで、XboxInfoTwitユーザーのリストを作ってみることにしました。手動で探して登録も大変なので、プログラムでクロールして追加していきましょう。パブリックイタイムラインからXboxInfoTwit利用者(Source=XboxInfoTwit)の人を片っ端からリスト登録するという方針で行きます。以下、C#でのTwitterストリーミングAPIの使用法と実際のコードになります。同じようなことをやりたい人は、適当に書き替えてどうぞ使ってください。突っ込みどころ多数なのでむしろ突っ込んで欲しい……。

2010/4/29 追記:このコードはストリームAPIの利用法にしては冗長すぎるので、書き直しました → neue cc - C#とLinq to JsonとTwitterのChirpUserStreamsとReactive Extensions ストリームAPI取得コードを参考にする場合は、新しいほうを見てください。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Xml.Linq;
using System.Collections.Specialized;
using System.Threading;
using System.Xml;
 
static class Program
{
    // ザ・決めうち文字列s
    const string UserName = "neuecc"; // 自分のアカウントのユーザー名を
    const string Password = "password"; // 同じくパスワードを
    const string ListName = "xboxinfotwitusers"; // リストを、入力ですです
    const string StreamApi = "http://stream.twitter.com/1/statuses/sample.xml";
    const string ListMembersApiFormat = "http://twitter.com/{0}/{1}/members.xml";
 
    /// <summary>指定リストにメンバーを追加する</summary>
    static void AddMemberToList(string userName, string listName, int id)
    {
        var url = string.Format(ListMembersApiFormat, userName, listName);
        var wc = new WebClient { Credentials = new NetworkCredential(UserName, Password) };
        wc.UploadValues(url, new NameValueCollection() { { "id", id.ToString() } });
    }
 
    /// <summary>指定リストのメンバーIDを全て取得する</summary>
    static IEnumerable<int> EnumerateListMemberID(string userName, string listName)
    {
        var format = string.Format(ListMembersApiFormat, userName, listName) + "?cursor={0}";
        var cursor = -1L;
        var xmlReaderSettings = new XmlReaderSettings
        {
            XmlResolver = new XmlUrlResolver { Credentials = new NetworkCredential(UserName, Password) }
        };
        while (true)
        {
            using (var xr = XmlReader.Create(string.Format(format, cursor), xmlReaderSettings))
            {
                var xEle = XElement.Load(xr);
                foreach (var item in xEle.Descendants("user").Select(x => (int)x.Element("id")))
                {
                    yield return item;
                }
                cursor = long.Parse(xEle.Element("next_cursor").Value);
                if (cursor == 0) yield break;
            }
        }
    }
 
    /// <summary>ストリームAPIのパブリックタイムラインから無限に取得</summary>
    static IEnumerable<XElement> EnumeratePublicTimeline(StreamReader reader)
    {
        while (true)
        {
            var xmlString = reader.EnumerateLines()
                .TakeWhile(s => s != "<?xml version=\"1.0\" encoding=\"UTF-8\"?>")
                .Join();
            if (xmlString == "") continue;
            yield return XElement.Parse(xmlString);
        }
    }
 
    /// <summary>サーバーが死んでないか確認</summary>
    static bool IsServerStatusOK()
    {
        var req = WebRequest.Create("http://twitter.com/help/test.xml");
        HttpWebResponse res = null;
        try
        {
            res = (HttpWebResponse)req.GetResponse();
            if (res.StatusCode == HttpStatusCode.OK) return true;
        }
        catch (WebException e) { Console.WriteLine(e); }
        finally { if (res != null) res.Close(); } // どうでもいいと思っていたり
 
        return false;
    }
 
    static void Main(string[] args)
    {
        ServicePointManager.Expect100Continue = false; // おまじない(笑)
        var count = 0; // モニタリング用のカウント変数(動作的には別に使わない)
 
        var following = new HashSet<int>(EnumerateListMemberID(UserName, ListName));
        var webRequest = (HttpWebRequest)HttpWebRequest.Create(StreamApi);
        webRequest.KeepAlive = true;
        webRequest.Credentials = new NetworkCredential(UserName, Password);
 
    LOOP:
        using (var res = webRequest.GetResponse())
        using (var stream = res.GetResponseStream())
        using (var reader = new StreamReader(stream))
        {
            try
            {
                // 例外が発生しなければ、無限リピートになっているのでこの部分を永久に続けます
                EnumeratePublicTimeline(reader)
                    .Do(_ => { if (++count % 100 == 0) Console.WriteLine("{0} : {1}", DateTime.Now, count); }) // 確認表示用
                    .Where(x => x.Name == "status")
                    .Select(x => new
                    {
                        Source = x.Element("source").Value,
                        ID = (int)x.Element("user").Element("id"),
                        Name = x.Element("user").Element("screen_name").Value
                    })
                    .Where(a => a.Source.Contains("XboxInfoTwit"))
                    .Do(a => Console.WriteLine("Found:{0}", a.Name)) // ここでも確認表示用
                    .Where(a => following.Add(a.ID))
                    .ForEach(a =>
                    {
                        AddMemberToList(UserName, ListName, a.ID);
                        Console.WriteLine("{0} : {1} : {2}", a.Name, DateTime.Now, count); // 確認表示用
                    });
            }
            catch (IOException e)
            {
                Console.WriteLine(e); // 接続が閉じられてたりするのでー。
                while (!IsServerStatusOK())
                {
                    Thread.Sleep(TimeSpan.FromMinutes(5)); // サーバー死んでたら5分間お休み
                }
            }
            finally
            {
                webRequest.Abort(); // これ呼ぶ前にCloseするとハング
            }
        }
        goto LOOP; // goto! goto!
    }
 
    // Extension Methods
 
    public static IEnumerable<string> EnumerateLines(this StreamReader streamReader)
    {
        while (!streamReader.EndOfStream)
        {
            yield return streamReader.ReadLine();
        }
    }
 
    public static string Join<T>(this IEnumerable<T> source)
    {
        return source.Aggregate(new StringBuilder(), (sb, s) => sb.Append(s)).ToString();
    }
 
    public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
    {
        foreach (var item in source)
        {
            action(item);
        }
    }
 
    public static IEnumerable<T> Do<T>(this IEnumerable<T> source, Action<T> action)
    {
        foreach (var item in source)
        {
            action(item);
            yield return item;
        }
    }
}

ストリーミングAPIとは無関係のリスト関連の処理や、投げやりなtry-catch-gotoがあって、ちょっとゴチャゴチャしてますが、基本的にはusing三段重ねの部分だけです。無限にXMLが継ぎ足されてくるので、接続を切らさずひたすらReadLine。XMLの切れ目は、XML宣言部を使うことにしましたが、今一つスマートではないです。文字列にしてParseってのもあまり良い感じじゃなく。あ、あと例外処理は全然出来てませんので何かあると平然と死にます。

コードは、書き捨て感全開。例によって何でもLinq、何でもIEnumerable。コレクションになりそうな気配があると、すぐにじゃあyieldね、と考える癖がついてしまっていて。細かいことは後段に任せればいーんだよ、というのが楽ちんでして。リストメンバー全件取得の部分なんかは、わりとスマートに書けてるかと思うのですがどうでしょう。

なお、このストリーミングAPIは全件を漏れなく取得出来るわけではないので、それなり、というかかなり漏れが出ます。なのでXboxInfoTwit使ってるのに登録されねーぞ、という場合は、しょーがない。です。そのうち登録されると思います。あと、このプログラムはサーバー上で24時間動かしているわけじゃなく、私のローカルPC上で動かしているだけなので、私の気まぐれで動かしてたり動かしてなかったりします。私が寝てる間はPCがウルサイので動いてませんし、私が家に居ない時は省エネのために動いてません。なので、むしろ登録されるほうが珍しいです。レアです。効率的には20000件に1人登録出来るか出来ないか、って感じでした。一時間に一人見つかるかどうかも怪しいぐらいの頻度。とてもレア。ぶっちゃけgoogle経由で引っ張ってくるとかしたほうが遙かに効率良さそうですが、まあ、Streaming API使ってみたかったというだけなので。

そういえばですが、逆にリストに登録されてUZEEEE、という場合は、現状はリスト機能がベータのせいなのか拒否は出来ないようです。すみません。UZEEEE、と思っても我慢してください。どうしても嫌な場合は私の方にメッセージをくれれば、リストからの撤去と、プログラムから以後の追加をしないようなコードを入れたいと思っています。

3桁到達

開発者用のOAuth管理ページで認証ユーザー数が見れるのですが(「誰が」までは分からないので安心してください)、いつのまにやらユーザー数が100を超えていました。三桁!奇跡的ですね。はてな同期云々とかフォトライフ云々は壊滅的な状態なので。世の中そんなものです。ついでにこのサイトのアクセス数も壊滅的だったりはします。成り行きでプログラミング系サイトに転換してから半年、以上は経つ感じですが、伸びもせず縮みもせず、ずっと低調をキープ。サイトの内容がガラッと変わったのにアクセス平均が変わらないってのも面白いですけど。検索サイトからのアクセスがほとんどなので、検索キーワードが入れ替わって、でも流入人口は変わらずという。あー、まあ、少し増えた、かな。つまりかわりに常連的な人口が減った、と。おお、虚しい!悲しい!RSSリーダー登録数も伸びないしね。しょぼーん。

ゲーム系サイトへは、XNAで返り咲きしたいとこっそり思ってます。XNAは使用言語がC#だからね。C#好きーなのです、私。積みタスクを全部消化したらXNAやりたいんですが中々どうして……。 そういえばインディーズゲーム(XBIG)を完全スルー状態なのはぶっちけ(略)

fromにクライアント名が出るようになってからは、googleのサイト検索で利用具合が見つかるので、毎日24時間以内の結果を眺めていたりします。見ていて思うというか教訓は、デフォルト設定大事ってことでしょうかね。カスタマイズせずそのまま、カスタマイズする場合も、デフォルトを残しつつ細部を変える、という感じなので、デフォルトの投稿文はちゃんとしたものを用意しなきゃダメなんですね。当たり前といえば当たり前なのですけど、この辺はてなついったー同期ツールは大失敗していて、どうせカスタマイズするだろうと踏んで、書式のサンプルとばかりにゴテゴテのものをデフォルトにしてしまったので……。反省。

あと、デフォルトでは「プレイ中タイトルの状況が変わった時の投稿」はオフにしているのですが、意外とこれをオンにする人が多かったのも驚き。これオンにすると物凄い勢いで投稿されるんですよ。更新間隔を5分にすると、例えばGoW2のHordeすると、WAVE44,45,46…と、全部のWAVE投稿するんじゃないか、最新50個の投稿が全部XboxInfoTwit経由になってますが大丈夫?みたいなことになる。こういう滅茶苦茶なことが出来るのはローカルで動くツールならでは、なのですが(ウェブサービス系じゃあ、ちいと無理ですね、秋のTwitter対応が仮に実績やプレイ状況の投稿に対応するとしても、ここまでの連投は無理かと)フォロワーの目からどうなのか、というと、まあ、分からにゃい。いや、本人の満足が一番だと思いますよ。一日のプレイ後に投稿を眺めると、状況の変化がよく見えて結構楽しかったりはします。お薦めはしませんけどお薦め。別アカでやるなら何も問題なくお薦め。

ver 1.3.0.5 バグ修正とか動かない人用とか色々

動かない-、という人にログを出して貰ったお陰で、幾つか問題を潰せました。本当にありがとうございます。自分の環境で再現出来るエラーを潰せるのは当たり前。再現しないエラーを潰せないのは三流。というわけで三流な私はさっぱり分かりませんでした。分かってみれば、ああ、確かに問題だなーって感じなんですけどねえ。

変更内容は「オフライン→オンライン時投稿のチェックを外していた場合、同期に失敗する不具合を修正」だそうです。えー、こんな初歩ミス埋め込んでたのー、っていうと、そうです、はい、埋め込んでました、はい。げふんげふん。これは酷い。そう、「実績解除」だけ利用できればいいや、って人が利用出来なかったのです、なんだってー。あともう一つ、「GamerTagの入力を大文字小文字を区別しないように変更」です。今まで区別していたので、例えばnEUEcCとか入力すると、同期出来てませんでした。これはいかんですね。いかんので、区別しないようにしました。

ていうか、自分の環境で再現しない問題、じゃなくてただたんに例外ケースの見積もりが甘すぎなだけですなあ。もうちっと気を引き締めて書かないとダメですね。

追記

ver 1.3.0.6になりました。1.3.0.4以降は「+記号」が使えなかったっぽいので、それを直しました。ダメダメすぎて涙。

ver 1.3.0.4 動かなくなったのでちょっと書き換えた

昨日の朝だか昼だかから動かなくなってたっぽいので、それを直しました。何で動かなかったのかよく分かってないんですが、ダメになった箇所だけは分かったので別のアプローチで、ということで。応急処置もいいとこで、まー情けないのですが、動けばいっか、ってことで。眠いし。ダメですかダメですねすみません。

今後だと、一応ダッシュボードのアップデートが8月に控えているらしいので、それでも引っかかってダメになるかもしれませんが、ダメっぽくなったのを捕捉次第、速やかに直したいとは思っているのでダラダラとお待ちください。

ver 1.3.0.3 / ちょっとした拡張メソッド群

Gears of War 2やCall of Duty : World at Warでステータスが取得出来なかった件を修正しました。GoW2は修正を確認出来ましたがCoD:WaWは持ってないので分からない。多分直ってると思うけど。あと、この修正の影響で他がダメになる可能性がなくもないです。ダメっぽいのを発見したら即直す、という方向で暴走さえしなければある程度はいっかー、的にゆるふわ気分でいるのですがダメでしょうかダメですね。

ところで、int.Parseが多くなって結構面倒くさいので文字列に拡張メソッド。

public static int ToInt(this string value)
{
    return int.Parse(value);
}
 
public static int ToIntOrDefault(this string value)
{
    int result;
    int.TryParse(value, out result);
    return result;
}
 
public static string Remove(this string input, string pattern)
{
    return Regex.Replace(input, pattern, "");
}
 
public static Match Match(this string input, string pattern)
{
    return Regex.Match(input, pattern);
}
 
public static string Replace(this string input, string pattern, MatchEvaluator evaluator)
{
    return Regex.Replace(input, pattern, evaluator);
}

RemoveとReplaceは既存のメソッド名のものに足してるので、別の名前のほうが良いかしらん。あと、Replaceは引数が被るので、ただの文字列置換は用意できなかった。まあ、_ => “hogehoge” といった具合に「_ => 」が増えるだけならそう手間でもないような十二分に手間のような。

totalGamerScore = document
    .Descendants("div")
    .First(e => e.GetAttribute("className") == "XbcProfileSubHead")
    .Descendants("strong")
    .Last()
    .InnerText
    .Match(@":\s*(\d+)/")
    .Groups[1]
    .Value
    .ToInt();

こんな風に書けます。これでスクレイピングも快適ー。ドット繋げまくれてバンザイしちゃう。ただまあ、デバッグはし辛いですね。ログ吐くメソッドを仕込んだりもいいんですが、もっと単純に、ラムダ式挟めばデバッガで止められるので

public static class Ext
{
    public static TResult Tap<T, TResult>(this T self, Func<T, TResult> func)
    {
        return func(self);
    }
 
    public static T Tap<T>(this T self, Action<T> action)
    {
        action(self);
        return self;
    }
}

RubyのTapみたいな、ということで。

ダミー置いて、その場で止められるようになる。それで作って、出来あがったらTapの行をCtrl+Xでゴミ箱ぽい。IEnumerableの場合は、普通にダミーのSelect置けばいいだけでしたね! 今、そのことに気付いた。うーん、あと.Groups.Cast<Group>().Skip(1).Select(g => g.Value) も、頻繁にあるあるなので、Matchに拡張メソッド埋めときますか。

ver 1.3.0.2 色々バグ修正

バグフィックス祭り! まずバグ報告があってそれを直して、つまりは普段と少し違う状態の時の処理が全く入ってなかったので、洗い出して処理を入れました。別に全く考えていなかったわけじゃないのですが、想定からの漏れが幾つか、というかたっぷりありまして……。具体的には「申請待ちのフレンドがいるとエラーになる」「ステータスが「退席中」の際にデータが正しく取得出来ない」「ステータスが「取り込み中」の際にエラーになる」です。不具合くせえええ、と思ったら遠慮無く言っていただけるととっても助かりますので、お願いします。

ver 1.3.0.1(XboxInfoTwit) / exception(アーケード)

全てのゲームタイトルで、オンラインマッチ時にステータスが途中参加可能な状態で実績取得が出来ず、タイトル名がおかしくなる件を修正しました。Xbox.comから文字列決めうちで切り出しているので、どんな文言が来るのかのパターンに漏れがありましたというか知らんかった……。(ちなみに現在、待機中でも同様の問題があったりするので、直します、次回に、他にもないかちゃんと調べてから)。こうしてバグ発覚な上に(しかもこの問題は以前からあったっぽいです、全然気づいてなかった)、自信満々に互換性向上!とか言っておきながら、動かなかったって人がいるみたいでごめんなさい。しかも以前のバージョンでは動いていたとなると、もう本当にごめんなさい……。

//

ところで話かわって、exceptionのアーケード版がロケテされてるらしい、新宿南口ワールドで7/9~15だそうだ。というわけで行ってきましたは二日目。実は初日にも行ったんですが、お上りさんなので間違えて東口のタイトーステーションに行って、置いてないなあ、と思ってた。いや、「タイトーステーション 新宿南口ゲームワールド店」自体には何度か行ったことはあるんですよ!でも、タイトーって印象がないというか真面目にセガだと思ってた(笑) 別に西口のセガのと思い違いしてたわけでもなく素で。それで、東口のほうにはインベーダー(=タイトー)の印象があったので、ほぅほぅ、あんなところも南口に含めるのか、とか思ってたとか思ってたとか。悲しい。ゲーセンの店名って意識したことないのよね、あまり行かない人なので。

アーケード版のゲーム内容ですが、普通にexceptionでした。ステージは若干アレンジされていたり、オリジナルステージがあったりなどですが、内容は全くそのままPC版のexception。処理落ちなどもなく、ヌルヌルと動いておりました。ただし破片量はノーマルか、それより少ないか、といった感じで若干の寂しさは否めない。

気になる操作方法はジョイスティックとボタン3つ。全方位STGでスティック一本ということで、ボタン一つが方向 転換用の固定ボタン(PC版のディレクション)に割り当てられていて、これがぶっちゃけ操作しづらい。残りのボタンはレーザーとレイ、二つを(一度離して)同時押しでカタパルト。このカタパルト動作が筋力貧弱な私には存外辛くて、終わり頃には腕が疲れちゃってた。カタパルト→カタパルト→カタパルトと連打するわけなので、ワンボタンで出したいなあ、と思いつつも、まあボタンあまってないししょうがないね。

そんなこんなだから、というわけじゃないのですが、あえなくPC版3面ボス(アーケードでは5面、だったかな?)で死亡してスコアは5位でした。ランキング一位は、PC版作者のi-saint氏のよう。i-saint氏が初見で?クリアしたようなので、なら私も!とか意気込んでいたのですがズタボロ。いてれーたんに普通にいいように弄ばれるという微笑ましさを見せつけました。

ステージ構成はPC 版->アーケードオリジナル->PC版……と交互のようです。PC版は若干短く簡素にアレンジ。アーケードオリジナルのほうは破片の少なさを補うためにギミック中心の構造になっていて新鮮で面白い。ボスも同様に飛び道具系、というか変形する。アーケードオリジナルボス2体目の倒し方が分からなかくてライフが半分減るまで無意味なことやってたり。新鮮なのはよいことです。

全体的には、昔のアーケード→ファミコン移植のような印象。おお、頑張ってる!という。破片量はしょうがないとしても、操作し辛いのはどうしたものかなー。今回私は3ボタン使う上級者向け操作を選んだので、2ボタン操作の初心者向け操作だと印象違うかもしれません。そういえば、エフェクトも弱めなのでカタパルトの爽快感が薄かったような気がする。回りを破片で囲まれてピンチをカタパルトで一気に突破!的なのが、画面が真っ白になって抜け出せたのか何なのかわけわからないというか自機が本当に白に埋まって見失う、のはどうなのだろう、慣れかな?ジオメトリとかも見失うって言われるわけだし。

とはいえ普通にexceptionだったので普通に面白いと思います。オリジナル面も良い感じだし。PC版に比べてどうよ?と言われると完全にノーコメント(すみません)ですけど、ジョイスティックでやるのも新鮮、ということで遊びに行ける人は遊びに行くと良いやも。とりあえず私は途中で死亡したのが悔しいので、明日も行きます、というかロケテ中にはクリアしたい。(それにしても音楽も効果音も全く聞こえなかった、周囲の音うるさすぎ、私はひなびた田舎のゲーセンが好きですよ(笑))

あと、せっかくなので明後日、7/12の日曜日の22:00頃からUstreamでexceptionとexception conflictを実況プレイしようと思うので良ければ見てやってください。

ver 1.3.0.0 ロジック変更/OAuth対応

公開から半年以上経って、何故かここ数週間で利用者が増えてきたようで同時に動かない報告も目にするようになって嬉しくも悲しい。なので、今回ロジック部分を完全に書き換えました。これで色々な環境でも動作するようになった、はず、です。以前に試して動かなかったという方は、再度試していただけると嬉しいです。ついでに取得処理も軽量化しました。真面目に10倍ぐらいは速くなってます(今までが酷過ぎただけなんだけどね!)。そんなこんなで手を入れた部分が多いので、(ただでさえ不安定との定評があったのに)安定性はむしろ下がった、かも……。

それと、認証がパスワード方式からOAuth方式に変更になりました。ユーザーにとってOAuth対応によるメリットは、クライアントアプリではぶっちゃけ別にないどころかむしろ色々な不都合のほうが多いのですが私がやりたかった、ということで勘弁してください。投稿にfrom XboxInfoTwitってクライアント名が表示されるようになって嬉しいのです、にひひ。自動アップデート経由の方は、認証を一度済ませなければならないので手間です、すみません。

あ、で、ver1.2からそうなのですが、現在はAPIは一切利用していませんので遅延はありません。Xbox.comのステータス反映速度(1分以下で追随しているようです)で処理していますので、原理的には他の同種のアプリと同じです。が、多機能風味なため、巡回ページ数が多いのでチンタラ通信してたりする時があって、そのせいで反映が遅いという印象を持たれるかもしれません。今回の軽量化で若干印象は変わると思いますがそんなに変わらないかもしれません。その他、投稿の法則やFAQは同梱の説明書きを見てください。

↑通信内容はこんな感じです(Fiddlerで観測)。30秒かかりました。結構長いけれど、Xbox.comが重いサイトだからねえ……。まずフレンドリストの取得、そこからフレンドのフレンドに飛んで自分のステータスを取得(取得できない場合は取得出来るまで次のフレンドに飛ぶ)、トップページに飛んでプレイ中ゲームのURLを取得、プレイ中ゲームのページから実績を取得。という流れです。これがステータス取得の最短パスじゃないかと思っています。自分のステータスがFriendsOfFriend経由でしか見れないというのが意味不明で困るところ。

そういえばでどうでもいいんですが、タスクバー右クリックに「ゲイツポイントを買う」が追加されてます。アフィです、アフィ。んーと、ノーコメント。

XboxInfoTwit ver 1.2.0.0 フルスクレイピングに変更

Xbox.comのサイトがリニューアルされましたね!フレンドのフレンド表示機能のお陰で、ついに念願かなって自分自身のステータス取得に成功。野良APIを使わずに全てスクレイピングで実装することができました。よって、今回からは100%取りこぼしなく、ステータスの変更も瞬時に反映されます。他に変更点としては日付が投稿時の時間になりました(Xbox.com上からは時間を取れるものがないので) あと、書式に実績の数を追加しました。”%TitleTotalAchievementCount%:タイトルの最大実績数”, “%TitleAchievementCount%:取得した実績数” です。この変のよくわからないうえに長ったらしい名前はとても後悔しています……。

バグがあったら教えてください。どう見てもテスト不足なのでサクッと落ちるかもしれません。(言ってるそばから不具合発見しました。おまけに、zipファイルに格納するファイルが足りなかったので新規にダウンロードした場合は動かなかったっぽい。酷すぎる。というわけでver 1.2.0.1になりました。)

ver 1.1.0.1 日本語が含まれる場合に正しく投稿されない問題を修正

neue cc - XboxInfoTwit

表題通りの修正、です。これが酷い話で、大往生を買った頃に不具合が発覚していたというのに、今の今まで放置していたという。指摘があって(ありがとうございます!) ようやく重たい腰をあげて……。しかも大体どこが問題発生の場所なのか検討ついてたし、事実その通りだったし、しかも一行書き換えるだけで終わる程度の話だったうえに、不具合原因が超凡ミスだったり、それなのに今の今まで放置していたし、と二重三重に酷い話です。

もう本当にごめんなさいとしか言いようがない。

ver.1.1.0.0

neue cc - XboxInfoTwit

更新内容

  • 実績取得元を変更(取得漏れの大幅低減)

あまりにも取りこぼしが多いので色々と変えました。これでほぼ100%反映されていくはず、です。

Xbox360のステータスをTwitterに投稿するツール ver1.0

neue cc - XboxInfoTwit

これ何?

Xbox360のステータス(ジオメトリをプレイ中だとか実績解除したとか)をTwitterに投稿するアプリケーション。xboxstatusがあるのに、前回に引き続き車輪の再発明。最近はxboxtweetというそのものズバりなサービスまで出てきて、ますます存在価値が……。メリットはTwitterのパスワードを他人に預けなくて済むことと書式や投稿条件を細かにカスタマイズできること。デメリットはPCを起動していなければならないこと。ああ、あとメリットに実績が原則的には獲得したもの全て投稿される(確かxboxtweetは30分以内に獲得したもののうち最新一つだけ)という点も、あるには、ある。

良いところと実例

投稿内容の細かいカスタマイズのほかに、投稿条件も「5種類+ダッシュボードは無視」を用意しました。実績解除時のみ投稿することや、電源オン時と電源オフ時のみ投稿することなど、自分のTwitterスタイルに合わせて設定出来ます。なお、「状況が変わった時」の投稿を本アカウントで行うのはお薦めしません、ウザいほど投稿が多くなるので。別アカウントを用意しておくと1日のプレイ状況が確認出来て結構楽しいです。私は。例→http://twitter.com/SeaWeeze_Xbox 別ディレクトリにアプリケーションを用意することで複数個の起動も可能なので、本アカウント用と専用アカウント用に複数起動すると楽しい、かも、しれない、意味ないだけ、かもしれない。

前作のはてなついったー同期ツールが華麗に自爆しただけに(使ってるの私だけ) 今回は利用者、せめて2桁は行くといいな、と、悲しくも等身大に思っています。外見は前作と似てますけど中身は完全に書きなおしてますので、わりと使いやすいと思います。設定ウィンドウの拡大で入力確認欄がウニーッと伸びるので長い書式も入力しやすい、とか自動アップデート機能搭載とか入力補助ボタンとか。地味ですね。ていうか一度設定したらタスクバー引き籠りなアプリで違いなんてそんなに出せません。

| Next

Search/Archive

Category

Profile


Yoshifumi Kawai
Microsoft MVP for Visual Studio and Development Technologies(C#)

April 2011
|
July 2018

Twitter:@neuecc
GitHub:neuecc
ils@neue.cc