Category: C#

OpenCVで虹顔の識別(※ただし表情がとぼしくて可愛いおにゃのこに限る)

このエントリーをはてなブックマークに追加
はてなブックマーク - OpenCVで虹顔の識別(※ただし表情がとぼしくて可愛いおにゃのこに限る)

はい、ちょっと***な諸事情で、画像中に全く同じ画像が含まれているかを調べる必要があったのですが、自力ではうまく行かず、
OpenCV使ったら一瞬で出来てしまって不覚にも感動してしまいました。
、という更新。

こんな紛らわしい画像(右側は微妙に口の形が違う)も「違う」と判断してくれてさすがです。

 

、何がしたかったのかというと、虹画像の表情を判断したかったわけで、
さらにその表情ごとの画像は一意に決まってるというまあなんともレアケース。

笑ってるときは画像01、泣いてるときは画像02、みたいに。
表情が乏しいとも言います、はい。

むすっとした顔の穹ちゃんはかわいいけどね!!

、これらの画像から、下のおにゃのこの表情が何なのかを調べてみますと、
    

まあよく分かりませんが「一番左の画像が完全に一致」、と判定してくれるわけです。
OpenCV便利すぎくそわろうた、です。

今回はなんとなくC#でOpenCvSharp使いました。
OpenCv:2.1
.NET:4.0
公式のサンプル引っ張ってきただけです、はい。

要なのはこの2行だけで、

Cv.MatchTemplate(target, template, dst_img, MatchTemplateMethod.CCorrNormed);
Cv.MinMaxLoc(dst_img, out  min_val, out  max_val, out  min_loc, out  max_loc, null);

MatchTemplateで各点の相関係数をdst_imgに、
MinMaxLocで相関係数の最大値と最小値とそれぞれの位置を取得できるみたいです。

相関係数が1に近いほど正の相関があるので1に近ければ近いほど、類似の画像が含まれているということになります。
相関係数が1のときは完全に一致。

似たような画像でも0.998とか高めの値が出たので今回はしきい値を0.999にしました、テキトウw

using System;
using System.Collections.Generic;
using System.Linq;
using OpenCvSharp;
using System.IO;

namespace CVSharpTest
{
    class Program
    {        
        static void Main(string[] args)
        {
            //templateフォルダ以下にテンプレート画像を入れておく。
            IEnumerable<string> tempFiles = Directory.EnumerateFiles("./template","*",SearchOption.TopDirectoryOnly);
            Dictionary<string, double> NCCPair = new Dictionary<string, double>();

            IplImage target = IplImage.FromFile("target.png",LoadMode.Unchanged);            
            IplImage template;
           
            foreach (string file in tempFiles)
            {                
                template = IplImage.FromFile(file, LoadMode.Unchanged);
                double ncc = GetNCCMax(target,template);

                //対象画像からテンプレート画像を検索して表示する
                TemplateMatch(target, template);
               
                NCCPair.Add(file, ncc);
                Console.WriteLine("{0}:{1}", file, ncc);
            }

            //「相関係数の最大値が一番高いもの」が一致画像の可能性が高い
            var result = NCCPair.OrderByDescending((val) => val.Value).First();
            Console.WriteLine("MAX:{0} {1}",result.Key,result.Value);
           
            return;
        }
       
        /// <summary>
        /// 相関係数の最大値を計算する
        /// </summary>
        private static double GetNCCMax(IplImage target, IplImage template)
        {
            IplImage dst_img;                        
            double min_val, max_val;
            CvPoint min_loc, max_loc;
            CvSize dst_size;

            dst_size = new CvSize(target.Width - template.Width + 1, target.Height - template.Height + 1);
            dst_img = Cv.CreateImage(dst_size, BitDepth.F32, 1);
            Cv.MatchTemplate(target, template, dst_img, MatchTemplateMethod.CCorrNormed);
           
            //相関係数の最大値と最小値を取得
            Cv.MinMaxLoc(dst_img, out  min_val, out  max_val, out  min_loc, out  max_loc, null);
           
            Cv.ReleaseImage(dst_img);

            //相関係数の最大値を返す
            return max_val;
        }

