« Twitterクライアントをつくる2 | トップページ | Twitterクライアントをつくる 関連リンク »

2010年10月27日 (水)

Twitterクライアントをつくる3

twitter4rでOauth認証は大丈夫か?問題ない。

ということでver 0.5.0辺りからOauth認証がサポートされていました。ただしドキュメントがまとまっていなくてMLから情報を拾い出してきてやっとやり方が分かりました。認証部分を引用するとこんな感じです。

  1. Twitter::Client.configure do |conf|
  2.   conf.oauth_consumer_token = CONSUMER_KEY
  3.   conf.oauth_consumer_secret = CONSUMER_SECRET
  4. end
  5. client = Twitter::Client.new(:oauth_access => { :key => ACCESS_KEY, :secret => ACCESS_SECRET })

元ネタはココ

どうやら設定にConsumer key/secretを設定してあげて、認証にAccess key/secretを使えばよい、ということらしい。

とりあえずこれでt4rが使えそうなことは解ったので、じゃあOauthってどうやるのか。こちらの記事がとても参考になります。

まとめると

  1. twitter.comに自作アプリを登録 => Consumer token key/secretをゲット
  2. 自作アプリがConsumer token key/secretを使ってtwitter.comからRequest tokenをゲット
  3. Request tokenを使ってtwitter.comにリダイレクト => ユーザからRequest tokenの承認をゲット、PINの発行
  4. 自作アプリからPINと承認されたRequest tokenをtwitter.comに送付 => Access token key/secretをゲット
  5. Access token key/secretを使って認証 => twitter.comにログイン

ということらしい。解ったような解らないような...。

正しく登録された信頼のあるアプリから要求を受け、ちゃんとユーザから承認されたので以降はアクセス用のトークンをキーとして認証できます、ということかな。信頼ベースのしくみ。

うーむ、これだとWebサービスは良いけどクライアントアプリとして配る際にConsumer tokenがばれる可能性があるからなりすましとかで信頼関係が崩れてダメな仕組みの気がするが大丈夫か?大丈夫だ、配布するつもりはないので問題ない。

では実装してみよう。何も考えず味気ない名前のクライアント名でアプリを登録してみました。そしてこちらの記事のコードをベースに、下記のような画面を表示してユーザ承認とPIN入力をGUIやブラウザで求めるようにしてみました。

2

3

ブラウザで承認画面へリダイレクトするためにWatirというライブラリを使ってみたのですがこれがruby1.9で当初上手く動作せず、他のライブラリ等を試しながらいろいろなgemがインストールされまくりました。msvcrt-ruby18.dllが見つからないというruby1.8系に依存した何かのgemから出されるダイアログが起動するたびに出るようになりうざいのです。

やはりまだruby1.9系は時期尚早なのでしょうか。ライブラリの問題対処でかなり時間がかかってしまいます。この時期尚早も時期早尚が正しいのかどうかでまた時間が...。

ちうことで恒例のソースです。とりあえず自分のつぶやきの読み書きができます。Reloadで読み込み、tweetで書き込みです。

ライブラリは

  1. gem install twitter4r --remote
  2. gem install watir --remote

