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;
48 public class Twitter : IDisposable
50 #region Regexp from twitter-text-js
52 // The code in this region code block incorporates works covered by
53 // the following copyright and permission notices:
55 // Copyright 2011 Twitter, Inc.
57 // Licensed under the Apache License, Version 2.0 (the "License"); you
58 // may not use this work except in compliance with the License. You
59 // may obtain a copy of the License in the LICENSE file, or at:
61 // http://www.apache.org/licenses/LICENSE-2.0
63 // Unless required by applicable law or agreed to in writing, software
64 // distributed under the License is distributed on an "AS IS" BASIS,
65 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
66 // implied. See the License for the specific language governing
67 // permissions and limitations under the License.
70 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";
71 private const string NON_LATIN_HASHTAG_CHARS = @"\u0400-\u04ff\u0500-\u0527\u1100-\u11ff\u3130-\u3185\uA960-\uA97F\uAC00-\uD7AF\uD7B0-\uD7FF";
72 //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";
73 private const string CJ_HASHTAG_CHARACTERS = @"\u30A1-\u30FA\u30FC\u3005\uFF66-\uFF9F\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\u3041-\u309A\u3400-\u4DBF\p{IsCJKUnifiedIdeographs}";
74 private const string HASHTAG_BOUNDARY = @"^|$|\s|「|」|。|\.|!";
75 private const string HASHTAG_ALPHA = "[a-z_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]";
76 private const string HASHTAG_ALPHANUMERIC = "[a-z0-9_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]";
77 private const string HASHTAG_TERMINATOR = "[^a-z0-9_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]";
78 public const string HASHTAG = "(" + HASHTAG_BOUNDARY + ")(#|#)(" + HASHTAG_ALPHANUMERIC + "*" + HASHTAG_ALPHA + HASHTAG_ALPHANUMERIC + "*)(?=" + HASHTAG_TERMINATOR + "|" + HASHTAG_BOUNDARY + ")";
80 private const string url_valid_preceding_chars = @"(?:[^A-Za-z0-9@@$##\ufffe\ufeff\uffff\u202a-\u202e]|^)";
81 public const string url_invalid_without_protocol_preceding_chars = @"[-_./]$";
82 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";
83 private const string url_valid_domain_chars = @"[^" + url_invalid_domain_chars + "]";
84 private const string url_valid_subdomain = @"(?:(?:" + url_valid_domain_chars + @"(?:[_-]|" + url_valid_domain_chars + @")*)?" + url_valid_domain_chars + @"\.)";
85 private const string url_valid_domain_name = @"(?:(?:" + url_valid_domain_chars + @"(?:-|" + url_valid_domain_chars + @")*)?" + url_valid_domain_chars + @"\.)";
86 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]|$))";
87 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]|$))";
88 private const string url_valid_punycode = @"(?:xn--[0-9a-z]+)";
89 private const string url_valid_domain = @"(?<domain>" + url_valid_subdomain + "*" + url_valid_domain_name + "(?:" + url_valid_GTLD + "|" + url_valid_CCTLD + ")|" + url_valid_punycode + ")";
90 public const string url_valid_ascii_domain = @"(?:(?:[a-z0-9" + LATIN_ACCENTS + @"]+)\.)+(?:" + url_valid_GTLD + "|" + url_valid_CCTLD + "|" + url_valid_punycode + ")";
91 public const string url_invalid_short_domain = "^" + url_valid_domain_name + url_valid_CCTLD + "$";
92 private const string url_valid_port_number = @"[0-9]+";
94 private const string url_valid_general_path_chars = @"[a-z0-9!*';:=+,.$/%#\[\]\-_~|&" + LATIN_ACCENTS + "]";
95 private const string url_balance_parens = @"(?:\(" + url_valid_general_path_chars + @"+\))";
96 private const string url_valid_path_ending_chars = @"(?:[+\-a-z0-9=_#/" + LATIN_ACCENTS + "]|" + url_balance_parens + ")";
97 private const string pth = "(?:" +
99 url_valid_general_path_chars + "*" +
100 "(?:" + url_balance_parens + url_valid_general_path_chars + "*)*" +
101 url_valid_path_ending_chars +
102 ")|(?:@" + url_valid_general_path_chars + "+/)" +
104 private const string qry = @"(?<query>\?[a-z0-9!?*'();:&=+$/%#\[\]\-_.,~|]*[a-z0-9_&=#/])?";
105 public const string rgUrl = @"(?<before>" + url_valid_preceding_chars + ")" +
106 "(?<url>(?<protocol>https?://)?" +
107 "(?<domain>" + url_valid_domain + ")" +
108 "(?::" + url_valid_port_number + ")?" +
109 "(?<path>/" + pth + "*)?" +
116 /// Twitter API のステータスページのURL
118 public const string ServiceAvailabilityStatusUrl = "https://status.io.watchmouse.com/7617";
121 /// ツイートへのパーマリンクURLを判定する正規表現
123 public static readonly Regex StatusUrlRegex = new Regex(@"https?://([^.]+\.)?twitter\.com/(#!/)?(?<ScreenName>[a-zA-Z0-9_]+)/status(es)?/(?<StatusId>[0-9]+)(/photo)?", RegexOptions.IgnoreCase);
126 /// FavstarやaclogなどTwitter関連サービスのパーマリンクURLからステータスIDを抽出する正規表現
128 public static readonly Regex ThirdPartyStatusUrlRegex = new Regex(@"https?://(?:[^.]+\.)?(?:
129 favstar\.fm/users/[a-zA-Z0-9_]+/status/ # Favstar
130 | favstar\.fm/t/ # Favstar (short)
131 | aclog\.koba789\.com/i/ # aclog
132 | frtrt\.net/solo_status\.php\?status= # RtRT
133 )(?<StatusId>[0-9]+)", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
135 delegate void GetIconImageDelegate(PostClass post);
136 private readonly object LockObj = new object();
137 private List<long> followerId = new List<long>();
138 private bool _GetFollowerResult = false;
139 private List<long> noRTId = new List<long>();
140 private bool _GetNoRetweetResult = false;
142 private int _followersCount = 0;
143 private int _friendsCount = 0;
144 private int _statusesCount = 0;
145 private string _location = "";
146 private string _bio = "";
147 private string _protocol = "https://";
150 private string _uname;
152 private bool _getIcon;
154 private bool _tinyUrlResolve;
155 private bool _restrictFavCheck;
157 private bool _readOwnPost;
158 private List<string> _hashList = new List<string>();
160 private Outputz op = new Outputz();
161 //max_idで古い発言を取得するために保持(lists分は個別タブで管理)
162 private long minHomeTimeline = long.MaxValue;
163 private long minMentions = long.MaxValue;
164 private long minDirectmessage = long.MaxValue;
165 private long minDirectmessageSent = long.MaxValue;
167 //private FavoriteQueue favQueue;
169 private HttpTwitter twCon = new HttpTwitter();
171 //private List<PostClass> _deletemessages = new List<PostClass>();
173 public TwitterApiAccessLevel AccessLevel
177 if (HttpTwitter.API11Enabled)
178 return MyCommon.TwitterApiInfo11.AccessLevel;
180 return MyCommon.TwitterApiInfo.AccessLevel;
184 protected void ResetApiStatus()
186 if (HttpTwitter.API11Enabled)
187 MyCommon.TwitterApiInfo11.Reset();
189 MyCommon.TwitterApiInfo.Reset();
192 public string Authenticate(string username, string password)
197 this.ResetApiStatus();
200 res = twCon.AuthUserAndPass(username, password, ref content);
204 return "Err:" + ex.Message;
209 case HttpStatusCode.OK:
210 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
211 _uname = username.ToLower();
212 if (AppendSettingDialog.Instance.UserstreamStartup) this.ReconnectUserStream();
214 case HttpStatusCode.Unauthorized:
216 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
217 var errMsg = GetErrorMessageJson(content);
218 if (string.IsNullOrEmpty(errMsg))
220 return Properties.Resources.Unauthorized + Environment.NewLine + content;
224 return "Auth error:" + errMsg;
227 case HttpStatusCode.Forbidden:
229 var errMsg = GetErrorMessageJson(content);
230 if (string.IsNullOrEmpty(errMsg))
232 return "Err:Forbidden";
236 return "Err:" + errMsg;
240 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
244 public string StartAuthentication(ref string pinPageUrl)
249 this.ResetApiStatus();
252 res = twCon.AuthGetRequestToken(ref pinPageUrl);
256 return "Err:" + "Failed to access auth server.";
262 public string Authenticate(string pinCode)
267 this.ResetApiStatus();
270 res = twCon.AuthGetAccessToken(pinCode);
274 return "Err:" + "Failed to access auth acc server.";
279 case HttpStatusCode.OK:
280 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
281 _uname = Username.ToLower();
282 if (AppendSettingDialog.Instance.UserstreamStartup) this.ReconnectUserStream();
284 case HttpStatusCode.Unauthorized:
286 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
287 var errMsg = GetErrorMessageJson(content);
288 if (string.IsNullOrEmpty(errMsg))
290 return "Check the PIN or retry." + Environment.NewLine + content;
294 return "Auth error:" + errMsg;
297 case HttpStatusCode.Forbidden:
299 var errMsg = GetErrorMessageJson(content);
300 if (string.IsNullOrEmpty(errMsg))
302 return "Err:Forbidden";
306 return "Err:" + errMsg;
310 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
314 public void ClearAuthInfo()
316 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
317 this.ResetApiStatus();
318 twCon.ClearAuthInfo();
321 public void VerifyCredentials()
323 HttpStatusCode res = HttpStatusCode.BadRequest;
328 res = twCon.VerifyCredentials(ref content);
335 if (res == HttpStatusCode.OK)
337 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
338 TwitterDataModel.User user;
341 user = MyCommon.CreateDataFromJson<TwitterDataModel.User>(content);
343 catch(SerializationException)
347 twCon.AuthenticatedUserId = user.Id;
351 private string GetErrorMessageJson(string content)
355 if (!string.IsNullOrEmpty(content))
357 using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(content), XmlDictionaryReaderQuotas.Max))
359 var xElm = XElement.Load(jsonReader);
360 if (xElm.Element("error") != null)
362 return xElm.Element("error").Value;
381 public void Initialize(string token, string tokenSecret, string username, long userId)
384 if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(tokenSecret) || string.IsNullOrEmpty(username))
386 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
388 this.ResetApiStatus();
389 twCon.Initialize(token, tokenSecret, username, userId);
390 _uname = username.ToLower();
391 if (AppendSettingDialog.Instance.UserstreamStartup) this.ReconnectUserStream();
394 public string PreProcessUrl(string orgData)
398 //var IDNConveter = new IdnMapping();
399 var href = "<a href=\"";
403 if (orgData.IndexOf(href, posl2, StringComparison.Ordinal) > -1)
407 posl1 = orgData.IndexOf(href, posl2, StringComparison.Ordinal);
408 posl1 += href.Length;
409 posl2 = orgData.IndexOf("\"", posl1, StringComparison.Ordinal);
410 urlStr = orgData.Substring(posl1, posl2 - posl1);
412 if (!urlStr.StartsWith("http://") && !urlStr.StartsWith("https://") && !urlStr.StartsWith("ftp://"))
417 var replacedUrl = MyCommon.IDNDecode(urlStr);
418 if (replacedUrl == null) continue;
419 if (replacedUrl == urlStr) continue;
421 orgData = orgData.Replace("<a href=\"" + urlStr, "<a href=\"" + replacedUrl);
432 private string GetPlainText(string orgData)
434 return WebUtility.HtmlDecode(Regex.Replace(orgData, "(?<tagStart><a [^>]+>)(?<text>[^<]+)(?<tagEnd></a>)", "${text}"));
437 // htmlの簡易サニタイズ(詳細表示に不要なタグの除去)
439 private string SanitizeHtml(string orgdata)
441 var retdata = orgdata;
443 retdata = Regex.Replace(retdata, "<(script|object|applet|image|frameset|fieldset|legend|style).*" +
444 "</(script|object|applet|image|frameset|fieldset|legend|style)>", "", RegexOptions.IgnoreCase);
446 retdata = Regex.Replace(retdata, "<(frame|link|iframe|img)>", "", RegexOptions.IgnoreCase);
451 private string AdjustHtml(string orgData)
453 var retStr = orgData;
454 //var m = Regex.Match(retStr, "<a [^>]+>[#|#](?<1>[a-zA-Z0-9_]+)</a>");
459 // _hashList.Add("#" + m.Groups(1).Value);
463 retStr = Regex.Replace(retStr, "<a [^>]*href=\"/", "<a href=\"https://twitter.com/");
464 retStr = retStr.Replace("<a href=", "<a target=\"_self\" href=");
465 retStr = Regex.Replace(retStr, @"(\r\n?|\n)", "<br>"); // CRLF, CR, LF は全て <br> に置換する
467 //半角スペースを置換(Thanks @anis774)
471 ret = EscapeSpace(ref retStr);
474 return SanitizeHtml(retStr);
477 private bool EscapeSpace(ref string html)
479 //半角スペースを置換(Thanks @anis774)
481 for (int i = 0; i < html.Length; i++)
492 if ((!isTag) && (html[i] == ' '))
494 html = html.Remove(i, 1);
495 html = html.Insert(i, " ");
502 private struct PostInfo
504 public string CreatedAt;
507 public string UserId;
508 public PostInfo(string Created, string IdStr, string txt, string uid)
515 public bool Equals(PostInfo dst)
517 if (this.CreatedAt == dst.CreatedAt && this.Id == dst.Id && this.Text == dst.Text && this.UserId == dst.UserId)
528 static private PostInfo _prev = new PostInfo("", "", "", "");
529 private bool IsPostRestricted(TwitterDataModel.Status status)
531 var _current = new PostInfo("", "", "", "");
533 _current.CreatedAt = status.CreatedAt;
534 _current.Id = status.IdStr;
535 if (status.Text == null)
541 _current.Text = status.Text;
543 _current.UserId = status.User.IdStr;
545 if (_current.Equals(_prev))
549 _prev.CreatedAt = _current.CreatedAt;
550 _prev.Id = _current.Id;
551 _prev.Text = _current.Text;
552 _prev.UserId = _current.UserId;
557 public string PostStatus(string postStr, long reply_to)
560 if (MyCommon._endingFlag) return "";
562 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
564 postStr = postStr.Trim();
566 if (Regex.Match(postStr, "^DM? +(?<id>[a-zA-Z0-9_]+) +(?<body>.+)", RegexOptions.IgnoreCase | RegexOptions.Singleline).Success)
568 return SendDirectMessage(postStr);
571 HttpStatusCode res = HttpStatusCode.BadRequest;
575 res = twCon.UpdateStatus(postStr, reply_to, ref content);
579 return "Err:" + ex.Message;
584 case HttpStatusCode.OK:
585 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
586 TwitterDataModel.Status status;
589 status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
591 catch(SerializationException ex)
593 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
594 return "Err:Json Parse Error(DataContractJsonSerializer)";
598 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
599 return "Err:Invalid Json!";
601 _followersCount = status.User.FollowersCount;
602 _friendsCount = status.User.FriendsCount;
603 _statusesCount = status.User.StatusesCount;
604 _location = status.User.Location;
605 _bio = status.User.Description;
607 if (IsPostRestricted(status))
609 return "OK:Delaying?";
611 if (op.Post(postStr.Length))
617 return "Outputz:Failed";
619 case HttpStatusCode.NotFound:
621 case HttpStatusCode.Forbidden:
622 case HttpStatusCode.BadRequest:
624 var errMsg = GetErrorMessageJson(content);
625 if (string.IsNullOrEmpty(errMsg))
627 return "Warn:" + res.ToString();
631 return "Warn:" + errMsg;
634 case HttpStatusCode.Conflict:
635 case HttpStatusCode.ExpectationFailed:
636 case HttpStatusCode.Gone:
637 case HttpStatusCode.LengthRequired:
638 case HttpStatusCode.MethodNotAllowed:
639 case HttpStatusCode.NotAcceptable:
640 case HttpStatusCode.PaymentRequired:
641 case HttpStatusCode.PreconditionFailed:
642 case HttpStatusCode.RequestedRangeNotSatisfiable:
643 case HttpStatusCode.RequestEntityTooLarge:
644 case HttpStatusCode.RequestTimeout:
645 case HttpStatusCode.RequestUriTooLong:
646 //仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
647 return "Warn:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
648 case HttpStatusCode.Unauthorized:
650 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
651 var errMsg = GetErrorMessageJson(content);
652 if (string.IsNullOrEmpty(errMsg))
654 return Properties.Resources.Unauthorized;
658 return "Auth err:" + errMsg;
662 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
666 public string PostStatusWithMedia(string postStr, long reply_to, FileInfo mediaFile)
668 if (MyCommon._endingFlag) return "";
670 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
672 postStr = postStr.Trim();
674 HttpStatusCode res = HttpStatusCode.BadRequest;
678 res = twCon.UpdateStatusWithMedia(postStr, reply_to, mediaFile, ref content);
682 return "Err:" + ex.Message;
687 case HttpStatusCode.OK:
688 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
689 TwitterDataModel.Status status;
692 status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
694 catch(SerializationException ex)
696 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
697 return "Err:Json Parse Error(DataContractJsonSerializer)";
701 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
702 return "Err:Invalid Json!";
704 _followersCount = status.User.FollowersCount;
705 _friendsCount = status.User.FriendsCount;
706 _statusesCount = status.User.StatusesCount;
707 _location = status.User.Location;
708 _bio = status.User.Description;
710 if (IsPostRestricted(status))
712 return "OK:Delaying?";
714 if (op.Post(postStr.Length))
720 return "Outputz:Failed";
722 case HttpStatusCode.NotFound:
724 case HttpStatusCode.Forbidden:
725 case HttpStatusCode.BadRequest:
727 var errMsg = GetErrorMessageJson(content);
728 if (string.IsNullOrEmpty(errMsg))
730 return "Warn:" + res.ToString();
734 return "Warn:" + errMsg;
737 case HttpStatusCode.Conflict:
738 case HttpStatusCode.ExpectationFailed:
739 case HttpStatusCode.Gone:
740 case HttpStatusCode.LengthRequired:
741 case HttpStatusCode.MethodNotAllowed:
742 case HttpStatusCode.NotAcceptable:
743 case HttpStatusCode.PaymentRequired:
744 case HttpStatusCode.PreconditionFailed:
745 case HttpStatusCode.RequestedRangeNotSatisfiable:
746 case HttpStatusCode.RequestEntityTooLarge:
747 case HttpStatusCode.RequestTimeout:
748 case HttpStatusCode.RequestUriTooLong:
749 //仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
750 return "Warn:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
751 case HttpStatusCode.Unauthorized:
753 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
754 var errMsg = GetErrorMessageJson(content);
755 if (string.IsNullOrEmpty(errMsg))
757 return Properties.Resources.Unauthorized;
761 return "Auth err:" + errMsg;
765 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
769 public string SendDirectMessage(string postStr)
771 if (MyCommon._endingFlag) return "";
773 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
775 if (this.AccessLevel == TwitterApiAccessLevel.Read || this.AccessLevel == TwitterApiAccessLevel.ReadWrite)
777 return "Auth Err:try to re-authorization.";
780 postStr = postStr.Trim();
782 HttpStatusCode res = HttpStatusCode.BadRequest;
785 var mc = Regex.Match(postStr, "^DM? +(?<id>[a-zA-Z0-9_]+) +(?<body>.+)", RegexOptions.IgnoreCase | RegexOptions.Singleline);
789 res = twCon.SendDirectMessage(mc.Groups["body"].Value, mc.Groups["id"].Value, ref content);
793 return "Err:" + ex.Message;
798 case HttpStatusCode.OK:
799 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
800 TwitterDataModel.Directmessage status;
803 status = MyCommon.CreateDataFromJson<TwitterDataModel.Directmessage>(content);
805 catch(SerializationException ex)
807 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
808 return "Err:Json Parse Error(DataContractJsonSerializer)";
812 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
813 return "Err:Invalid Json!";
815 _followersCount = status.Sender.FollowersCount;
816 _friendsCount = status.Sender.FriendsCount;
817 _statusesCount = status.Sender.StatusesCount;
818 _location = status.Sender.Location;
819 _bio = status.Sender.Description;
821 if (op.Post(postStr.Length))
827 return "Outputz:Failed";
829 case HttpStatusCode.Forbidden:
830 case HttpStatusCode.BadRequest:
832 var errMsg = GetErrorMessageJson(content);
833 if (string.IsNullOrEmpty(errMsg))
835 return "Warn:" + res.ToString();
839 return "Warn:" + errMsg;
842 case HttpStatusCode.Conflict:
843 case HttpStatusCode.ExpectationFailed:
844 case HttpStatusCode.Gone:
845 case HttpStatusCode.LengthRequired:
846 case HttpStatusCode.MethodNotAllowed:
847 case HttpStatusCode.NotAcceptable:
848 case HttpStatusCode.NotFound:
849 case HttpStatusCode.PaymentRequired:
850 case HttpStatusCode.PreconditionFailed:
851 case HttpStatusCode.RequestedRangeNotSatisfiable:
852 case HttpStatusCode.RequestEntityTooLarge:
853 case HttpStatusCode.RequestTimeout:
854 case HttpStatusCode.RequestUriTooLong:
855 //仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
856 return "Warn:" + res.ToString();
857 case HttpStatusCode.Unauthorized:
859 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
860 var errMsg = GetErrorMessageJson(content);
861 if (string.IsNullOrEmpty(errMsg))
863 return Properties.Resources.Unauthorized;
867 return "Auth err:" + errMsg;
871 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
875 public string RemoveStatus(long id)
877 if (MyCommon._endingFlag) return "";
879 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
881 HttpStatusCode res = HttpStatusCode.BadRequest;
885 res = twCon.DestroyStatus(id);
889 return "Err:" + ex.Message;
894 case HttpStatusCode.OK:
895 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
897 case HttpStatusCode.Unauthorized:
898 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
899 return Properties.Resources.Unauthorized;
900 case HttpStatusCode.NotFound:
903 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
907 public string PostRetweet(long id, bool read)
909 if (MyCommon._endingFlag) return "";
910 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
914 var post = TabInformations.GetInstance()[id];
917 return "Err:Target isn't found.";
919 if (TabInformations.GetInstance()[id].RetweetedId > 0)
921 target = TabInformations.GetInstance()[id].RetweetedId; //再RTの場合は元発言をRT
924 HttpStatusCode res = HttpStatusCode.BadRequest;
928 res = twCon.RetweetStatus(target, ref content);
932 return "Err:" + ex.Message;
935 if (res == HttpStatusCode.Unauthorized)
937 //Blockユーザーの発言をRTすると認証エラー返る
938 //Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid
939 return Properties.Resources.Unauthorized + " or blocked user.";
941 else if (res != HttpStatusCode.OK)
943 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
946 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
948 TwitterDataModel.Status status;
951 status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
953 catch(SerializationException ex)
955 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
956 return "Err:Json Parse Error(DataContractJsonSerializer)";
960 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
961 return "Err:Invalid Json!";
965 post = CreatePostsFromStatusData(status);
966 if (post == null) return "Invalid Json!";
971 if (TabInformations.GetInstance().ContainsKey(post.StatusId)) return "";
974 if (post.RetweetedId == 0) return "Invalid Json!";
980 if (_readOwnPost) post.IsRead = true;
983 TabInformations.GetInstance().AddPost(post);
988 public string RemoveDirectMessage(long id, PostClass post)
990 if (MyCommon._endingFlag) return "";
992 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
994 if (this.AccessLevel == TwitterApiAccessLevel.Read || this.AccessLevel == TwitterApiAccessLevel.ReadWrite)
996 return "Auth Err:try to re-authorization.";
999 HttpStatusCode res = HttpStatusCode.BadRequest;
1002 // _deletemessages.Add(post)
1006 res = twCon.DestroyDirectMessage(id);
1010 return "Err:" + ex.Message;
1015 case HttpStatusCode.OK:
1016 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1018 case HttpStatusCode.Unauthorized:
1019 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1020 return Properties.Resources.Unauthorized;
1021 case HttpStatusCode.NotFound:
1024 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1028 public string PostFollowCommand(string screenName)
1030 if (MyCommon._endingFlag) return "";
1032 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1034 HttpStatusCode res = HttpStatusCode.BadRequest;
1039 res = twCon.CreateFriendships(screenName, ref content);
1043 return "Err:" + ex.Message;
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 PostRemoveCommand(string screenName)
1071 if (MyCommon._endingFlag) return "";
1073 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1075 HttpStatusCode res = HttpStatusCode.BadRequest;
1080 res = twCon.DestroyFriendships(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 PostCreateBlock(string screenName)
1112 if (MyCommon._endingFlag) return "";
1114 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1116 HttpStatusCode res = HttpStatusCode.BadRequest;
1121 res = twCon.CreateBlock(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 PostDestroyBlock(string screenName)
1153 if (MyCommon._endingFlag) return "";
1155 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1157 HttpStatusCode res = HttpStatusCode.BadRequest;
1162 res = twCon.DestroyBlock(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 PostReportSpam(string screenName)
1194 if (MyCommon._endingFlag) return "";
1196 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1198 HttpStatusCode res = HttpStatusCode.BadRequest;
1203 res = twCon.ReportSpam(screenName, ref content);
1207 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1212 case HttpStatusCode.OK:
1213 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1215 case HttpStatusCode.Unauthorized:
1216 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1217 return Properties.Resources.Unauthorized;
1218 case HttpStatusCode.Forbidden:
1219 var errMsg = GetErrorMessageJson(content);
1220 if (string.IsNullOrEmpty(errMsg))
1222 return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1226 return "Err:" + errMsg;
1229 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1233 public string GetFriendshipInfo(string screenName, ref bool isFollowing, ref bool isFollowed)
1235 if (MyCommon._endingFlag) return "";
1237 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1239 HttpStatusCode res = HttpStatusCode.BadRequest;
1243 res = twCon.ShowFriendships(_uname, screenName, ref content);
1247 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1252 case HttpStatusCode.OK:
1255 var relation = MyCommon.CreateDataFromJson<TwitterDataModel.Relationship>(content);
1256 isFollowing = relation.relationship.Source.Following;
1257 isFollowed = relation.relationship.Source.FollowedBy;
1260 catch(SerializationException ex)
1262 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
1263 return "Err:Json Parse Error(DataContractJsonSerializer)";
1267 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
1268 return "Err:Invalid Json!";
1270 case HttpStatusCode.BadRequest:
1271 return "Err:API Limits?";
1272 case HttpStatusCode.Unauthorized:
1273 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1274 return Properties.Resources.Unauthorized;
1276 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1280 public string GetUserInfo(string screenName, ref TwitterDataModel.User user)
1282 if (MyCommon._endingFlag) return "";
1284 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1286 HttpStatusCode res = HttpStatusCode.BadRequest;
1291 res = twCon.ShowUserInfo(screenName, ref content);
1295 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1300 case HttpStatusCode.OK:
1301 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1304 user = MyCommon.CreateDataFromJson<TwitterDataModel.User>(content);
1306 catch (SerializationException ex)
1308 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
1309 return "Err:Json Parse Error(DataContractJsonSerializer)";
1311 catch (Exception ex)
1313 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
1314 return "Err:Invalid Json!";
1317 case HttpStatusCode.BadRequest:
1318 return "Err:API Limits?";
1319 case HttpStatusCode.Unauthorized:
1320 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1321 var errMsg = GetErrorMessageJson(content);
1322 if (string.IsNullOrEmpty(errMsg))
1324 return Properties.Resources.Unauthorized;
1328 return "Auth err:" + errMsg;
1331 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1335 public string GetStatus_Retweeted_Count(long StatusId, ref int retweeted_count)
1337 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1339 if (MyCommon._endingFlag) return "";
1341 HttpStatusCode res = HttpStatusCode.BadRequest;
1346 res = twCon.ShowStatuses(StatusId, ref content);
1348 catch (Exception ex)
1350 return "Err:" + ex.Message;
1354 case HttpStatusCode.OK:
1355 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1357 case HttpStatusCode.Unauthorized:
1358 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1359 return Properties.Resources.Unauthorized;
1360 case HttpStatusCode.BadRequest:
1361 return "Err:API Limits?";
1362 case HttpStatusCode.Forbidden:
1363 return "Err:protected user's tweet";
1365 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1368 TwitterDataModel.Status status;
1371 status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
1373 catch (SerializationException ex)
1375 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
1376 return "Json Parse Error(DataContractJsonSerializer)";
1378 catch (Exception ex)
1380 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
1381 return "Invalid Json!";
1384 if (int.TryParse(status.RetweetCount, out tmp))
1385 retweeted_count = tmp;
1389 public string PostFavAdd(long id)
1391 if (MyCommon._endingFlag) return "";
1393 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1395 //if (this.favQueue == null) this.favQueue = new FavoriteQueue(this)
1397 //if (this.favQueue.Contains(id)) this.favQueue.Remove(id)
1399 HttpStatusCode res = HttpStatusCode.BadRequest;
1403 res = twCon.CreateFavorites(id, ref content);
1407 //this.favQueue.Add(id)
1408 //return "Err:->FavoriteQueue:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1409 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1414 case HttpStatusCode.OK:
1415 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1416 //this.favQueue.FavoriteCacheStart();
1417 if (!_restrictFavCheck) return "";
1419 case HttpStatusCode.Unauthorized:
1420 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1421 return Properties.Resources.Unauthorized;
1422 case HttpStatusCode.Forbidden:
1423 var errMsg = GetErrorMessageJson(content);
1424 if (string.IsNullOrEmpty(errMsg))
1426 return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1430 //if (errMsg.Contains("It's great that you like so many updates"))
1431 // //this.favQueue.Add(id)
1432 // return "Err:->FavoriteQueue:" + errMsg;
1434 return "Err:" + errMsg;
1436 //Case HttpStatusCode.BadGateway, HttpStatusCode.ServiceUnavailable, HttpStatusCode.InternalServerError, HttpStatusCode.RequestTimeout
1437 // //this.favQueue.Add(id)
1438 // return "Err:->FavoriteQueue:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1440 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1443 //http://twitter.com/statuses/show/id.xml APIを発行して本文を取得
1449 res = twCon.ShowStatuses(id, ref content);
1453 return "Err:" + ex.Message;
1458 case HttpStatusCode.OK:
1459 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1460 TwitterDataModel.Status status;
1463 status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
1465 catch (SerializationException ex)
1467 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
1468 return "Err:Json Parse Error(DataContractJsonSerializer)";
1470 catch (Exception ex)
1472 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
1473 return "Err:Invalid Json!";
1475 if (status.Favorited)
1481 return "NG(Restricted?)";
1483 case HttpStatusCode.Unauthorized:
1484 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1485 return Properties.Resources.Unauthorized;
1486 case HttpStatusCode.BadRequest:
1487 return "Err:API Limits?";
1489 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1493 public string PostFavRemove(long id)
1495 if (MyCommon._endingFlag) return "";
1497 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1499 //if (this.favQueue == null) this.favQueue = new FavoriteQueue(this)
1501 //if (this.favQueue.Contains(id))
1502 // this.favQueue.Remove(id)
1506 HttpStatusCode res = HttpStatusCode.BadRequest;
1510 res = twCon.DestroyFavorites(id, ref content);
1514 return "Err:" + ex.Message;
1519 case HttpStatusCode.OK:
1520 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1522 case HttpStatusCode.Unauthorized:
1523 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1524 return Properties.Resources.Unauthorized;
1525 case HttpStatusCode.Forbidden:
1526 var errMsg = GetErrorMessageJson(content);
1527 if (string.IsNullOrEmpty(errMsg))
1529 return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1533 return "Err:" + errMsg;
1536 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1540 public string PostUpdateProfile(string name, string url, string location, string description)
1542 if (MyCommon._endingFlag) return "";
1544 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1546 HttpStatusCode res = HttpStatusCode.BadRequest;
1550 res = twCon.UpdateProfile(name, url, location, description, ref content);
1554 return "Err:" + ex.Message;
1559 case HttpStatusCode.OK:
1560 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1562 case HttpStatusCode.Unauthorized:
1563 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1564 return Properties.Resources.Unauthorized;
1565 case HttpStatusCode.Forbidden:
1566 var errMsg = GetErrorMessageJson(content);
1567 if (string.IsNullOrEmpty(errMsg))
1569 return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1573 return "Err:" + errMsg;
1576 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1580 public string PostUpdateProfileImage(string filename)
1582 if (MyCommon._endingFlag) return "";
1584 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1586 HttpStatusCode res = HttpStatusCode.BadRequest;
1590 res = twCon.UpdateProfileImage(new FileInfo(filename), ref content);
1594 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
1599 case HttpStatusCode.OK:
1600 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1602 case HttpStatusCode.Unauthorized:
1603 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1604 return Properties.Resources.Unauthorized;
1605 case HttpStatusCode.Forbidden:
1606 var errMsg = GetErrorMessageJson(content);
1607 if (string.IsNullOrEmpty(errMsg))
1609 return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
1613 return "Err:" + errMsg;
1616 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1620 public string Username
1624 return twCon.AuthenticatedUsername;
1632 return twCon.AuthenticatedUserId;
1636 public string Password
1640 return twCon.Password;
1644 private static MyCommon.ACCOUNT_STATE _accountState = MyCommon.ACCOUNT_STATE.Valid;
1645 public static MyCommon.ACCOUNT_STATE AccountState
1649 return _accountState;
1653 _accountState = value;
1665 public bool TinyUrlResolve
1669 _tinyUrlResolve = value;
1673 public bool RestrictFavCheck
1677 _restrictFavCheck = value;
1690 public string GetVersionInfo()
1693 if (!(new HttpVarious()).GetData(ApplicationSettings.VersionInfoUrl + "?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), null, out content, MyCommon.GetUserAgentString()))
1695 throw new Exception("GetVersionInfo Failed");
1700 public string GetTweenBinary(string strVer)
1705 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/Tween" + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1706 Path.Combine(MyCommon.settingPath, "TweenNew.exe")))
1708 return "Err:Download failed";
1711 if (!Directory.Exists(Path.Combine(MyCommon.settingPath, "en")))
1713 Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, "en"));
1715 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenResEn" + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1716 Path.Combine(Path.Combine(MyCommon.settingPath, "en"), "Tween.resourcesNew.dll")))
1718 return "Err:Download failed";
1720 //その他言語圏のリソース。取得失敗しても継続
1723 if (!Thread.CurrentThread.CurrentUICulture.IsNeutralCulture)
1725 var idx = Thread.CurrentThread.CurrentUICulture.Name.LastIndexOf('-');
1728 curCul = Thread.CurrentThread.CurrentUICulture.Name.Substring(0, idx);
1732 curCul = Thread.CurrentThread.CurrentUICulture.Name;
1737 curCul = Thread.CurrentThread.CurrentUICulture.Name;
1739 if (!string.IsNullOrEmpty(curCul) && curCul != "en" && curCul != "ja")
1741 if (!Directory.Exists(Path.Combine(MyCommon.settingPath, curCul)))
1743 Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, curCul));
1745 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenRes" + curCul + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1746 Path.Combine(Path.Combine(MyCommon.settingPath, curCul), "Tween.resourcesNew.dll")))
1748 //return "Err:Download failed";
1753 if (!Thread.CurrentThread.CurrentCulture.IsNeutralCulture)
1755 var idx = Thread.CurrentThread.CurrentCulture.Name.LastIndexOf('-');
1758 curCul2 = Thread.CurrentThread.CurrentCulture.Name.Substring(0, idx);
1762 curCul2 = Thread.CurrentThread.CurrentCulture.Name;
1767 curCul2 = Thread.CurrentThread.CurrentCulture.Name;
1769 if (!string.IsNullOrEmpty(curCul2) && curCul2 != "en" && curCul2 != curCul)
1771 if (!Directory.Exists(Path.Combine(MyCommon.settingPath, curCul2)))
1773 Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, curCul2));
1775 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenRes" + curCul2 + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1776 Path.Combine(Path.Combine(MyCommon.settingPath, curCul2), "Tween.resourcesNew.dll")))
1778 //return "Err:Download failed";
1783 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenUp3.gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1784 Path.Combine(MyCommon.settingPath, "TweenUp3.exe")))
1786 return "Err:Download failed";
1789 if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenDll" + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
1790 Path.Combine(MyCommon.settingPath, "TweenNew.XmlSerializers.dll")))
1792 return "Err:Download failed";
1798 return "Err:Download failed";
1803 public bool ReadOwnPost
1807 return _readOwnPost;
1811 _readOwnPost = value;
1815 public int FollowersCount
1819 return _followersCount;
1823 public int FriendsCount
1827 return _friendsCount;
1831 public int StatusesCount
1835 return _statusesCount;
1839 public string Location
1859 HttpTwitter.UseSsl = value;
1862 _protocol = "https://";
1866 _protocol = "http://";
1871 public string GetTimelineApi(bool read,
1872 MyCommon.WORKERTYPE gType,
1876 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1878 if (MyCommon._endingFlag) return "";
1882 var count = AppendSettingDialog.Instance.CountApi;
1883 if (gType == MyCommon.WORKERTYPE.Reply) count = AppendSettingDialog.Instance.CountApiReply;
1884 if (AppendSettingDialog.Instance.UseAdditionalCount)
1886 if (more && AppendSettingDialog.Instance.MoreCountApi != 0)
1888 count = AppendSettingDialog.Instance.MoreCountApi;
1890 else if (startup && AppendSettingDialog.Instance.FirstCountApi != 0 && gType == MyCommon.WORKERTYPE.Timeline)
1892 count = AppendSettingDialog.Instance.FirstCountApi;
1897 if (gType == MyCommon.WORKERTYPE.Timeline)
1901 res = twCon.HomeTimeline(count, this.minHomeTimeline, 0, ref content);
1905 res = twCon.HomeTimeline(count, 0, 0, ref content);
1912 res = twCon.Mentions(count, this.minMentions, 0, ref content);
1916 res = twCon.Mentions(count, 0, 0, ref content);
1922 return "Err:" + ex.Message;
1926 case HttpStatusCode.OK:
1927 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1929 case HttpStatusCode.Unauthorized:
1930 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
1931 return Properties.Resources.Unauthorized;
1932 case HttpStatusCode.BadRequest:
1933 return "Err:API Limits?";
1935 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
1938 if (gType == MyCommon.WORKERTYPE.Timeline)
1940 return CreatePostsFromJson(content, gType, null, read, count, ref this.minHomeTimeline);
1944 return CreatePostsFromJson(content, gType, null, read, count, ref this.minMentions);
1948 public string GetUserTimelineApi(bool read,
1954 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
1956 if (MyCommon._endingFlag) return "";
1958 HttpStatusCode res = HttpStatusCode.BadRequest;
1961 if (count == 0) count = 20;
1964 if (string.IsNullOrEmpty(userName))
1966 var target = tab.User;
1967 if (string.IsNullOrEmpty(target)) return "";
1969 res = twCon.UserTimeline(0, target, count, 0, 0, ref content);
1975 res = twCon.UserTimeline(0, userName, count, tab.OldestId, 0, ref content);
1979 res = twCon.UserTimeline(0, userName, count, 0, 0, ref content);
1985 return "Err:" + ex.Message;
1989 case HttpStatusCode.OK:
1990 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1992 case HttpStatusCode.Unauthorized:
1993 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
1994 return "Err:@" + userName + "'s Tweets are protected.";
1995 case HttpStatusCode.BadRequest:
1996 return "Err:API Limits?";
1998 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2001 List<TwitterDataModel.Status> items;
2004 items = MyCommon.CreateDataFromJson<List<TwitterDataModel.Status>>(content);
2006 catch(SerializationException ex)
2008 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2009 return "Json Parse Error(DataContractJsonSerializer)";
2013 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2014 return "Invalid Json!";
2017 foreach (var status in items)
2019 var item = CreatePostsFromStatusData(status);
2020 if (item == null) continue;
2021 if (item.StatusId < tab.OldestId) tab.OldestId = item.StatusId;
2023 if (item.IsMe && !read && _readOwnPost) item.IsRead = true;
2024 if (tab != null) item.RelTabName = tab.TabName;
2025 //非同期アイコン取得&StatusDictionaryに追加
2026 TabInformations.GetInstance().AddPost(item);
2032 public string GetStatusApi(bool read,
2036 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
2038 if (MyCommon._endingFlag) return "";
2040 HttpStatusCode res = HttpStatusCode.BadRequest;
2045 res = twCon.ShowStatuses(id, ref content);
2049 return "Err:" + ex.Message;
2053 case HttpStatusCode.OK:
2054 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
2056 case HttpStatusCode.Unauthorized:
2057 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
2058 return Properties.Resources.Unauthorized;
2059 case HttpStatusCode.BadRequest:
2060 return "Err:API Limits?";
2061 case HttpStatusCode.Forbidden:
2062 return "Err:protected user's tweet";
2064 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2067 TwitterDataModel.Status status;
2070 status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
2072 catch(SerializationException ex)
2074 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2075 return "Json Parse Error(DataContractJsonSerializer)";
2079 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2080 return "Invalid Json!";
2083 var item = CreatePostsFromStatusData(status);
2084 if (item == null) return "Err:Can't create post";
2086 if (item.IsMe && !read && _readOwnPost) item.IsRead = true;
2092 public string GetStatusApi(bool read,
2096 PostClass post = null;
2097 var r = this.GetStatusApi(read, id, ref post);
2099 if (string.IsNullOrEmpty(r))
2101 if (tab != null) post.RelTabName = tab.TabName;
2102 //非同期アイコン取得&StatusDictionaryに追加
2103 TabInformations.GetInstance().AddPost(post);
2109 private PostClass CreatePostsFromStatusData(TwitterDataModel.Status status)
2111 var post = new PostClass();
2112 TwitterDataModel.Entities entities;
2114 post.StatusId = status.Id;
2115 if (status.RetweetedStatus != null)
2117 var retweeted = status.RetweetedStatus;
2119 post.CreatedAt = MyCommon.DateTimeParse(retweeted.CreatedAt);
2122 post.RetweetedId = retweeted.Id;
2124 post.TextFromApi = retweeted.Text;
2125 entities = retweeted.Entities;
2126 //Source取得(htmlの場合は、中身を取り出し)
2127 post.Source = retweeted.Source;
2129 long inReplyToStatusId;
2130 long.TryParse(retweeted.InReplyToStatusId, out inReplyToStatusId);
2131 post.InReplyToStatusId = inReplyToStatusId;
2132 post.InReplyToUser = retweeted.InReplyToScreenName;
2133 long inReplyToUserId;
2134 long.TryParse(status.InReplyToUserId, out inReplyToUserId);
2135 post.InReplyToUserId = inReplyToUserId;
2138 var tc = TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites);
2139 post.IsFav = tc.Contains(post.RetweetedId);
2141 if (retweeted.Geo != null) post.PostGeo = new PostClass.StatusGeo {Lat = retweeted.Geo.Coordinates[0], Lng = retweeted.Geo.Coordinates[1]};
2144 var user = retweeted.User;
2146 if (user.ScreenName == null || status.User.ScreenName == null) return null;
2148 post.UserId = user.Id;
2149 post.ScreenName = user.ScreenName;
2150 post.Nickname = user.Name.Trim();
2151 post.ImageUrl = user.ProfileImageUrlHttps;
2152 post.IsProtect = user.Protected;
2155 post.RetweetedBy = status.User.ScreenName;
2156 post.RetweetedByUserId = status.User.Id;
2157 post.IsMe = post.RetweetedBy.ToLower().Equals(_uname);
2161 post.CreatedAt = MyCommon.DateTimeParse(status.CreatedAt);
2163 post.TextFromApi = status.Text;
2164 entities = status.Entities;
2165 //Source取得(htmlの場合は、中身を取り出し)
2166 post.Source = status.Source;
2167 long inReplyToStatusId;
2168 long.TryParse(status.InReplyToStatusId, out inReplyToStatusId);
2169 post.InReplyToStatusId = inReplyToStatusId;
2170 post.InReplyToUser = status.InReplyToScreenName;
2171 long inReplyToUserId;
2172 long.TryParse(status.InReplyToUserId, out inReplyToUserId);
2173 post.InReplyToUserId = inReplyToUserId;
2175 if (status.Geo != null) post.PostGeo = new PostClass.StatusGeo {Lat = status.Geo.Coordinates[0], Lng = status.Geo.Coordinates[1]};
2178 var user = status.User;
2180 if (user.ScreenName == null) return null;
2182 post.UserId = user.Id;
2183 post.ScreenName = user.ScreenName;
2184 post.Nickname = user.Name.Trim();
2185 post.ImageUrl = user.ProfileImageUrlHttps;
2186 post.IsProtect = user.Protected;
2187 post.IsMe = post.ScreenName.ToLower().Equals(_uname);
2190 var tc = TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites);
2191 post.IsFav = tc.Contains(post.StatusId) && TabInformations.GetInstance()[post.StatusId].IsFav;
2194 string textFromApi = post.TextFromApi;
2195 post.Text = CreateHtmlAnchor(ref textFromApi, post.ReplyToList, entities, post.Media);
2196 post.TextFromApi = textFromApi;
2197 post.TextFromApi = this.ReplaceTextFromApi(post.TextFromApi, entities);
2198 post.TextFromApi = WebUtility.HtmlDecode(post.TextFromApi);
2199 post.TextFromApi = post.TextFromApi.Replace("<3", "\u2661");
2204 post.IsReply = post.ReplyToList.Contains(_uname);
2205 post.IsExcludeReply = false;
2213 if (followerId.Count > 0) post.IsOwl = !followerId.Contains(post.UserId);
2220 private string CreatePostsFromJson(string content, MyCommon.WORKERTYPE gType, TabClass tab, bool read, int count, ref long minimumId)
2222 List<TwitterDataModel.Status> items;
2225 items = MyCommon.CreateDataFromJson<List<TwitterDataModel.Status>>(content);
2227 catch(SerializationException ex)
2229 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2230 return "Json Parse Error(DataContractJsonSerializer)";;
2234 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2235 return "Invalid Json!";
2238 foreach (var status in items)
2240 PostClass post = null;
2241 post = CreatePostsFromStatusData(status);
2242 if (post == null) continue;
2244 if (minimumId > post.StatusId) minimumId = post.StatusId;
2250 if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue;
2254 if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue;
2259 if (post.RetweetedId > 0 && this.noRTId.Contains(post.RetweetedByUserId)) continue;
2262 if (post.IsMe && !read && _readOwnPost) post.IsRead = true;
2264 if (tab != null) post.RelTabName = tab.TabName;
2265 //非同期アイコン取得&StatusDictionaryに追加
2266 TabInformations.GetInstance().AddPost(post);
2273 private string CreatePostsFromSearch11Json(string content, TabClass tab, bool read, int count, ref long minimumId, bool more)
2275 TwitterDataModel.SearchResult11 items;
2278 items = MyCommon.CreateDataFromJson<TwitterDataModel.SearchResult11>(content);
2280 catch (SerializationException ex)
2282 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2283 return "Json Parse Error(DataContractJsonSerializer)";
2285 catch (Exception ex)
2287 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2288 return "Invalid Json!";
2290 foreach (var result in items.Statuses)
2292 PostClass post = null;
2293 post = CreatePostsFromStatusData(result);
2294 if (post == null) continue;
2296 if (minimumId > post.StatusId) minimumId = post.StatusId;
2297 if (!more && post.StatusId > tab.SinceId) tab.SinceId = post.StatusId;
2303 if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue;
2307 if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue;
2312 if ((post.IsMe && !read) && this._readOwnPost) post.IsRead = true;
2314 if (tab != null) post.RelTabName = tab.TabName;
2315 //非同期アイコン取得&StatusDictionaryに追加
2316 TabInformations.GetInstance().AddPost(post);
2322 private string CreatePostsFromSearchJson(string content, TabClass tab, bool read, int count, ref long minimumId, bool more)
2324 TwitterDataModel.SearchResult items;
2327 items = MyCommon.CreateDataFromJson<TwitterDataModel.SearchResult>(content);
2329 catch (SerializationException ex)
2331 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2332 return "Json Parse Error(DataContractJsonSerializer)";
2334 catch (Exception ex)
2336 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2337 return "Invalid Json!";
2339 foreach (var result in items.Results)
2341 PostClass post = null;
2342 post = CreatePostsFromSearchResultData(result);
2343 if (post == null) continue;
2345 if (minimumId > post.StatusId) minimumId = post.StatusId;
2346 if (!more && post.StatusId > tab.SinceId) tab.SinceId = post.StatusId;
2352 if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue;
2356 if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue;
2361 if ((post.IsMe && !read) && this._readOwnPost) post.IsRead = true;
2363 if (tab != null) post.RelTabName = tab.TabName;
2364 //非同期アイコン取得&StatusDictionaryに追加
2365 TabInformations.GetInstance().AddPost(post);
2371 private PostClass CreatePostsFromSearchResultData(TwitterDataModel.SearchResultData status)
2373 var post = new PostClass();
2374 post.StatusId = status.Id;
2375 post.CreatedAt = MyCommon.DateTimeParse(status.CreatedAt);
2377 post.TextFromApi = status.Text;
2378 var entities = status.Entities;
2379 post.Source = WebUtility.HtmlDecode(status.Source);
2380 post.InReplyToStatusId = status.InReplyToStatusId;
2381 post.InReplyToUser = status.ToUser;
2382 post.InReplyToUserId = !status.ToUserId.HasValue ? 0 : (long)status.ToUserId;
2384 if (status.Geo != null) post.PostGeo = new PostClass.StatusGeo { Lat = status.Geo.Coordinates[0], Lng = status.Geo.Coordinates[1] };
2386 if (status.FromUser == null) return null;
2388 post.UserId = status.FromUserId;
2389 post.ScreenName = status.FromUser;
2390 post.Nickname = status.FromUserName.Trim();
2391 post.ImageUrl = status.ProfileImageUrl;
2392 post.IsProtect = false;
2393 post.IsMe = post.ScreenName.ToLower().Equals(this._uname);
2396 var tc = TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites);
2397 post.IsFav = tc.Contains(post.StatusId) && TabInformations.GetInstance()[post.StatusId].IsFav;
2400 string textFromApi = post.TextFromApi;
2401 post.Text = this.CreateHtmlAnchor(ref textFromApi, post.ReplyToList, entities, post.Media);
2402 post.TextFromApi = this.ReplaceTextFromApi(post.TextFromApi, entities);
2403 post.TextFromApi = WebUtility.HtmlDecode(post.TextFromApi);
2404 post.TextFromApi = post.TextFromApi.Replace("<3", "\u2661");
2407 this.CreateSource(post);
2409 post.IsReply = post.ReplyToList.Contains(this._uname);
2410 post.IsExcludeReply = false;
2417 private string CreatePostsFromPhoenixSearch(string content, MyCommon.WORKERTYPE gType, TabClass tab, bool read, int count, ref long minimumId, ref string nextPageQuery)
2419 TwitterDataModel.SearchResultPhoenix items;
2422 items = MyCommon.CreateDataFromJson<TwitterDataModel.SearchResultPhoenix>(content);
2424 catch(SerializationException ex)
2426 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2427 return "Json Parse Error(DataContractJsonSerializer)";
2431 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2432 return "Invalid Json!";
2435 nextPageQuery = items.NextPage;
2437 foreach (var status in items.Statuses)
2439 PostClass post = null;
2440 post = CreatePostsFromStatusData(status);
2441 if (post == null) continue;
2443 if (minimumId > post.StatusId) minimumId = post.StatusId;
2449 if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue;
2453 if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue;
2458 if (post.IsMe && !read && _readOwnPost) post.IsRead = true;
2460 if (tab != null) post.RelTabName = tab.TabName;
2461 //非同期アイコン取得&StatusDictionaryに追加
2462 TabInformations.GetInstance().AddPost(post);
2465 return string.IsNullOrEmpty(items.ErrMsg) ? "" : "Err:" + items.ErrMsg;
2468 public string GetListStatus(bool read,
2473 if (MyCommon._endingFlag) return "";
2478 if (AppendSettingDialog.Instance.UseAdditionalCount)
2480 count = AppendSettingDialog.Instance.ListCountApi;
2483 if (more && AppendSettingDialog.Instance.MoreCountApi != 0)
2485 count = AppendSettingDialog.Instance.MoreCountApi;
2487 else if (startup && AppendSettingDialog.Instance.FirstCountApi != 0)
2489 count = AppendSettingDialog.Instance.FirstCountApi;
2493 count = AppendSettingDialog.Instance.CountApi;
2499 count = AppendSettingDialog.Instance.CountApi;
2505 res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, tab.OldestId, 0, AppendSettingDialog.Instance.IsListStatusesIncludeRts, ref content);
2509 res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, 0, 0, AppendSettingDialog.Instance.IsListStatusesIncludeRts, ref content);
2514 return "Err:" + ex.Message;
2518 case HttpStatusCode.OK:
2519 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
2521 case HttpStatusCode.Unauthorized:
2522 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
2523 return Properties.Resources.Unauthorized;
2524 case HttpStatusCode.BadRequest:
2525 return "Err:API Limits?";
2527 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2530 return CreatePostsFromJson(content, MyCommon.WORKERTYPE.List, tab, read, count, ref tab.OldestId);
2534 /// startStatusId からリプライ先の発言を辿る。発言は posts 以外からは検索しない。
2536 /// <returns>posts の中から検索されたリプライチェインの末端</returns>
2537 internal static PostClass FindTopOfReplyChain(IDictionary<Int64, PostClass> posts, Int64 startStatusId)
2539 if (!posts.ContainsKey(startStatusId))
2540 throw new ArgumentException("startStatusId (" + startStatusId + ") が posts の中から見つかりませんでした。");
2542 var nextPost = posts[startStatusId];
2543 while (nextPost.InReplyToStatusId != 0)
2545 if (!posts.ContainsKey(nextPost.InReplyToStatusId))
2547 nextPost = posts[nextPost.InReplyToStatusId];
2553 public string GetRelatedResult(bool read, TabClass tab)
2556 var relPosts = new Dictionary<Int64, PostClass>();
2557 if (tab.RelationTargetPost.TextFromApi.Contains("@") && tab.RelationTargetPost.InReplyToStatusId == 0)
2560 var p = TabInformations.GetInstance()[tab.RelationTargetPost.StatusId];
2561 if (p != null && p.InReplyToStatusId > 0)
2563 tab.RelationTargetPost = p;
2567 rslt = this.GetStatusApi(read, tab.RelationTargetPost.StatusId, ref p);
2568 if (!string.IsNullOrEmpty(rslt)) return rslt;
2569 tab.RelationTargetPost = p;
2572 relPosts.Add(tab.RelationTargetPost.StatusId, tab.RelationTargetPost.Clone());
2574 // 一周目: 非公式な related_results API を使用してリプライチェインを辿る
2575 var nextPost = relPosts[tab.RelationTargetPost.StatusId];
2579 rslt = this.GetRelatedResultsApi(nextPost, relPosts);
2580 if (!string.IsNullOrEmpty(rslt)) break;
2581 nextPost = FindTopOfReplyChain(relPosts, nextPost.StatusId);
2582 } while (nextPost.InReplyToStatusId != 0 && loopCount++ <= 5);
2584 // 二周目: in_reply_to_status_id を使用してリプライチェインを辿る
2585 nextPost = FindTopOfReplyChain(relPosts, tab.RelationTargetPost.StatusId);
2587 while (nextPost.InReplyToStatusId != 0 && loopCount++ <= 20)
2589 var inReplyToId = nextPost.InReplyToStatusId;
2591 var inReplyToPost = TabInformations.GetInstance()[inReplyToId];
2592 if (inReplyToPost != null)
2594 inReplyToPost = inReplyToPost.Clone();
2598 var errorText = this.GetStatusApi(read, inReplyToId, ref inReplyToPost);
2599 if (!string.IsNullOrEmpty(errorText))
2606 relPosts.Add(inReplyToPost.StatusId, inReplyToPost);
2608 nextPost = FindTopOfReplyChain(relPosts, nextPost.StatusId);
2611 //MRTとかに対応のためツイート内にあるツイートを指すURLを取り込む
2612 var text = tab.RelationTargetPost.Text;
2613 var ma = Twitter.StatusUrlRegex.Matches(text).Cast<Match>()
2614 .Concat(Twitter.ThirdPartyStatusUrlRegex.Matches(text).Cast<Match>());
2615 foreach (var _match in ma)
2618 if (Int64.TryParse(_match.Groups["StatusId"].Value, out _statusId))
2620 if (relPosts.ContainsKey(_statusId))
2624 var _post = TabInformations.GetInstance()[_statusId];
2627 this.GetStatusApi(read, _statusId, ref p);
2635 relPosts.Add(p.StatusId, p);
2639 relPosts.Values.ToList().ForEach(p =>
2641 if (p.IsMe && !read && this._readOwnPost)
2646 p.RelTabName = tab.TabName;
2647 TabInformations.GetInstance().AddPost(p);
2653 private string GetRelatedResultsApi(PostClass post,
2654 IDictionary<Int64, PostClass> relatedPosts)
2656 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
2658 if (MyCommon._endingFlag) return "";
2660 HttpStatusCode res = HttpStatusCode.BadRequest;
2664 if (post.RetweetedId > 0)
2666 res = twCon.GetRelatedResults(post.RetweetedId, ref content);
2670 res = twCon.GetRelatedResults(post.StatusId, ref content);
2675 return "Err:" + ex.Message;
2679 case HttpStatusCode.OK:
2680 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
2682 case HttpStatusCode.Unauthorized:
2683 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
2684 return Properties.Resources.Unauthorized;
2685 case HttpStatusCode.BadRequest:
2686 return "Err:API Limits?";
2688 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2691 List<TwitterDataModel.RelatedResult> items;
2694 items = MyCommon.CreateDataFromJson<List<TwitterDataModel.RelatedResult>>(content);
2696 catch(SerializationException ex)
2698 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2699 return "Json Parse Error(DataContractJsonSerializer)";
2703 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2704 return "Invalid Json!";
2707 foreach (var relatedData in items)
2709 foreach (var result in relatedData.Results)
2711 var item = CreatePostsFromStatusData(result.Status);
2712 if (item == null) continue;
2713 //非同期アイコン取得&StatusDictionaryに追加
2714 if (!relatedPosts.ContainsKey(item.StatusId))
2715 relatedPosts.Add(item.StatusId, item);
2722 public string GetSearch(bool read,
2726 if (MyCommon._endingFlag) return "";
2733 if (AppendSettingDialog.Instance.UseAdditionalCount &&
2734 AppendSettingDialog.Instance.SearchCountApi != 0)
2736 count = AppendSettingDialog.Instance.SearchCountApi;
2740 count = AppendSettingDialog.Instance.CountApi;
2744 page = tab.GetSearchPage(count);
2748 sinceId = tab.SinceId;
2753 // TODO:一時的に40>100件に 件数変更UI作成の必要あり
2754 res = twCon.Search(tab.SearchWords, tab.SearchLang, count, page, sinceId, ref content);
2758 return "Err:" + ex.Message;
2762 case HttpStatusCode.BadRequest:
2763 return "Invalid query";
2764 case HttpStatusCode.NotFound:
2765 return "Invalid query";
2766 case HttpStatusCode.PaymentRequired: //API Documentには420と書いてあるが、該当コードがないので402にしてある
2767 return "Search API Limit?";
2768 case HttpStatusCode.OK:
2771 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2774 if (!TabInformations.GetInstance().ContainsTab(tab)) return "";
2776 if (HttpTwitter.API11Enabled)
2777 return this.CreatePostsFromSearch11Json(content, tab, read, count, ref tab.OldestId, more);
2779 return this.CreatePostsFromSearchJson(content, tab, read, count, ref tab.OldestId, more);
2782 public string GetPhoenixSearch(bool read,
2786 if (MyCommon._endingFlag) return "";
2794 if (AppendSettingDialog.Instance.UseAdditionalCount &&
2795 AppendSettingDialog.Instance.SearchCountApi != 0)
2797 count = AppendSettingDialog.Instance.SearchCountApi;
2801 page = tab.GetSearchPage(count);
2802 if (!string.IsNullOrEmpty(tab.NextPageQuery))
2804 querystr = tab.NextPageQuery;
2809 sinceId = tab.SinceId;
2814 if (string.IsNullOrEmpty(querystr))
2816 res = twCon.PhoenixSearch(tab.SearchWords, tab.SearchLang, count, page, sinceId, ref content);
2820 res = twCon.PhoenixSearch(querystr, ref content);
2825 return "Err:" + ex.Message;
2829 case HttpStatusCode.BadRequest:
2830 return "Invalid query";
2831 case HttpStatusCode.NotFound:
2832 return "Invalid query";
2833 case HttpStatusCode.PaymentRequired: //API Documentには420と書いてあるが、該当コードがないので402にしてある
2834 return "Search API Limit?";
2835 case HttpStatusCode.OK:
2838 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
2841 if (!TabInformations.GetInstance().ContainsTab(tab)) return "";
2844 //// 遡るための情報max_idやnext_pageの情報を保持する
2846 string nextPageQuery = tab.NextPageQuery;
2847 var ret = CreatePostsFromPhoenixSearch(content, MyCommon.WORKERTYPE.PublicSearch, tab, read, count, ref tab.OldestId, ref nextPageQuery);
2848 tab.NextPageQuery = nextPageQuery;
2852 private string CreateDirectMessagesFromJson(string content, MyCommon.WORKERTYPE gType, bool read)
2854 List<TwitterDataModel.Directmessage> item;
2857 if (gType == MyCommon.WORKERTYPE.UserStream)
2859 var itm = MyCommon.CreateDataFromJson<List<TwitterDataModel.DirectmessageEvent>>(content);
2860 item = new List<TwitterDataModel.Directmessage>();
2861 foreach (var dat in itm)
2863 item.Add(dat.Directmessage);
2868 item = MyCommon.CreateDataFromJson<List<TwitterDataModel.Directmessage>>(content);
2871 catch(SerializationException ex)
2873 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
2874 return "Json Parse Error(DataContractJsonSerializer)";
2878 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2879 return "Invalid Json!";
2882 foreach (var message in item)
2884 var post = new PostClass();
2887 post.StatusId = message.Id;
2888 if (gType != MyCommon.WORKERTYPE.UserStream)
2890 if (gType == MyCommon.WORKERTYPE.DirectMessegeRcv)
2892 if (minDirectmessage > post.StatusId) minDirectmessage = post.StatusId;
2896 if (minDirectmessageSent > post.StatusId) minDirectmessageSent = post.StatusId;
2903 if (TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.DirectMessage).Contains(post.StatusId)) continue;
2907 post.CreatedAt = MyCommon.DateTimeParse(message.CreatedAt);
2909 post.TextFromApi = message.Text;
2911 post.Text = CreateHtmlAnchor(post.TextFromApi, post.ReplyToList, post.Media);
2912 post.TextFromApi = WebUtility.HtmlDecode(post.TextFromApi);
2913 post.TextFromApi = post.TextFromApi.Replace("<3", "\u2661");
2917 TwitterDataModel.User user;
2918 if (gType == MyCommon.WORKERTYPE.UserStream)
2920 if (twCon.AuthenticatedUsername.Equals(message.Recipient.ScreenName, StringComparison.CurrentCultureIgnoreCase))
2922 user = message.Sender;
2928 user = message.Recipient;
2935 if (gType == MyCommon.WORKERTYPE.DirectMessegeRcv)
2937 user = message.Sender;
2943 user = message.Recipient;
2949 post.UserId = user.Id;
2950 post.ScreenName = user.ScreenName;
2951 post.Nickname = user.Name.Trim();
2952 post.ImageUrl = user.ProfileImageUrlHttps;
2953 post.IsProtect = user.Protected;
2957 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
2958 MessageBox.Show("Parse Error(CreateDirectMessagesFromJson)");
2963 if (post.IsMe && !read && _readOwnPost) post.IsRead = true;
2964 post.IsReply = false;
2965 post.IsExcludeReply = false;
2968 TabInformations.GetInstance().AddPost(post);
2975 public string GetDirectMessageApi(bool read,
2976 MyCommon.WORKERTYPE gType,
2979 if (MyCommon._endingFlag) return "";
2981 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
2983 if (this.AccessLevel == TwitterApiAccessLevel.Read || this.AccessLevel == TwitterApiAccessLevel.ReadWrite)
2985 return "Auth Err:try to re-authorization.";
2988 HttpStatusCode res = HttpStatusCode.BadRequest;
2993 if (gType == MyCommon.WORKERTYPE.DirectMessegeRcv)
2997 res = twCon.DirectMessages(20, minDirectmessage, 0, ref content);
3001 res = twCon.DirectMessages(20, 0, 0, ref content);
3008 res = twCon.DirectMessagesSent(20, minDirectmessageSent, 0, ref content);
3012 res = twCon.DirectMessagesSent(20, 0, 0, ref content);
3018 return "Err:" + ex.Message;
3023 case HttpStatusCode.OK:
3024 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3026 case HttpStatusCode.Unauthorized:
3027 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3028 return Properties.Resources.Unauthorized;
3029 case HttpStatusCode.BadRequest:
3030 return "Err:API Limits?";
3032 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3035 return CreateDirectMessagesFromJson(content, gType, read);
3038 static int page_ = 1;
3039 public string GetFavoritesApi(bool read,
3040 MyCommon.WORKERTYPE gType,
3043 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3045 if (MyCommon._endingFlag) return "";
3049 var count = AppendSettingDialog.Instance.CountApi;
3050 if (AppendSettingDialog.Instance.UseAdditionalCount &&
3051 AppendSettingDialog.Instance.FavoritesCountApi != 0)
3053 count = AppendSettingDialog.Instance.FavoritesCountApi;
3056 // 前ページ取得の場合はページカウンタをインクリメント、それ以外の場合はページカウンタリセット
3068 res = twCon.Favorites(count, page_, ref content);
3072 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3077 case HttpStatusCode.OK:
3078 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3080 case HttpStatusCode.Unauthorized:
3081 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3082 return Properties.Resources.Unauthorized;
3083 case HttpStatusCode.BadRequest:
3084 return "Err:API Limits?";
3086 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3089 var serializer = new DataContractJsonSerializer(typeof(List<TwitterDataModel.Status>));
3090 List<TwitterDataModel.Status> item;
3094 item = MyCommon.CreateDataFromJson<List<TwitterDataModel.Status>>(content);
3096 catch(SerializationException ex)
3098 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3099 return "Json Parse Error(DataContractJsonSerializer)";
3103 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3104 return "Invalid Json!";
3107 foreach (var status in item)
3109 var post = new PostClass();
3110 TwitterDataModel.Entities entities;
3114 post.StatusId = status.Id;
3118 if (TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Contains(post.StatusId)) continue;
3121 if (status.RetweetedStatus != null)
3123 var retweeted = status.RetweetedStatus;
3124 post.CreatedAt = MyCommon.DateTimeParse(retweeted.CreatedAt);
3127 post.RetweetedId = post.StatusId;
3129 post.TextFromApi = retweeted.Text;
3130 entities = retweeted.Entities;
3131 //Source取得(htmlの場合は、中身を取り出し)
3132 post.Source = retweeted.Source;
3134 long inReplyToStatusId;
3135 long.TryParse(retweeted.InReplyToStatusId, out inReplyToStatusId);
3136 post.InReplyToStatusId = inReplyToStatusId;
3137 post.InReplyToUser = retweeted.InReplyToScreenName;
3138 long inReplyToUserId;
3139 long.TryParse(retweeted.InReplyToUserId, out inReplyToUserId);
3140 post.InReplyToUserId = inReplyToUserId;
3144 var user = retweeted.User;
3145 post.UserId = user.Id;
3146 post.ScreenName = user.ScreenName;
3147 post.Nickname = user.Name.Trim();
3148 post.ImageUrl = user.ProfileImageUrlHttps;
3149 post.IsProtect = user.Protected;
3152 post.RetweetedBy = status.User.ScreenName;
3153 post.IsMe = post.RetweetedBy.ToLower().Equals(_uname);
3157 post.CreatedAt = MyCommon.DateTimeParse(status.CreatedAt);
3160 post.TextFromApi = status.Text;
3161 entities = status.Entities;
3162 //Source取得(htmlの場合は、中身を取り出し)
3163 post.Source = status.Source;
3164 long inReplyToStatusId;
3165 long.TryParse(status.InReplyToStatusId, out inReplyToStatusId);
3166 post.InReplyToStatusId = inReplyToStatusId;
3167 post.InReplyToUser = status.InReplyToScreenName;
3168 long inReplyToUserId;
3169 long.TryParse(status.InReplyToUserId, out inReplyToUserId);
3170 post.InReplyToUserId = inReplyToUserId;
3175 var user = status.User;
3176 post.UserId = user.Id;
3177 post.ScreenName = user.ScreenName;
3178 post.Nickname = user.Name.Trim();
3179 post.ImageUrl = user.ProfileImageUrlHttps;
3180 post.IsProtect = user.Protected;
3181 post.IsMe = post.ScreenName.ToLower().Equals(_uname);
3184 string textFromApi = post.TextFromApi;
3185 post.Text = CreateHtmlAnchor(ref textFromApi, post.ReplyToList, entities, post.Media);
3186 post.TextFromApi = textFromApi;
3187 post.TextFromApi = this.ReplaceTextFromApi(post.TextFromApi, entities);
3188 post.TextFromApi = WebUtility.HtmlDecode(post.TextFromApi);
3189 post.TextFromApi = post.TextFromApi.Replace("<3", "\u2661");
3194 post.IsReply = post.ReplyToList.Contains(_uname);
3195 post.IsExcludeReply = false;
3203 if (followerId.Count > 0) post.IsOwl = !followerId.Contains(post.UserId);
3210 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3214 TabInformations.GetInstance().AddPost(post);
3221 private string ReplaceTextFromApi(string text, TwitterDataModel.Entities entities)
3223 if (entities != null)
3225 if (entities.Urls != null)
3227 foreach (var m in entities.Urls)
3229 if (!string.IsNullOrEmpty(m.DisplayUrl)) text = text.Replace(m.Url, m.DisplayUrl);
3232 if (entities.Media != null)
3234 foreach (var m in entities.Media)
3236 if (!string.IsNullOrEmpty(m.DisplayUrl)) text = text.Replace(m.Url, m.DisplayUrl);
3243 public string GetFollowersApi()
3245 if (MyCommon._endingFlag) return "";
3247 var tmpFollower = new List<long>(followerId);
3252 var ret = FollowerApi(ref cursor);
3253 if (!string.IsNullOrEmpty(ret))
3256 followerId.AddRange(tmpFollower);
3257 _GetFollowerResult = false;
3260 } while (cursor > 0);
3262 TabInformations.GetInstance().RefreshOwl(followerId);
3264 _GetFollowerResult = true;
3268 public bool GetFollowersSuccess
3272 return _GetFollowerResult;
3276 private string FollowerApi(ref long cursor)
3278 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3280 HttpStatusCode res = HttpStatusCode.BadRequest;
3284 res = twCon.FollowerIds(cursor, ref content);
3288 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3293 case HttpStatusCode.OK:
3294 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3296 case HttpStatusCode.Unauthorized:
3297 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3298 return Properties.Resources.Unauthorized;
3299 case HttpStatusCode.BadRequest:
3300 return "Err:API Limits?";
3302 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3307 var followers = MyCommon.CreateDataFromJson<TwitterDataModel.Ids>(content);
3308 followerId.AddRange(followers.Id);
3309 cursor = followers.NextCursor;
3312 catch(SerializationException ex)
3314 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3315 return "Err:Json Parse Error(DataContractJsonSerializer)";
3319 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3320 return "Err:Invalid Json!";
3324 public string GetNoRetweetIdsApi()
3326 if (MyCommon._endingFlag) return "";
3328 var tmpIds = new List<long>(noRTId);
3333 var ret = NoRetweetApi(ref cursor);
3334 if (!string.IsNullOrEmpty(ret))
3337 noRTId.AddRange(tmpIds);
3338 _GetNoRetweetResult = false;
3341 } while (cursor > 0);
3343 _GetNoRetweetResult = true;
3347 private string NoRetweetApi(ref long cursor)
3349 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3351 HttpStatusCode res = HttpStatusCode.BadRequest;
3355 res = twCon.NoRetweetIds(cursor, ref content);
3359 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3364 case HttpStatusCode.OK:
3365 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3367 case HttpStatusCode.Unauthorized:
3368 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3369 return Properties.Resources.Unauthorized;
3370 case HttpStatusCode.BadRequest:
3371 return "Err:API Limits?";
3373 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3378 var ids = MyCommon.CreateDataFromJson<long[]>(content);
3379 noRTId.AddRange(ids);
3380 cursor = 0; //0より小さければ何でも良い。
3383 catch(SerializationException ex)
3385 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3386 return "Err:Json Parse Error(DataContractJsonSerializer)";
3390 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3391 return "Err:Invalid Json!";
3395 public bool GetNoRetweetSuccess
3399 return _GetNoRetweetResult;
3403 public string ConfigurationApi()
3409 res = twCon.GetConfiguration(ref content);
3413 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3418 case HttpStatusCode.OK:
3419 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3421 case HttpStatusCode.Unauthorized:
3422 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3423 return Properties.Resources.Unauthorized;
3424 case HttpStatusCode.BadRequest:
3425 return "Err:API Limits?";
3427 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3432 AppendSettingDialog.Instance.TwitterConfiguration = MyCommon.CreateDataFromJson<TwitterDataModel.Configuration>(content);
3435 catch(SerializationException ex)
3437 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3438 return "Err:Json Parse Error(DataContractJsonSerializer)";
3442 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3443 return "Err:Invalid Json!";
3447 public string GetListsApi()
3449 return HttpTwitter.API11Enabled ? this.GetListsApi11() : this.GetListsApi10();
3452 private string GetListsApi10()
3454 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3456 HttpStatusCode res = HttpStatusCode.BadRequest;
3460 var lists = new List<ListElement>();
3465 res = twCon.GetLists(this.Username, cursor, ref content);
3469 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3474 case HttpStatusCode.OK:
3475 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3477 case HttpStatusCode.Unauthorized:
3478 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3479 return Properties.Resources.Unauthorized;
3480 case HttpStatusCode.BadRequest:
3481 return "Err:API Limits?";
3483 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3488 var lst = MyCommon.CreateDataFromJson<TwitterDataModel.Lists>(content);
3489 lists.AddRange(from le in lst.lists select new ListElement(le, this));
3490 cursor = lst.NextCursor;
3492 catch(SerializationException ex)
3494 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3495 return "Err:Json Parse Error(DataContractJsonSerializer)";
3499 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3500 return "Err:Invalid Json!";
3502 } while (cursor != 0);
3510 res = twCon.GetListsSubscriptions(this.Username, cursor, ref content);
3514 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3519 case HttpStatusCode.OK:
3520 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3522 case HttpStatusCode.Unauthorized:
3523 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3524 return Properties.Resources.Unauthorized;
3525 case HttpStatusCode.BadRequest:
3526 return "Err:API Limits?";
3528 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3533 var lst = MyCommon.CreateDataFromJson<TwitterDataModel.Lists>(content);
3534 lists.AddRange(from le in lst.lists select new ListElement(le, this));
3535 cursor = lst.NextCursor;
3537 catch(SerializationException ex)
3539 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3540 return "Err:Json Parse Error(DataContractJsonSerializer)";
3544 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3545 return "Err:Invalid Json!";
3547 } while (cursor != 0);
3549 TabInformations.GetInstance().SubscribableLists = lists;
3553 private string GetListsApi11()
3555 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3557 HttpStatusCode res = HttpStatusCode.BadRequest;
3558 IEnumerable<ListElement> lists;
3563 res = twCon.GetLists(this.Username, null, ref content);
3565 catch (Exception ex)
3567 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3572 case HttpStatusCode.OK:
3573 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3575 case HttpStatusCode.Unauthorized:
3576 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3577 return Properties.Resources.Unauthorized;
3578 case HttpStatusCode.BadRequest:
3579 return "Err:API Limits?";
3581 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3586 lists = MyCommon.CreateDataFromJson<List<TwitterDataModel.ListElementData>>(content)
3587 .Select(x => new ListElement(x, this));
3589 catch (SerializationException ex)
3591 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3592 return "Err:Json Parse Error(DataContractJsonSerializer)";
3594 catch (Exception ex)
3596 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3597 return "Err:Invalid Json!";
3603 res = twCon.GetListsSubscriptions(this.Username, null, ref content);
3605 catch (Exception ex)
3607 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3612 case HttpStatusCode.OK:
3613 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3615 case HttpStatusCode.Unauthorized:
3616 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3617 return Properties.Resources.Unauthorized;
3618 case HttpStatusCode.BadRequest:
3619 return "Err:API Limits?";
3621 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3626 lists = lists.Concat(MyCommon.CreateDataFromJson<List<TwitterDataModel.ListElementData>>(content)
3627 .Select(x => new ListElement(x, this)));
3629 catch (SerializationException ex)
3631 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3632 return "Err:Json Parse Error(DataContractJsonSerializer)";
3634 catch (Exception ex)
3636 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3637 return "Err:Invalid Json!";
3640 TabInformations.GetInstance().SubscribableLists = lists.ToList();
3644 public string DeleteList(string list_id)
3646 HttpStatusCode res = HttpStatusCode.BadRequest;
3651 res = twCon.DeleteListID(this.Username, list_id, ref content);
3655 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3660 case HttpStatusCode.OK:
3661 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3663 case HttpStatusCode.Unauthorized:
3664 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3665 return Properties.Resources.Unauthorized;
3666 case HttpStatusCode.BadRequest:
3667 return "Err:API Limits?";
3669 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3675 public string EditList(string list_id, string new_name, bool isPrivate, string description, ref ListElement list)
3677 HttpStatusCode res = HttpStatusCode.BadRequest;
3682 res = twCon.UpdateListID(this.Username, list_id, new_name, isPrivate, description, ref content);
3686 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3691 case HttpStatusCode.OK:
3692 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3694 case HttpStatusCode.Unauthorized:
3695 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3696 return Properties.Resources.Unauthorized;
3697 case HttpStatusCode.BadRequest:
3698 return "Err:API Limits?";
3700 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3705 var le = MyCommon.CreateDataFromJson<TwitterDataModel.ListElementData>(content);
3706 var newList = new ListElement(le, this);
3707 list.Description = newList.Description;
3708 list.Id = newList.Id;
3709 list.IsPublic = newList.IsPublic;
3710 list.MemberCount = newList.MemberCount;
3711 list.Name = newList.Name;
3712 list.SubscriberCount = newList.SubscriberCount;
3713 list.Slug = newList.Slug;
3714 list.Nickname = newList.Nickname;
3715 list.Username = newList.Username;
3716 list.UserId = newList.UserId;
3719 catch(SerializationException ex)
3721 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3722 return "Err:Json Parse Error(DataContractJsonSerializer)";
3726 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3727 return "Err:Invalid Json!";
3732 public string GetListMembers(string list_id, List<UserInfo> lists, ref long cursor)
3734 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3736 HttpStatusCode res = HttpStatusCode.BadRequest;
3742 res = twCon.GetListMembers(this.Username, list_id, cursor, ref content);
3746 return "Err:" + ex.Message;
3751 case HttpStatusCode.OK:
3752 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3754 case HttpStatusCode.Unauthorized:
3755 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3756 return Properties.Resources.Unauthorized;
3757 case HttpStatusCode.BadRequest:
3758 return "Err:API Limits?";
3760 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3765 var users = MyCommon.CreateDataFromJson<TwitterDataModel.Users>(content);
3766 Array.ForEach<TwitterDataModel.User>(
3768 new Action<TwitterDataModel.User>(u => lists.Add(new UserInfo(u))));
3769 cursor = users.NextCursor;
3772 catch(SerializationException ex)
3774 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3775 return "Err:Json Parse Error(DataContractJsonSerializer)";
3779 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3780 return "Err:Invalid Json!";
3784 public string CreateListApi(string listName, bool isPrivate, string description)
3786 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3788 HttpStatusCode res = HttpStatusCode.BadRequest;
3793 res = twCon.CreateLists(listName, isPrivate, description, ref content);
3797 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3802 case HttpStatusCode.OK:
3803 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3805 case HttpStatusCode.Unauthorized:
3806 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3807 return Properties.Resources.Unauthorized;
3808 case HttpStatusCode.BadRequest:
3809 return "Err:API Limits?";
3811 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3816 var le = MyCommon.CreateDataFromJson<TwitterDataModel.ListElementData>(content);
3817 TabInformations.GetInstance().SubscribableLists.Add(new ListElement(le, this));
3820 catch(SerializationException ex)
3822 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
3823 return "Err:Json Parse Error(DataContractJsonSerializer)";
3827 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
3828 return "Err:Invalid Json!";
3832 public string ContainsUserAtList(string listId, string user, ref bool value)
3836 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
3838 HttpStatusCode res = HttpStatusCode.BadRequest;
3843 res = this.twCon.ShowListMember(listId, user, ref content);
3847 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3852 case HttpStatusCode.OK:
3853 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3855 case HttpStatusCode.Unauthorized:
3856 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3857 return Properties.Resources.Unauthorized;
3858 case HttpStatusCode.BadRequest:
3859 return "Err:API Limits?";
3860 case HttpStatusCode.NotFound:
3864 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3869 var u = MyCommon.CreateDataFromJson<TwitterDataModel.User>(content);
3880 public string AddUserToList(string listId, string user)
3883 HttpStatusCode res = HttpStatusCode.BadRequest;
3887 res = twCon.CreateListMembers(listId, user, ref content);
3891 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3896 case HttpStatusCode.OK:
3897 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3899 case HttpStatusCode.Unauthorized:
3900 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3901 return Properties.Resources.Unauthorized;
3902 case HttpStatusCode.BadRequest:
3903 return "Err:" + GetErrorMessageJson(content);
3905 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3911 public string RemoveUserToList(string listId, string user)
3915 HttpStatusCode res = HttpStatusCode.BadRequest;
3919 res = twCon.DeleteListMembers(listId, user, ref content);
3923 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
3928 case HttpStatusCode.OK:
3929 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
3931 case HttpStatusCode.Unauthorized:
3932 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
3933 return Properties.Resources.Unauthorized;
3934 case HttpStatusCode.BadRequest:
3935 return "Err:" + GetErrorMessageJson(content);
3937 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
3945 public int fromIndex { get; set; }
3946 public int toIndex { get; set; }
3947 public range(int fromIndex, int toIndex)
3949 this.fromIndex = fromIndex;
3950 this.toIndex = toIndex;
3953 public string CreateHtmlAnchor(string Text, List<string> AtList, Dictionary<string, string> media)
3955 if (Text == null) return null;
3956 var retStr = Text.Replace(">", "<<<<<tweenだいなり>>>>>").Replace("<", "<<<<<tweenしょうなり>>>>>");
3958 //const string url_valid_domain = "(?<domain>(?:[^\p{P}\s][\.\-_](?=[^\p{P}\s])|[^\p{P}\s]){1,}\.[a-z]{2,}(?::[0-9]+)?)"
3959 //const string url_valid_general_path_chars = "[a-z0-9!*';:=+$/%#\[\]\-_,~]"
3960 //const string url_balance_parens = "(?:\(" + url_valid_general_path_chars + "+\))"
3961 //const string url_valid_url_path_ending_chars = "(?:[a-z0-9=_#/\-\+]+|" + url_balance_parens + ")"
3962 //const string pth = "(?:" + url_balance_parens +
3963 // "|@" + url_valid_general_path_chars + "+/" +
3964 // "|[.,]?" + url_valid_general_path_chars + "+" +
3966 //const string pth2 = "(/(?:" +
3967 // pth + "+" + url_valid_url_path_ending_chars + "|" +
3968 // pth + "+" + url_valid_url_path_ending_chars + "?|" +
3969 // url_valid_url_path_ending_chars +
3971 //const string qry = "(?<query>\?[a-z0-9!*'();:&=+$/%#\[\]\-_.,~]*[a-z0-9_&=#])?"
3972 //const string rgUrl = "(?<before>(?:[^\""':!=#]|^|\:/))" +
3973 // "(?<url>(?<protocol>https?://)" +
3974 // url_valid_domain +
3978 //const string rgUrl = "(?<before>(?:[^\""':!=#]|^|\:/))" +
3979 // "(?<url>(?<protocol>https?://|www\.)" +
3980 // url_valid_domain +
3985 retStr = Regex.Replace(retStr,
3987 new MatchEvaluator((Match mu) =>
3989 var sb = new StringBuilder(mu.Result("${before}<a href=\""));
3990 //if (mu.Result("${protocol}").StartsWith("w", StringComparison.OrdinalIgnoreCase))
3991 // sb.Append("http://");
3993 var url = mu.Result("${url}");
3994 var title = ShortUrl.ResolveMedia(url, true);
3997 title = ShortUrl.ResolveMedia(title, false);
3999 sb.Append(url + "\" title=\"" + title + "\">").Append(url).Append("</a>");
4000 if (media != null && !media.ContainsKey(url)) media.Add(url, title);
4001 return sb.ToString();
4003 RegexOptions.IgnoreCase);
4006 retStr = Regex.Replace(retStr,
4007 @"(^|[^a-zA-Z0-9_/])([@@]+)([a-zA-Z0-9_]{1,20}/[a-zA-Z][a-zA-Z0-9\p{IsLatin-1Supplement}\-]{0,79})",
4008 "$1$2<a href=\"/$3\">$3</a>");
4010 var m = Regex.Match(retStr, "(^|[^a-zA-Z0-9_])[@@]([a-zA-Z0-9_]{1,20})");
4013 if (!AtList.Contains(m.Result("$2").ToLower())) AtList.Add(m.Result("$2").ToLower());
4017 retStr = Regex.Replace(retStr,
4018 "(^|[^a-zA-Z0-9_/])([@@])([a-zA-Z0-9_]{1,20})",
4019 "$1$2<a href=\"/$3\">$3</a>");
4022 var anchorRange = new List<range>();
4023 for (int i = 0; i < retStr.Length; i++)
4025 var index = retStr.IndexOf("<a ", i);
4026 if (index > -1 && index < retStr.Length)
4029 var toIndex = retStr.IndexOf("</a>", index);
4032 anchorRange.Add(new range(index, toIndex + 3));
4037 //retStr = Regex.Replace(retStr,
4038 // "(^|[^a-zA-Z0-9/&])([##])([0-9a-zA-Z_]*[a-zA-Z_]+[a-zA-Z0-9_\xc0-\xd6\xd8-\xf6\xf8-\xff]*)",
4039 // new MatchEvaluator(Function(mh As Match)
4040 // foreach (var rng in anchorRange)
4042 // if (mh.Index >= rng.fromIndex &&
4043 // mh.Index <= rng.toIndex) return mh.Result("$0");
4045 // if (IsNumeric(mh.Result("$3"))) return mh.Result("$0");
4048 // _hashList.Add("#" + mh.Result("$3"))
4050 // return mh.Result("$1") + "<a href=\"" + _protocol + "twitter.com/search?q=%23" + mh.Result("$3") + "\">" + mh.Result("$2$3") + "</a>";
4052 // RegexOptions.IgnoreCase)
4053 retStr = Regex.Replace(retStr,
4055 new MatchEvaluator(mh =>
4057 foreach (var rng in anchorRange)
4059 if (mh.Index >= rng.fromIndex &&
4060 mh.Index <= rng.toIndex) return mh.Result("$0");
4064 _hashList.Add("#" + mh.Result("$3"));
4066 return mh.Result("$1") + "<a href=\"" + _protocol + "twitter.com/search?q=%23" + mh.Result("$3") + "\">" + mh.Result("$2$3") + "</a>";
4068 RegexOptions.IgnoreCase);
4071 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>");
4073 retStr = retStr.Replace("<<<<<tweenだいなり>>>>>", ">").Replace("<<<<<tweenしょうなり>>>>>", "<");
4075 //retStr = AdjustHtml(ShortUrl.Resolve(PreProcessUrl(retStr), true)) //IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
4076 retStr = AdjustHtml(PreProcessUrl(retStr)); //IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
4080 private class EntityInfo
4082 public int StartIndex { get; set; }
4083 public int EndIndex { get; set; }
4084 public string Text { get; set; }
4085 public string Html { get; set; }
4086 public string Display { get; set; }
4088 public string CreateHtmlAnchor(ref string Text, List<string> AtList, TwitterDataModel.Entities entities, Dictionary<string, string> media)
4092 if (entities != null)
4094 var etInfo = new SortedList<int, EntityInfo>();
4096 if (entities.Urls != null)
4098 foreach (var ent in entities.Urls)
4100 if (string.IsNullOrEmpty(ent.DisplayUrl))
4102 etInfo.Add(ent.Indices[0],
4103 new EntityInfo {StartIndex = ent.Indices[0],
4104 EndIndex = ent.Indices[1],
4106 Html = "<a href=\"" + ent.Url + "\">" + ent.Url + "</a>"});
4110 var expanded = ShortUrl.ResolveMedia(ent.ExpandedUrl, false);
4111 etInfo.Add(ent.Indices[0],
4112 new EntityInfo {StartIndex = ent.Indices[0],
4113 EndIndex = ent.Indices[1],
4115 Html = "<a href=\"" + ent.Url + "\" title=\"" + expanded + "\">" + ent.DisplayUrl + "</a>",
4116 Display = ent.DisplayUrl});
4117 if (media != null && !media.ContainsKey(ent.Url)) media.Add(ent.Url, expanded);
4121 if (entities.Hashtags != null)
4123 foreach (var ent in entities.Hashtags)
4125 var hash = Text.Substring(ent.Indices[0], ent.Indices[1] - ent.Indices[0]);
4126 etInfo.Add(ent.Indices[0],
4127 new EntityInfo {StartIndex = ent.Indices[0],
4128 EndIndex = ent.Indices[1],
4130 Html = "<a href=\"" + _protocol + "twitter.com/search?q=%23" + ent.Text + "\">" + hash + "</a>"});
4133 _hashList.Add("#" + ent.Text);
4137 if (entities.UserMentions != null)
4139 foreach (var ent in entities.UserMentions)
4141 var screenName = Text.Substring(ent.Indices[0] + 1, ent.Indices[1] - ent.Indices[0] - 1);
4142 etInfo.Add(ent.Indices[0] + 1,
4143 new EntityInfo {StartIndex = ent.Indices[0] + 1,
4144 EndIndex = ent.Indices[1],
4145 Text = ent.ScreenName,
4146 Html = "<a href=\"/" + ent.ScreenName + "\">" + screenName + "</a>"});
4147 if (!AtList.Contains(ent.ScreenName.ToLower())) AtList.Add(ent.ScreenName.ToLower());
4150 if (entities.Media != null)
4152 foreach (var ent in entities.Media)
4154 if (ent.Type == "photo")
4156 etInfo.Add(ent.Indices[0],
4157 new EntityInfo {StartIndex = ent.Indices[0],
4158 EndIndex = ent.Indices[1],
4160 Html = "<a href=\"" + ent.Url + "\" title=\"" + ent.ExpandedUrl + "\">" + ent.DisplayUrl + "</a>",
4161 Display = ent.DisplayUrl});
4162 if (media != null && !media.ContainsKey(ent.Url)) media.Add(ent.Url, ent.MediaUrl);
4166 if (etInfo.Count > 0)
4172 foreach (var et in etInfo)
4174 ret += Text.Substring(idx, et.Key - idx) + et.Value.Html;
4175 idx = et.Value.EndIndex;
4177 ret += Text.Substring(idx);
4179 catch(ArgumentOutOfRangeException)
4181 //Twitterのバグで不正なエンティティ(Index指定範囲が重なっている)が返ってくる場合の対応
4184 if (media != null) media.Clear();
4189 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>");
4190 ret = AdjustHtml(ShortUrl.Resolve(PreProcessUrl(ret), false)); //IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
4196 private void CreateSource(PostClass post)
4198 if (string.IsNullOrEmpty(post.Source)) return;
4200 if (post.Source.StartsWith("<"))
4202 if (!post.Source.Contains("</a>"))
4204 post.Source += "</a>";
4206 var mS = Regex.Match(post.Source, ">(?<source>.+)<");
4209 post.SourceHtml = ShortUrl.Resolve(PreProcessUrl(post.Source), false);
4210 post.Source = WebUtility.HtmlDecode(mS.Result("${source}"));
4215 post.SourceHtml = "";
4220 if (post.Source == "web")
4222 post.SourceHtml = Properties.Resources.WebSourceString;
4224 else if (post.Source == "Keitai Mail")
4226 post.SourceHtml = Properties.Resources.KeitaiMailSourceString;
4230 post.SourceHtml = post.Source;
4235 public TwitterApiStatus GetInfoApi10()
4237 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return null;
4239 if (MyCommon._endingFlag) return null;
4241 HttpStatusCode res = HttpStatusCode.BadRequest;
4245 res = twCon.RateLimitStatus(ref content);
4249 this.ResetApiStatus();
4253 if (res != HttpStatusCode.OK) return null;
4257 var limit = MyCommon.CreateDataFromJson<TwitterDataModel.RateLimitStatus>(content);
4258 MyCommon.TwitterApiInfo.UpdateFromApi(limit);
4260 return MyCommon.TwitterApiInfo;
4264 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
4265 MyCommon.TwitterApiInfo.Reset();
4270 public TwitterApiStatus11 GetInfoApi11()
4272 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return null;
4274 if (MyCommon._endingFlag) return null;
4276 HttpStatusCode res = HttpStatusCode.BadRequest;
4280 res = twCon.RateLimitStatus(ref content);
4284 this.ResetApiStatus();
4288 if (res != HttpStatusCode.OK) return null;
4292 MyCommon.TwitterApiInfo11.UpdateFromJson(content);
4293 return MyCommon.TwitterApiInfo11;
4295 catch (Exception ex)
4297 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
4298 MyCommon.TwitterApiInfo.Reset();
4303 public string GetBlockUserIds()
4305 if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
4307 HttpStatusCode res = HttpStatusCode.BadRequest;
4312 res = twCon.GetBlockUserIds(ref content);
4316 return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
4321 case HttpStatusCode.OK:
4322 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
4324 case HttpStatusCode.Unauthorized:
4325 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
4326 return Properties.Resources.Unauthorized;
4327 case HttpStatusCode.BadRequest:
4328 return "Err:API Limits?";
4330 return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
4335 var Ids = MyCommon.CreateDataFromJson<List<long>>(content);
4336 if (Ids.Contains(this.UserId)) Ids.Remove(this.UserId);
4337 TabInformations.GetInstance().BlockIds.AddRange(Ids);
4340 catch(SerializationException ex)
4342 MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
4343 return "Err:Json Parse Error(DataContractJsonSerializer)";
4347 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
4348 return "Err:Invalid Json!";
4353 public string[] GetHashList()
4358 hashArray = _hashList.ToArray();
4364 public string AccessToken
4368 return twCon.AccessToken;
4372 public string AccessTokenSecret
4376 return twCon.AccessTokenSecret;
4380 #region "UserStream"
4381 private string trackWord_ = "";
4382 public string TrackWord
4393 private bool allAtReply_ = false;
4394 public bool AllAtReply
4402 allAtReply_ = value;
4406 public event Action NewPostFromStream;
4407 public event Action UserStreamStarted;
4408 public event Action UserStreamStopped;
4409 public event Action<long> PostDeleted;
4410 public event Action<FormattedEvent> UserStreamEventReceived;
4411 private DateTime _lastUserstreamDataReceived;
4412 private TwitterUserstream userStream;
4414 public class FormattedEvent
4416 public MyCommon.EVENTTYPE Eventtype { get; set; }
4417 public DateTime CreatedAt { get; set; }
4418 public string Event { get; set; }
4419 public string Username { get; set; }
4420 public string Target { get; set; }
4421 public Int64 Id { get; set; }
4422 public bool IsMe { get; set; }
4425 public List<FormattedEvent> storedEvent_ = new List<FormattedEvent>();
4426 public List<FormattedEvent> StoredEvent
4430 return storedEvent_;
4434 storedEvent_ = value;
4438 private class EventTypeTableElement
4441 public MyCommon.EVENTTYPE Type;
4443 public EventTypeTableElement(string name, MyCommon.EVENTTYPE type)
4450 private EventTypeTableElement[] EventTable = {
4451 new EventTypeTableElement("favorite", MyCommon.EVENTTYPE.Favorite),
4452 new EventTypeTableElement("unfavorite", MyCommon.EVENTTYPE.Unfavorite),
4453 new EventTypeTableElement("follow", MyCommon.EVENTTYPE.Follow),
4454 new EventTypeTableElement("list_member_added", MyCommon.EVENTTYPE.ListMemberAdded),
4455 new EventTypeTableElement("list_member_removed", MyCommon.EVENTTYPE.ListMemberRemoved),
4456 new EventTypeTableElement("block", MyCommon.EVENTTYPE.Block),
4457 new EventTypeTableElement("unblock", MyCommon.EVENTTYPE.Unblock),
4458 new EventTypeTableElement("user_update", MyCommon.EVENTTYPE.UserUpdate),
4459 new EventTypeTableElement("deleted", MyCommon.EVENTTYPE.Deleted),
4460 new EventTypeTableElement("list_created", MyCommon.EVENTTYPE.ListCreated),
4461 new EventTypeTableElement("list_updated", MyCommon.EVENTTYPE.ListUpdated),
4462 new EventTypeTableElement("unfollow", MyCommon.EVENTTYPE.Unfollow),
4465 public MyCommon.EVENTTYPE EventNameToEventType(string EventName)
4467 return (from tbl in EventTable where tbl.Name.Equals(EventName) select tbl.Type).FirstOrDefault();
4470 public bool IsUserstreamDataReceived
4474 return DateTime.Now.Subtract(this._lastUserstreamDataReceived).TotalSeconds < 31;
4478 private void userStream_StatusArrived(string line)
4480 this._lastUserstreamDataReceived = DateTime.Now;
4481 if (string.IsNullOrEmpty(line)) return;
4487 using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(line), XmlDictionaryReaderQuotas.Max))
4489 var xElm = XElement.Load(jsonReader);
4490 if (xElm.Element("friends") != null)
4492 Debug.WriteLine("friends");
4495 else if (xElm.Element("delete") != null)
4497 Debug.WriteLine("delete");
4499 if (xElm.Element("delete").Element("direct_message") != null &&
4500 xElm.Element("delete").Element("direct_message").Element("id") != null)
4503 long.TryParse(xElm.Element("delete").Element("direct_message").Element("id").Value, out id);
4504 if (PostDeleted != null)
4509 else if (xElm.Element("delete").Element("status") != null &&
4510 xElm.Element("delete").Element("status").Element("id") != null)
4513 long.TryParse(xElm.Element("delete").Element("status").Element("id").Value, out id);
4514 if (PostDeleted != null)
4521 MyCommon.TraceOut("delete:" + line);
4524 for (int i = this.StoredEvent.Count - 1; i >= 0; i--)
4526 var sEvt = this.StoredEvent[i];
4527 if (sEvt.Id == id && (sEvt.Event == "favorite" || sEvt.Event == "unfavorite"))
4529 this.StoredEvent.RemoveAt(i);
4534 else if (xElm.Element("limit") != null)
4536 Debug.WriteLine(line);
4539 else if (xElm.Element("event") != null)
4541 Debug.WriteLine("event: " + xElm.Element("event").Value);
4542 CreateEventFromJson(line);
4545 else if (xElm.Element("direct_message") != null)
4547 Debug.WriteLine("direct_message");
4550 else if (xElm.Element("scrub_geo") != null)
4554 TabInformations.GetInstance().ScrubGeoReserve(long.Parse(xElm.Element("scrub_geo").Element("user_id").Value),
4555 long.Parse(xElm.Element("scrub_geo").Element("up_to_status_id").Value));
4559 MyCommon.TraceOut("scrub_geo:" + line);
4565 var res = new StringBuilder();
4573 CreateDirectMessagesFromJson(res.ToString(), MyCommon.WORKERTYPE.UserStream, false);
4578 CreatePostsFromJson(res.ToString(), MyCommon.WORKERTYPE.Timeline, null, false, 0, ref dummy);
4581 catch(NullReferenceException)
4583 MyCommon.TraceOut("NullRef StatusArrived: " + line);
4586 if (NewPostFromStream != null)
4588 NewPostFromStream();
4592 private void CreateEventFromJson(string content)
4594 TwitterDataModel.EventData eventData = null;
4597 eventData = MyCommon.CreateDataFromJson<TwitterDataModel.EventData>(content);
4599 catch(SerializationException ex)
4601 MyCommon.TraceOut(ex, "Event Serialize Exception!" + Environment.NewLine + content);
4605 MyCommon.TraceOut(ex, "Event Exception!" + Environment.NewLine + content);
4608 var evt = new FormattedEvent();
4609 evt.CreatedAt = MyCommon.DateTimeParse(eventData.CreatedAt);
4610 evt.Event = eventData.Event;
4611 evt.Username = eventData.Source.ScreenName;
4612 evt.IsMe = evt.Username.ToLower().Equals(this.Username.ToLower());
4613 evt.Eventtype = EventNameToEventType(evt.Event);
4614 switch (eventData.Event)
4616 case "access_revoked":
4619 if (eventData.Target.ScreenName.ToLower().Equals(_uname))
4621 if (!this.followerId.Contains(eventData.Source.Id)) this.followerId.Add(eventData.Source.Id);
4625 return; //Block後のUndoをすると、SourceとTargetが逆転したfollowイベントが帰ってくるため。
4633 evt.Target = "@" + eventData.TargetObject.User.ScreenName + ":" + WebUtility.HtmlDecode(eventData.TargetObject.Text);
4634 evt.Id = eventData.TargetObject.Id;
4635 if (AppendSettingDialog.Instance.IsRemoveSameEvent)
4637 if (StoredEvent.Any(ev =>
4639 return ev.Username == evt.Username && ev.Eventtype == evt.Eventtype && ev.Target == evt.Target;
4642 if (TabInformations.GetInstance().ContainsKey(eventData.TargetObject.Id))
4644 var post = TabInformations.GetInstance()[eventData.TargetObject.Id];
4645 if (eventData.Event == "favorite")
4647 if (evt.Username.ToLower().Equals(_uname))
4650 TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Add(post.StatusId, post.IsRead, false);
4654 post.FavoritedCount++;
4655 if (!TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Contains(post.StatusId))
4657 if (AppendSettingDialog.Instance.FavEventUnread && post.IsRead)
4659 post.IsRead = false;
4661 TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Add(post.StatusId, post.IsRead, false);
4665 if (AppendSettingDialog.Instance.FavEventUnread)
4667 TabInformations.GetInstance().SetRead(false, TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).TabName, TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).IndexOf(post.StatusId));
4674 if (evt.Username.ToLower().Equals(_uname))
4680 post.FavoritedCount--;
4681 if (post.FavoritedCount < 0) post.FavoritedCount = 0;
4686 case "list_member_added":
4687 case "list_member_removed":
4688 case "list_updated":
4689 evt.Target = eventData.TargetObject.FullName;
4692 if (!TabInformations.GetInstance().BlockIds.Contains(eventData.Target.Id)) TabInformations.GetInstance().BlockIds.Add(eventData.Target.Id);
4696 if (TabInformations.GetInstance().BlockIds.Contains(eventData.Target.Id)) TabInformations.GetInstance().BlockIds.Remove(eventData.Target.Id);
4702 case "list_created":
4706 MyCommon.TraceOut("Unknown Event:" + evt.Event + Environment.NewLine + content);
4709 this.StoredEvent.Insert(0, evt);
4710 if (UserStreamEventReceived != null)
4712 UserStreamEventReceived(evt);
4716 private void userStream_Started()
4718 if (UserStreamStarted != null)
4720 UserStreamStarted();
4724 private void userStream_Stopped()
4726 if (UserStreamStopped != null)
4728 UserStreamStopped();
4732 public bool UserStreamEnabled
4736 return userStream == null ? false : userStream.Enabled;
4740 public void StartUserStream()
4742 if (userStream != null)
4746 userStream = new TwitterUserstream(twCon);
4747 userStream.StatusArrived += userStream_StatusArrived;
4748 userStream.Started += userStream_Started;
4749 userStream.Stopped += userStream_Stopped;
4750 userStream.Start(this.AllAtReply, this.TrackWord);
4753 public void StopUserStream()
4755 if (userStream != null) userStream.Dispose();
4757 if (!MyCommon._endingFlag)
4759 if (UserStreamStopped != null)
4761 UserStreamStopped();
4766 public void ReconnectUserStream()
4768 if (userStream != null)
4770 this.StartUserStream();
4774 private class TwitterUserstream : IDisposable
4776 public event Action<string> StatusArrived;
4777 public event Action Stopped;
4778 public event Action Started;
4779 private HttpTwitter twCon;
4781 private Thread _streamThread;
4782 private bool _streamActive;
4784 private bool _allAtreplies = false;
4785 private string _trackwords = "";
4787 public TwitterUserstream(HttpTwitter twitterConnection)
4789 twCon = (HttpTwitter)twitterConnection.Clone();
4792 public void Start(bool allAtReplies, string trackwords)
4794 this.AllAtReplies = allAtReplies;
4795 this.TrackWords = trackwords;
4796 _streamActive = true;
4797 if (_streamThread != null && _streamThread.IsAlive) return;
4798 _streamThread = new Thread(UserStreamLoop);
4799 _streamThread.Name = "UserStreamReceiver";
4800 _streamThread.IsBackground = true;
4801 _streamThread.Start();
4808 return _streamActive;
4812 public bool AllAtReplies
4816 return _allAtreplies;
4820 _allAtreplies = value;
4824 public string TrackWords
4832 _trackwords = value;
4836 private void UserStreamLoop()
4842 StreamReader sr = null;
4845 if (!MyCommon.IsNetworkAvailable())
4851 if (Started != null)
4855 var res = twCon.UserStream(ref st, _allAtreplies, _trackwords, MyCommon.GetUserAgentString());
4859 case HttpStatusCode.OK:
4860 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
4862 case HttpStatusCode.Unauthorized:
4863 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
4871 //MyCommon.TraceOut("Stop:stream is null")
4875 sr = new StreamReader(st);
4877 while (_streamActive && !sr.EndOfStream && Twitter.AccountState == MyCommon.ACCOUNT_STATE.Valid)
4879 if (StatusArrived != null)
4881 StatusArrived(sr.ReadLine());
4883 //this.LastTime = Now;
4886 if (sr.EndOfStream || Twitter.AccountState == MyCommon.ACCOUNT_STATE.Invalid)
4889 //MyCommon.TraceOut("Stop:EndOfStream")
4894 catch(WebException ex)
4896 if (ex.Status == WebExceptionStatus.Timeout)
4898 sleepSec = 30; //MyCommon.TraceOut("Stop:Timeout")
4900 else if (ex.Response != null && (int)((HttpWebResponse)ex.Response).StatusCode == 420)
4902 //MyCommon.TraceOut("Stop:Connection Limit")
4908 //MyCommon.TraceOut("Stop:WebException " + ex.Status.ToString())
4911 catch(ThreadAbortException)
4918 //MyCommon.TraceOut("Stop:IOException with Active." + Environment.NewLine + ex.Message)
4920 catch(ArgumentException ex)
4922 //System.ArgumentException: ストリームを読み取れませんでした。
4923 //サーバー側もしくは通信経路上で切断された場合?タイムアウト頻発後発生
4925 MyCommon.TraceOut(ex, "Stop:ArgumentException");
4929 MyCommon.TraceOut("Stop:Exception." + Environment.NewLine + ex.Message);
4930 MyCommon.ExceptionOut(ex);
4937 if (Stopped != null)
4942 twCon.RequestAbort();
4943 if (sr != null) sr.Close();
4947 while (_streamActive && ms < sleepSec * 1000)
4955 } while (this._streamActive);
4959 if (Stopped != null)
4964 MyCommon.TraceOut("Stop:EndLoop");
4967 #region "IDisposable Support"
4968 private bool disposedValue; // 重複する呼び出しを検出するには
4971 protected virtual void Dispose(bool disposing)
4973 if (!this.disposedValue)
4977 // TODO: マネージ状態を破棄します (マネージ オブジェクト)。
4978 _streamActive = false;
4979 if (_streamThread != null && _streamThread.IsAlive)
4981 _streamThread.Abort();
4985 // TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下の Finalize() をオーバーライドします。
4986 // TODO: 大きなフィールドを null に設定します。
4988 this.disposedValue = true;
4991 // TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードがある場合にのみ、Finalize() をオーバーライドします。
4992 //protected Overrides void Finalize()
4994 // // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
4996 // MyBase.Finalize()
4999 // このコードは、破棄可能なパターンを正しく実装できるように Visual Basic によって追加されました。
5000 public void Dispose()
5002 // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
5004 GC.SuppressFinalize(this);
5011 #region "IDisposable Support"
5012 private bool disposedValue; // 重複する呼び出しを検出するには
5015 protected virtual void Dispose(bool disposing)
5017 if (!this.disposedValue)
5021 // TODO: マネージ状態を破棄します (マネージ オブジェクト)。
5022 this.StopUserStream();
5025 // TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下の Finalize() をオーバーライドします。
5026 // TODO: 大きなフィールドを null に設定します。
5028 this.disposedValue = true;
5031 // TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードがある場合にのみ、Finalize() をオーバーライドします。
5032 //protected Overrides void Finalize()
5034 // // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
5036 // MyBase.Finalize()
5039 // このコードは、破棄可能なパターンを正しく実装できるように Visual Basic によって追加されました。
5040 public void Dispose()
5042 // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
5044 GC.SuppressFinalize(this);