        /// <summary>
        /// 対象画像からテンプレート画像の位置を探す
        /// </summary>        
        private static double TemplateMatch(IplImage target, IplImage template, double limen=0.999)
        {
            IplImage dst_img;
            double min_val, max_val;
            CvPoint min_loc, max_loc;
            CvSize dst_size;

            dst_size = new CvSize(target.Width - template.Width + 1, target.Height - template.Height + 1);
            dst_img = Cv.CreateImage(dst_size, BitDepth.F32, 1);
            Cv.MatchTemplate(target, template, dst_img, MatchTemplateMethod.CCorrNormed);

            Cv.MinMaxLoc(dst_img, out  min_val, out  max_val, out  min_loc, out  max_loc, null);

            if (max_val > limen)            
                Cv.Rectangle(target, max_loc,new CvPoint(max_loc.X + template.Width, max_loc.Y +template.Height),new CvScalar(0,0,250),3);                
           
            Cv.NamedWindow("Image", WindowMode.AutoSize);
            Cv.NamedWindow("Image2", WindowMode.AutoSize);
            Cv.ShowImage("Image", target);
            Cv.ShowImage("Image2", template);

            Cv.WaitKey(0);
            Cv.DestroyWindow("Image");
            Cv.DestroyWindow("Image2");  
            Cv.ReleaseImage(dst_img);

            return max_val;
        }
    }
}

今回の本当の目的はちょっとぴーぴーぴーなことなので詳しくはまあアレなのですが、
OpenCVの便利さ、そして手軽さはすごいなあと。

このエントリーをはてなブックマークに追加
はてなブックマーク - OpenCVで虹顔の識別(※ただし表情がとぼしくて可愛いおにゃのこに限る)

ListViewコントロールで文字列による ListViewItemの検索・列挙

このエントリーをはてなブックマークに追加
はてなブックマーク - ListViewコントロールで文字列による ListViewItemの検索・列挙

標準で用意されてると思いきやなかった。ぐぐっても気に入ったものひっかからないのでメモ。
C#3.0以上の拡張メソッド。ListViewItemのテキストまたはSubItemsのテキストから一致するものを検索。

static class Extensions
{
    public static ListViewItem[] Find(this ListView listView, string keyWord)
    {
        return FindSubItems(listView, keyWord, 0);
    }

    public static ListViewItem[] FindSubItems(this ListView listView, string keyWord, int index)
    {
        List<ListViewItem> hitItemList = new List<ListViewItem>();
        foreach (ListViewItem item in listView.Items)
            if (item.SubItems[index].Text.Contains(keyWord))
                hitItemList.Add(item);

        return hitItemList.ToArray<ListViewItem>();
    }
}

拡張メソッドの禁じ手とか知らないのだがこれはいいのだろうか。
元から同じ名前のメソッドがある場合は元のほうが優先して呼び出されるようだ。

検索して引っかかった項目だけ表示~っていうのがやりたかっただけ。
というか項目の表示・非表示を切り替えられればいのだが。

一回項目を全て保存しておいて、項目全部消して検索結果の項目追加して~もう一回検索するときは消して追加して・・・っていう流れが適当

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    List<ListViewItem> itemList = new List<ListViewItem>();

    private void Form1_Load(object sender, EventArgs e)
    {
        itemList.Add(new ListViewItem(new string[] { "1", "灼眼のシャナ"}));
        itemList.Add(new ListViewItem(new string[] { "2", "sola" }));
        itemList.Add(new ListViewItem(new string[] { "2", "灼眼のシャナII" }));
        itemList.Add(new ListViewItem(new string[] { "3", "true tears" }));
        itemList.Add(new ListViewItem(new string[] { "4", "そらのおとしもの" }));
        itemList.Add(new ListViewItem(new string[] { "5", "とらドラ!" }));
        itemList.Add(new ListViewItem(new string[] { "6", "劇場版 灼眼のシャナ" }));

        listView1.Items.AddRange(itemList.ToArray());
    }

    private void textBox1_TextChanged(object sender, EventArgs e)
    {
        string keyWord = textBox1.Text;

        listView1.Items.Clear();
        listView1.Items.AddRange(itemList.ToArray<ListViewItem>());

        ListViewItem[] hitItem = listView1.FindSubItems(keyWord, 1);
        listView1.Items.Clear();
        listView1.Items.AddRange(hitItem);
    }
}

このエントリーをはてなブックマークに追加
はてなブックマーク - ListViewコントロールで文字列による ListViewItemの検索・列挙

