Skip to content

Instantly share code, notes, and snippets.

@mashiro
Created April 26, 2010 03:21
Show Gist options
  • Select an option

  • Save mashiro/378935 to your computer and use it in GitHub Desktop.

Select an option

Save mashiro/378935 to your computer and use it in GitHub Desktop.
--- TwitterService.cs.org 2010-04-26 12:11:48.000000000 +0900
+++ TwitterService.cs 2010-03-15 23:09:04.000000000 +0900
@@ -8,6 +8,7 @@
using System.Xml;
using System.Xml.Serialization;
using System.IO.Compression;
+using System.Linq;
namespace Misuzilla.Applications.TwitterIrcGateway
{
@@ -27,10 +28,10 @@
private Timer _timerDirectMessage;
private Timer _timerReplies;
- private DateTime _lastAccessTimeline = new DateTime();
- private DateTime _lastAccessReplies = new DateTime();
private DateTime _lastAccessDirectMessage = DateTime.Now;
- private Int64 _lastAccessDirectMessageId = 0;
+ private Int64 _lastAccessTimelineId = 1;
+ private Int64 _lastAccessRepliesId = 1;
+ private Int64 _lastAccessDirectMessageId = 1;
private Boolean _isFirstTime = true;
private Boolean _isFirstTimeReplies = true;
private Boolean _isFirstTimeDirectMessage = true;
@@ -61,7 +62,7 @@
/// <summary>
/// Twitter APIのエンドポイントURLのプレフィックスを取得・設定します。
/// </summary>
- public String ServiceServerPrefix = "http://twitter.com";
+ public String ServiceServerPrefix = "http://api.twitter.com/1";
/// <summary>
/// リクエストのRefererを取得・設定します。
/// </summary>
@@ -255,6 +256,7 @@
}
});
}
+
/// <summary>
/// ステータスを更新します。
/// </summary>
@@ -275,7 +277,8 @@
String encodedMessage = TwitterService.EncodeMessage(message);
return ExecuteRequest<Status>(() =>
{
- String responseBody = POST(String.Format("/statuses/update.xml?status={0}&source={1}{2}", encodedMessage, ClientName, (inReplyToStatusId != 0 ? "&in_reply_to_status_id="+inReplyToStatusId : "")), new byte[] {});
+ String postData = String.Format("status={0}&source={1}{2}", encodedMessage, ClientName, (inReplyToStatusId != 0 ? "&in_reply_to_status_id=" + inReplyToStatusId : ""));
+ String responseBody = POST("/statuses/update.xml", postData);
if (NilClasses.CanDeserialize(responseBody))
{
return null;
@@ -298,7 +301,8 @@
String encodedMessage = TwitterService.EncodeMessage(message);
ExecuteRequest(() =>
{
- String responseBody = POST(String.Format("/direct_messages/new.xml?user={0}&text={1}", GetUserId(screenName), encodedMessage), new Byte[0]);
+ String postData = String.Format("user={0}&text={1}", GetUserId(screenName), encodedMessage);
+ String responseBody = POST("/direct_messages/new.xml", postData);
});
}
@@ -319,34 +323,36 @@
/// <exception cref="TwitterServiceException"></exception>
public User[] GetFriends(Int32 maxPage)
{
- List<User> usersList = new List<User>();
- Int32 page = 0;
+ List<User> users = new List<User>();
+ Int64 cursor = -1;
+ Int32 page = maxPage;
return ExecuteRequest<User[]>(() =>
{
- while (page++ != maxPage)
+ while (cursor != 0 && page > 0)
{
- String responseBody = GET(String.Format("/statuses/friends.xml?page={0}&lite=true", page));
+ String responseBody = GET(String.Format("/statuses/friends.xml?cursor={0}&lite=true", cursor));
if (NilClasses.CanDeserialize(responseBody))
{
- return usersList.ToArray();
+ return users.ToArray();
}
else
{
- Users users = Users.Serializer.Deserialize(new StringReader(responseBody)) as Users;
- if (users == null || users.User == null || users.User.Length == 0)
+ UsersList usersList = UsersList.Serializer.Deserialize(new StringReader(responseBody)) as UsersList;
+ if (usersList == null || usersList.Users == null || usersList.Users.User == null || usersList.Users.User.Length == 0)
{
- return usersList.ToArray();
+ return users.ToArray();
}
else
{
- usersList.AddRange(users.User);
- if (users.User.Length < FriendsPerPageThreshold)
- break;
+ users.AddRange(usersList.Users.User);
}
+
+ --page;
+ cursor = usersList.NextCursor;
}
}
- // あまりに多い場合はそこまで。
- return usersList.ToArray();
+
+ return users.ToArray();
});
}
@@ -435,6 +441,47 @@
}
/// <summary>
+ /// timeline を取得します。
+ /// </summary>
+ /// <param name="sinceId">最後に取得したID</param>
+ /// <returns>ステータス</returns>
+ public Statuses GetTimeline(Int64 sinceId)
+ {
+ return GetTimeline(sinceId, FetchCount);
+ }
+
+ /// <summary>
+ /// timeline を取得します。
+ /// </summary>
+ /// <param name="sinceId">最後に取得したID</param>
+ /// <param name="count">取得数</param>
+ /// <returns>ステータス</returns>
+ public Statuses GetTimeline(Int64 sinceId, Int32 count)
+ {
+ return ExecuteRequest<Statuses>(() =>
+ {
+ String responseBody = GET(String.Format("/statuses/home_timeline.xml?since_id={0}&count={1}", sinceId, count));
+ Statuses statuses;
+ if (NilClasses.CanDeserialize(responseBody))
+ {
+ statuses = new Statuses();
+ statuses.Status = new Status[0];
+ }
+ else
+ {
+ statuses = Statuses.Serializer.Deserialize(new StringReader(responseBody)) as Statuses;
+ if (statuses == null || statuses.Status == null)
+ {
+ statuses = new Statuses();
+ statuses.Status = new Status[0];
+ }
+ }
+
+ return statuses;
+ });
+ }
+
+ /// <summary>
/// replies を取得します。
/// </summary>
/// <exception cref="WebException"></exception>
@@ -444,6 +491,7 @@
{
return GetMentions();
}
+
/// <summary>
/// mentions を取得します。
/// </summary>
@@ -453,7 +501,7 @@
{
return ExecuteRequest<Statuses>(() =>
{
- String responseBody = GET("/statuses/replies.xml");
+ String responseBody = GET("/statuses/mentions.xml");
Statuses statuses;
if (NilClasses.CanDeserialize(responseBody))
{
@@ -508,10 +556,6 @@
}
/// <summary>
- /// 指定したユーザの timeline を取得します。
- /// </summary>
-
- /// <summary>
/// direct messages を取得します。
/// </summary>
/// <param name="sinceId">最後に取得したID</param>
@@ -684,6 +728,29 @@
}
});
}
+
+ /// <summary>
+ /// メッセージをretweetします。
+ /// </summary>
+ /// <param name="id"></param>
+ /// <returns></returns>
+ public Status RetweetStatus(Int64 id)
+ {
+ return ExecuteRequest<Status>(() =>
+ {
+ String responseBody = POST(String.Format("/statuses/retweet/{0}.xml", id), new byte[0]);
+ if (NilClasses.CanDeserialize(responseBody))
+ {
+ return null;
+ }
+ else
+ {
+ Status status = Status.Serializer.Deserialize(new StringReader(responseBody)) as Status;
+ return status;
+ }
+ });
+ }
+
/// <summary>
/// ユーザをfollowします。
/// </summary>
@@ -693,7 +760,8 @@
{
return ExecuteRequest<User>(() =>
{
- String responseBody = POST(String.Format("/friendships/create.xml?screen_name={0}", screenName), new byte[0]);
+ String postData = String.Format("screen_name={0}", screenName);
+ String responseBody = POST("/friendships/create.xml", postData);
if (NilClasses.CanDeserialize(responseBody))
{
return null;
@@ -714,7 +782,8 @@
{
return ExecuteRequest<User>(() =>
{
- String responseBody = POST(String.Format("/friendships/destroy.xml?screen_name={0}", screenName), new byte[0]);
+ String postData = String.Format("screen_name={0}", screenName);
+ String responseBody = POST("/friendships/destroy.xml", postData);
if (NilClasses.CanDeserialize(responseBody))
{
return null;
@@ -735,7 +804,8 @@
{
return ExecuteRequest<User>(() =>
{
- String responseBody = POST(String.Format("/blocks/create/{0}.xml", GetUserId(screenName)), new byte[0]);
+ String postData = String.Format("screen_name={0}", screenName);
+ String responseBody = POST("/blocks/create.xml", postData);
if (NilClasses.CanDeserialize(responseBody))
{
return null;
@@ -756,7 +826,8 @@
{
return ExecuteRequest<User>(() =>
{
- String responseBody = POST(String.Format("/blocks/destroy/{0}.xml", GetUserId(screenName)), new byte[0]);
+ String postData = String.Format("screen_name={0}", screenName);
+ String responseBody = POST("/blocks/destroy.xml", postData);
if (NilClasses.CanDeserialize(responseBody))
{
return null;
@@ -868,17 +939,17 @@
// 最終更新時刻
if (_enableDropProtection)
{
- // 取りこぼし防止しているときは一番古い日付
- if (status.CreatedAt < _lastAccessTimeline)
+ // 取りこぼし防止しているときは一番古いID
+ if (status.Id < _lastAccessTimelineId)
{
- _lastAccessTimeline = status.CreatedAt;
+ _lastAccessTimelineId = status.Id;
}
}
else
{
- if (status.CreatedAt > _lastAccessTimeline)
+ if (status.Id > _lastAccessTimelineId)
{
- _lastAccessTimeline = status.CreatedAt;
+ _lastAccessTimelineId = status.Id;
}
}
}
@@ -900,17 +971,17 @@
// 最終更新時刻
if (_enableDropProtection)
{
- // 取りこぼし防止しているときは一番古い日付
- if (status.CreatedAt < _lastAccessTimeline)
+ // 取りこぼし防止しているときは一番古いID
+ if (status.Id < _lastAccessTimelineId)
{
- _lastAccessTimeline = status.CreatedAt;
+ _lastAccessTimelineId = status.Id;
}
}
else
{
- if (status.CreatedAt > _lastAccessTimeline)
+ if (status.Id > _lastAccessTimelineId)
{
- _lastAccessTimeline = status.CreatedAt;
+ _lastAccessTimelineId = status.Id;
}
}
});
@@ -932,7 +1003,7 @@
List<Status> statusList = new List<Status>();
foreach (Status status in statuses.Status)
{
- if (status.CreatedAt < _lastAccessReplies)
+ if (status.Id < _lastAccessRepliesId)
continue;
if (ProcessDropProtection(_repliesBuffer, status.Id) && ProcessDropProtection(_statusBuffer, status.Id))
@@ -943,16 +1014,16 @@
if (_enableDropProtection)
{
// 取りこぼし防止しているときは一番古い日付
- if (status.CreatedAt < _lastAccessTimeline)
+ if (status.Id < _lastAccessTimelineId)
{
- _lastAccessTimeline = status.CreatedAt;
+ _lastAccessTimelineId = status.Id;
}
}
else
{
- if (status.CreatedAt > _lastAccessTimeline)
+ if (status.Id > _lastAccessTimelineId)
{
- _lastAccessTimeline = status.CreatedAt;
+ _lastAccessTimelineId = status.Id;
}
}
}
@@ -969,7 +1040,7 @@
Boolean friendsCheckRequired = false;
RunCheck(delegate
{
- Statuses statuses = GetTimeline(_lastAccessTimeline);
+ Statuses statuses = GetTimeline(_lastAccessTimelineId);
Array.Reverse(statuses.Status);
// 差分チェック
ProcessStatuses(statuses, (s) =>
@@ -979,7 +1050,8 @@
if (_isFirstTime && _enableDropProtection)
{
- _lastAccessTimeline = DateTime.Now;
+ if (statuses.Status != null && statuses.Status.Length > 0)
+ _lastAccessTimelineId = statuses.Status.Select(s => s.Id).Max();
}
_isFirstTime = false;
});
@@ -1017,7 +1089,7 @@
Boolean friendsCheckRequired = false;
RunCheck(delegate
{
- Statuses statuses = GetReplies();
+ Statuses statuses = GetMentions();
Array.Reverse(statuses.Status);
// 差分チェック
@@ -1031,7 +1103,8 @@
if (_isFirstTimeReplies && _enableDropProtection)
{
- _lastAccessReplies = DateTime.Now;
+ if (statuses.Status != null && statuses.Status.Length > 0)
+ _lastAccessRepliesId = statuses.Status.Select(s => s.Id).Max();
}
_isFirstTimeReplies = false;
});
@@ -1120,6 +1193,7 @@
throw new TwitterServiceException(ie);
}
}
+
private void ExecuteRequest(Procedure execProc)
{
try
@@ -1167,12 +1241,14 @@
}
catch (TwitterServiceException ex2)
{
- try { OnCheckError(new ErrorEventArgs(ex2)); } catch { }
+ try { OnCheckError(new ErrorEventArgs(ex2)); }
+ catch { }
return false;
}
catch (Exception ex3)
{
- try { OnCheckError(new ErrorEventArgs(ex3)); } catch { }
+ try { OnCheckError(new ErrorEventArgs(ex3)); }
+ catch { }
TraceLogger.Twitter.Information("RunCheck(Unhandled Exception): "+ex3.ToString());
return false;
}
@@ -1308,11 +1384,17 @@
return sr.ReadToEnd();
}
+ public String POST(String url, String postData)
+ {
+ return POST(url, Encoding.UTF8.GetBytes(postData));
+ }
+
protected virtual HttpWebRequest CreateHttpWebRequest(String url, String method)
{
HttpWebRequest webRequest = HttpWebRequest.Create(url) as HttpWebRequest;
//webRequest.Credentials = _credential;
//webRequest.PreAuthenticate = true;
+ webRequest.ServicePoint.Expect100Continue = false;
webRequest.Proxy = _proxy;
webRequest.Method = method;
webRequest.Accept = "text/xml, application/xml, text/html;q=0.5";
@@ -1771,6 +1853,42 @@
/// <summary>
/// Userのセットを格納します。
/// </summary>
+ [XmlType("users_list")]
+ public class UsersList
+ {
+ [XmlElement("users")]
+ public Users Users { get; set; }
+
+ [XmlElement("next_cursor")]
+ public Int64 NextCursor { get; set; }
+
+ [XmlElement("previous_cursor")]
+ public Int64 PreviousCursor { get; set; }
+
+ private static Object _syncObject = new object();
+ private static XmlSerializer _serializer = null;
+ static UsersList()
+ {
+ lock (_syncObject)
+ {
+ if (_serializer == null)
+ {
+ _serializer = new XmlSerializer(typeof(UsersList));
+ }
+ }
+ }
+ public static XmlSerializer Serializer
+ {
+ get
+ {
+ return _serializer;
+ }
+ }
+ }
+
+ /// <summary>
+ /// Userのセットを格納します。
+ /// </summary>
[XmlType("users")]
public class Users
{
@@ -1878,6 +1996,8 @@
public String InReplyToStatusId;
[XmlElement("in_reply_to_user_id")]
public String InReplyToUserId;
+ [XmlElement("retweeted_status")]
+ public Status RetweetedStatus;
[XmlElement("text")]
public String _textOriginal;
[XmlElement("user")]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment