twitterクライアントを作ってみるテスト

どうも、Ramencozoです(´ω`)
東北から関東にかけて、大地震で大変です。
被災者のみなさま、自分からはほとんどなにもできないのですが、ご無事をお祈りいたします。
救出にあたった自衛隊員さん、海外から派遣された方々、現場で直接手伝った方々、みなさまおつかれさまです。

素っ気無い書き方になってしまい申し訳ありませんが、自分の技術ネタのお話に入ります。

自己プロジェクトのひとつでいろいろやる中で、Twitter使ったうんぬんなものを思いついてまして。
内容的にはPCとマイコンをシリアル通信させといて(ryというのですが、全貌はできあがってからお見せします(´ω`)

さて。今回作ろうとしたクライアントの仕様としては下記。
・PC(Ubuntu)で動作
・リプライをコマンドとして受信
・シリアルポートの先のマイコンへそのコマンドを伝達
Java Swing(GUI) + Twitter4J(つぃったラッパクラス) + RXTX(COMポートライブラリ)を使う

でもって。今回やったのは下記な実装。
GUI周り
TwitterアカウントのOAuth認証
・クライアントからのTweet
・クライアントにてリプライ取得&最新リプのみ抽出

とりあえずここにひとまずのソース置いてみます。

クライアント画面としては下イメージ。

上左から右下へ向かって順に、OAuth認証用PINナンバー入力エリア、認証ボタン、認証ページ(Webブラウザ経由)起動ボタン、Post用入力エリア、最新リプ取得ボタン、もろもろログ表示窓。

たぶん後々PostするためのTextFieldはいらなくなるかと。(そういうクライアントではないので。今はとりあえず挙動確認のためつけたけども)

とりあえずソース下記。


Main.java(GUI担当)

import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Desktop;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.EtchedBorder;

class Main extends JFrame implements ActionListener {
private final String title = "こんとろーらーだッ!";
private final String version = "ver0.1";

// アプリ固有のつぃったコンシューマキー・シークレット
private final String consumerKey = "自分のソレ";
private final String consumerSecret = "自分のソレ";

//画面パーツ関連
private JTextField pincode;
private JButton authButton;
private JButton authPageButton;
private JLabel authStatus;

private JTextField tweetField;
private JButton getMentionButton;

private JTextArea logArea;

//Twitter4J含んだヲレヲレクラス(後述)
private TwiUtils twiUtils;

//メンション一覧関連
private List previousMentions;
private List newMentions;
private boolean isFirstReply;

public static void main(String[] args) {
Main mainWindow = new Main();
mainWindow.setVisible(true);
}

Main() {
//初回メンション取得のためのフラグ
isFirstReply = true;

//ヲレヲレクラスのインスタンス生成
twiUtils = new TwiUtils(consumerKey, consumerSecret);

//JFrameなウィンドウ(x=800,y=600)の設定
setTitle(title + " " + version);
setBounds(100, 100, 800, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container contentPane = getContentPane();

//親パネル、上段パネル、中段パネル、下段パネル
JPanel parentPanel = new JPanel();
JPanel authPanel = new JPanel();
JPanel tweetPanel = new JPanel();
JPanel logPanel = new JPanel();

//各部品の準備(new、パネルに貼り付け、リスナ登録)
pincode = new JTextField(8);
authPanel.add(pincode, BorderLayout.CENTER);
pincode.addActionListener(this);

authButton = new JButton("認証");
authButton.addActionListener(this);
authPanel.add(authButton, BorderLayout.CENTER);

authPageButton = new JButton("OAuth認証ページへ");
authPageButton.addActionListener(this);
authPanel.add(authPageButton, BorderLayout.CENTER);

authStatus = new JLabel("認証状態:未");
authPanel.add(authStatus, BorderLayout.CENTER);

tweetField = new JTextField(50);
tweetPanel.add(tweetField, BorderLayout.CENTER);
tweetField.addActionListener(this);

getMentionButton = new JButton("リプライ取得");
getMentionButton.addActionListener(this);
tweetPanel.add(getMentionButton, BorderLayout.CENTER);

//ログウィンドウの生成(書き込み不可、枠付き)
logArea = new JTextArea(20, 60);
logArea.setEditable(false);
logArea.setBorder(new EtchedBorder(EtchedBorder.RAISED));

//ログウィンドウにスクロール(縦・横、必要時に自動出現)をセット
JScrollPane scrollPane = new JScrollPane(
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.getVerticalScrollBar().setUnitIncrement(25);
scrollPane.getViewport().add(logArea);

logPanel.add(scrollPane, BorderLayout.CENTER);

parentPanel.add(authPanel, BorderLayout.NORTH);
parentPanel.add(tweetPanel, BorderLayout.CENTER);
parentPanel.add(logPanel, BorderLayout.SOUTH);
contentPane.add(parentPanel, BorderLayout.CENTER);
}

//各部品のイベント
@Override
public void actionPerformed(ActionEvent ae) {
// TODO Auto-generated method stub
if (ae.getSource() == authButton || ae.getSource() == pincode) {
boolean authFlag = twiUtils.getOAuth(pincode.getText());
if (authFlag) {
logArea.append("OAuth認証完了しました。本アプリケーションが使用可能となります" + "\n");
authStatus.setText("認証状態:済");
} else {
logArea.append("OAuth認証に失敗しました。再起動してやり直してみてください。" + "\n");
}
logArea.setCaretPosition(logArea.getText().length());
}
if (ae.getSource() == authPageButton) {
Desktop desktop = Desktop.getDesktop();
logArea.append("認証のためブラウザを起動します・・・" + "\n");
try {
desktop.browse(twiUtils.getPinViaWebPage());
logArea.append("ブラウザが起動しました。認証してください" + "\n");
} catch (IOException e) {
e.printStackTrace();
logArea.append("ブラウザの起動に失敗しました" + "\n");
}
logArea.setCaretPosition(logArea.getText().length());
}
if (ae.getSource() == tweetField) {
new Thread() {
public void run() {
twiUtils.Tweet(tweetField.getText());
logArea.append("あなたのツィート-> " + tweetField.getText() + "\n");
tweetField.setText("");
logArea.setCaretPosition(logArea.getText().length());
}
}.start();
}
if (ae.getSource() == getMentionButton) {
new Thread() {
public void run() {
newMentions = twiUtils.getMention();
int latestMention = 0;

if(!isFirstReply){
latestMention = newMentions.indexOf(previousMentions.get(0));
}else{
latestMention = -1;
}

if (latestMention == -1) {
logArea.append("すべて最新のリプライのようです・・・\n");
latestMention = newMentions.size();
} else if(latestMention > 0){
logArea.append("更新されたリプライがあります\n");
latestMention = newMentions.indexOf(previousMentions.get(0));
} else{
logArea.append("リプライは来てないようです\n");
latestMention = 0;
}
for (int i = 0; i < latestMention; i++) {
logArea.append("リプライ-> " + newMentions.get(i) + "\n");
}
previousMentions = newMentions;
isFirstReply = false;
logArea.setCaretPosition(logArea.getText().length());
}
}.start();
}
}
}



TwiUtils.java(つぃった周り担当)

import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;

//twitter4j-core-2.2.0.jar(バージョンは変動)をビルドパスに含むべし
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;

class TwiUtils {
private TwitterFactory factory;
private AccessToken accessToken;
private Twitter twitter;
private String url;
private RequestToken requestToken;

//Main.javaからコンシューマキー・シークレットもらう
//コンストラクタ内では認証URL生成までしとく
TwiUtils(String consumerKey, String consumerSecret) {
factory = new TwitterFactory();

twitter = factory.getInstance();
twitter.setOAuthConsumer(consumerKey, consumerSecret);
try {
requestToken = twitter.getOAuthRequestToken();
url = requestToken.getAuthorizationURL();
} catch (TwitterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

//コンストラクタで得た認証URLを与える
public URI getPinViaWebPage() {
URI uri = null;
try {
uri = new URI(url);
System.out.println("URI getting is OK.");
} catch (URISyntaxException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println(e.getMessage());
}
return uri;
}

//ブラウザで表示されたPINコードをMain.javaのTextFieldから与えてもらう
//ソレ使って正式にユーザー認証完了する
public boolean getOAuth(String pin) {
try {
if (pin.length() > 0) {
accessToken = twitter.getOAuthAccessToken(requestToken, pin);
twitter.setOAuthAccessToken(accessToken);
} else {
accessToken = twitter.getOAuthAccessToken();
twitter.setOAuthAccessToken(accessToken);
}
return true;
} catch (TwitterException te) {
// TODO Auto-generated catch block
if (te.getStatusCode() == 401) {
System.out.println("Unable to get the access token");
} else {
te.printStackTrace();
}
return false;
}
}

//アプリからPostを行う
public void Tweet(String message) {
try {
twitter.updateStatus(message + " [from 自作クラ]"); //フッタ要らんけど
System.out.println("Tweet Done!");
} catch (TwitterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("Something Wrong...");
}
}

//最新メンションゲトー(初回:最新18件リプ取得、次回:更新されたリプのみ)
public List getMention() {
List mentions = null;
try {
List mentionstatus = twitter.getMentions();
for(Status status : mentionstatus){
mentions.add(status.getUser().getScreenName() + ":" + status.getText());
}
} catch (TwitterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Getting Reply Done!");
return mentions;
}
}

ひとまず細かい部分のネタは後日(ry