C#でOAuth認証でTwitterにpost

このエントリーをはてなブックマークに追加
はてなブックマーク - C#でOAuth認証でTwitterにpost

【追記】2010/5/12 生成後のsignatureをパーセントエンコードしていなかったミスを修正
signature生成に必要なパラメータは大文字、signature自体は小文字でURLエンコードする必要があったようだ。

————————————————————————-
C#でOAuth認証でTL取得とほとんど変わらない。
signatureを生成するとき、signature生成前にstatus(post本文)をURLエンコードしておかないとうまく認証されないみたい。

これでひとまずぼとすたやらシャナbotのOAuthへの移行へめどが立った。
おかしなところ指摘していただけると助かります。

Read more »

このエントリーをはてなブックマークに追加
はてなブックマーク - C#でOAuth認証でTwitterにpost

C#でOAuth認証でTL取得

このエントリーをはてなブックマークに追加
はてなブックマーク - C#でOAuth認証でTL取得

【追記】2010/5/12 生成後のsignatureをパーセントエンコードしていなかったミスを修正
signature生成に必要なパラメータは大文字、signature自体は小文字でURLエンコードする必要があったようだ。

————————————————————————-

tokenとtoken_secretが取得できたところで、TwitterAPIをOAuth認証で利用してタイムラインを取得してみた。
basic認証とは違ってsignatureの生成が面倒だが、そのほかはほとんど変わらない。

まずは以下のパラメータと、consumer_secret,token_secretを利用してsignatureを生成する。

  • oauth_consumer_key(登録時に発行)
  • oauth_nonce(ランダムな毎回異なる8文字以上の英数字)
  • oauth_token(ここで取得)
  • oauth_version(“1.0″を指定)
  • oauth_timestamp(取得時間をUNIX時刻で)
  • oauth_signature_method(“HMAC-SHA1″を指定)
  • 各種APIで使うパラメータ(pageとかsince_idとか)

signatureの生成には、各種APIで使うパラメータ(pageとかsince_idとか)も含めたパラメータを使う。

signatureBaseはこんなかんじになる。
詳しくは検索で・・・だが、HttpMethodとエンコード済みのURL、エンコード済みのパラメータを連結したものを連結。
ここらへんが参考になった。


GET&http%3A%2F%2Ftwitter.com%2Fstatuses%2Ffriends_timeline.xml&oauth_consumer_key%3DCONSUMER_KEY%26oauth_nonce%3DNONCE%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3DTIMESTAMP%26oauth_token%3DTOKEN%26oauth_version%3D1.0%26page%3D2



ハッシュ値を求める際のキーには、consumer_secreとtoken_secretをURLエンコードし、&で結んだものを使用する。
signatureを生成したら、上記パラメータとsignatureをAPIに送るだけ。

例えばこんな感じで送る。


oauth_consumer_key=CONSUMER_KEY&oauth_nonce=NONCE&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1272290458&oauth_token=TOKEN&oauth_version=1.0&page=2&oauth_signature=CaQiJNAfkdIlfb3MxeF1F0P+jqg=

以下、コード。とりあえず動くレベル。


class OAuthTwitter
{
    const string consumer_key = "**********************";
    const string consumer_secret = "**********************";
    const string token = "**********************";
    const string token_secret = "**********************";

    //APIのURLとパラメータでAPIにアクセス
    private string GetAPI(string APIURL)
    {
        return GetAPI(APIURL, null);
    }

    //APIのURLとパラメータでAPIにアクセス
    private string GetAPI(string APIURL, Dictionary<string, string> query)
    {
        string result, queryString;
        result = queryString = string.Empty;

        //signature生成
        string signature = GenerateSignature(APIURL, "GET", query, consumer_secret, token_secret, out queryString);

        //生成したsignatureは小文字でパーセントエンコード
        string postString = queryString + string.Format("&oauth_signature={0}", UrlEncodeSmall(signature));

        //取得開始    
        byte[] data = Encoding.ASCII.GetBytes(postString);        
        WebRequest req = WebRequest.Create(string.Format("{0}?{1}",APIURL,postString));
        WebResponse res;
        try
        {
            res = req.GetResponse();
            Stream stream = res.GetResponseStream();
            StreamReader reader = new StreamReader(stream);
       
            result = reader.ReadToEnd();
            reader.Close();
            stream.Close();
        }
        catch (WebException ex)
        {
            if (ex.Status == WebExceptionStatus.ProtocolError)
            {
                if (((HttpWebResponse)ex.Response).StatusCode == HttpStatusCode.Unauthorized)
                {
                    /*401 Unauthorized                    
                    *認証失敗*/

                    return "401 Unauthorized";
                }
                else if (((HttpWebResponse)ex.Response).StatusCode == HttpStatusCode.BadRequest)
                {
                    /*400 Bad Request                    
                    *リクエストが不正*/

                    return "400 Bad Request";
                }  
            }
            else
            {
                return ex.Message;
            }
                   
        }
        return result;
    }

