1 // OpenTween - Client of Twitter
2 // Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
3 // (c) 2008-2011 Moz (@syo68k)
4 // (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
5 // (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
6 // (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
7 // (c) 2011 Egtra (@egtra) <http://dev.activebasic.com/egtra/>
8 // All rights reserved.
10 // This file is part of OpenTween.
12 // This program is free software; you can redistribute it and/or modify it
13 // under the terms of the GNU General Public License as published by the Free
14 // Software Foundation; either version 3 of the License, or (at your option)
17 // This program is distributed in the hope that it will be useful, but
18 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 // You should have received a copy of the GNU General Public License along
23 // with this program. If not, see <http://www.gnu.org/licenses/>, or write to
24 // the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
25 // Boston, MA 02110-1301, USA.
27 using System.Diagnostics;
31 using System.Runtime.Serialization;
32 using System.Runtime.Serialization.Json;
34 using System.Text.RegularExpressions;
35 using System.Threading;
38 using System.Xml.Linq;
40 using System.Reflection;
41 using System.Collections.Generic;
43 using System.Windows.Forms;
47 public class Twitter : IDisposable
49 #region Regexp from twitter-text-js
51 // The code in this region code block incorporates works covered by
52 // the following copyright and permission notices:
54 // Copyright 2011 Twitter, Inc.
56 // Licensed under the Apache License, Version 2.0 (the "License"); you
57 // may not use this work except in compliance with the License. You
58 // may obtain a copy of the License in the LICENSE file, or at:
60 // http://www.apache.org/licenses/LICENSE-2.0
62 // Unless required by applicable law or agreed to in writing, software
63 // distributed under the License is distributed on an "AS IS" BASIS,
64 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
65 // implied. See the License for the specific language governing
66 // permissions and limitations under the License.
69 private const string LATIN_ACCENTS = @"\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\u024f\u0253\u0254\u0256\u0257\u0259\u025b\u0263\u0268\u026f\u0272\u0289\u028b\u02bb\u1e00-\u1eff";
70 private const string NON_LATIN_HASHTAG_CHARS = @"\u0400-\u04ff\u0500-\u0527\u1100-\u11ff\u3130-\u3185\uA960-\uA97F\uAC00-\uD7AF\uD7B0-\uD7FF";
71 //private const string CJ_HASHTAG_CHARACTERS = @"\u30A1-\u30FA\uFF66-\uFF9F\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\u3041-\u3096\u3400-\u4DBF\u4E00-\u9FFF\u20000-\u2A6DF\u2A700-\u2B73F\u2B740-\u2B81F\u2F800-\u2FA1F";
72 private const string CJ_HASHTAG_CHARACTERS = @"\u30A1-\u30FA\u30FC\u3005\uFF66-\uFF9F\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\u3041-\u309A\u3400-\u4DBF\p{IsCJKUnifiedIdeographs}";
73 private const string HASHTAG_BOUNDARY = @"^|$|\s|「|」|。|\.|!";
74 private const string HASHTAG_ALPHA = "[a-z_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]";
75 private const string HASHTAG_ALPHANUMERIC = "[a-z0-9_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]";
76 private const string HASHTAG_TERMINATOR = "[^a-z0-9_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]";
77 public const string HASHTAG = "(" + HASHTAG_BOUNDARY + ")(#|#)(" + HASHTAG_ALPHANUMERIC + "*" + HASHTAG_ALPHA + HASHTAG_ALPHANUMERIC + "*)(?=" + HASHTAG_TERMINATOR + "|" + HASHTAG_BOUNDARY + ")";
79 private const string url_valid_preceding_chars = @"(?:[^A-Za-z0-9@@$##\ufffe\ufeff\uffff\u202a-\u202e]|^)";
80 public const string url_invalid_without_protocol_preceding_chars = @"[-_./]$";
81 private const string url_invalid_domain_chars = @"\!'#%&'\(\)*\+,\\\-\.\/:;<=>\?@\[\]\^_{|}~\$\u2000-\u200a\u0009-\u000d\u0020\u0085\u00a0\u1680\u180e\u2028\u2029\u202f\u205f\u3000\ufffe\ufeff\uffff\u202a-\u202e";
82 private const string url_valid_domain_chars = @"[^" + url_invalid_domain_chars + "]";
83 private const string url_valid_subdomain = @"(?:(?:" + url_valid_domain_chars + @"(?:[_-]|" + url_valid_domain_chars + @")*)?" + url_valid_domain_chars + @"\.)";
84 private const string url_valid_domain_name = @"(?:(?:" + url_valid_domain_chars + @"(?:-|" + url_valid_domain_chars + @")*)?" + url_valid_domain_chars + @"\.)";
85 private const string url_valid_GTLD = @"(?:(?:aero|asia|biz|cat|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel|xxx)(?=[^0-9a-zA-Z]|$))";
86 private const string url_valid_CCTLD = @"(?:(?:ac|ad|ae|af|ag|ai|al|am|an|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cs|cu|cv|cx|cy|cz|dd|de|dj|dk|dm|do|dz|ec|ee|eg|eh|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|ss|st|su|sv|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tp|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw)(?=[^0-9a-zA-Z]|$))";
87 private const string url_valid_punycode = @"(?:xn--[0-9a-z]+)";
88 private const string url_valid_domain = @"(?<domain>" + url_valid_subdomain + "*" + url_valid_domain_name + "(?:" + url_valid_GTLD + "|" + url_valid_CCTLD + ")|" + url_valid_punycode + ")";
89 public const string url_valid_ascii_domain = @"(?:(?:[a-z0-9" + LATIN_ACCENTS + @"]+)\.)+(?:" + url_valid_GTLD + "|" + url_valid_CCTLD + "|" + url_valid_punycode + ")";
90 public const string url_invalid_short_domain = "^" + url_valid_domain_name + url_valid_CCTLD + "$";
91 private const string url_valid_port_number = @"[0-9]+";
93 private const string url_valid_general_path_chars = @"[a-z0-9!*';:=+,.$/%#\[\]\-_~|&" + LATIN_ACCENTS + "]";
94 private const string url_balance_parens = @"(?:\(" + url_valid_general_path_chars + @"+\))";
95 private const string url_valid_path_ending_chars = @"(?:[+\-a-z0-9=_#/" + LATIN_ACCENTS + "]|" + url_balance_parens + ")";
96 private const string pth = "(?:" +
98 url_valid_general_path_chars + "*" +
99 "(?:" + url_balance_parens + url_valid_general_path_chars + "*)*" +
100 url_valid_path_ending_chars +
101 ")|(?:@" + url_valid_general_path_chars + "+/)" +
103 private const string qry = @"(?<query>\?[a-z0-9!?*'();:&=+$/%#\[\]\-_.,~|]*[a-z0-9_&=#/])?";
104 public const string rgUrl = @"(?<before>" + url_valid_preceding_chars + ")" +
105 "(?<url>(?<protocol>https?://)?" +
106 "(?<domain>" + url_valid_domain + ")" +
107 "(?::" + url_valid_port_number + ")?" +
108 "(?<path>/" + pth + "*)?" +
114 delegate void GetIconImageDelegate(PostClass post);
115 private readonly object LockObj = new object();
116 private List<long> followerId = new List<long>();
117 private bool _GetFollowerResult = false;
118 private List<long> noRTId = new List<long>();
119 private bool _GetNoRetweetResult = false;
121 private int _followersCount = 0;
122 private int _friendsCount = 0;
123 private int _statusesCount = 0;
124 private string _location = "";
125 private string _bio = "";
126 private string _protocol = "https://";
129 private string _uname;
131 private bool _getIcon;
132 private IDictionary<string, Image> _dIcon;
134 private bool _tinyUrlResolve;
135 private bool _restrictFavCheck;
137 private bool _readOwnPost;
138 private List<string> _hashList = new List<string>();
140 private Outputz op = new Outputz();
141 //max_idで古い発言を取得するために保持(lists分は個別タブで管理)
142 private long minHomeTimeline = long.MaxValue;
143 private long minMentions = long.MaxValue;
144 private long minDirectmessage = long.MaxValue;
145 private long minDirectmessageSent = long.MaxValue;
147 //private FavoriteQueue favQueue;
149 private HttpTwitter twCon = new HttpTwitter();
151 //private List<PostClass> _deletemessages = new List<PostClass>();
153 public string Authenticate(string username, string password)
158 MyCommon.TwitterApiInfo.Initialize();
161 res = twCon.AuthUserAndPass(username, password, ref content);
165 return "Err:" + ex.Message;
170 case HttpStatusCode.OK:
171 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
172 _uname = username.ToLower();
173 if (AppendSettingDialog.Instance.UserstreamStartup) this.ReconnectUserStream();
175 case HttpStatusCode.Unauthorized:
177 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
178 var errMsg = GetErrorMessageJson(content);
179 if (string.IsNullOrEmpty(errMsg))
181 return Properties.Resources.Unauthorized + Environment.NewLine + content;
185 return "Auth error:" + errMsg;
188 case HttpStatusCode.Forbidden:
190 var errMsg = GetErrorMessageJson(content);
191 if (string.IsNullOrEmpty(errMsg))
193 return "Err:Forbidden";
197 return "Err:" + errMsg;
201 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
205 public string StartAuthentication(ref string pinPageUrl)
210 MyCommon.TwitterApiInfo.Initialize();
213 res = twCon.AuthGetRequestToken(ref pinPageUrl);
217 return "Err:" + "Failed to access auth server.";
223 public string Authenticate(string pinCode)
228 MyCommon.TwitterApiInfo.Initialize();
231 res = twCon.AuthGetAccessToken(pinCode);
235 return "Err:" + "Failed to access auth acc server.";
240 case HttpStatusCode.OK:
241 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
242 _uname = Username.ToLower();
243 if (AppendSettingDialog.Instance.UserstreamStartup) this.ReconnectUserStream();
245 case HttpStatusCode.Unauthorized:
247 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
248 var errMsg = GetErrorMessageJson(content);
249 if (string.IsNullOrEmpty(errMsg))
251 return "Check the PIN or retry." + Environment.NewLine + content;
255 return "Auth error:" + errMsg;
258 case HttpStatusCode.Forbidden:
260 var errMsg = GetErrorMessageJson(content);
261 if (string.IsNullOrEmpty(errMsg))
263 return "Err:Forbidden";
267 return "Err:" + errMsg;
271 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
275 public void ClearAuthInfo()
277 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
278 MyCommon.TwitterApiInfo.Initialize();
279 twCon.ClearAuthInfo();
282 public void VerifyCredentials()
284 HttpStatusCode res = HttpStatusCode.BadRequest;
289 res = twCon.VerifyCredentials(ref content);
296 if (res == HttpStatusCode.OK)
298 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
299 TwitterDataModel.User user;
302 user = MyCommon.CreateDataFromJson<TwitterDataModel.User>(content);
304 catch(SerializationException)
308 twCon.AuthenticatedUserId = user.Id;
312 private string GetErrorMessageJson(string content)
316 if (!string.IsNullOrEmpty(content))
318 using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(content), XmlDictionaryReaderQuotas.Max))
320 var xElm = XElement.Load(jsonReader);
321 if (xElm.Element("error") != null)
323 return xElm.Element("error").Value;
342 public void Initialize(string token, string tokenSecret, string username, long userId)
345 if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(tokenSecret) || string.IsNullOrEmpty(username))
347 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
349 MyCommon.TwitterApiInfo.Initialize();
350 twCon.Initialize(token, tokenSecret, username, userId);
351 _uname = username.ToLower();
352 if (AppendSettingDialog.Instance.UserstreamStartup) this.ReconnectUserStream();
355 public string PreProcessUrl(string orgData)
359 //var IDNConveter = new IdnMapping();
360 var href = "<a href=\"";
364 if (orgData.IndexOf(href, posl2, StringComparison.Ordinal) > -1)
368 posl1 = orgData.IndexOf(href, posl2, StringComparison.Ordinal);
369 posl1 += href.Length;
370 posl2 = orgData.IndexOf("\"", posl1, StringComparison.Ordinal);
371 urlStr = orgData.Substring(posl1, posl2 - posl1);
373 if (!urlStr.StartsWith("http://") && !urlStr.StartsWith("https://") && !urlStr.StartsWith("ftp://"))
378 var replacedUrl = MyCommon.IDNDecode(urlStr);
379 if (replacedUrl == null) continue;
380 if (replacedUrl == urlStr) continue;
382 orgData = orgData.Replace("<a href=\"" + urlStr, "<a href=\"" + replacedUrl);
393 private string GetPlainText(string orgData)
395 return HttpUtility.HtmlDecode(Regex.Replace(orgData, "(?<tagStart><a [^>]+>)(?<text>[^<]+)(?<tagEnd></a>)", "${text}"));
398 // htmlの簡易サニタイズ(詳細表示に不要なタグの除去)
400 private string SanitizeHtml(string orgdata)
402 var retdata = orgdata;
404 retdata = Regex.Replace(retdata, "<(script|object|applet|image|frameset|fieldset|legend|style).*" +
405 "</(script|object|applet|image|frameset|fieldset|legend|style)>", "", RegexOptions.IgnoreCase);
407 retdata = Regex.Replace(retdata, "<(frame|link|iframe|img)>", "", RegexOptions.IgnoreCase);
412 private string AdjustHtml(string orgData)
414 var retStr = orgData;
415 //var m = Regex.Match(retStr, "<a [^>]+>[#|#](?<1>[a-zA-Z0-9_]+)</a>");
420 // _hashList.Add("#" + m.Groups(1).Value);
424 retStr = Regex.Replace(retStr, "<a [^>]*href=\"/", "<a href=\"" + _protocol + "twitter.com/");
425 retStr = retStr.Replace("<a href=", "<a target=\"_self\" href=");
426 retStr = retStr.Replace("\n", "<br>");
428 //半角スペースを置換(Thanks @anis774)
432 ret = EscapeSpace(ref retStr);
435 return SanitizeHtml(retStr);
438 private bool EscapeSpace(ref string html)
440 //半角スペースを置換(Thanks @anis774)
442 for (int i = 0; i < html.Length; i++)
453 if ((!isTag) && (html[i] == ' '))
455 html = html.Remove(i, 1);
456 html = html.Insert(i, " ");
463 private struct PostInfo
465 public string CreatedAt;
468 public string UserId;
469 public PostInfo(string Created, string IdStr, string txt, string uid)
476 public bool Equals(PostInfo dst)
478 if (this.CreatedAt == dst.CreatedAt && this.Id == dst.Id && this.Text == dst.Text && this.UserId == dst.UserId)
489 static private PostInfo _prev = new PostInfo("", "", "", "");
490 private bool IsPostRestricted(TwitterDataModel.Status status)
492 var _current = new PostInfo("", "", "", "");
494 _current.CreatedAt = status.CreatedAt;
495 _current.Id = status.IdStr;
496 if (status.Text == null)
502 _current.Text = status.Text;
504 _current.UserId = status.User.IdStr;
506 if (_current.Equals(_prev))
510 _prev.CreatedAt = _current.CreatedAt;
511 _prev.Id = _current.Id;
512 _prev.Text = _current.Text;
513 _prev.UserId = _current.UserId;
518 public string PostStatus(string postStr, long reply_to)
521 if (MyCommon._endingFlag) return "";
523 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
525 postStr = postStr.Trim();
527 if (Regex.Match(postStr, "^DM? +(?<id>[a-zA-Z0-9_]+) +(?<body>.+)", RegexOptions.IgnoreCase | RegexOptions.Singleline).Success)
529 return SendDirectMessage(postStr);
532 HttpStatusCode res = HttpStatusCode.BadRequest;
536 res = twCon.UpdateStatus(postStr, reply_to, ref content);
540 return "Err:" + ex.Message;
545 case HttpStatusCode.OK:
546 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
547 TwitterDataModel.Status status;
550 status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
552 catch(SerializationException ex)
554 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
555 return "Err:Json Parse Error(DataContractJsonSerializer)";
559 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
560 return "Err:Invalid Json!";
562 _followersCount = status.User.FollowersCount;
563 _friendsCount = status.User.FriendsCount;
564 _statusesCount = status.User.StatusesCount;
565 _location = status.User.Location;
566 _bio = status.User.Description;
568 if (IsPostRestricted(status))
570 return "OK:Delaying?";
572 if (op.Post(postStr.Length))
578 return "Outputz:Failed";
580 case HttpStatusCode.NotFound:
582 case HttpStatusCode.Forbidden:
583 case HttpStatusCode.BadRequest:
585 var errMsg = GetErrorMessageJson(content);
586 if (string.IsNullOrEmpty(errMsg))
588 return "Warn:" + res.ToString();
592 return "Warn:" + errMsg;
595 case HttpStatusCode.Conflict:
596 case HttpStatusCode.ExpectationFailed:
597 case HttpStatusCode.Gone:
598 case HttpStatusCode.LengthRequired:
599 case HttpStatusCode.MethodNotAllowed:
600 case HttpStatusCode.NotAcceptable:
601 case HttpStatusCode.PaymentRequired:
602 case HttpStatusCode.PreconditionFailed:
603 case HttpStatusCode.RequestedRangeNotSatisfiable:
604 case HttpStatusCode.RequestEntityTooLarge:
605 case HttpStatusCode.RequestTimeout:
606 case HttpStatusCode.RequestUriTooLong:
607 //仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
608 return "Warn:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
609 case HttpStatusCode.Unauthorized:
611 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
612 var errMsg = GetErrorMessageJson(content);
613 if (string.IsNullOrEmpty(errMsg))
615 return Properties.Resources.Unauthorized;
619 return "Auth err:" + errMsg;
623 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
627 public string PostStatusWithMedia(string postStr, long reply_to, FileInfo mediaFile)
629 if (MyCommon._endingFlag) return "";
631 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
633 postStr = postStr.Trim();
635 HttpStatusCode res = HttpStatusCode.BadRequest;
639 res = twCon.UpdateStatusWithMedia(postStr, reply_to, mediaFile, ref content);
643 return "Err:" + ex.Message;
648 case HttpStatusCode.OK:
649 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
650 TwitterDataModel.Status status;
653 status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
655 catch(SerializationException ex)
657 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
658 return "Err:Json Parse Error(DataContractJsonSerializer)";
662 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
663 return "Err:Invalid Json!";
665 _followersCount = status.User.FollowersCount;
666 _friendsCount = status.User.FriendsCount;
667 _statusesCount = status.User.StatusesCount;
668 _location = status.User.Location;
669 _bio = status.User.Description;
671 if (IsPostRestricted(status))
673 return "OK:Delaying?";
675 if (op.Post(postStr.Length))
681 return "Outputz:Failed";
683 case HttpStatusCode.NotFound:
685 case HttpStatusCode.Forbidden:
686 case HttpStatusCode.BadRequest:
688 var errMsg = GetErrorMessageJson(content);
689 if (string.IsNullOrEmpty(errMsg))
691 return "Warn:" + res.ToString();
695 return "Warn:" + errMsg;
698 case HttpStatusCode.Conflict:
699 case HttpStatusCode.ExpectationFailed:
700 case HttpStatusCode.Gone:
701 case HttpStatusCode.LengthRequired:
702 case HttpStatusCode.MethodNotAllowed:
703 case HttpStatusCode.NotAcceptable:
704 case HttpStatusCode.PaymentRequired:
705 case HttpStatusCode.PreconditionFailed:
706 case HttpStatusCode.RequestedRangeNotSatisfiable:
707 case HttpStatusCode.RequestEntityTooLarge:
708 case HttpStatusCode.RequestTimeout:
709 case HttpStatusCode.RequestUriTooLong:
710 //仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
711 return "Warn:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
712 case HttpStatusCode.Unauthorized:
714 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
715 var errMsg = GetErrorMessageJson(content);
716 if (string.IsNullOrEmpty(errMsg))
718 return Properties.Resources.Unauthorized;
722 return "Auth err:" + errMsg;
726 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
730 public string SendDirectMessage(string postStr)
732 if (MyCommon._endingFlag) return "";
734 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
735 if (MyCommon.TwitterApiInfo.AccessLevel != ApiAccessLevel.None)
737 if (!MyCommon.TwitterApiInfo.IsDirectMessagePermission) return "Auth Err:try to re-authorization.";
740 postStr = postStr.Trim();
742 HttpStatusCode res = HttpStatusCode.BadRequest;
745 var mc = Regex.Match(postStr, "^DM? +(?<id>[a-zA-Z0-9_]+) +(?<body>.+)", RegexOptions.IgnoreCase | RegexOptions.Singleline);
749 res = twCon.SendDirectMessage(mc.Groups["body"].Value, mc.Groups["id"].Value, ref content);
753 return "Err:" + ex.Message;
758 case HttpStatusCode.OK:
759 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
760 TwitterDataModel.Directmessage status;
763 status = MyCommon.CreateDataFromJson<TwitterDataModel.Directmessage>(content);
765 catch(SerializationException ex)
767 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
768 return "Err:Json Parse Error(DataContractJsonSerializer)";
772 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
773 return "Err:Invalid Json!";
775 _followersCount = status.Sender.FollowersCount;
776 _friendsCount = status.Sender.FriendsCount;
777 _statusesCount = status.Sender.StatusesCount;
778 _location = status.Sender.Location;
779 _bio = status.Sender.Description;
781 if (op.Post(postStr.Length))
787 return "Outputz:Failed";
789 case HttpStatusCode.Forbidden:
790 case HttpStatusCode.BadRequest:
792 var errMsg = GetErrorMessageJson(content);
793 if (string.IsNullOrEmpty(errMsg))
795 return "Warn:" + res.ToString();
799 return "Warn:" + errMsg;
802 case HttpStatusCode.Conflict:
803 case HttpStatusCode.ExpectationFailed:
804 case HttpStatusCode.Gone:
805 case HttpStatusCode.LengthRequired:
806 case HttpStatusCode.MethodNotAllowed:
807 case HttpStatusCode.NotAcceptable:
808 case HttpStatusCode.NotFound:
809 case HttpStatusCode.PaymentRequired:
810 case HttpStatusCode.PreconditionFailed:
811 case HttpStatusCode.RequestedRangeNotSatisfiable:
812 case HttpStatusCode.RequestEntityTooLarge:
813 case HttpStatusCode.RequestTimeout:
814 case HttpStatusCode.RequestUriTooLong:
815 //仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
816 return "Warn:" + res.ToString();
817 case HttpStatusCode.Unauthorized:
819 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
820 var errMsg = GetErrorMessageJson(content);
821 if (string.IsNullOrEmpty(errMsg))
823 return Properties.Resources.Unauthorized;
827 return "Auth err:" + errMsg;
831 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
835 public string RemoveStatus(long id)
837 if (MyCommon._endingFlag) return "";
839 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
841 HttpStatusCode res = HttpStatusCode.BadRequest;
845 res = twCon.DestroyStatus(id);
849 return "Err:" + ex.Message;
854 case HttpStatusCode.OK:
855 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
857 case HttpStatusCode.Unauthorized:
858 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
859 return Properties.Resources.Unauthorized;
860 case HttpStatusCode.NotFound:
863 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
867 public string PostRetweet(long id, bool read)
869 if (MyCommon._endingFlag) return "";
870 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
874 var post = TabInformations.GetInstance()[id];
877 return "Err:Target isn't found.";
879 if (TabInformations.GetInstance()[id].RetweetedId > 0)
881 target = TabInformations.GetInstance()[id].RetweetedId; //再RTの場合は元発言をRT
884 HttpStatusCode res = HttpStatusCode.BadRequest;
888 res = twCon.RetweetStatus(target, ref content);
892 return "Err:" + ex.Message;
895 if (res == HttpStatusCode.Unauthorized)
897 //Blockユーザーの発言をRTすると認証エラー返る
898 //Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid
899 return Properties.Resources.Unauthorized + " or blocked user.";
901 else if (res != HttpStatusCode.OK)
903 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
906 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
908 TwitterDataModel.Status status;
911 status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
913 catch(SerializationException ex)
915 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
916 return "Err:Json Parse Error(DataContractJsonSerializer)";
920 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
921 return "Err:Invalid Json!";
925 post = CreatePostsFromStatusData(status);
926 if (post == null) return "Invalid Json!";
931 if (TabInformations.GetInstance().ContainsKey(post.StatusId)) return "";
934 if (post.RetweetedId == 0) return "Invalid Json!";
940 if (_readOwnPost) post.IsRead = true;
943 TabInformations.GetInstance().AddPost(post);
948 public string RemoveDirectMessage(long id, PostClass post)
950 if (MyCommon._endingFlag) return "";
952 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
953 if (MyCommon.TwitterApiInfo.AccessLevel != ApiAccessLevel.None)
955 if (!MyCommon.TwitterApiInfo.IsDirectMessagePermission) return "Auth Err:try to re-authorization.";
958 HttpStatusCode res = HttpStatusCode.BadRequest;
961 // _deletemessages.Add(post)
965 res = twCon.DestroyDirectMessage(id);
969 return "Err:" + ex.Message;
974 case HttpStatusCode.OK:
975 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
977 case HttpStatusCode.Unauthorized:
978 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
979 return Properties.Resources.Unauthorized;
980 case HttpStatusCode.NotFound:
983 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
987 public string PostFollowCommand(string screenName)
989 if (MyCommon._endingFlag) return "";
991 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
993 HttpStatusCode res = HttpStatusCode.BadRequest;
998 res = twCon.CreateFriendships(screenName, ref content);
1002 return "Err:" + ex.Message;
1007 case HttpStatusCode.OK:
1008 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1010 case HttpStatusCode.Unauthorized:
1011 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1012 return Properties.Resources.Unauthorized;
1013 case HttpStatusCode.Forbidden:
1014 var errMsg = GetErrorMessageJson(content);
1015 if (string.IsNullOrEmpty(errMsg))
1017 return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1021 return "Err:" + errMsg;
1024 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1028 public string PostRemoveCommand(string screenName)
1030 if (MyCommon._endingFlag) return "";
1032 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1034 HttpStatusCode res = HttpStatusCode.BadRequest;
1039 res = twCon.DestroyFriendships(screenName, ref content);
1043 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1048 case HttpStatusCode.OK:
1049 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1051 case HttpStatusCode.Unauthorized:
1052 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1053 return Properties.Resources.Unauthorized;
1054 case HttpStatusCode.Forbidden:
1055 var errMsg = GetErrorMessageJson(content);
1056 if (string.IsNullOrEmpty(errMsg))
1058 return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1062 return "Err:" + errMsg;
1065 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1069 public string PostCreateBlock(string screenName)
1071 if (MyCommon._endingFlag) return "";
1073 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1075 HttpStatusCode res = HttpStatusCode.BadRequest;
1080 res = twCon.CreateBlock(screenName, ref content);
1084 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1089 case HttpStatusCode.OK:
1090 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1092 case HttpStatusCode.Unauthorized:
1093 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1094 return Properties.Resources.Unauthorized;
1095 case HttpStatusCode.Forbidden:
1096 var errMsg = GetErrorMessageJson(content);
1097 if (string.IsNullOrEmpty(errMsg))
1099 return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1103 return "Err:" + errMsg;
1106 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1110 public string PostDestroyBlock(string screenName)
1112 if (MyCommon._endingFlag) return "";
1114 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1116 HttpStatusCode res = HttpStatusCode.BadRequest;
1121 res = twCon.DestroyBlock(screenName, ref content);
1125 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1130 case HttpStatusCode.OK:
1131 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1133 case HttpStatusCode.Unauthorized:
1134 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1135 return Properties.Resources.Unauthorized;
1136 case HttpStatusCode.Forbidden:
1137 var errMsg = GetErrorMessageJson(content);
1138 if (string.IsNullOrEmpty(errMsg))
1140 return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1144 return "Err:" + errMsg;
1147 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1151 public string PostReportSpam(string screenName)
1153 if (MyCommon._endingFlag) return "";
1155 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1157 HttpStatusCode res = HttpStatusCode.BadRequest;
1162 res = twCon.ReportSpam(screenName, ref content);
1166 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1171 case HttpStatusCode.OK:
1172 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1174 case HttpStatusCode.Unauthorized:
1175 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1176 return Properties.Resources.Unauthorized;
1177 case HttpStatusCode.Forbidden:
1178 var errMsg = GetErrorMessageJson(content);
1179 if (string.IsNullOrEmpty(errMsg))
1181 return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1185 return "Err:" + errMsg;
1188 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1192 public string GetFriendshipInfo(string screenName, ref bool isFollowing, ref bool isFollowed)
1194 if (MyCommon._endingFlag) return "";
1196 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1198 HttpStatusCode res = HttpStatusCode.BadRequest;
1202 res = twCon.ShowFriendships(_uname, screenName, ref content);
1206 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1211 case HttpStatusCode.OK:
1214 var relation = MyCommon.CreateDataFromJson<TwitterDataModel.Relationship>(content);
1215 isFollowing = relation.relationship.Source.Following;
1216 isFollowed = relation.relationship.Source.FollowedBy;
1219 catch(SerializationException ex)
1221 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
1222 return "Err:Json Parse Error(DataContractJsonSerializer)";
1226 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
1227 return "Err:Invalid Json!";
1229 case HttpStatusCode.BadRequest:
1230 return "Err:API Limits?";
1231 case HttpStatusCode.Unauthorized:
1232 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1233 return Properties.Resources.Unauthorized;
1235 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1239 public string GetUserInfo(string screenName, ref TwitterDataModel.User user)
1241 if (MyCommon._endingFlag) return "";
1243 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1245 HttpStatusCode res = HttpStatusCode.BadRequest;
1250 res = twCon.ShowUserInfo(screenName, ref content);
1254 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1259 case HttpStatusCode.OK:
1260 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1263 user = MyCommon.CreateDataFromJson<TwitterDataModel.User>(content);
1265 catch (SerializationException ex)
1267 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
1268 return "Err:Json Parse Error(DataContractJsonSerializer)";
1270 catch (Exception ex)
1272 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
1273 return "Err:Invalid Json!";
1276 case HttpStatusCode.BadRequest:
1277 return "Err:API Limits?";
1278 case HttpStatusCode.Unauthorized:
1279 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1280 var errMsg = GetErrorMessageJson(content);
1281 if (string.IsNullOrEmpty(errMsg))
1283 return Properties.Resources.Unauthorized;
1287 return "Auth err:" + errMsg;
1290 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1294 public string GetStatus_Retweeted_Count(long StatusId, ref int retweeted_count)
1296 if (MyCommon._endingFlag) return "";
1298 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1300 HttpStatusCode res = HttpStatusCode.BadRequest;
1303 retweeted_count = 0;
1305 // 注:dev.twitter.comに記述されているcountパラメータは間違い。100が正しい
1306 for (int i = 1; i <= 100; i++)
1310 res = twCon.Statusid_retweeted_by_ids(StatusId, 100, i, ref content);
1314 return "Err:" + ex.Message;
1319 case HttpStatusCode.OK:
1322 var ids = MyCommon.CreateDataFromJson<Int64[]>(content);
1323 retweeted_count += ids.Length;
1324 if (ids.Length < 100) goto exit_for;
1326 catch (SerializationException ex)
1328 retweeted_count = -1;
1329 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
1330 return "Err:Json Parse Error(DataContractJsonSerializer)";
1332 catch (Exception ex)
1334 retweeted_count = -1;
1335 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
1336 return "Err:Invalid Json!";
1339 case HttpStatusCode.BadRequest:
1340 retweeted_count = -1;
1341 return "Err:API Limits?";
1342 case HttpStatusCode.Unauthorized:
1343 retweeted_count = -1;
1344 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1345 return Properties.Resources.Unauthorized;
1347 retweeted_count = -1;
1348 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1355 public string PostFavAdd(long id)
1357 if (MyCommon._endingFlag) return "";
1359 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1361 //if (this.favQueue == null) this.favQueue = new FavoriteQueue(this)
1363 //if (this.favQueue.Contains(id)) this.favQueue.Remove(id)
1365 HttpStatusCode res = HttpStatusCode.BadRequest;
1369 res = twCon.CreateFavorites(id, ref content);
1373 //this.favQueue.Add(id)
1374 //return "Err:->FavoriteQueue:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1375 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1380 case HttpStatusCode.OK:
1381 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1382 //this.favQueue.FavoriteCacheStart();
1383 if (!_restrictFavCheck) return "";
1385 case HttpStatusCode.Unauthorized:
1386 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1387 return Properties.Resources.Unauthorized;
1388 case HttpStatusCode.Forbidden:
1389 var errMsg = GetErrorMessageJson(content);
1390 if (string.IsNullOrEmpty(errMsg))
1392 return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1396 //if (errMsg.Contains("It's great that you like so many updates"))
1397 // //this.favQueue.Add(id)
1398 // return "Err:->FavoriteQueue:" + errMsg;
1400 return "Err:" + errMsg;
1402 //Case HttpStatusCode.BadGateway, HttpStatusCode.ServiceUnavailable, HttpStatusCode.InternalServerError, HttpStatusCode.RequestTimeout
1403 // //this.favQueue.Add(id)
1404 // return "Err:->FavoriteQueue:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1406 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1409 //http://twitter.com/statuses/show/id.xml APIを発行して本文を取得
1415 res = twCon.ShowStatuses(id, ref content);
1419 return "Err:" + ex.Message;
1424 case HttpStatusCode.OK:
1425 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1426 TwitterDataModel.Status status;
1429 status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
1431 catch (SerializationException ex)
1433 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
1434 return "Err:Json Parse Error(DataContractJsonSerializer)";
1436 catch (Exception ex)
1438 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
1439 return "Err:Invalid Json!";
1441 if (status.Favorited)
1447 return "NG(Restricted?)";
1449 case HttpStatusCode.Unauthorized:
1450 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1451 return Properties.Resources.Unauthorized;
1452 case HttpStatusCode.BadRequest:
1453 return "Err:API Limits?";
1455 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1459 public string PostFavRemove(long id)
1461 if (MyCommon._endingFlag) return "";
1463 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1465 //if (this.favQueue == null) this.favQueue = new FavoriteQueue(this)
1467 //if (this.favQueue.Contains(id))
1468 // this.favQueue.Remove(id)
1472 HttpStatusCode res = HttpStatusCode.BadRequest;
1476 res = twCon.DestroyFavorites(id, ref content);
1480 return "Err:" + ex.Message;
1485 case HttpStatusCode.OK:
1486 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1488 case HttpStatusCode.Unauthorized:
1489 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1490 return Properties.Resources.Unauthorized;
1491 case HttpStatusCode.Forbidden:
1492 var errMsg = GetErrorMessageJson(content);
1493 if (string.IsNullOrEmpty(errMsg))
1495 return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1499 return "Err:" + errMsg;
1502 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1506 public string PostUpdateProfile(string name, string url, string location, string description)
1508 if (MyCommon._endingFlag) return "";
1510 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1512 HttpStatusCode res = HttpStatusCode.BadRequest;
1516 res = twCon.UpdateProfile(name, url, location, description, ref content);
1520 return "Err:" + ex.Message;
1525 case HttpStatusCode.OK:
1526 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1528 case HttpStatusCode.Unauthorized:
1529 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1530 return Properties.Resources.Unauthorized;
1531 case HttpStatusCode.Forbidden:
1532 var errMsg = GetErrorMessageJson(content);
1533 if (string.IsNullOrEmpty(errMsg))
1535 return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1539 return "Err:" + errMsg;
1542 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1546 public string PostUpdateProfileImage(string filename)
1548 if (MyCommon._endingFlag) return "";
1550 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1552 HttpStatusCode res = HttpStatusCode.BadRequest;
1556 res = twCon.UpdateProfileImage(new FileInfo(filename), ref content);
1560 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1565 case HttpStatusCode.OK:
1566 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1568 case HttpStatusCode.Unauthorized:
1569 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1570 return Properties.Resources.Unauthorized;
1571 case HttpStatusCode.Forbidden:
1572 var errMsg = GetErrorMessageJson(content);
1573 if (string.IsNullOrEmpty(errMsg))
1575 return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1579 return "Err:" + errMsg;
1582 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1586 public string Username
1590 return twCon.AuthenticatedUsername;
1598 return twCon.AuthenticatedUserId;
1602 public string Password
1606 return twCon.Password;
1610 private static MyCommon.ACCOUNT_STATE _accountState = MyCommon.ACCOUNT_STATE.Valid;
1611 public static MyCommon.ACCOUNT_STATE AccountState
1615 return _accountState;
1619 _accountState = value;
1631 public bool TinyUrlResolve
1635 _tinyUrlResolve = value;
1639 public bool RestrictFavCheck
1643 _restrictFavCheck = value;
1656 public string GetVersionInfo()
1659 if (!(new HttpVarious()).GetData(ApplicationSettings.VersionInfoUrl + "?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), null, out content, MyCommon.GetUserAgentString()))
1661 throw new Exception("GetVersionInfo Failed");
1666 public string GetTweenBinary(string strVer)
1671 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/Tween" + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1672 Path.Combine(MyCommon.settingPath, "TweenNew.exe")))
1674 return "Err:Download failed";
1677 if (!Directory.Exists(Path.Combine(MyCommon.settingPath, "en")))
1679 Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, "en"));
1681 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenResEn" + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1682 Path.Combine(Path.Combine(MyCommon.settingPath, "en"), "Tween.resourcesNew.dll")))
1684 return "Err:Download failed";
1686 //その他言語圏のリソース。取得失敗しても継続
1689 if (!Thread.CurrentThread.CurrentUICulture.IsNeutralCulture)
1691 var idx = Thread.CurrentThread.CurrentUICulture.Name.LastIndexOf('-');
1694 curCul = Thread.CurrentThread.CurrentUICulture.Name.Substring(0, idx);
1698 curCul = Thread.CurrentThread.CurrentUICulture.Name;
1703 curCul = Thread.CurrentThread.CurrentUICulture.Name;
1705 if (!string.IsNullOrEmpty(curCul) && curCul != "en" && curCul != "ja")
1707 if (!Directory.Exists(Path.Combine(MyCommon.settingPath, curCul)))
1709 Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, curCul));
1711 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenRes" + curCul + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1712 Path.Combine(Path.Combine(MyCommon.settingPath, curCul), "Tween.resourcesNew.dll")))
1714 //return "Err:Download failed";
1719 if (!Thread.CurrentThread.CurrentCulture.IsNeutralCulture)
1721 var idx = Thread.CurrentThread.CurrentCulture.Name.LastIndexOf('-');
1724 curCul2 = Thread.CurrentThread.CurrentCulture.Name.Substring(0, idx);
1728 curCul2 = Thread.CurrentThread.CurrentCulture.Name;
1733 curCul2 = Thread.CurrentThread.CurrentCulture.Name;
1735 if (!string.IsNullOrEmpty(curCul2) && curCul2 != "en" && curCul2 != curCul)
1737 if (!Directory.Exists(Path.Combine(MyCommon.settingPath, curCul2)))
1739 Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, curCul2));
1741 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenRes" + curCul2 + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1742 Path.Combine(Path.Combine(MyCommon.settingPath, curCul2), "Tween.resourcesNew.dll")))
1744 //return "Err:Download failed";
1749 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenUp3.gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1750 Path.Combine(MyCommon.settingPath, "TweenUp3.exe")))
1752 return "Err:Download failed";
1755 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenDll" + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1756 Path.Combine(MyCommon.settingPath, "TweenNew.XmlSerializers.dll")))
1758 return "Err:Download failed";
1764 return "Err:Download failed";
1769 public IDictionary<string, Image> DetailIcon
1781 public bool ReadOwnPost
1785 return _readOwnPost;
1789 _readOwnPost = value;
1793 public int FollowersCount
1797 return _followersCount;
1801 public int FriendsCount
1805 return _friendsCount;
1809 public int StatusesCount
1813 return _statusesCount;
1817 public string Location
1837 HttpTwitter.UseSsl = value;
1840 _protocol = "https://";
1844 _protocol = "http://";
1849 public string GetTimelineApi(bool read,
1850 MyCommon.WORKERTYPE gType,
1854 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1856 if (MyCommon._endingFlag) return "";
1860 var count = AppendSettingDialog.Instance.CountApi;
1861 if (gType == MyCommon.WORKERTYPE.Reply) count = AppendSettingDialog.Instance.CountApiReply;
1862 if (AppendSettingDialog.Instance.UseAdditionalCount)
1864 if (more && AppendSettingDialog.Instance.MoreCountApi != 0)
1866 count = AppendSettingDialog.Instance.MoreCountApi;
1868 else if (startup && AppendSettingDialog.Instance.FirstCountApi != 0 && gType == MyCommon.WORKERTYPE.Timeline)
1870 count = AppendSettingDialog.Instance.FirstCountApi;
1875 if (gType == MyCommon.WORKERTYPE.Timeline)
1879 res = twCon.HomeTimeline(count, this.minHomeTimeline, 0, ref content);
1883 res = twCon.HomeTimeline(count, 0, 0, ref content);
1890 res = twCon.Mentions(count, this.minMentions, 0, ref content);
1894 res = twCon.Mentions(count, 0, 0, ref content);
1900 return "Err:" + ex.Message;
1904 case HttpStatusCode.OK:
1905 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1907 case HttpStatusCode.Unauthorized:
1908 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1909 return Properties.Resources.Unauthorized;
1910 case HttpStatusCode.BadRequest:
1911 return "Err:API Limits?";
1913 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1916 if (gType == MyCommon.WORKERTYPE.Timeline)
1918 return CreatePostsFromJson(content, gType, null, read, count, ref this.minHomeTimeline);
1922 return CreatePostsFromJson(content, gType, null, read, count, ref this.minMentions);
1926 public string GetUserTimelineApi(bool read,
1932 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1934 if (MyCommon._endingFlag) return "";
1936 HttpStatusCode res = HttpStatusCode.BadRequest;
1939 if (count == 0) count = 20;
1942 if (string.IsNullOrEmpty(userName))
1944 var target = tab.User;
1945 if (string.IsNullOrEmpty(target)) return "";
1947 res = twCon.UserTimeline(0, target, count, 0, 0, ref content);
1953 res = twCon.UserTimeline(0, userName, count, tab.OldestId, 0, ref content);
1957 res = twCon.UserTimeline(0, userName, count, 0, 0, ref content);
1963 return "Err:" + ex.Message;
1967 case HttpStatusCode.OK:
1968 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1970 case HttpStatusCode.Unauthorized:
1971 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1972 return "Err:@" + userName + "'s Tweets are protected.";
1973 case HttpStatusCode.BadRequest:
1974 return "Err:API Limits?";
1976 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1979 List<TwitterDataModel.Status> items;
1982 items = MyCommon.CreateDataFromJson<List<TwitterDataModel.Status>>(content);
1984 catch(SerializationException ex)
1986 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
1987 return "Json Parse Error(DataContractJsonSerializer)";
1991 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
1992 return "Invalid Json!";
1995 foreach (var status in items)
1997 var item = CreatePostsFromStatusData(status);
1998 if (item == null) continue;
1999 if (item.StatusId < tab.OldestId) tab.OldestId = item.StatusId;
2001 if (item.IsMe && !read && _readOwnPost) item.IsRead = true;
2002 if (tab != null) item.RelTabName = tab.TabName;
2003 //非同期アイコン取得&StatusDictionaryに追加
2004 TabInformations.GetInstance().AddPost(item);
2010 public string GetStatusApi(bool read,
2014 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
2016 if (MyCommon._endingFlag) return "";
2018 HttpStatusCode res = HttpStatusCode.BadRequest;
2023 res = twCon.ShowStatuses(id, ref content);
2027 return "Err:" + ex.Message;
2031 case HttpStatusCode.OK:
2032 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
2034 case HttpStatusCode.Unauthorized:
2035 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
2036 return Properties.Resources.Unauthorized;
2037 case HttpStatusCode.BadRequest:
2038 return "Err:API Limits?";
2039 case HttpStatusCode.Forbidden:
2040 return "Err:protected user's tweet";
2042 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2045 TwitterDataModel.Status status;
2048 status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
2050 catch(SerializationException ex)
2052 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2053 return "Json Parse Error(DataContractJsonSerializer)";
2057 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2058 return "Invalid Json!";
2061 var item = CreatePostsFromStatusData(status);
2062 if (item == null) return "Err:Can't create post";
2064 if (item.IsMe && !read && _readOwnPost) item.IsRead = true;
2070 public string GetStatusApi(bool read,
2074 PostClass post = null;
2075 var r = this.GetStatusApi(read, id, ref post);
2077 if (string.IsNullOrEmpty(r))
2079 if (tab != null) post.RelTabName = tab.TabName;
2080 //非同期アイコン取得&StatusDictionaryに追加
2081 TabInformations.GetInstance().AddPost(post);
2087 private PostClass CreatePostsFromStatusData(TwitterDataModel.Status status)
2089 var post = new PostClass();
2090 TwitterDataModel.Entities entities;
2092 post.StatusId = status.Id;
2093 if (status.RetweetedStatus != null)
2095 var retweeted = status.RetweetedStatus;
2097 post.CreatedAt = MyCommon.DateTimeParse(retweeted.CreatedAt);
2100 post.RetweetedId = retweeted.Id;
2102 post.TextFromApi = retweeted.Text;
2103 entities = retweeted.Entities;
2104 //Source取得(htmlの場合は、中身を取り出し)
2105 post.Source = retweeted.Source;
2107 long inReplyToStatusId;
2108 long.TryParse(retweeted.InReplyToStatusId, out inReplyToStatusId);
2109 post.InReplyToStatusId = inReplyToStatusId;
2110 post.InReplyToUser = retweeted.InReplyToScreenName;
2111 long inReplyToUserId;
2112 long.TryParse(status.InReplyToUserId, out inReplyToUserId);
2113 post.InReplyToUserId = inReplyToUserId;
2116 var tc = TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites);
2117 post.IsFav = tc.Contains(post.RetweetedId);
2119 if (retweeted.Geo != null) post.PostGeo = new PostClass.StatusGeo {Lat = retweeted.Geo.Coordinates[0], Lng = retweeted.Geo.Coordinates[1]};
2122 var user = retweeted.User;
2124 if (user.ScreenName == null || status.User.ScreenName == null) return null;
2126 post.UserId = user.Id;
2127 post.ScreenName = user.ScreenName;
2128 post.Nickname = user.Name.Trim();
2129 post.ImageUrl = user.ProfileImageUrl;
2130 post.IsProtect = user.Protected;
2133 post.RetweetedBy = status.User.ScreenName;
2134 post.RetweetedByUserId = status.User.Id;
2135 post.IsMe = post.RetweetedBy.ToLower().Equals(_uname);
2139 post.CreatedAt = MyCommon.DateTimeParse(status.CreatedAt);
2141 post.TextFromApi = status.Text;
2142 entities = status.Entities;
2143 //Source取得(htmlの場合は、中身を取り出し)
2144 post.Source = status.Source;
2145 long inReplyToStatusId;
2146 long.TryParse(status.InReplyToStatusId, out inReplyToStatusId);
2147 post.InReplyToStatusId = inReplyToStatusId;
2148 post.InReplyToUser = status.InReplyToScreenName;
2149 long inReplyToUserId;
2150 long.TryParse(status.InReplyToUserId, out inReplyToUserId);
2151 post.InReplyToUserId = inReplyToUserId;
2153 if (status.Geo != null) post.PostGeo = new PostClass.StatusGeo {Lat = status.Geo.Coordinates[0], Lng = status.Geo.Coordinates[1]};
2156 var user = status.User;
2158 if (user.ScreenName == null) return null;
2160 post.UserId = user.Id;
2161 post.ScreenName = user.ScreenName;
2162 post.Nickname = user.Name.Trim();
2163 post.ImageUrl = user.ProfileImageUrl;
2164 post.IsProtect = user.Protected;
2165 post.IsMe = post.ScreenName.ToLower().Equals(_uname);
2168 var tc = TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites);
2169 post.IsFav = tc.Contains(post.StatusId) && TabInformations.GetInstance()[post.StatusId].IsFav;
2172 string textFromApi = post.TextFromApi;
2173 post.Text = CreateHtmlAnchor(ref textFromApi, post.ReplyToList, entities, post.Media);
2174 post.TextFromApi = textFromApi;
2175 post.TextFromApi = this.ReplaceTextFromApi(post.TextFromApi, entities);
2176 post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi);
2177 post.TextFromApi = post.TextFromApi.Replace("<3", "\u2661");
2180 CreateSource(ref post);
2182 post.IsReply = post.ReplyToList.Contains(_uname);
2183 post.IsExcludeReply = false;
2191 if (followerId.Count > 0) post.IsOwl = !followerId.Contains(post.UserId);
2198 private string CreatePostsFromJson(string content, MyCommon.WORKERTYPE gType, TabClass tab, bool read, int count, ref long minimumId)
2200 List<TwitterDataModel.Status> items;
2203 items = MyCommon.CreateDataFromJson<List<TwitterDataModel.Status>>(content);
2205 catch(SerializationException ex)
2207 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2208 return "Json Parse Error(DataContractJsonSerializer)";;
2212 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2213 return "Invalid Json!";
2216 foreach (var status in items)
2218 PostClass post = null;
2219 post = CreatePostsFromStatusData(status);
2220 if (post == null) continue;
2222 if (minimumId > post.StatusId) minimumId = post.StatusId;
2228 if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue;
2232 if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue;
2237 if (post.RetweetedId > 0 && this.noRTId.Contains(post.RetweetedByUserId)) continue;
2240 if (post.IsMe && !read && _readOwnPost) post.IsRead = true;
2242 if (tab != null) post.RelTabName = tab.TabName;
2243 //非同期アイコン取得&StatusDictionaryに追加
2244 TabInformations.GetInstance().AddPost(post);
2250 private string CreatePostsFromPhoenixSearch(string content, MyCommon.WORKERTYPE gType, TabClass tab, bool read, int count, ref long minimumId, ref string nextPageQuery)
2252 TwitterDataModel.SearchResult items;
2255 items = MyCommon.CreateDataFromJson<TwitterDataModel.SearchResult>(content);
2257 catch(SerializationException ex)
2259 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2260 return "Json Parse Error(DataContractJsonSerializer)";
2264 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2265 return "Invalid Json!";
2268 nextPageQuery = items.NextPage;
2270 foreach (var status in items.Statuses)
2272 PostClass post = null;
2273 post = CreatePostsFromStatusData(status);
2274 if (post == null) continue;
2276 if (minimumId > post.StatusId) minimumId = post.StatusId;
2282 if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue;
2286 if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue;
2291 if (post.IsMe && !read && _readOwnPost) post.IsRead = true;
2293 if (tab != null) post.RelTabName = tab.TabName;
2294 //非同期アイコン取得&StatusDictionaryに追加
2295 TabInformations.GetInstance().AddPost(post);
2298 return string.IsNullOrEmpty(items.ErrMsg) ? "" : "Err:" + items.ErrMsg;
2301 public string GetListStatus(bool read,
2306 if (MyCommon._endingFlag) return "";
2311 if (AppendSettingDialog.Instance.UseAdditionalCount)
2313 count = AppendSettingDialog.Instance.ListCountApi;
2316 if (more && AppendSettingDialog.Instance.MoreCountApi != 0)
2318 count = AppendSettingDialog.Instance.MoreCountApi;
2320 else if (startup && AppendSettingDialog.Instance.FirstCountApi != 0)
2322 count = AppendSettingDialog.Instance.FirstCountApi;
2326 count = AppendSettingDialog.Instance.CountApi;
2332 count = AppendSettingDialog.Instance.CountApi;
2338 res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, tab.OldestId, 0, AppendSettingDialog.Instance.IsListStatusesIncludeRts, ref content);
2342 res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, 0, 0, AppendSettingDialog.Instance.IsListStatusesIncludeRts, ref content);
2347 return "Err:" + ex.Message;
2351 case HttpStatusCode.OK:
2352 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
2354 case HttpStatusCode.Unauthorized:
2355 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
2356 return Properties.Resources.Unauthorized;
2357 case HttpStatusCode.BadRequest:
2358 return "Err:API Limits?";
2360 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2363 return CreatePostsFromJson(content, MyCommon.WORKERTYPE.List, tab, read, count, ref tab.OldestId);
2367 private PostClass CheckReplyToPost(List<PostClass> relPosts)
2369 var tmpPost = relPosts[0];
2370 PostClass lastPost = null;
2371 while (tmpPost != null)
2373 if (tmpPost.InReplyToStatusId == 0) return null;
2375 var replyToPost = from p in relPosts
2376 where p.StatusId == tmpPost.InReplyToStatusId
2378 tmpPost = replyToPost.FirstOrDefault();
2383 public string GetRelatedResult(bool read, TabClass tab)
2386 var relPosts = new List<PostClass>();
2387 if (tab.RelationTargetPost.TextFromApi.Contains("@") && tab.RelationTargetPost.InReplyToStatusId == 0)
2390 var p = TabInformations.GetInstance()[tab.RelationTargetPost.StatusId];
2391 if (p != null && p.InReplyToStatusId > 0)
2393 tab.RelationTargetPost = p;
2397 rslt = this.GetStatusApi(read, tab.RelationTargetPost.StatusId, ref p);
2398 if (!string.IsNullOrEmpty(rslt)) return rslt;
2399 tab.RelationTargetPost = p;
2402 relPosts.Add(tab.RelationTargetPost.Copy());
2403 var tmpPost = relPosts[0];
2406 rslt = this.GetRelatedResultsApi(read, tmpPost, tab, relPosts);
2407 if (!string.IsNullOrEmpty(rslt)) break;
2408 tmpPost = CheckReplyToPost(relPosts);
2409 } while (tmpPost != null);
2411 relPosts.ForEach(p => TabInformations.GetInstance().AddPost(p));
2415 private string GetRelatedResultsApi(bool read,
2418 List<PostClass> relatedPosts)
2420 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
2422 if (MyCommon._endingFlag) return "";
2424 HttpStatusCode res = HttpStatusCode.BadRequest;
2428 if (post.RetweetedId > 0)
2430 res = twCon.GetRelatedResults(post.RetweetedId, ref content);
2434 res = twCon.GetRelatedResults(post.StatusId, ref content);
2439 return "Err:" + ex.Message;
2443 case HttpStatusCode.OK:
2444 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
2446 case HttpStatusCode.Unauthorized:
2447 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
2448 return Properties.Resources.Unauthorized;
2449 case HttpStatusCode.BadRequest:
2450 return "Err:API Limits?";
2452 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2455 List<TwitterDataModel.RelatedResult> items;
2458 items = MyCommon.CreateDataFromJson<List<TwitterDataModel.RelatedResult>>(content);
2460 catch(SerializationException ex)
2462 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2463 return "Json Parse Error(DataContractJsonSerializer)";
2467 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2468 return "Invalid Json!";
2471 var targetItem = post;
2472 if (targetItem == null)
2478 targetItem = targetItem.Copy();
2480 targetItem.RelTabName = tab.TabName;
2481 TabInformations.GetInstance().AddPost(targetItem);
2483 PostClass replyToItem = null;
2484 var replyToUserName = targetItem.InReplyToUser;
2485 if (targetItem.InReplyToStatusId > 0 && TabInformations.GetInstance()[targetItem.InReplyToStatusId] != null)
2487 replyToItem = TabInformations.GetInstance()[targetItem.InReplyToStatusId].Copy();
2488 replyToItem.IsRead = read;
2489 if (replyToItem.IsMe && !read && _readOwnPost) replyToItem.IsRead = true;
2490 replyToItem.RelTabName = tab.TabName;
2493 var replyAdded = false;
2494 foreach (var relatedData in items)
2496 foreach (var result in relatedData.Results)
2498 var item = CreatePostsFromStatusData(result.Status);
2499 if (item == null) continue;
2500 if (targetItem.InReplyToStatusId == item.StatusId)
2506 if (item.IsMe && !read && _readOwnPost) item.IsRead = true;
2507 if (tab != null) item.RelTabName = tab.TabName;
2508 //非同期アイコン取得&StatusDictionaryに追加
2509 relatedPosts.Add(item);
2512 if (replyToItem != null)
2514 relatedPosts.Add(replyToItem);
2516 else if (targetItem.InReplyToStatusId > 0 && !replyAdded)
2520 rslt = GetStatusApi(read, targetItem.InReplyToStatusId, ref p);
2521 if (string.IsNullOrEmpty(rslt))
2524 p.RelTabName = tab.TabName;
2525 relatedPosts.Add(p);
2530 //発言者・返信先ユーザーの直近10発言取得
2531 //var rslt = this.GetUserTimelineApi(read, 10, "", tab);
2532 //if (!string.IsNullOrEmpty(rslt)) return rslt;
2533 //if (!string.IsNullOrEmpty(replyToUserName))
2534 // rslt = this.GetUserTimelineApi(read, 10, replyToUserName, tab);
2539 //MRTとかに対応のためツイート内にあるツイートを指すURLを取り込む
2540 var ma = Regex.Matches(tab.RelationTargetPost.Text, "title=\"https?://twitter.com/(#!/)?(?<ScreenName>[a-zA-Z0-9_]+)(/status(es)?/(?<StatusId>[0-9]+))\"");
2541 foreach (Match _match in ma)
2544 if (Int64.TryParse(_match.Groups["StatusId"].Value, out _statusId))
2547 var _post = TabInformations.GetInstance()[_statusId];
2550 var rslt = this.GetStatusApi(read, _statusId, ref p);
2559 p.RelTabName = tab.TabName;
2560 relatedPosts.Add(p);
2567 public string GetSearch(bool read,
2571 if (MyCommon._endingFlag) return "";
2578 if (AppendSettingDialog.Instance.UseAdditionalCount &&
2579 AppendSettingDialog.Instance.SearchCountApi != 0)
2581 count = AppendSettingDialog.Instance.SearchCountApi;
2585 count = AppendSettingDialog.Instance.CountApi;
2589 page = tab.GetSearchPage(count);
2593 sinceId = tab.SinceId;
2598 // TODO:一時的に40>100件に 件数変更UI作成の必要あり
2599 res = twCon.Search(tab.SearchWords, tab.SearchLang, count, page, sinceId, ref content);
2603 return "Err:" + ex.Message;
2607 case HttpStatusCode.BadRequest:
2608 return "Invalid query";
2609 case HttpStatusCode.NotFound:
2610 return "Invalid query";
2611 case HttpStatusCode.PaymentRequired: //API Documentには420と書いてあるが、該当コードがないので402にしてある
2612 return "Search API Limit?";
2613 case HttpStatusCode.OK:
2616 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2619 if (!TabInformations.GetInstance().ContainsTab(tab)) return "";
2620 content = Regex.Replace(content, @"[\x00-\x1f-[\x0a\x0d]]+", " ");
2621 var xdoc = new XmlDocument();
2624 xdoc.LoadXml(content);
2628 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2629 return "Invalid ATOM!";
2631 var nsmgr = new XmlNamespaceManager(xdoc.NameTable);
2632 nsmgr.AddNamespace("search", "http://www.w3.org/2005/Atom");
2633 nsmgr.AddNamespace("twitter", "http://api.twitter.com/");
2634 nsmgr.AddNamespace("georss", "http://www.georss.org/georss");
2635 foreach (var xentryNode in xdoc.DocumentElement.SelectNodes("/search:feed/search:entry", nsmgr))
2637 var xentry = (XmlElement)xentryNode;
2638 var post = new PostClass();
2641 post.StatusId = long.Parse(xentry["id"].InnerText.Split(':')[2]);
2642 if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue;
2643 post.CreatedAt = DateTime.Parse(xentry["published"].InnerText);
2645 post.TextFromApi = xentry["title"].InnerText;
2646 //Source取得(htmlの場合は、中身を取り出し)
2647 post.Source = xentry["twitter:source"].InnerText;
2648 post.InReplyToStatusId = 0;
2649 post.InReplyToUser = "";
2650 post.InReplyToUserId = 0;
2653 // Geoが勝手に付加されるバグがいっこうに修正されないので暫定的にGeo情報を無視する
2654 if (xentry["twitter:geo"].HasChildNodes)
2656 var pnt = ((XmlElement)xentry.SelectSingleNode("twitter:geo/georss:point", nsmgr)).InnerText.Split(' ');
2657 post.PostGeo = new PostClass.StatusGeo {Lat = Double.Parse(pnt[0]), Lng = Double.Parse(pnt[1])};
2661 var xUentry = (XmlElement)xentry.SelectSingleNode("./search:author", nsmgr);
2663 post.ScreenName = xUentry["name"].InnerText.Split(' ')[0].Trim();
2664 post.Nickname = xUentry["name"].InnerText.Substring(post.ScreenName.Length).Trim();
2665 if (post.Nickname.Length > 2)
2667 post.Nickname = post.Nickname.Substring(1, post.Nickname.Length - 2);
2671 post.Nickname = post.ScreenName;
2673 post.ImageUrl = ((XmlElement)xentry.SelectSingleNode("./search:link[@type='image/png']", nsmgr)).GetAttribute("href");
2674 post.IsProtect = false;
2675 post.IsMe = post.ScreenName.ToLower().Equals(_uname);
2678 post.Text = CreateHtmlAnchor(HttpUtility.HtmlEncode(post.TextFromApi), post.ReplyToList, post.Media);
2679 post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi);
2681 CreateSource(ref post);
2684 post.IsReply = post.ReplyToList.Contains(_uname);
2685 post.IsExcludeReply = false;
2688 if (post.IsMe && !read && _readOwnPost) post.IsRead = true;
2690 post.RelTabName = tab.TabName;
2691 if (!more && post.StatusId > tab.SinceId) tab.SinceId = post.StatusId;
2695 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2699 //this._dIcon.Add(post.ImageUrl, null);
2700 TabInformations.GetInstance().AddPost(post);
2705 // 遡るための情報max_idやnext_pageの情報を保持する
2708 var xNode = xdoc.DocumentElement.SelectSingleNode("/search:feed/twitter:warning", nsmgr);
2712 return "Warn:" + xNode.InnerText + "(" + MethodBase.GetCurrentMethod().Name + ")";
2719 public string GetPhoenixSearch(bool read,
2723 if (MyCommon._endingFlag) return "";
2731 if (AppendSettingDialog.Instance.UseAdditionalCount &&
2732 AppendSettingDialog.Instance.SearchCountApi != 0)
2734 count = AppendSettingDialog.Instance.SearchCountApi;
2738 page = tab.GetSearchPage(count);
2739 if (!string.IsNullOrEmpty(tab.NextPageQuery))
2741 querystr = tab.NextPageQuery;
2746 sinceId = tab.SinceId;
2751 if (string.IsNullOrEmpty(querystr))
2753 res = twCon.PhoenixSearch(tab.SearchWords, tab.SearchLang, count, page, sinceId, ref content);
2757 res = twCon.PhoenixSearch(querystr, ref content);
2762 return "Err:" + ex.Message;
2766 case HttpStatusCode.BadRequest:
2767 return "Invalid query";
2768 case HttpStatusCode.NotFound:
2769 return "Invalid query";
2770 case HttpStatusCode.PaymentRequired: //API Documentには420と書いてあるが、該当コードがないので402にしてある
2771 return "Search API Limit?";
2772 case HttpStatusCode.OK:
2775 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2778 if (!TabInformations.GetInstance().ContainsTab(tab)) return "";
2781 //// 遡るための情報max_idやnext_pageの情報を保持する
2783 string nextPageQuery = tab.NextPageQuery;
2784 var ret = CreatePostsFromPhoenixSearch(content, MyCommon.WORKERTYPE.PublicSearch, tab, read, count, ref tab.OldestId, ref nextPageQuery);
2785 tab.NextPageQuery = nextPageQuery;
2789 private string CreateDirectMessagesFromJson(string content, MyCommon.WORKERTYPE gType, bool read)
2791 List<TwitterDataModel.Directmessage> item;
2794 if (gType == MyCommon.WORKERTYPE.UserStream)
2796 var itm = MyCommon.CreateDataFromJson<List<TwitterDataModel.DirectmessageEvent>>(content);
2797 item = new List<TwitterDataModel.Directmessage>();
2798 foreach (var dat in itm)
2800 item.Add(dat.Directmessage);
2805 item = MyCommon.CreateDataFromJson<List<TwitterDataModel.Directmessage>>(content);
2808 catch(SerializationException ex)
2810 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2811 return "Json Parse Error(DataContractJsonSerializer)";
2815 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2816 return "Invalid Json!";
2819 foreach (var message in item)
2821 var post = new PostClass();
2824 post.StatusId = message.Id;
2825 if (gType != MyCommon.WORKERTYPE.UserStream)
2827 if (gType == MyCommon.WORKERTYPE.DirectMessegeRcv)
2829 if (minDirectmessage > post.StatusId) minDirectmessage = post.StatusId;
2833 if (minDirectmessageSent > post.StatusId) minDirectmessageSent = post.StatusId;
2840 if (TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.DirectMessage).Contains(post.StatusId)) continue;
2844 post.CreatedAt = MyCommon.DateTimeParse(message.CreatedAt);
2846 post.TextFromApi = message.Text;
2848 post.Text = CreateHtmlAnchor(post.TextFromApi, post.ReplyToList, post.Media);
2849 post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi);
2850 post.TextFromApi = post.TextFromApi.Replace("<3", "\u2661");
2854 TwitterDataModel.User user;
2855 if (gType == MyCommon.WORKERTYPE.UserStream)
2857 if (twCon.AuthenticatedUsername.Equals(message.Recipient.ScreenName, StringComparison.CurrentCultureIgnoreCase))
2859 user = message.Sender;
2865 user = message.Recipient;
2872 if (gType == MyCommon.WORKERTYPE.DirectMessegeRcv)
2874 user = message.Sender;
2880 user = message.Recipient;
2886 post.UserId = user.Id;
2887 post.ScreenName = user.ScreenName;
2888 post.Nickname = user.Name.Trim();
2889 post.ImageUrl = user.ProfileImageUrl;
2890 post.IsProtect = user.Protected;
2894 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2895 MessageBox.Show("Parse Error(CreateDirectMessagesFromJson)");
2900 if (post.IsMe && !read && _readOwnPost) post.IsRead = true;
2901 post.IsReply = false;
2902 post.IsExcludeReply = false;
2905 TabInformations.GetInstance().AddPost(post);
2912 public string GetDirectMessageApi(bool read,
2913 MyCommon.WORKERTYPE gType,
2916 if (MyCommon._endingFlag) return "";
2918 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
2919 if (MyCommon.TwitterApiInfo.AccessLevel != ApiAccessLevel.None)
2921 if (!MyCommon.TwitterApiInfo.IsDirectMessagePermission) return "Auth Err:try to re-authorization.";
2924 HttpStatusCode res = HttpStatusCode.BadRequest;
2929 if (gType == MyCommon.WORKERTYPE.DirectMessegeRcv)
2933 res = twCon.DirectMessages(20, minDirectmessage, 0, ref content);
2937 res = twCon.DirectMessages(20, 0, 0, ref content);
2944 res = twCon.DirectMessagesSent(20, minDirectmessageSent, 0, ref content);
2948 res = twCon.DirectMessagesSent(20, 0, 0, ref content);
2954 return "Err:" + ex.Message;
2959 case HttpStatusCode.OK:
2960 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
2962 case HttpStatusCode.Unauthorized:
2963 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
2964 return Properties.Resources.Unauthorized;
2965 case HttpStatusCode.BadRequest:
2966 return "Err:API Limits?";
2968 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2971 return CreateDirectMessagesFromJson(content, gType, read);
2974 static int page_ = 1;
2975 public string GetFavoritesApi(bool read,
2976 MyCommon.WORKERTYPE gType,
2979 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
2981 if (MyCommon._endingFlag) return "";
2985 var count = AppendSettingDialog.Instance.CountApi;
2986 if (AppendSettingDialog.Instance.UseAdditionalCount &&
2987 AppendSettingDialog.Instance.FavoritesCountApi != 0)
2989 count = AppendSettingDialog.Instance.FavoritesCountApi;
2992 // 前ページ取得の場合はページカウンタをインクリメント、それ以外の場合はページカウンタリセット
3004 res = twCon.Favorites(count, page_, ref content);
3008 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3013 case HttpStatusCode.OK:
3014 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3016 case HttpStatusCode.Unauthorized:
3017 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3018 return Properties.Resources.Unauthorized;
3019 case HttpStatusCode.BadRequest:
3020 return "Err:API Limits?";
3022 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3025 var serializer = new DataContractJsonSerializer(typeof(List<TwitterDataModel.Status>));
3026 List<TwitterDataModel.Status> item;
3030 item = MyCommon.CreateDataFromJson<List<TwitterDataModel.Status>>(content);
3032 catch(SerializationException ex)
3034 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3035 return "Json Parse Error(DataContractJsonSerializer)";
3039 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3040 return "Invalid Json!";
3043 foreach (var status in item)
3045 var post = new PostClass();
3046 TwitterDataModel.Entities entities;
3050 post.StatusId = status.Id;
3054 if (TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Contains(post.StatusId)) continue;
3057 if (status.RetweetedStatus != null)
3059 var retweeted = status.RetweetedStatus;
3060 post.CreatedAt = MyCommon.DateTimeParse(retweeted.CreatedAt);
3063 post.RetweetedId = post.StatusId;
3065 post.TextFromApi = retweeted.Text;
3066 entities = retweeted.Entities;
3067 //Source取得(htmlの場合は、中身を取り出し)
3068 post.Source = retweeted.Source;
3070 long inReplyToStatusId;
3071 long.TryParse(retweeted.InReplyToStatusId, out inReplyToStatusId);
3072 post.InReplyToStatusId = inReplyToStatusId;
3073 post.InReplyToUser = retweeted.InReplyToScreenName;
3074 long inReplyToUserId;
3075 long.TryParse(retweeted.InReplyToUserId, out inReplyToUserId);
3076 post.InReplyToUserId = inReplyToUserId;
3080 var user = retweeted.User;
3081 post.UserId = user.Id;
3082 post.ScreenName = user.ScreenName;
3083 post.Nickname = user.Name.Trim();
3084 post.ImageUrl = user.ProfileImageUrl;
3085 post.IsProtect = user.Protected;
3088 post.RetweetedBy = status.User.ScreenName;
3089 post.IsMe = post.RetweetedBy.ToLower().Equals(_uname);
3093 post.CreatedAt = MyCommon.DateTimeParse(status.CreatedAt);
3096 post.TextFromApi = status.Text;
3097 entities = status.Entities;
3098 //Source取得(htmlの場合は、中身を取り出し)
3099 post.Source = status.Source;
3100 long inReplyToStatusId;
3101 long.TryParse(status.InReplyToStatusId, out inReplyToStatusId);
3102 post.InReplyToStatusId = inReplyToStatusId;
3103 post.InReplyToUser = status.InReplyToScreenName;
3104 long inReplyToUserId;
3105 long.TryParse(status.InReplyToUserId, out inReplyToUserId);
3106 post.InReplyToUserId = inReplyToUserId;
3111 var user = status.User;
3112 post.UserId = user.Id;
3113 post.ScreenName = user.ScreenName;
3114 post.Nickname = user.Name.Trim();
3115 post.ImageUrl = user.ProfileImageUrl;
3116 post.IsProtect = user.Protected;
3117 post.IsMe = post.ScreenName.ToLower().Equals(_uname);
3120 string textFromApi = post.TextFromApi;
3121 post.Text = CreateHtmlAnchor(ref textFromApi, post.ReplyToList, entities, post.Media);
3122 post.TextFromApi = textFromApi;
3123 post.TextFromApi = this.ReplaceTextFromApi(post.TextFromApi, entities);
3124 post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi);
3125 post.TextFromApi = post.TextFromApi.Replace("<3", "\u2661");
3127 CreateSource(ref post);
3130 post.IsReply = post.ReplyToList.Contains(_uname);
3131 post.IsExcludeReply = false;
3139 if (followerId.Count > 0) post.IsOwl = !followerId.Contains(post.UserId);
3146 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3150 TabInformations.GetInstance().AddPost(post);
3157 private string ReplaceTextFromApi(string text, TwitterDataModel.Entities entities)
3159 if (entities != null)
3161 if (entities.Urls != null)
3163 foreach (var m in entities.Urls)
3165 if (!string.IsNullOrEmpty(m.DisplayUrl)) text = text.Replace(m.Url, m.DisplayUrl);
3168 if (entities.Media != null)
3170 foreach (var m in entities.Media)
3172 if (!string.IsNullOrEmpty(m.DisplayUrl)) text = text.Replace(m.Url, m.DisplayUrl);
3179 public string GetFollowersApi()
3181 if (MyCommon._endingFlag) return "";
3183 var tmpFollower = new List<long>(followerId);
3188 var ret = FollowerApi(ref cursor);
3189 if (!string.IsNullOrEmpty(ret))
3192 followerId.AddRange(tmpFollower);
3193 _GetFollowerResult = false;
3196 } while (cursor > 0);
3198 TabInformations.GetInstance().RefreshOwl(followerId);
3200 _GetFollowerResult = true;
3204 public bool GetFollowersSuccess
3208 return _GetFollowerResult;
3212 private string FollowerApi(ref long cursor)
3214 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3216 HttpStatusCode res = HttpStatusCode.BadRequest;
3220 res = twCon.FollowerIds(cursor, ref content);
3224 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3229 case HttpStatusCode.OK:
3230 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3232 case HttpStatusCode.Unauthorized:
3233 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3234 return Properties.Resources.Unauthorized;
3235 case HttpStatusCode.BadRequest:
3236 return "Err:API Limits?";
3238 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3243 var followers = MyCommon.CreateDataFromJson<TwitterDataModel.Ids>(content);
3244 followerId.AddRange(followers.Id);
3245 cursor = followers.NextCursor;
3248 catch(SerializationException ex)
3250 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3251 return "Err:Json Parse Error(DataContractJsonSerializer)";
3255 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3256 return "Err:Invalid Json!";
3260 public string GetNoRetweetIdsApi()
3262 if (MyCommon._endingFlag) return "";
3264 var tmpIds = new List<long>(noRTId);
3269 var ret = NoRetweetApi(ref cursor);
3270 if (!string.IsNullOrEmpty(ret))
3273 noRTId.AddRange(tmpIds);
3274 _GetNoRetweetResult = false;
3277 } while (cursor > 0);
3279 _GetNoRetweetResult = true;
3283 private string NoRetweetApi(ref long cursor)
3285 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3287 HttpStatusCode res = HttpStatusCode.BadRequest;
3291 res = twCon.NoRetweetIds(cursor, ref content);
3295 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3300 case HttpStatusCode.OK:
3301 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3303 case HttpStatusCode.Unauthorized:
3304 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3305 return Properties.Resources.Unauthorized;
3306 case HttpStatusCode.BadRequest:
3307 return "Err:API Limits?";
3309 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3314 var ids = MyCommon.CreateDataFromJson<long[]>(content);
3315 noRTId.AddRange(ids);
3316 cursor = 0; //0より小さければ何でも良い。
3319 catch(SerializationException ex)
3321 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3322 return "Err:Json Parse Error(DataContractJsonSerializer)";
3326 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3327 return "Err:Invalid Json!";
3331 public bool GetNoRetweetSuccess
3335 return _GetNoRetweetResult;
3339 public string ConfigurationApi()
3345 res = twCon.GetConfiguration(ref content);
3349 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3354 case HttpStatusCode.OK:
3355 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3357 case HttpStatusCode.Unauthorized:
3358 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3359 return Properties.Resources.Unauthorized;
3360 case HttpStatusCode.BadRequest:
3361 return "Err:API Limits?";
3363 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3368 AppendSettingDialog.Instance.TwitterConfiguration = MyCommon.CreateDataFromJson<TwitterDataModel.Configuration>(content);
3371 catch(SerializationException ex)
3373 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3374 return "Err:Json Parse Error(DataContractJsonSerializer)";
3378 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3379 return "Err:Invalid Json!";
3383 public string GetListsApi()
3385 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3387 HttpStatusCode res = HttpStatusCode.BadRequest;
3391 var lists = new List<ListElement>();
3396 res = twCon.GetLists(this.Username, cursor, ref content);
3400 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3405 case HttpStatusCode.OK:
3406 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3408 case HttpStatusCode.Unauthorized:
3409 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3410 return Properties.Resources.Unauthorized;
3411 case HttpStatusCode.BadRequest:
3412 return "Err:API Limits?";
3414 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3419 var lst = MyCommon.CreateDataFromJson<TwitterDataModel.Lists>(content);
3420 lists.AddRange(from le in lst.lists select new ListElement(le, this));
3421 cursor = lst.NextCursor;
3423 catch(SerializationException ex)
3425 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3426 return "Err:Json Parse Error(DataContractJsonSerializer)";
3430 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3431 return "Err:Invalid Json!";
3433 } while (cursor != 0);
3441 res = twCon.GetListsSubscriptions(this.Username, cursor, ref content);
3445 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3450 case HttpStatusCode.OK:
3451 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3453 case HttpStatusCode.Unauthorized:
3454 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3455 return Properties.Resources.Unauthorized;
3456 case HttpStatusCode.BadRequest:
3457 return "Err:API Limits?";
3459 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3464 var lst = MyCommon.CreateDataFromJson<TwitterDataModel.Lists>(content);
3465 lists.AddRange(from le in lst.lists select new ListElement(le, this));
3466 cursor = lst.NextCursor;
3468 catch(SerializationException ex)
3470 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3471 return "Err:Json Parse Error(DataContractJsonSerializer)";
3475 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3476 return "Err:Invalid Json!";
3478 } while (cursor != 0);
3480 TabInformations.GetInstance().SubscribableLists = lists;
3484 public string DeleteList(string list_id)
3486 HttpStatusCode res = HttpStatusCode.BadRequest;
3491 res = twCon.DeleteListID(this.Username, list_id, ref content);
3495 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3500 case HttpStatusCode.OK:
3501 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3503 case HttpStatusCode.Unauthorized:
3504 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3505 return Properties.Resources.Unauthorized;
3506 case HttpStatusCode.BadRequest:
3507 return "Err:API Limits?";
3509 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3515 public string EditList(string list_id, string new_name, bool isPrivate, string description, ref ListElement list)
3517 HttpStatusCode res = HttpStatusCode.BadRequest;
3522 res = twCon.UpdateListID(this.Username, list_id, new_name, isPrivate, description, ref content);
3526 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3531 case HttpStatusCode.OK:
3532 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3534 case HttpStatusCode.Unauthorized:
3535 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3536 return Properties.Resources.Unauthorized;
3537 case HttpStatusCode.BadRequest:
3538 return "Err:API Limits?";
3540 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3545 var le = MyCommon.CreateDataFromJson<TwitterDataModel.ListElementData>(content);
3546 var newList = new ListElement(le, this);
3547 list.Description = newList.Description;
3548 list.Id = newList.Id;
3549 list.IsPublic = newList.IsPublic;
3550 list.MemberCount = newList.MemberCount;
3551 list.Name = newList.Name;
3552 list.SubscriberCount = newList.SubscriberCount;
3553 list.Slug = newList.Slug;
3554 list.Nickname = newList.Nickname;
3555 list.Username = newList.Username;
3556 list.UserId = newList.UserId;
3559 catch(SerializationException ex)
3561 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3562 return "Err:Json Parse Error(DataContractJsonSerializer)";
3566 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3567 return "Err:Invalid Json!";
3572 public string GetListMembers(string list_id, List<UserInfo> lists, ref long cursor)
3574 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3576 HttpStatusCode res = HttpStatusCode.BadRequest;
3582 res = twCon.GetListMembers(this.Username, list_id, cursor, ref content);
3586 return "Err:" + ex.Message;
3591 case HttpStatusCode.OK:
3592 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3594 case HttpStatusCode.Unauthorized:
3595 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3596 return Properties.Resources.Unauthorized;
3597 case HttpStatusCode.BadRequest:
3598 return "Err:API Limits?";
3600 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3605 var users = MyCommon.CreateDataFromJson<TwitterDataModel.Users>(content);
3606 Array.ForEach<TwitterDataModel.User>(
3608 new Action<TwitterDataModel.User>(u => lists.Add(new UserInfo(u))));
3609 cursor = users.NextCursor;
3612 catch(SerializationException ex)
3614 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3615 return "Err:Json Parse Error(DataContractJsonSerializer)";
3619 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3620 return "Err:Invalid Json!";
3624 public string CreateListApi(string listName, bool isPrivate, string description)
3626 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3628 HttpStatusCode res = HttpStatusCode.BadRequest;
3633 res = twCon.CreateLists(listName, isPrivate, description, ref content);
3637 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3642 case HttpStatusCode.OK:
3643 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3645 case HttpStatusCode.Unauthorized:
3646 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3647 return Properties.Resources.Unauthorized;
3648 case HttpStatusCode.BadRequest:
3649 return "Err:API Limits?";
3651 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3656 var le = MyCommon.CreateDataFromJson<TwitterDataModel.ListElementData>(content);
3657 TabInformations.GetInstance().SubscribableLists.Add(new ListElement(le, this));
3660 catch(SerializationException ex)
3662 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3663 return "Err:Json Parse Error(DataContractJsonSerializer)";
3667 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3668 return "Err:Invalid Json!";
3672 public string ContainsUserAtList(string listId, string user, ref bool value)
3676 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3678 HttpStatusCode res = HttpStatusCode.BadRequest;
3683 res = this.twCon.ShowListMember(listId, user, ref content);
3687 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3692 case HttpStatusCode.OK:
3693 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3695 case HttpStatusCode.Unauthorized:
3696 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3697 return Properties.Resources.Unauthorized;
3698 case HttpStatusCode.BadRequest:
3699 return "Err:API Limits?";
3700 case HttpStatusCode.NotFound:
3704 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3709 var u = MyCommon.CreateDataFromJson<TwitterDataModel.User>(content);
3720 public string AddUserToList(string listId, string user)
3723 HttpStatusCode res = HttpStatusCode.BadRequest;
3727 res = twCon.CreateListMembers(listId, user, ref content);
3731 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3736 case HttpStatusCode.OK:
3737 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3739 case HttpStatusCode.Unauthorized:
3740 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3741 return Properties.Resources.Unauthorized;
3742 case HttpStatusCode.BadRequest:
3743 return "Err:" + GetErrorMessageJson(content);
3745 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3751 public string RemoveUserToList(string listId, string user)
3755 HttpStatusCode res = HttpStatusCode.BadRequest;
3759 res = twCon.DeleteListMembers(listId, user, ref content);
3763 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3768 case HttpStatusCode.OK:
3769 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3771 case HttpStatusCode.Unauthorized:
3772 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3773 return Properties.Resources.Unauthorized;
3774 case HttpStatusCode.BadRequest:
3775 return "Err:" + GetErrorMessageJson(content);
3777 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3785 public int fromIndex { get; set; }
3786 public int toIndex { get; set; }
3787 public range(int fromIndex, int toIndex)
3789 this.fromIndex = fromIndex;
3790 this.toIndex = toIndex;
3793 public string CreateHtmlAnchor(string Text, List<string> AtList, Dictionary<string, string> media)
3795 if (Text == null) return null;
3796 var retStr = Text.Replace(">", "<<<<<tweenだいなり>>>>>").Replace("<", "<<<<<tweenしょうなり>>>>>");
3798 //const string url_valid_domain = "(?<domain>(?:[^\p{P}\s][\.\-_](?=[^\p{P}\s])|[^\p{P}\s]){1,}\.[a-z]{2,}(?::[0-9]+)?)"
3799 //const string url_valid_general_path_chars = "[a-z0-9!*';:=+$/%#\[\]\-_,~]"
3800 //const string url_balance_parens = "(?:\(" + url_valid_general_path_chars + "+\))"
3801 //const string url_valid_url_path_ending_chars = "(?:[a-z0-9=_#/\-\+]+|" + url_balance_parens + ")"
3802 //const string pth = "(?:" + url_balance_parens +
3803 // "|@" + url_valid_general_path_chars + "+/" +
3804 // "|[.,]?" + url_valid_general_path_chars + "+" +
3806 //const string pth2 = "(/(?:" +
3807 // pth + "+" + url_valid_url_path_ending_chars + "|" +
3808 // pth + "+" + url_valid_url_path_ending_chars + "?|" +
3809 // url_valid_url_path_ending_chars +
3811 //const string qry = "(?<query>\?[a-z0-9!*'();:&=+$/%#\[\]\-_.,~]*[a-z0-9_&=#])?"
3812 //const string rgUrl = "(?<before>(?:[^\""':!=#]|^|\:/))" +
3813 // "(?<url>(?<protocol>https?://)" +
3814 // url_valid_domain +
3818 //const string rgUrl = "(?<before>(?:[^\""':!=#]|^|\:/))" +
3819 // "(?<url>(?<protocol>https?://|www\.)" +
3820 // url_valid_domain +
3825 retStr = Regex.Replace(retStr,
3827 new MatchEvaluator((Match mu) =>
3829 var sb = new StringBuilder(mu.Result("${before}<a href=\""));
3830 //if (mu.Result("${protocol}").StartsWith("w", StringComparison.OrdinalIgnoreCase))
3831 // sb.Append("http://");
3833 var url = mu.Result("${url}");
3834 var title = ShortUrl.ResolveMedia(url, true);
3837 title = ShortUrl.ResolveMedia(title, false);
3839 sb.Append(url + "\" title=\"" + title + "\">").Append(url).Append("</a>");
3840 if (media != null && !media.ContainsKey(url)) media.Add(url, title);
3841 return sb.ToString();
3843 RegexOptions.IgnoreCase);
3846 retStr = Regex.Replace(retStr,
3847 @"(^|[^a-zA-Z0-9_/])([@@]+)([a-zA-Z0-9_]{1,20}/[a-zA-Z][a-zA-Z0-9\p{IsLatin-1Supplement}\-]{0,79})",
3848 "$1$2<a href=\"/$3\">$3</a>");
3850 var m = Regex.Match(retStr, "(^|[^a-zA-Z0-9_])[@@]([a-zA-Z0-9_]{1,20})");
3853 if (!AtList.Contains(m.Result("$2").ToLower())) AtList.Add(m.Result("$2").ToLower());
3857 retStr = Regex.Replace(retStr,
3858 "(^|[^a-zA-Z0-9_/])([@@])([a-zA-Z0-9_]{1,20})",
3859 "$1$2<a href=\"/$3\">$3</a>");
3862 var anchorRange = new List<range>();
3863 for (int i = 0; i < retStr.Length; i++)
3865 var index = retStr.IndexOf("<a ", i);
3866 if (index > -1 && index < retStr.Length)
3869 var toIndex = retStr.IndexOf("</a>", index);
3872 anchorRange.Add(new range(index, toIndex + 3));
3877 //retStr = Regex.Replace(retStr,
3878 // "(^|[^a-zA-Z0-9/&])([##])([0-9a-zA-Z_]*[a-zA-Z_]+[a-zA-Z0-9_\xc0-\xd6\xd8-\xf6\xf8-\xff]*)",
3879 // new MatchEvaluator(Function(mh As Match)
3880 // foreach (var rng in anchorRange)
3882 // if (mh.Index >= rng.fromIndex &&
3883 // mh.Index <= rng.toIndex) return mh.Result("$0");
3885 // if (IsNumeric(mh.Result("$3"))) return mh.Result("$0");
3888 // _hashList.Add("#" + mh.Result("$3"))
3890 // return mh.Result("$1") + "<a href=\"" + _protocol + "twitter.com/search?q=%23" + mh.Result("$3") + "\">" + mh.Result("$2$3") + "</a>";
3892 // RegexOptions.IgnoreCase)
3893 retStr = Regex.Replace(retStr,
3895 new MatchEvaluator(mh =>
3897 foreach (var rng in anchorRange)
3899 if (mh.Index >= rng.fromIndex &&
3900 mh.Index <= rng.toIndex) return mh.Result("$0");
3904 _hashList.Add("#" + mh.Result("$3"));
3906 return mh.Result("$1") + "<a href=\"" + _protocol + "twitter.com/search?q=%23" + mh.Result("$3") + "\">" + mh.Result("$2$3") + "</a>";
3908 RegexOptions.IgnoreCase);
3911 retStr = Regex.Replace(retStr, "(^|[^a-zA-Z0-9_/&##@@>=.~])(sm|nm)([0-9]{1,10})", "$1<a href=\"http://www.nicovideo.jp/watch/$2$3\">$2$3</a>");
3913 retStr = retStr.Replace("<<<<<tweenだいなり>>>>>", ">").Replace("<<<<<tweenしょうなり>>>>>", "<");
3915 //retStr = AdjustHtml(ShortUrl.Resolve(PreProcessUrl(retStr), true)) //IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
3916 retStr = AdjustHtml(PreProcessUrl(retStr)); //IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
3920 private class EntityInfo
3922 public int StartIndex { get; set; }
3923 public int EndIndex { get; set; }
3924 public string Text { get; set; }
3925 public string Html { get; set; }
3926 public string Display { get; set; }
3928 public string CreateHtmlAnchor(ref string Text, List<string> AtList, TwitterDataModel.Entities entities, Dictionary<string, string> media)
3932 if (entities != null)
3934 var etInfo = new SortedList<int, EntityInfo>();
3936 if (entities.Urls != null)
3938 foreach (var ent in entities.Urls)
3940 if (string.IsNullOrEmpty(ent.DisplayUrl))
3942 etInfo.Add(ent.Indices[0],
3943 new EntityInfo {StartIndex = ent.Indices[0],
3944 EndIndex = ent.Indices[1],
3946 Html = "<a href=\"" + ent.Url + "\">" + ent.Url + "</a>"});
3950 var expanded = ShortUrl.ResolveMedia(ent.ExpandedUrl, false);
3951 etInfo.Add(ent.Indices[0],
3952 new EntityInfo {StartIndex = ent.Indices[0],
3953 EndIndex = ent.Indices[1],
3955 Html = "<a href=\"" + ent.Url + "\" title=\"" + expanded + "\">" + ent.DisplayUrl + "</a>",
3956 Display = ent.DisplayUrl});
3957 if (media != null && !media.ContainsKey(ent.Url)) media.Add(ent.Url, expanded);
3961 if (entities.Hashtags != null)
3963 foreach (var ent in entities.Hashtags)
3965 var hash = Text.Substring(ent.Indices[0], ent.Indices[1] - ent.Indices[0]);
3966 etInfo.Add(ent.Indices[0],
3967 new EntityInfo {StartIndex = ent.Indices[0],
3968 EndIndex = ent.Indices[1],
3970 Html = "<a href=\"" + _protocol + "twitter.com/search?q=%23" + ent.Text + "\">" + hash + "</a>"});
3973 _hashList.Add("#" + ent.Text);
3977 if (entities.UserMentions != null)
3979 foreach (var ent in entities.UserMentions)
3981 var screenName = Text.Substring(ent.Indices[0] + 1, ent.Indices[1] - ent.Indices[0] - 1);
3982 etInfo.Add(ent.Indices[0] + 1,
3983 new EntityInfo {StartIndex = ent.Indices[0] + 1,
3984 EndIndex = ent.Indices[1],
3985 Text = ent.ScreenName,
3986 Html = "<a href=\"/" + ent.ScreenName + "\">" + screenName + "</a>"});
3987 if (!AtList.Contains(ent.ScreenName.ToLower())) AtList.Add(ent.ScreenName.ToLower());
3990 if (entities.Media != null)
3992 foreach (var ent in entities.Media)
3994 if (ent.Type == "photo")
3996 etInfo.Add(ent.Indices[0],
3997 new EntityInfo {StartIndex = ent.Indices[0],
3998 EndIndex = ent.Indices[1],
4000 Html = "<a href=\"" + ent.Url + "\" title=\"" + ent.ExpandedUrl + "\">" + ent.DisplayUrl + "</a>",
4001 Display = ent.DisplayUrl});
4002 if (media != null && !media.ContainsKey(ent.Url)) media.Add(ent.Url, ent.MediaUrl);
4006 if (etInfo.Count > 0)
4012 foreach (var et in etInfo)
4014 ret += Text.Substring(idx, et.Key - idx) + et.Value.Html;
4015 idx = et.Value.EndIndex;
4017 ret += Text.Substring(idx);
4019 catch(ArgumentOutOfRangeException)
4021 //Twitterのバグで不正なエンティティ(Index指定範囲が重なっている)が返ってくる場合の対応
4024 if (media != null) media.Clear();
4029 ret = Regex.Replace(ret, "(^|[^a-zA-Z0-9_/&##@@>=.~])(sm|nm)([0-9]{1,10})", "$1<a href=\"http://www.nicovideo.jp/watch/$2$3\">$2$3</a>");
4030 ret = AdjustHtml(ShortUrl.Resolve(PreProcessUrl(ret), false)); //IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
4036 private void CreateSource(ref PostClass post)
4038 if (post.Source.StartsWith("<"))
4040 if (!post.Source.Contains("</a>"))
4042 post.Source += "</a>";
4044 var mS = Regex.Match(post.Source, ">(?<source>.+)<");
4047 post.SourceHtml = string.Copy(ShortUrl.Resolve(PreProcessUrl(post.Source), false));
4048 post.Source = HttpUtility.HtmlDecode(mS.Result("${source}"));
4053 post.SourceHtml = "";
4058 if (post.Source == "web")
4060 post.SourceHtml = Properties.Resources.WebSourceString;
4062 else if (post.Source == "Keitai Mail")
4064 post.SourceHtml = Properties.Resources.KeitaiMailSourceString;
4068 post.SourceHtml = string.Copy(post.Source);
4073 public bool GetInfoApi(ApiInfo info)
4075 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return true;
4077 if (MyCommon._endingFlag) return true;
4079 HttpStatusCode res = HttpStatusCode.BadRequest;
4083 res = twCon.RateLimitStatus(ref content);
4087 MyCommon.TwitterApiInfo.Initialize();
4091 if (res != HttpStatusCode.OK) return false;
4095 var limit = MyCommon.CreateDataFromJson<TwitterDataModel.RateLimitStatus>(content);
4096 var arg = new ApiInformationChangedEventArgs();
4097 arg.ApiInfo.MaxCount = limit.HourlyLimit;
4098 arg.ApiInfo.RemainCount = limit.RemainingHits;
4099 arg.ApiInfo.ResetTime = MyCommon.DateTimeParse(limit.RestTime);
4100 arg.ApiInfo.ResetTimeInSeconds = limit.RestTimeInSeconds;
4103 arg.ApiInfo.UsingCount = info.UsingCount;
4105 info.MaxCount = arg.ApiInfo.MaxCount;
4106 info.RemainCount = arg.ApiInfo.RemainCount;
4107 info.ResetTime = arg.ApiInfo.ResetTime;
4108 info.ResetTimeInSeconds = arg.ApiInfo.ResetTimeInSeconds;
4111 if (ApiInformationChanged != null)
4113 ApiInformationChanged(this, arg);
4115 MyCommon.TwitterApiInfo.WriteBackEventArgs(arg);
4120 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
4121 MyCommon.TwitterApiInfo.Initialize();
4125 public string GetBlockUserIds()
4127 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
4129 HttpStatusCode res = HttpStatusCode.BadRequest;
4134 res = twCon.GetBlockUserIds(ref content);
4138 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
4143 case HttpStatusCode.OK:
4144 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
4146 case HttpStatusCode.Unauthorized:
4147 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
4148 return Properties.Resources.Unauthorized;
4149 case HttpStatusCode.BadRequest:
4150 return "Err:API Limits?";
4152 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
4157 var Ids = MyCommon.CreateDataFromJson<List<long>>(content);
4158 if (Ids.Contains(this.UserId)) Ids.Remove(this.UserId);
4159 TabInformations.GetInstance().BlockIds.AddRange(Ids);
4162 catch(SerializationException ex)
4164 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
4165 return "Err:Json Parse Error(DataContractJsonSerializer)";
4169 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
4170 return "Err:Invalid Json!";
4175 public string[] GetHashList()
4180 hashArray = _hashList.ToArray();
4186 public string AccessToken
4190 return twCon.AccessToken;
4194 public string AccessTokenSecret
4198 return twCon.AccessTokenSecret;
4202 public event EventHandler<ApiInformationChangedEventArgs> ApiInformationChanged;
4204 private void Twitter_ApiInformationChanged(object sender, ApiInformationChangedEventArgs e)
4210 ApiInformationChanged += Twitter_ApiInformationChanged;
4213 #region "UserStream"
4214 private string trackWord_ = "";
4215 public string TrackWord
4226 private bool allAtReply_ = false;
4227 public bool AllAtReply
4235 allAtReply_ = value;
4239 public event Action NewPostFromStream;
4240 public event Action UserStreamStarted;
4241 public event Action UserStreamStopped;
4242 public event Action<long> PostDeleted;
4243 public event Action<FormattedEvent> UserStreamEventReceived;
4244 private DateTime _lastUserstreamDataReceived;
4245 private TwitterUserstream userStream;
4247 public class FormattedEvent
4249 public MyCommon.EVENTTYPE Eventtype { get; set; }
4250 public DateTime CreatedAt { get; set; }
4251 public string Event { get; set; }
4252 public string Username { get; set; }
4253 public string Target { get; set; }
4254 public Int64 Id { get; set; }
4255 public bool IsMe { get; set; }
4258 public List<FormattedEvent> storedEvent_ = new List<FormattedEvent>();
4259 public List<FormattedEvent> StoredEvent
4263 return storedEvent_;
4267 storedEvent_ = value;
4271 private class EventTypeTableElement
4274 public MyCommon.EVENTTYPE Type;
4276 public EventTypeTableElement(string name, MyCommon.EVENTTYPE type)
4283 private EventTypeTableElement[] EventTable = {
4284 new EventTypeTableElement("favorite", MyCommon.EVENTTYPE.Favorite),
4285 new EventTypeTableElement("unfavorite", MyCommon.EVENTTYPE.Unfavorite),
4286 new EventTypeTableElement("follow", MyCommon.EVENTTYPE.Follow),
4287 new EventTypeTableElement("list_member_added", MyCommon.EVENTTYPE.ListMemberAdded),
4288 new EventTypeTableElement("list_member_removed", MyCommon.EVENTTYPE.ListMemberRemoved),
4289 new EventTypeTableElement("block", MyCommon.EVENTTYPE.Block),
4290 new EventTypeTableElement("unblock", MyCommon.EVENTTYPE.Unblock),
4291 new EventTypeTableElement("user_update", MyCommon.EVENTTYPE.UserUpdate),
4292 new EventTypeTableElement("deleted", MyCommon.EVENTTYPE.Deleted),
4293 new EventTypeTableElement("list_created", MyCommon.EVENTTYPE.ListCreated),
4294 new EventTypeTableElement("list_updated", MyCommon.EVENTTYPE.ListUpdated),
4297 public MyCommon.EVENTTYPE EventNameToEventType(string EventName)
4299 return (from tbl in EventTable where tbl.Name.Equals(EventName) select tbl.Type).FirstOrDefault();
4302 public bool IsUserstreamDataReceived
4306 return DateTime.Now.Subtract(this._lastUserstreamDataReceived).TotalSeconds < 31;
4310 private void userStream_StatusArrived(string line)
4312 this._lastUserstreamDataReceived = DateTime.Now;
4313 if (string.IsNullOrEmpty(line)) return;
4319 using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(line), XmlDictionaryReaderQuotas.Max))
4321 var xElm = XElement.Load(jsonReader);
4322 if (xElm.Element("friends") != null)
4324 Debug.WriteLine("friends");
4327 else if (xElm.Element("delete") != null)
4329 Debug.WriteLine("delete");
4331 if (xElm.Element("delete").Element("direct_message") != null &&
4332 xElm.Element("delete").Element("direct_message").Element("id") != null)
4335 long.TryParse(xElm.Element("delete").Element("direct_message").Element("id").Value, out id);
4336 if (PostDeleted != null)
4341 else if (xElm.Element("delete").Element("status") != null &&
4342 xElm.Element("delete").Element("status").Element("id") != null)
4345 long.TryParse(xElm.Element("delete").Element("status").Element("id").Value, out id);
4346 if (PostDeleted != null)
4353 MyCommon.TraceOut("delete:" + line);
4356 for (int i = this.StoredEvent.Count - 1; i >= 0; i--)
4358 var sEvt = this.StoredEvent[i];
4359 if (sEvt.Id == id && (sEvt.Event == "favorite" || sEvt.Event == "unfavorite"))
4361 this.StoredEvent.RemoveAt(i);
4366 else if (xElm.Element("limit") != null)
4368 Debug.WriteLine(line);
4371 else if (xElm.Element("event") != null)
4373 Debug.WriteLine("event: " + xElm.Element("event").Value);
4374 CreateEventFromJson(line);
4377 else if (xElm.Element("direct_message") != null)
4379 Debug.WriteLine("direct_message");
4382 else if (xElm.Element("scrub_geo") != null)
4386 TabInformations.GetInstance().ScrubGeoReserve(long.Parse(xElm.Element("scrub_geo").Element("user_id").Value),
4387 long.Parse(xElm.Element("scrub_geo").Element("up_to_status_id").Value));
4391 MyCommon.TraceOut("scrub_geo:" + line);
4397 var res = new StringBuilder();
4405 CreateDirectMessagesFromJson(res.ToString(), MyCommon.WORKERTYPE.UserStream, false);
4410 CreatePostsFromJson(res.ToString(), MyCommon.WORKERTYPE.Timeline, null, false, 0, ref dummy);
4413 catch(NullReferenceException)
4415 MyCommon.TraceOut("NullRef StatusArrived: " + line);
4418 if (NewPostFromStream != null)
4420 NewPostFromStream();
4424 private void CreateEventFromJson(string content)
4426 TwitterDataModel.EventData eventData = null;
4429 eventData = MyCommon.CreateDataFromJson<TwitterDataModel.EventData>(content);
4431 catch(SerializationException ex)
4433 MyCommon.TraceOut(ex, "Event Serialize Exception!" + Environment.NewLine + content);
4437 MyCommon.TraceOut(ex, "Event Exception!" + Environment.NewLine + content);
4440 var evt = new FormattedEvent();
4441 evt.CreatedAt = MyCommon.DateTimeParse(eventData.CreatedAt);
4442 evt.Event = eventData.Event;
4443 evt.Username = eventData.Source.ScreenName;
4444 evt.IsMe = evt.Username.ToLower().Equals(this.Username.ToLower());
4445 evt.Eventtype = EventNameToEventType(evt.Event);
4446 switch (eventData.Event)
4448 case "access_revoked":
4451 if (eventData.Target.ScreenName.ToLower().Equals(_uname))
4453 if (!this.followerId.Contains(eventData.Source.Id)) this.followerId.Add(eventData.Source.Id);
4457 return; //Block後のUndoをすると、SourceとTargetが逆転したfollowイベントが帰ってくるため。
4463 evt.Target = "@" + eventData.TargetObject.User.ScreenName + ":" + HttpUtility.HtmlDecode(eventData.TargetObject.Text);
4464 evt.Id = eventData.TargetObject.Id;
4465 if (AppendSettingDialog.Instance.IsRemoveSameEvent)
4467 if (StoredEvent.Any(ev =>
4469 return ev.Username == evt.Username && ev.Eventtype == evt.Eventtype && ev.Target == evt.Target;
4472 if (TabInformations.GetInstance().ContainsKey(eventData.TargetObject.Id))
4474 var post = TabInformations.GetInstance()[eventData.TargetObject.Id];
4475 if (eventData.Event == "favorite")
4477 if (evt.Username.ToLower().Equals(_uname))
4480 TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Add(post.StatusId, post.IsRead, false);
4484 post.FavoritedCount++;
4485 if (!TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Contains(post.StatusId))
4487 if (AppendSettingDialog.Instance.FavEventUnread && post.IsRead)
4489 post.IsRead = false;
4491 TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Add(post.StatusId, post.IsRead, false);
4495 if (AppendSettingDialog.Instance.FavEventUnread)
4497 TabInformations.GetInstance().SetRead(false, TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).TabName, TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).IndexOf(post.StatusId));
4504 if (evt.Username.ToLower().Equals(_uname))
4510 post.FavoritedCount--;
4511 if (post.FavoritedCount < 0) post.FavoritedCount = 0;
4516 case "list_member_added":
4517 case "list_member_removed":
4518 case "list_updated":
4519 evt.Target = eventData.TargetObject.FullName;
4522 if (!TabInformations.GetInstance().BlockIds.Contains(eventData.Target.Id)) TabInformations.GetInstance().BlockIds.Add(eventData.Target.Id);
4526 if (TabInformations.GetInstance().BlockIds.Contains(eventData.Target.Id)) TabInformations.GetInstance().BlockIds.Remove(eventData.Target.Id);
4532 case "list_created":
4536 MyCommon.TraceOut("Unknown Event:" + evt.Event + Environment.NewLine + content);
4539 this.StoredEvent.Insert(0, evt);
4540 if (UserStreamEventReceived != null)
4542 UserStreamEventReceived(evt);
4546 private void userStream_Started()
4548 if (UserStreamStarted != null)
4550 UserStreamStarted();
4554 private void userStream_Stopped()
4556 if (UserStreamStopped != null)
4558 UserStreamStopped();
4562 public bool UserStreamEnabled
4566 return userStream == null ? false : userStream.Enabled;
4570 public void StartUserStream()
4572 if (userStream != null)
4576 userStream = new TwitterUserstream(twCon);
4577 userStream.StatusArrived += userStream_StatusArrived;
4578 userStream.Started += userStream_Started;
4579 userStream.Stopped += userStream_Stopped;
4580 userStream.Start(this.AllAtReply, this.TrackWord);
4583 public void StopUserStream()
4585 if (userStream != null) userStream.Dispose();
4587 if (!MyCommon._endingFlag)
4589 if (UserStreamStopped != null)
4591 UserStreamStopped();
4596 public void ReconnectUserStream()
4598 if (userStream != null)
4600 this.StartUserStream();
4604 private class TwitterUserstream : IDisposable
4606 public event Action<string> StatusArrived;
4607 public event Action Stopped;
4608 public event Action Started;
4609 private HttpTwitter twCon;
4611 private Thread _streamThread;
4612 private bool _streamActive;
4614 private bool _allAtreplies = false;
4615 private string _trackwords = "";
4617 public TwitterUserstream(HttpTwitter twitterConnection)
4619 twCon = (HttpTwitter)twitterConnection.Clone();
4622 public void Start(bool allAtReplies, string trackwords)
4624 this.AllAtReplies = allAtReplies;
4625 this.TrackWords = trackwords;
4626 _streamActive = true;
4627 if (_streamThread != null && _streamThread.IsAlive) return;
4628 _streamThread = new Thread(UserStreamLoop);
4629 _streamThread.Name = "UserStreamReceiver";
4630 _streamThread.IsBackground = true;
4631 _streamThread.Start();
4638 return _streamActive;
4642 public bool AllAtReplies
4646 return _allAtreplies;
4650 _allAtreplies = value;
4654 public string TrackWords
4662 _trackwords = value;
4666 private void UserStreamLoop()
4672 StreamReader sr = null;
4675 if (!MyCommon.IsNetworkAvailable())
4681 if (Started != null)
4685 var res = twCon.UserStream(ref st, _allAtreplies, _trackwords, MyCommon.GetUserAgentString());
4689 case HttpStatusCode.OK:
4690 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
4692 case HttpStatusCode.Unauthorized:
4693 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
4701 //MyCommon.TraceOut("Stop:stream is null")
4705 sr = new StreamReader(st);
4707 while (_streamActive && !sr.EndOfStream && Twitter.AccountState == MyCommon.ACCOUNT_STATE.Valid)
4709 if (StatusArrived != null)
4711 StatusArrived(sr.ReadLine());
4713 //this.LastTime = Now;
4716 if (sr.EndOfStream || Twitter.AccountState == MyCommon.ACCOUNT_STATE.Invalid)
4719 //MyCommon.TraceOut("Stop:EndOfStream")
4724 catch(WebException ex)
4726 if (ex.Status == WebExceptionStatus.Timeout)
4728 sleepSec = 30; //MyCommon.TraceOut("Stop:Timeout")
4730 else if (ex.Response != null && (int)((HttpWebResponse)ex.Response).StatusCode == 420)
4732 //MyCommon.TraceOut("Stop:Connection Limit")
4738 //MyCommon.TraceOut("Stop:WebException " + ex.Status.ToString())
4741 catch(ThreadAbortException)
4748 //MyCommon.TraceOut("Stop:IOException with Active." + Environment.NewLine + ex.Message)
4750 catch(ArgumentException ex)
4752 //System.ArgumentException: ストリームを読み取れませんでした。
4753 //サーバー側もしくは通信経路上で切断された場合?タイムアウト頻発後発生
4755 MyCommon.TraceOut(ex, "Stop:ArgumentException");
4759 MyCommon.TraceOut("Stop:Exception." + Environment.NewLine + ex.Message);
4760 MyCommon.ExceptionOut(ex);
4767 if (Stopped != null)
4772 twCon.RequestAbort();
4773 if (sr != null) sr.Close();
4777 while (_streamActive && ms < sleepSec * 1000)
4785 } while (this._streamActive);
4789 if (Stopped != null)
4794 MyCommon.TraceOut("Stop:EndLoop");
4797 #region "IDisposable Support"
4798 private bool disposedValue; // 重複する呼び出しを検出するには
4801 protected virtual void Dispose(bool disposing)
4803 if (!this.disposedValue)
4807 // TODO: マネージ状態を破棄します (マネージ オブジェクト)。
4808 _streamActive = false;
4809 if (_streamThread != null && _streamThread.IsAlive)
4811 _streamThread.Abort();
4815 // TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下の Finalize() をオーバーライドします。
4816 // TODO: 大きなフィールドを null に設定します。
4818 this.disposedValue = true;
4821 // TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードがある場合にのみ、Finalize() をオーバーライドします。
4822 //protected Overrides void Finalize()
4824 // // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
4826 // MyBase.Finalize()
4829 // このコードは、破棄可能なパターンを正しく実装できるように Visual Basic によって追加されました。
4830 public void Dispose()
4832 // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
4834 GC.SuppressFinalize(this);
4841 #region "IDisposable Support"
4842 private bool disposedValue; // 重複する呼び出しを検出するには
4845 protected virtual void Dispose(bool disposing)
4847 if (!this.disposedValue)
4851 // TODO: マネージ状態を破棄します (マネージ オブジェクト)。
4852 this.StopUserStream();
4855 // TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下の Finalize() をオーバーライドします。
4856 // TODO: 大きなフィールドを null に設定します。
4858 this.disposedValue = true;
4861 // TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードがある場合にのみ、Finalize() をオーバーライドします。
4862 //protected Overrides void Finalize()
4864 // // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
4866 // MyBase.Finalize()
4869 // このコードは、破棄可能なパターンを正しく実装できるように Visual Basic によって追加されました。
4870 public void Dispose()
4872 // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
4874 GC.SuppressFinalize(this);