Edited at

CoreTweetでPIN番号を手入力せずに認証

お久しぶりです.N.Mです.最近は趣味でTwitter APIを使ったGUIアプリケーションをC#で作ってます(C#だとGUIアプリケーションが作りやすいと思ったので).今後不定期で,その制作過程で大変だったことを発信していければと思います.今回はその第1弾.

(次回はウィンドウサイズの調整について

今はこんな感じの,直近のツイートから誰がTL上にいるのかをリストアップするアプリを作っています.

(アプリケーション自体は,ここで配布しております.今後も更新予定です!)

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

TLMem.PNG


CoreTweetの認証

Twitter APIについてはC#のライブラリ"CoreTweet"を利用していまして,ここにも認証の仕方が書いてあるのですが,そのアカウントでアプリを使うことを認証した後に,7桁のPINコードが表示されます.APIをつかうために,そのPINコードを手動で入力するか,HTML解析するかしてOAuth.GetTokensに渡す必要があります.手動で入力する方法はたくさん情報が出てきたので,最初はそれを採用してましたが,使ってるうちに手入力が面倒になったので,今回 HTML解析による取得 に挑戦しました.


環境・前提


  • Visual Studio 2017で開発

  • Twitter APIの利用のためにCoreTweetを使用

  • WPFアプリケーションを制作(Formsではない)


仕組み

認証ページは最初のURLはhttps://api.twitter.com/oauth/authorizeの後にいろいろ文字列がついた状態になっています.そして,認証してもしなくても,最終的にURLがhttps://api.twitter.com/oauth/authorizeになります.認証した場合はHTML内に

<code>7桁のPINコード</code>

が埋め込まれています(そして<code>タグはページのこの部分にしか出てきません).なので,やるべきこととしては


  1. 認証ページを表示して,ユーザに認証させる

  2. ページのリンクがhttps://api.twitter.com/oauth/authorizeになったら,ページのHTMLコードを取得する.

  3. HTMLソースから<code>の文字列を検索し,あれば後ろの7桁のPINコードを取り出し,認証する(なければ認証失敗のエラーを出す).

となります.


1. 認証ページを表示させる

CoreTweetの日本語WikiだとSystem.Diagnostics.Process.Startで認証ページを表示していますが,HTMLソースを抜き取るのが難しそうだったので,認証ページの表示はWPFアプリケーションにWebBrowserのコンポーネントを埋め込むことにしました('twitterWeb'と命名しました).で,WebBrowserNavigateメソッドで認証ページのURLに飛べば表示できます.

TLMemAuth.PNG

こんな感じ.ここから,ユーザが認証したら,すぐにAPIが使える状態にしていきます.


2. ページのURIの取得,HTMLコードの取得

ここでいろいろつまづきました.つまづいたポイントとしては


  • WebBrowserがnullで参照できなかった.

  • WPF(System.Windows.Controls
    )ではなく,Formsのほう(System.Windows.Forms)の情報ばかり出て混乱した.

  • WPFのWebBrowserからHTMLを抜き取る方法が分からなかった.

の3点ですね.

まず,1点目の参照の問題は,Navigateメソッドを呼び出したあとに,WebBrowserのURLを確認しようとしたんですけど,そのタイミングだと,まだ処理が完了してないのでnullが返ってきてしまうそうです.なので,WebBrowserの処理が完了した際に呼ばれるイベントハンドラLoadCompletedで,もろもろの処理を行います.

twitterWeb.Navigate(session.AuthorizeUri);

LoadCompletedEventHandler twitterWebUpdate = null;

twitterWebUpdate = (tSender, e) =>
{
string pin = "";
string finishedUrl = @"https://api.twitter.com/oauth/authorize";
string url = twitterWeb.Source.AbsoluteUri;
if (url == finishedUrl)
{
//ここにHTMLソースの取得とPINコードの抽出処理を実装
}
};
twitterWeb.LoadCompleted += twitterWebUpdate;

finishedURLに認証後に行くはずのページのURLを,URLtwitterWebの現在のページのURLを格納しておき,この2つが一致したら認証後のページに行ったと判断します.

WebBrowserからのHTMLの取得ですが,Visual StudioのプロジェクトでCOMのmshtmlを参照するようにして,mshtml.IHTMLDocument2を介して,取得するようです(参考).

HTML抽出部分を加えるとこんな感じ

twitterWeb.Navigate(session.AuthorizeUri);

LoadCompletedEventHandler twitterWebUpdate = null;

twitterWebUpdate = (tSender, e) =>
{
string pin = "";
string finishedUrl = @"https://api.twitter.com/oauth/authorize";
string url = twitterWeb.Source.AbsoluteUri;
if (url == finishedUrl)
{
mshtml.IHTMLDocument2 doc = (mshtml.IHTMLDocument2)twitterWeb.Document;
string html = doc.body.innerHTML;
//ここにPINコードの抽出処理を実装
}
};
twitterWeb.LoadCompleted += twitterWebUpdate;


3. PINコードの抽出

ここまでできれば,あとはhtmlから<code>の文字列をIndexOfメソッドで検索して,

パパパっとSubstringメソッドでPINコードの7桁の数字を抽出して終わり!

twitterWeb.Navigate(session.AuthorizeUri);

LoadCompletedEventHandler twitterWebUpdate = null;

twitterWebUpdate = (tSender, e) =>
{
string pin = "";
string finishedUrl = @"https://api.twitter.com/oauth/authorize";
string url = twitterWeb.Source.AbsoluteUri;
if (url == finishedUrl)
{
mshtml.IHTMLDocument2 doc = (mshtml.IHTMLDocument2)twitterWeb.Document;
string html = doc.body.innerHTML;
int codeIndex = html.IndexOf("<code>");
if (codeIndex >= 0)
{
//<code>があった=認証に成功した場合
pin = html.Substring(codeIndex + 6, 7);
t = OAuth.GetTokens(session, pin);
}
else
{
//<code>がなかった=認証に失敗した場合
MessageBox.Show("認証に失敗しました.");
}
}
};
twitterWeb.LoadCompleted += twitterWebUpdate;

これでTokens変数 tを介して,Twitter APIをいじることができます.


補足


  • 認証ページにはほかにもリンクがありましたが,そこに飛ぼうとすると新しいタブで開かれるためか,Internet Explorerが新規で実行されます.WebBrower側のページは認証のリンク以外では飛ばなかったので,この仕組みでもうまくいっているようです.