    //APIのURL,HttpMethod(POST/GET),パラメータ,consumer_secret,token_secretでsignature生成
    private string GenerateSignature(string url, string httpMethod, Dictionary<string, string> query, string consumer_secret, string token_secret, out string conectedQuery)
    {
        //SortedDictionaryでパラメータをkey順でソート
        SortedDictionary<string, string> sortedParams;
        if(query==null)
            sortedParams = new SortedDictionary<string, string>();
        else
            sortedParams = new SortedDictionary<string, string>(query);
   
        string timestamp = GenerateTimestamp();
        string nonce = GenerateNonce();
   
        sortedParams["oauth_consumer_key"] = consumer_key;
        sortedParams["oauth_token"] = token;
        sortedParams["oauth_version"] = "1.0";
        sortedParams["oauth_timestamp"] = timestamp;
        sortedParams["oauth_nonce"] = nonce;
        sortedParams["oauth_signature_method"] = "HMAC-SHA1";
   
        StringBuilder sb = new StringBuilder();
        bool first = true;
        foreach (var p in sortedParams)
        {
            if (first)
            {
                sb.Append(p.Key + "=" + p.Value);
                first = false;
            }
            else
                sb.Append(@"&" + p.Key + "=" + p.Value);
        }
        conectedQuery = sb.ToString();
        sstring signatureBace = string.Format(@"{0}&{1}&{2}", httpMethod, UrlEncode(url),UrlEncode(sb.ToString()));

        //consumer_secretとtoken_secretを鍵にしてハッシュ値を求める
        HMACSHA1 hmacsha1 = new HMACSHA1();
        hmacsha1.Key = Encoding.ASCII.GetBytes(string.Format("{0}&{1}", UrlEncode(consumer_secret), UrlEncode(token_secret)));
        byte[] dataBuffer = System.Text.Encoding.ASCII.GetBytes(signatureBace);
        byte[] hashBytes = hmacsha1.ComputeHash(dataBuffer);
   
        return Convert.ToBase64String(hashBytes);
    }

    private string GenerateNonce()
    {
        string letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        StringBuilder result = new StringBuilder(8);
        Random random = new Random();
        for (int i = 0; i < 8; ++i)
            result.Append(letters[random.Next(letters.Length)]);
        return result.ToString();
    }

    private string GenerateTimestamp()
    {
        TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
        return Convert.ToInt64(ts.TotalSeconds).ToString();
    }

    private string UrlEncode(string value)
    {
        string unreserved = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
        StringBuilder result = new StringBuilder();
        byte[] data = Encoding.UTF8.GetBytes(value);
        foreach (byte b in data)
        {
            if (b < 0x80 && unreserved.IndexOf((char)b) != -1)
                result.Append((char)b);
            else
                result.Append('%' + String.Format("{0:X2}", (int)b));
        }
        return result.ToString();
    }
    private string UrlEncodeSmall(string value)
    {
        string unreserved = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";

        StringBuilder result = new StringBuilder();
        byte[] data = Encoding.UTF8.GetBytes(value);
        foreach (byte b in data)
        {
            if (b < 0x80 && unreserved.IndexOf((char)b) != -1)
                result.Append((char)b);
                else
                result.Append('%' + String.Format("{0:x2}", (int)b));
            }
        return result.ToString();
        }
    }

class Program
{
     static void Main(string[] args)
    {
        System.Net.ServicePointManager.Expect100Continue = false;
       
        Dictionary<string,string> query = new Dictionary<string,string>();
        query["page"] = "2";
        OAuthTwitter oauthTwitter = new OAuthTwitter();      

        //TwitterAPIを利用してタイムライン取得
        Console.WriteLine(oauthTwitter.GetAPI("http://twitter.com/statuses/friends_timeline.xml",query));      
   }
}