で、インストール。authenticate?のメソッドが上手く動作せずコメントアウト。

  1. begin
  2.   require 'rubygems'
  3. rescue LoadError
  4. end
  5. require 'wx'
  6. require 'twitter'
  7. require './WTC.rb'
  8. require 'watir'
  9. # require 'vapir'
  10. # require 'vapir-ie'
  11. # require "watir-vapir"
  12.  
  13. # Authentication info file for Oauth
  14. OAUTH_ACCESS_FILE = File.join( File.dirname(__FILE__), "..", "config", "oauth_access.yml")
  15. OAUTH_CONSUMER_FILE = File.join( File.dirname(__FILE__), "..", "config", "oauth_consumer.yml")
  16.  
  17. class WxrubyTweetClient < WTC
  18.   def initialize(parent = nil)
  19.     super()
  20.  
  21.     # Event handler
  22.     evt_tool(m_tool_reload){|event| on_tool_reload}
  23.     evt_tool(m_tool_tweet){|event| on_tool_tweet}
  24.  
  25.     @consumer = load_consumer_file()
  26.     login()
  27.   end
  28.  
  29.   # Load consumer key/sercret from file
  30.   def load_consumer_file()
  31.     consumer = Hash.new
  32.     if File.exist?(OAUTH_CONSUMER_FILE)
  33.       # Load consumer key/secret file
  34.       begin
  35.         File.open(OAUTH_CONSUMER_FILE,'r') do |f|
  36.           consumer = YAML.load(f)
  37.         end
  38.       rescue => e
  39.         puts 'consumer file load err: ', e, e.backtrace
  40.         exit
  41.       end
  42.     else
  43.       puts "#{OAUTH_CONSUMER_FILE} is not exist", __LINE__
  44.       exit
  45.     end
  46.  
  47.     if (consumer["oauth_consumer"].has_key?("key") && consumer["oauth_consumer"].has_key?("secret"))
  48.       # Set Consumer key/secret
  49.       Twitter::Client.configure do |conf|
  50.         conf.oauth_consumer_token = consumer["oauth_consumer"]["key"]
  51.         conf.oauth_consumer_secret = consumer["oauth_consumer"]["secret"]
  52.       end
  53.       return consumer
  54.     else
  55.       puts "Consumer key/secret are not exists in #{OAUTH_CONSUMER_FILE}", __LINE__
  56.       exit
  57.     end
  58.   end
  59.  
  60.   # Request access token
  61.   def req_access_token()
  62.     consumer = OAuth::Consumer.new(
  63.         @consumer["oauth_consumer"]["key"],
  64.         @consumer["oauth_consumer"]["secret"],
  65.         :site => 'http://twitter.com'
  66.     )
  67.  
  68.     request_token = consumer.get_request_token
  69.  
  70.     # Launch browser to get PIN code
  71.     begin
  72.       # browser = Vapir::IE.new
  73.       # browser = Vapir::Browser.new
  74.       browser = Watir::Browser.new
  75.       browser.goto(request_token.authorize_url)
  76.     rescue => e
  77.       p e, e.backtrace
  78.       puts 'Browser related issue happened'
  79.       exit
  80.     end
  81.  
  82.     oauth_verifier = Wx::get_text_from_user("Input PIN from twitter.com")
  83.     exit if oauth_verifier.empty?
  84.  
  85.     access_token = request_token.get_access_token(
  86.       :oauth_verifier => oauth_verifier
  87.     )
  88.  
  89.     return {"key" => access_token.token, "secret" => access_token.secret}
  90.   end
  91.  
  92.   def login()
  93.     if File.exist?(OAUTH_ACCESS_FILE)
  94.       begin
  95.         File.open(OAUTH_ACCESS_FILE,'r') do |f|
  96.           access = YAML.load(f)
  97.           @twit = Twitter::Client.new(access)
  98.         end
  99.       rescue => e
  100.         puts 'oauth_access file load err: ', e, __LINE__
  101.         exit
  102.       end
  103.     else
  104.       begin
  105.         access = Hash.new
  106.         access["oauth_access"] = req_access_token()
  107.         # @twit = Twitter::Client.new
  108.         # @twit.authenticate?(access["oauth_access"]["key"], access["oauth_access"]["secret"])
  109.         @twit = Twitter::Client.new(access)
  110.         File.open(OAUTH_ACCESS_FILE,'w') do |f|
  111.           YAML.dump(access,f)
  112.         end
  113.       rescue => e
  114.         p e, e.backtrace
  115.         puts 'Oauth err, try again...'
  116.         retry
  117.       end
  118.     end
  119.   end
  120.  
  121.   def on_tool_reload
  122.     public_timeline = @twit.timeline_for(:me) do |status|
  123.     # public_timeline = @twit.my(:timeline) do |status|
  124.       m_textctrl_home.write_text(status.user.screen_name + "\n")
  125.       m_textctrl_home.write_text(status.text + "\n")
  126.     end
  127.   end
  128.  
  129.   def on_tool_tweet
  130.     msg = m_textctrl_tweet.value
  131.     status = @twit.status(:post, msg) if not msg.empty?
  132.   end
  133.  
  134. end
  135.  
  136. class App < Wx::App
  137.   def on_init
  138.     f =  ::WxrubyTweetClient.new
  139.     f.show
  140.   end
  141. end
  142. if not defined?(Ocra)
  143.   App.new.main_loop
  144. end

