Created
April 26, 2010 03:21
-
-
Save mashiro/378935 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| --- 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