このエントリーをはてなブックマークに追加
はてなブックマーク - C#でOAuth認証でTL取得

C#でTwitterのoauth_token取得

このエントリーをはてなブックマークに追加
はてなブックマーク - C#でTwitterのoauth_token取得

Twitter APIのBASIC認証は2010年6月に廃止予定、とのことで、
botたちを延命させるためにもOAuthへの乗換えを決めた。

ゼロから学ぶOAuth@IT OAuthを知るでなんとなくOAuthについては分かりそうだ。

consumer_keyとconsumer_key_secret、oauth_tokenとoauth_token_secretの4つが手に入ればTwitterAPIに今までどおりアクセスできる。
oauth_tokenとoauth_token_secretは1度取得すれば今のところ無期限に使えるらしい。(保存しておけば)

と、いうことでGoogleのOAuthBase.csを用いてauth_tokenを取得してみた。

OAuthBase.csのおかげでかなり楽ちんに取得できます。
送るべきパラメータやら順番はここが参考になりました。
Read more »

このエントリーをはてなブックマークに追加
はてなブックマーク - C#でTwitterのoauth_token取得

C#で2chのスレッドdat取得(差分も)

このエントリーをはてなブックマークに追加
はてなブックマーク - C#で2chのスレッドdat取得(差分も)

C#で2chのSubject.txt取得に続いて、今回は個々のスレッドのdat取得。

前回とほとんど変わらないが差分の取得やらdat落ち判定やらで僅かに長くなった。

datファイルはhttp://サーバ/板名/dat/スレッド番号.datにある。

(例)http://yutori7.2ch.net/news4vip/dat/1269850034.dat

初回dat取得時に応答ヘッダのLast-Modified(更新時刻)とRange(ファイルサイズ)を取得、次回それを付加して差分のみ取得。


※わざわざ差分取得せず毎回全部取得も出来る。


Read more »

このエントリーをはてなブックマークに追加
はてなブックマーク - C#で2chのスレッドdat取得(差分も)

C#で2chのSubject.txt取得

このエントリーをはてなブックマークに追加
はてなブックマーク - C#で2chのSubject.txt取得

botにも使っていたのだがこの際整理。

スレタイ一覧(subject.txt)はhttp://サーバ/板名/subject.txtにある。
(例)http://yutori7.2ch.net/news4vip/subject.txt


というわけでコード。

public string GetSubjects(string URL)
{
    //正規表現でサーバーと板の名前取得
    Match m = Regex.Match(URL, @"http://(.*?\.2ch\.net)/(news4vip)/");
    string server = m.Groups[1].Value;
    string board = m.Groups[2].Value;


    //リクエスト作成
    Encoding enc = Encoding.GetEncoding(932);
    HttpWebRequest req = null;
    try
    {
        req = (HttpWebRequest)WebRequest.Create(string.Format("http://{0}/{1}/subject.txt", server, board));
    }
    catch
    {
        return null;
    }

    // ポストデータ作成
    req.Method = "GET";
    req.UserAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.0; ja; rv:1.9.1.8) Gecko/20100202 Firefox/3.5.8 GTB6 (.NET CLR 3.5.30729)";
    req.Referer = string.Format("http://{0}/{1}/", server, board);
    req.Accept = "gzip";
    req.Timeout = 20000;


    //レスポンス取得、読み込み
    string dat = null;
    try
    {
        WebResponse res = req.GetResponse();
        Stream resStream = res.GetResponseStream();
        StreamReader sr = new StreamReader(resStream, enc);
        dat = sr.ReadToEnd();
        sr.Close();
        resStream.Close();
    }
    catch
    {
        return null;
    }
           
    //dat本文を返す
    return dat;
}

簡単にスレッド一覧が取得できる。

GetSubjects("http://yutori7.2ch.net/news4vip/")

などとすれば

1269606079.dat<>【来たれ声優】声劇つくっちゃおうぜ!【立てよ企画】 (171)
1269760693.dat<>好きな娘に一人でパフェ食ってるところ見られたwwwwwwwww (162)



UNIX時間.dat<>スレタイ(レス数)\n

と返ってくる。