GUI生成部分

  1.  
  2. # This class was automatically generated from XRC source. It is not
  3. # recommended that this file is edited directly; instead, inherit from
  4. # this class and extend its behaviour there. 
  5. #
  6. # Source file: noname.xrc
  7. # Generated at: 2010-10-24 17:37:30 +0900
  8.  
  9. class WTC < Wx::Frame
  10.  
  11.   attr_reader :m_panel4, :m_toolbar1, :m_tool_reload, :m_tool_tweet,
  12.               :m_tool_reply, :m_tool_group, :m_tool_search,
  13.               :m_tool_setting, :m_panel1, :m_textctrl_tweet,
  14.               :m_notebook1, :m_panel2, :m_textctrl_home
  15.  
  16.   def initialize(parent = nil)
  17.     super()
  18.     xml = Wx::XmlResource.get
  19.     xml.flags = 2 # Wx::XRC_NO_SUBCLASSING
  20.     xml.init_all_handlers
  21.     xml.load("noname.xrc")
  22.     xml.load_frame_subclass(self, parent, "MyFrame1")
  23.  
  24.     finder = lambda do | x |
  25.       int_id = Wx::xrcid(x)
  26.       begin
  27.         Wx::Window.find_window_by_id(int_id, self) || int_id
  28.       # Temporary hack to work around regression in 1.9.2; remove
  29.       # begin/rescue clause in later versions
  30.       rescue RuntimeError
  31.         int_id
  32.       end
  33.     end
  34.  
  35.     @m_panel4 = finder.call("m_panel4")
  36.     @m_toolbar1 = finder.call("m_toolBar1")
  37.     @m_tool_reload = finder.call("m_tool_reload")
  38.     @m_tool_tweet = finder.call("m_tool_tweet")
  39.     @m_tool_reply = finder.call("m_tool_reply")
  40.     @m_tool_group = finder.call("m_tool_group")
  41.     @m_tool_search = finder.call("m_tool_search")
  42.     @m_tool_setting = finder.call("m_tool_setting")
  43.     @m_panel1 = finder.call("m_panel1")
  44.     @m_textctrl_tweet = finder.call("m_textCtrl_tweet")
  45.     @m_notebook1 = finder.call("m_notebook1")
  46.     @m_panel2 = finder.call("m_panel2")
  47.     @m_textctrl_home = finder.call("m_textCtrl_home")
  48.     if self.class.method_defined? "on_init"
  49.       self.on_init()
  50.     end
  51.   end
  52. end
  53.  
  54.  
  55.  

XRCファイル

  1. <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
  2. <resource xmlns="http://www.wxwindows.org/wxxrc" version="2.3.0.1">
  3.   <object class="wxFrame" name="MyFrame1" subclass="WTC">
  4.     <style>wxCAPTION|wxCLOSE_BOX|wxMAXIMIZE_BOX|wxMINIMIZE_BOX|wxSYSTEM_MENU|wxTAB_TRAVERSAL</style>
  5.     <size>500,407</size>
  6.     <title>wxRuby Tweet Client</title>
  7.     <centered>1</centered>
  8.     <object class="wxPanel" name="m_panel4">
  9.       <style>wxTAB_TRAVERSAL</style>
  10.       <object class="wxBoxSizer">
  11.         <orient>wxVERTICAL</orient>
  12.         <object class="sizeritem">
  13.           <option>0</option>
  14.           <flag>wxEXPAND|wxALL</flag>
  15.           <border>5</border>
  16.           <object class="wxBoxSizer">
  17.             <orient>wxVERTICAL</orient>
  18.             <object class="sizeritem">
  19.               <option>0</option>
  20.               <flag>wxEXPAND</flag>
  21.               <border>5</border>
  22.               <object class="wxToolBar" name="m_toolBar1">
  23.                 <style>wxTB_HORIZONTAL</style>
  24.                 <bitmapsize></bitmapsize>
  25.                 <margins></margins>
  26.                 <packing>1</packing>
  27.                 <separation>5</separation>
  28.                 <object class="tool" name="m_tool_reload">
  29.                   <label>tool</label>
  30.                   <tooltip></tooltip>
  31.                   <longhelp></longhelp>
  32.                   <bitmap>../icon/eleganticons/images/Refresh.png</bitmap>
  33.                 </object>
  34.                 <object class="tool" name="m_tool_tweet">
  35.                   <label>Tweet</label>
  36.                   <tooltip>Tweet!</tooltip>
  37.                   <longhelp></longhelp>
  38.                   <bitmap>../icon/eleganticons/images/Speech-Bubble.png</bitmap>
  39.                 </object>
  40.                 <object class="tool" name="m_tool_reply">
  41.                   <label>Reply</label>
  42.                   <tooltip>Reply</tooltip>
  43.                   <longhelp></longhelp>
  44.                   <bitmap>../icon/eleganticons/images/Mail.png</bitmap>
  45.                 </object>
  46.                 <object class="tool" name="m_tool_group">
  47.                   <label>Group</label>
  48.                   <tooltip>Add Group</tooltip>
  49.                   <longhelp></longhelp>
  50.                   <bitmap>../icon/eleganticons/images/Person-group-add.png</bitmap>
  51.                 </object>
  52.                 <object class="tool" name="m_tool_search">
  53.                   <label>Search</label>
  54.                   <tooltip>Search</tooltip>
  55.                   <longhelp></longhelp>
  56.                   <bitmap>../icon/eleganticons/images/Search.png</bitmap>
  57.                 </object>
  58.                 <object class="tool" name="m_tool_setting">
  59.                   <label>Setting</label>
  60.                   <tooltip>Settings</tooltip>
  61.                   <longhelp></longhelp>
  62.                   <bitmap>../icon/eleganticons/images/Config.png</bitmap>
  63.                 </object>
  64.               </object>
  65.             </object>
  66.           </object>
  67.         </object>
  68.         <object class="sizeritem">
  69.           <option>0</option>
  70.           <flag>wxEXPAND|wxALL</flag>
  71.           <border>5</border>
  72.           <object class="wxBoxSizer">
  73.             <orient>wxVERTICAL</orient>
  74.             <object class="sizeritem">
  75.               <option>0</option>
  76.               <flag>wxEXPAND</flag>
  77.               <border>5</border>
  78.               <object class="wxPanel" name="m_panel1">
  79.                 <style>wxTAB_TRAVERSAL</style>
  80.                 <object class="wxBoxSizer">
  81.                   <orient>wxVERTICAL</orient>
  82.                   <object class="sizeritem">
  83.                     <option>0</option>
  84.                     <flag>wxEXPAND</flag>
  85.                     <border>5</border>
  86.                     <object class="wxTextCtrl" name="m_textCtrl_tweet">
  87.                       <style>wxTE_MULTILINE</style>
  88.                       <value></value>
  89.                       <maxlength>140</maxlength>
  90.                     </object>
  91.                   </object>
  92.                 </object>
  93.               </object>
  94.             </object>
  95.           </object>
  96.         </object>
  97.         <object class="sizeritem">
  98.           <option>1</option>
  99.           <flag>wxALL|wxEXPAND</flag>
  100.           <border>5</border>
  101.           <object class="wxBoxSizer">
  102.             <orient>wxVERTICAL</orient>
  103.             <object class="sizeritem">
  104.               <option>1</option>
  105.               <flag>wxEXPAND</flag>
  106.               <border>5</border>
  107.               <object class="wxNotebook" name="m_notebook1">
  108.                 <object class="notebookpage">
  109.                   <label>Home</label>
  110.                   <selected>0</selected>
  111.                   <object class="wxPanel" name="m_panel2">
  112.                     <style>wxTAB_TRAVERSAL</style>
  113.                     <object class="wxBoxSizer">
  114.                       <orient>wxVERTICAL</orient>
  115.                       <object class="sizeritem">
  116.                         <option>1</option>
  117.                         <flag>wxALL|wxEXPAND</flag>
  118.                         <border>5</border>
  119.                         <object class="wxTextCtrl" name="m_textCtrl_home">
  120.                           <style>wxTE_MULTILINE</style>
  121.                           <value></value>
  122.                           <maxlength>0</maxlength>
  123.                         </object>
  124.                       </object>
  125.                     </object>
  126.                   </object>
  127.                 </object>
  128.               </object>
  129.             </object>
  130.           </object>
  131.         </object>
  132.       </object>
  133.     </object>
  134.   </object>
  135. </resource>
  136.  

|

« Twitterクライアントをつくる2 | トップページ | Twitterクライアントをつくる 関連リンク »

Twitterクライアントをつくる」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/1201593/37378805

この記事へのトラックバック一覧です: Twitterクライアントをつくる3:

« Twitterクライアントをつくる2 | トップページ | Twitterクライアントをつくる 関連リンク »