参考:と~く2ちゃんねる

このエントリーをはてなブックマークに追加
はてなブックマーク - C#で2chのSubject.txt取得

C#よさげな書籍メモ

このエントリーをはてなブックマークに追加
はてなブックマーク - C#よさげな書籍メモ

本屋に行ってよさげな本を見つけた。
アソシエイトの規約によれば新しいサイトでアフィやるときは申請が必要らしい。

ということでただのリンクにしておく。

.NETのクラスライブラリ設計 開発チーム直伝の設計原則、コーディング標準、パターン (Microsoft.net Development Series)
.NETのクラスライブラリ設計 開発チーム直伝の設計原則、コーディング標準、パターン (Microsoft.net Development Series)


C# .NETアプリケーション開発 徹底攻略 C# 3.0/.NET Framework 3.5対応
C# .NETアプリケーション開発 徹底攻略 C# 3.0/.NET Framework 3.5対応

このエントリーをはてなブックマークに追加
はてなブックマーク - C#よさげな書籍メモ

無能風BOT完成

このエントリーをはてなブックマークに追加
はてなブックマーク - 無能風BOT完成

ひとまず完成。
主にMeCab+Yahoo形態素分析API+SQLiteを利用した。



MeCabの辞書にwikipediaのタイトルとHatenaKeyWordを追加、んで2chのSSスレから会話分6万行ほど抜き出してそれをMeCabで分かち書き、マルコフ連鎖用テーブル作ってDBにぶち込んだ。

で、あとはTLからpostを取得、その文章とDBを元にマルコフ連鎖で新たな文章を生成、post。

 


近々このブログを知り合い等に公開する(・・・のだろうか)ことも考えてIDは乗せないことにしよう。


しかし全く新しい文章が出来たときのwktkは半端じゃないな


反捕鯨団体シー・シェパードの元船長、ピーター・ベスーン容疑者(44)が、調査捕鯨船団の監視船「第2昭南丸」に侵入した事件で、海上保安庁は14日午前、ベスーン容疑者を艦船侵入容疑で東京地検に送検した。ベスーン容疑者の身柄は同日中に、東京・葛飾区の東京拘置所に移される。


反捕鯨団体シー・シェパードの元メイドに会うとそんなに嫌そうなのか…ま、まあいいや、やっぱり地の文のやりとりの中に出して食べてみます

いまどうしてる?

いまどうした所で何がなんだかわからねぇが敵の隙に小用を済まそうと思っているだけで完全に私を笑わせてあげるよね、その………

2chのSSスレから会話分6万行ほど抜き出してそれをMeCabで分かち書き

2chのアニメキャラ板のビリビリスレ、mixiのビリビリコミュ、その他ビリビリ関係の無い人を好きになってますぅ!!

結局のところ最初の2単語しか利用してない。
若干、というかしばしば電波的な内容になるゆえBOTだと疑う人も多いだろう。



意味不明な文章をできるだけ減らす方向で行くか、それとも電波多めを目指すか。
今後の方向性を定めなければ。



できるだけ減らすなら・・・やはり”学習”させることが必要だな
「不適切な文章を報告」みたいに、1文ずつこれはいい、これはだめって評価して、単語のつながりの”正しさ”をパラメータ化しようか。

いずれにしてもDB再再再再構築だけはゴメンだ。方向性と仕様を定めよう。


 


今後の目標


  • まともな文章を出力するようにする

このエントリーをはてなブックマークに追加
はてなブックマーク - 無能風BOT完成

C#でTwitterAPI使って(417) Expectation Failed

このエントリーをはてなブックマークに追加
はてなブックマーク - C#でTwitterAPI使って(417) Expectation Failed

人工無能風twitterBOTも完成、さてさて試しに動かすか、というところで地震。

ついでにTL遅延。

APIからは正常に取得できてるのか・・・な?

で、テスト稼動したところ、


リモート サーバーがエラーを返しました: (417) Expectation Failed


そういえば前回もこんなことがあったがすっかり忘れていた。

100-Continue 応答?なるものがあるらしく、.NETからtwitterにPostするときは


System.Net.ServicePointManager.Expect100Continue = false;

とすれば問題ない。

このエントリーをはてなブックマークに追加
はてなブックマーク - C#でTwitterAPI使って(417) Expectation Failed

Dansette