public void UnreadCount_Test() { var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline }; tab.UnreadManage = true; // 未読なし Assert.Equal(0, tab.UnreadCount); tab.AddPostToInnerStorage(new PostClass { StatusId = 100L, IsRead = false, // 未読 }); tab.AddSubmit(); Assert.Equal(1, tab.UnreadCount); tab.AddPostToInnerStorage(new PostClass { StatusId = 50L, IsRead = true, // 既読 }); tab.AddSubmit(); Assert.Equal(1, tab.UnreadCount); }
public void OldestUnreadId_DisabledTest() { var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline }; // 未読表示無効 tab.UnreadManage = false; tab.AddPostToInnerStorage(new PostClass { StatusId = 100L, IsRead = false, // 未読 }); tab.AddSubmit(); Assert.Equal(-1L, tab.OldestUnreadId); }
public void NextUnreadId_SortByIdAscTest() { var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline }; tab.UnreadManage = true; // ID の昇順でソート tab.SetSortMode(ComparerMode.Id, SortOrder.Ascending); // 画面には上から 100 → 200 → 300 の順に並ぶ tab.AddPostToInnerStorage(new PostClass { StatusId = 100L, IsRead = false }); tab.AddPostToInnerStorage(new PostClass { StatusId = 200L, IsRead = false }); tab.AddPostToInnerStorage(new PostClass { StatusId = 300L, IsRead = false }); tab.AddSubmit(); // 昇順/降順に関わらず、ID の小さい順に未読の ID を返す Assert.Equal(100L, tab.NextUnreadId); }
public void RemoveFilter_Test() { var tab = new TabClass(); var filter = new PostFilterRule(); tab.FilterArray = new[] { filter }; tab.FilterModified = false; tab.RemoveFilter(filter); Assert.Empty(tab.FilterArray); Assert.True(tab.FilterModified); }
public void OnFilterModified_Test() { var tab = new TabClass(); var filter = new PostFilterRule(); tab.FilterArray = new[] { filter }; tab.FilterModified = false; // TabClass に紐付いているフィルタを変更 filter.FilterSource = "OpenTween"; Assert.True(tab.FilterModified); }
private string CreatePostsFromPhoenixSearch(string content, MyCommon.WORKERTYPE gType, TabClass tab, bool read, int count, ref long minimumId, ref string nextPageQuery) { TwitterDataModel.SearchResult items; try { items = MyCommon.CreateDataFromJson<TwitterDataModel.SearchResult>(content); } catch(SerializationException ex) { MyCommon.TraceOut(ex.Message + Environment.NewLine + content); return "Json Parse Error(DataContractJsonSerializer)"; } catch(Exception ex) { MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content); return "Invalid Json!"; } nextPageQuery = items.NextPage; foreach (var status in items.Statuses) { PostClass post = null; post = CreatePostsFromStatusData(status); if (post == null) continue; if (minimumId > post.StatusId) minimumId = post.StatusId; //二重取得回避 lock (LockObj) { if (tab == null) { if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue; } else { if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue; } } post.IsRead = read; if (post.IsMe && !read && _readOwnPost) post.IsRead = true; if (tab != null) post.RelTabName = tab.TabName; //非同期アイコン取得&StatusDictionaryに追加 TabInformations.GetInstance().AddPost(post); } return string.IsNullOrEmpty(items.ErrMsg) ? string.Empty : "Err:" + items.ErrMsg; }
private async Task FavRemoveAsync(IReadOnlyList<long> statusIds, TabClass tab) { await this.workerSemaphore.WaitAsync(); try { var progress = new Progress<string>(x => this.StatusLabel.Text = x); await this.FavRemoveAsyncInternal(progress, this.workerCts.Token, statusIds, tab); } catch (WebApiException ex) { this._myStatusError = true; this.StatusLabel.Text = ex.Message; } finally { this.workerSemaphore.Release(); } }
public string GetSearch(bool read, TabClass tab, bool more) { if (MyCommon._endingFlag) return string.Empty; HttpStatusCode res; var content = string.Empty; var page = 0; var sinceId = 0; var count = 100; if (AppendSettingDialog.Instance.UseAdditionalCount && AppendSettingDialog.Instance.SearchCountApi != 0) { count = AppendSettingDialog.Instance.SearchCountApi; } else { count = AppendSettingDialog.Instance.CountApi; } if (more) { page = tab.GetSearchPage(count); } else { sinceId = (int)tab.SinceId; } try { // TODO:一時的に40>100件に 件数変更UI作成の必要あり res = twCon.Search(tab.SearchWords, tab.SearchLang, count, page, sinceId, ref content); } catch(Exception ex) { return "Err:" + ex.Message; } switch (res) { case HttpStatusCode.BadRequest: return "Invalid query"; case HttpStatusCode.NotFound: return "Invalid query"; case HttpStatusCode.PaymentRequired: //API Documentには420と書いてあるが、該当コードがないので402にしてある return "Search API Limit?"; case HttpStatusCode.OK: break; default: return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")"; } if (!TabInformations.GetInstance().ContainsTab(tab)) return string.Empty; content = Regex.Replace(content, @"[\x00-\x1f-[\x0a\x0d]]+", " "); var xdoc = new XmlDocument(); try { xdoc.LoadXml(content); } catch(Exception ex) { MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content); return "Invalid ATOM!"; } var nsmgr = new XmlNamespaceManager(xdoc.NameTable); nsmgr.AddNamespace("search", "http://www.w3.org/2005/Atom"); nsmgr.AddNamespace("twitter", "http://api.twitter.com/"); nsmgr.AddNamespace("georss", "http://www.georss.org/georss"); foreach (var xentryNode in xdoc.DocumentElement.SelectNodes("/search:feed/search:entry", nsmgr)) { var xentry = (XmlElement)xentryNode; var post = new PostClass(); try { post.StatusId = long.Parse(xentry["id"].InnerText.Split(':')[2]); if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue; post.CreatedAt = DateTime.Parse(xentry["published"].InnerText); //本文 post.TextFromApi = xentry["title"].InnerText; //Source取得(htmlの場合は、中身を取り出し) post.Source = xentry["twitter:source"].InnerText; post.InReplyToStatusId = 0; post.InReplyToUser = string.Empty; post.InReplyToUserId = 0; post.IsFav = false; // Geoが勝手に付加されるバグがいっこうに修正されないので暫定的にGeo情報を無視する if (xentry["twitter:geo"].HasChildNodes) { var pnt = ((XmlElement)xentry.SelectSingleNode("twitter:geo/georss:point", nsmgr)).InnerText.Split(' '); post.PostGeo = new PostClass.StatusGeo {Lat = Double.Parse(pnt[0]), Lng = Double.Parse(pnt[1])}; } //以下、ユーザー情報 var xUentry = (XmlElement)xentry.SelectSingleNode("./search:author", nsmgr); post.UserId = 0; post.ScreenName = xUentry["name"].InnerText.Split(' ')[0].Trim(); post.Nickname = xUentry["name"].InnerText.Substring(post.ScreenName.Length).Trim(); if (post.Nickname.Length > 2) { post.Nickname = post.Nickname.Substring(1, post.Nickname.Length - 2); } else { post.Nickname = post.ScreenName; } post.ImageUrl = ((XmlElement)xentry.SelectSingleNode("./search:link[@type='image/png']", nsmgr)).GetAttribute("href"); post.IsProtect = false; post.IsMe = post.ScreenName.ToLower().Equals(_uname); //HTMLに整形 post.Text = CreateHtmlAnchor(HttpUtility.HtmlEncode(post.TextFromApi), post.ReplyToList, post.Media); post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi); //Source整形 CreateSource(ref post); post.IsRead = read; post.IsReply = post.ReplyToList.Contains(_uname); post.IsExcludeReply = false; post.IsOwl = false; if (post.IsMe && !read && _readOwnPost) post.IsRead = true; post.IsDm = false; post.RelTabName = tab.TabName; if (!more && post.StatusId > tab.SinceId) tab.SinceId = post.StatusId; } catch(Exception ex) { MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content); continue; } //this._dIcon.Add(post.ImageUrl, null); TabInformations.GetInstance().AddPost(post); } // TODO // 遡るための情報max_idやnext_pageの情報を保持する #if UNDEFINED__ var xNode = xdoc.DocumentElement.SelectSingleNode("/search:feed/twitter:warning", nsmgr); if (xNode != null) { return "Warn:" + xNode.InnerText + "(" + MethodBase.GetCurrentMethod().Name + ")"; } #endif return string.Empty; }
public string GetUserTimelineApi(bool read, int count, string userName, TabClass tab, bool more) { if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return string.Empty; if (MyCommon._endingFlag) return string.Empty; HttpStatusCode res = HttpStatusCode.BadRequest; var content = string.Empty; if (count == 0) count = 20; try { if (string.IsNullOrEmpty(userName)) { var target = tab.User; if (string.IsNullOrEmpty(target)) return string.Empty; userName = target; res = twCon.UserTimeline(0, target, count, 0, 0, ref content); } else { if (more) { res = twCon.UserTimeline(0, userName, count, tab.OldestId, 0, ref content); } else { res = twCon.UserTimeline(0, userName, count, 0, 0, ref content); } } } catch(Exception ex) { return "Err:" + ex.Message; } switch (res) { case HttpStatusCode.OK: Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid; break; case HttpStatusCode.Unauthorized: Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid; return "Err:@" + userName + "'s Tweets are protected."; case HttpStatusCode.BadRequest: return "Err:API Limits?"; default: return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")"; } List<TwitterDataModel.Status> items; try { items = MyCommon.CreateDataFromJson<List<TwitterDataModel.Status>>(content); } catch(SerializationException ex) { MyCommon.TraceOut(ex.Message + Environment.NewLine + content); return "Json Parse Error(DataContractJsonSerializer)"; } catch(Exception ex) { MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content); return "Invalid Json!"; } foreach (var status in items) { var item = CreatePostsFromStatusData(status); if (item == null) continue; if (item.StatusId < tab.OldestId) tab.OldestId = item.StatusId; item.IsRead = read; if (item.IsMe && !read && _readOwnPost) item.IsRead = true; if (tab != null) item.RelTabName = tab.TabName; //非同期アイコン取得&StatusDictionaryに追加 TabInformations.GetInstance().AddPost(item); } return string.Empty; }
private async Task FavRemoveAsyncInternal(IProgress<string> p, CancellationToken ct, IReadOnlyList<long> statusIds, TabClass tab) { if (ct.IsCancellationRequested) return; if (!CheckAccountValid()) throw new WebApiException("Auth error. Check your account"); var successIds = new List<long>(); await Task.Run(() => { //スレッド処理はしない var allCount = 0; var failedCount = 0; foreach (var statusId in statusIds) { allCount++; var post = tab.Posts[statusId]; p.Report(Properties.Resources.GetTimelineWorker_RunWorkerCompletedText17 + allCount + "/" + statusIds.Count + Properties.Resources.GetTimelineWorker_RunWorkerCompletedText18 + failedCount); if (!post.IsFav) continue; var err = this.tw.PostFavRemove(post.RetweetedId ?? post.StatusId); if (!string.IsNullOrEmpty(err)) { failedCount++; continue; } successIds.Add(statusId); post.IsFav = false; // リスト再描画必要 if (this._statuses.ContainsKey(statusId)) { this._statuses[statusId].IsFav = false; } // 検索,リスト,UserTimeline,Relatedの各タブに反映 foreach (var tb in this._statuses.GetTabsInnerStorageType()) { if (tb.Contains(statusId)) tb.Posts[statusId].IsFav = false; } } }); if (ct.IsCancellationRequested) return; this.RemovePostFromFavTab(successIds.ToArray()); this.RefreshTimeline(false); if (this._curList != null && this._curTab != null && this._curTab.Text == tab.TabName) { if (tab.TabType == MyCommon.TabUsageType.Favorites) { // 色変えは不要 } else { using (ControlTransaction.Update(this._curList)) { foreach (var statusId in successIds) { var idx = tab.IndexOf(statusId); if (idx == -1) continue; var post = tab.Posts[statusId]; this.ChangeCacheStyleRead(post.IsRead, idx); } } if (successIds.Contains(this._curPost.StatusId)) await this.DispSelectedPost(true); // 選択アイテム再表示 } } }
public string GetUserTimelineApi(bool read, int count, string userName, TabClass tab, bool more) { if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return ""; if (MyCommon._endingFlag) return ""; IList<TwitterDataModel.Status> items; try { if (string.IsNullOrEmpty(userName)) { var target = tab.User; if (string.IsNullOrEmpty(target)) return ""; userName = target; items = GetUserTimelineStatusApi(target, count, 0); } else { if (more) { items = GetUserTimelineStatusApi(userName, count, tab.OldestId); } else { items = GetUserTimelineStatusApi(userName, count, 0); } } } catch (Exception ex) { return ex.Message; } IList<PostClass> postList = StatusListToPostClassList(items); foreach (var item in postList) { if (item.StatusId < tab.OldestId) tab.OldestId = item.StatusId; item.IsRead = read; if (item.IsMe && !read && _readOwnPost) item.IsRead = true; if (tab != null) item.RelTabName = tab.TabName; //非同期アイコン取得&StatusDictionaryに追加 TabInformations.GetInstance().AddPost(item); } return ""; }
public string GetUserTimelineApi(bool read, int count, string userName, TabClass tab, bool more) { if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return ""; if (MyCommon._endingFlag) return ""; HttpStatusCode res; var content = ""; if (count == 0) count = 20; try { if (string.IsNullOrEmpty(userName)) { var target = tab.User; if (string.IsNullOrEmpty(target)) return ""; userName = target; res = twCon.UserTimeline(null, target, count, null, null, ref content); } else { if (more) { res = twCon.UserTimeline(null, userName, count, tab.OldestId, null, ref content); } else { res = twCon.UserTimeline(null, userName, count, null, null, ref content); } } } catch(Exception ex) { return "Err:" + ex.Message; } if (res == HttpStatusCode.Unauthorized) return "Err:@" + userName + "'s Tweets are protected."; var err = this.CheckStatusCode(res, content); if (err != null) return err; return CreatePostsFromJson(content, MyCommon.WORKERTYPE.UserTimeline, tab, read, ref tab.OldestId); }
private string CreatePostsFromSearchJson(string content, TabClass tab, bool read, int count, ref long minimumId, bool more) { TwitterSearchResult items; try { items = TwitterSearchResult.ParseJson(content); } catch (SerializationException ex) { MyCommon.TraceOut(ex.Message + Environment.NewLine + content); return "Json Parse Error(DataContractJsonSerializer)"; } catch (Exception ex) { MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content); return "Invalid Json!"; } foreach (var result in items.Statuses) { PostClass post = null; post = CreatePostsFromStatusData(result); if (post == null) { // Search API は相変わらずぶっ壊れたデータを返すことがあるため、必要なデータが欠如しているものは取得し直す var ret = this.GetStatusApi(read, result.Id, ref post); if (!string.IsNullOrEmpty(ret)) continue; } if (minimumId > post.StatusId) minimumId = post.StatusId; if (!more && post.StatusId > tab.SinceId) tab.SinceId = post.StatusId; //二重取得回避 lock (LockObj) { if (tab == null) { if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue; } else { if (tab.Contains(post.StatusId)) continue; } } post.IsRead = read; if ((post.IsMe && !read) && this._readOwnPost) post.IsRead = true; if (tab != null) post.RelTabName = tab.TabName; //非同期アイコン取得&StatusDictionaryに追加 TabInformations.GetInstance().AddPost(post); } return ""; }
private string CreatePostsFromSearchJson(string content, TabClass tab, bool read, int count, ref long minimumId, bool more) { TwitterDataModel.SearchResult items; try { items = MyCommon.CreateDataFromJson<TwitterDataModel.SearchResult>(content); } catch (SerializationException ex) { MyCommon.TraceOut(ex.Message + Environment.NewLine + content); return "Json Parse Error(DataContractJsonSerializer)"; } catch (Exception ex) { MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content); return "Invalid Json!"; } foreach (var result in items.Results) { PostClass post = null; post = CreatePostsFromSearchResultData(result); if (post == null) continue; if (minimumId > post.StatusId) minimumId = post.StatusId; if (!more && post.StatusId > tab.SinceId) tab.SinceId = post.StatusId; //二重取得回避 lock (LockObj) { if (tab == null) { if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue; } else { if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue; } } post.IsRead = read; if ((post.IsMe && !read) && this._readOwnPost) post.IsRead = true; if (tab != null) post.RelTabName = tab.TabName; //非同期アイコン取得&StatusDictionaryに追加 TabInformations.GetInstance().AddPost(post); } return ""; }
public bool ContainsTab(TabClass ts) { return _tabs.ContainsValue(ts); }
public bool AddTab(string TabName, MyCommon.TabUsageType TabType, ListElement List) { if (_tabs.ContainsKey(TabName)) return false; var tb = new TabClass(TabName, TabType, List); _tabs.Add(TabName, tb); tb.SortMode = this.SortMode; tb.SortOrder = this.SortOrder; return true; }
public void OnFilterModified_DetachedTest() { var tab = new TabClass(); var filter = new PostFilterRule(); tab.FilterArray = new[] { filter }; tab.RemoveFilter(filter); tab.FilterModified = false; // TabClass から既に削除されたフィルタを変更 filter.FilterSource = "OpenTween"; Assert.False(tab.FilterModified); }
public string GetPhoenixSearch(bool read, TabClass tab, bool more) { if (MyCommon._endingFlag) return string.Empty; HttpStatusCode res; var content = string.Empty; var page = 0; var sinceId = 0; var count = 100; var querystr = string.Empty; if (AppendSettingDialog.Instance.UseAdditionalCount && AppendSettingDialog.Instance.SearchCountApi != 0) { count = AppendSettingDialog.Instance.SearchCountApi; } if (more) { page = tab.GetSearchPage(count); if (!string.IsNullOrEmpty(tab.NextPageQuery)) { querystr = tab.NextPageQuery; } } else { sinceId = (int)tab.SinceId; } try { if (string.IsNullOrEmpty(querystr)) { res = twCon.PhoenixSearch(tab.SearchWords, tab.SearchLang, count, page, sinceId, ref content); } else { res = twCon.PhoenixSearch(querystr, ref content); } } catch(Exception ex) { return "Err:" + ex.Message; } switch (res) { case HttpStatusCode.BadRequest: return "Invalid query"; case HttpStatusCode.NotFound: return "Invalid query"; case HttpStatusCode.PaymentRequired: //API Documentには420と書いてあるが、該当コードがないので402にしてある return "Search API Limit?"; case HttpStatusCode.OK: break; default: return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")"; } if (!TabInformations.GetInstance().ContainsTab(tab)) return string.Empty; //// TODO //// 遡るための情報max_idやnext_pageの情報を保持する string nextPageQuery = tab.NextPageQuery; var ret = CreatePostsFromPhoenixSearch(content, MyCommon.WORKERTYPE.PublicSearch, tab, read, count, ref tab.OldestId, ref nextPageQuery); tab.NextPageQuery = nextPageQuery; return ret; }
public string GetListStatus(bool read, TabClass tab, bool more, bool startup) { if (MyCommon._endingFlag) return ""; HttpStatusCode res; var content = ""; int count; if (SettingCommon.Instance.UseAdditionalCount) { count = SettingCommon.Instance.ListCountApi; if (count == 0) { if (more && SettingCommon.Instance.MoreCountApi != 0) { count = SettingCommon.Instance.MoreCountApi; } else if (startup && SettingCommon.Instance.FirstCountApi != 0) { count = SettingCommon.Instance.FirstCountApi; } else { count = SettingCommon.Instance.CountApi; } } } else { count = SettingCommon.Instance.CountApi; } try { if (more) { res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, tab.OldestId, null, SettingCommon.Instance.IsListsIncludeRts, ref content); } else { res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, null, null, SettingCommon.Instance.IsListsIncludeRts, ref content); } } catch(Exception ex) { return "Err:" + ex.Message; } var err = this.CheckStatusCode(res, content); if (err != null) return err; return CreatePostsFromJson(content, MyCommon.WORKERTYPE.List, tab, read, ref tab.OldestId); }
public string GetUserTimelineApi(bool read, int count, string userName, TabClass tab, bool more) { if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return ""; if (MyCommon._endingFlag) return ""; HttpStatusCode res; var content = ""; if (count == 0) count = 20; try { if (string.IsNullOrEmpty(userName)) { var target = tab.User; if (string.IsNullOrEmpty(target)) return ""; userName = target; res = twCon.UserTimeline(null, target, count, null, null, ref content); } else { if (more) { res = twCon.UserTimeline(null, userName, count, tab.OldestId, null, ref content); } else { res = twCon.UserTimeline(null, userName, count, null, null, ref content); } } } catch(Exception ex) { return "Err:" + ex.Message; } if (res == HttpStatusCode.Unauthorized) return "Err:@" + userName + "'s Tweets are protected."; var err = this.CheckStatusCode(res, content); if (err != null) return err; TwitterStatus[] items; try { items = TwitterStatus.ParseJsonArray(content); } catch(SerializationException ex) { MyCommon.TraceOut(ex.Message + Environment.NewLine + content); return "Json Parse Error(DataContractJsonSerializer)"; } catch(Exception ex) { MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content); return "Invalid Json!"; } foreach (var status in items) { var item = CreatePostsFromStatusData(status); if (item == null) continue; if (item.StatusId < tab.OldestId) tab.OldestId = item.StatusId; item.IsRead = read; if (item.IsMe && !read && _readOwnPost) item.IsRead = true; if (tab != null) item.RelTabName = tab.TabName; //非同期アイコン取得&StatusDictionaryに追加 TabInformations.GetInstance().AddPost(item); } return ""; }
public string GetListStatus(bool read, TabClass tab, bool more, bool startup) { if (MyCommon._endingFlag) return string.Empty; HttpStatusCode res; var content = string.Empty; int count; if (AppendSettingDialog.Instance.UseAdditionalCount) { count = AppendSettingDialog.Instance.ListCountApi; if (count == 0) { if (more && AppendSettingDialog.Instance.MoreCountApi != 0) { count = AppendSettingDialog.Instance.MoreCountApi; } else if (startup && AppendSettingDialog.Instance.FirstCountApi != 0) { count = AppendSettingDialog.Instance.FirstCountApi; } else { count = AppendSettingDialog.Instance.CountApi; } } } else { count = AppendSettingDialog.Instance.CountApi; } try { if (more) { res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, tab.OldestId, 0, AppendSettingDialog.Instance.IsListStatusesIncludeRts, ref content); } else { res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, 0, 0, AppendSettingDialog.Instance.IsListStatusesIncludeRts, ref content); } } catch(Exception ex) { return "Err:" + ex.Message; } switch (res) { case HttpStatusCode.OK: Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid; break; case HttpStatusCode.Unauthorized: Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid; return Properties.Resources.Unauthorized; case HttpStatusCode.BadRequest: return "Err:API Limits?"; default: return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")"; } return CreatePostsFromJson(content, MyCommon.WORKERTYPE.List, tab, read, count, ref tab.OldestId); }
public string GetRelatedResult(bool read, TabClass tab) { var rslt = ""; var relPosts = new Dictionary<Int64, PostClass>(); if (tab.RelationTargetPost.TextFromApi.Contains("@") && tab.RelationTargetPost.InReplyToStatusId == null) { //検索結果対応 var p = TabInformations.GetInstance()[tab.RelationTargetPost.StatusId]; if (p != null && p.InReplyToStatusId != null) { tab.RelationTargetPost = p; } else { rslt = this.GetStatusApi(read, tab.RelationTargetPost.StatusId, ref p); if (!string.IsNullOrEmpty(rslt)) return rslt; tab.RelationTargetPost = p; } } relPosts.Add(tab.RelationTargetPost.StatusId, tab.RelationTargetPost.Clone()); // in_reply_to_status_id を使用してリプライチェインを辿る var nextPost = FindTopOfReplyChain(relPosts, tab.RelationTargetPost.StatusId); var loopCount = 1; while (nextPost.InReplyToStatusId != null && loopCount++ <= 20) { var inReplyToId = nextPost.InReplyToStatusId.Value; var inReplyToPost = TabInformations.GetInstance()[inReplyToId]; if (inReplyToPost != null) { inReplyToPost = inReplyToPost.Clone(); } else { var errorText = this.GetStatusApi(read, inReplyToId, ref inReplyToPost); if (!string.IsNullOrEmpty(errorText)) { rslt = errorText; break; } } relPosts.Add(inReplyToPost.StatusId, inReplyToPost); nextPost = FindTopOfReplyChain(relPosts, nextPost.StatusId); } //MRTとかに対応のためツイート内にあるツイートを指すURLを取り込む var text = tab.RelationTargetPost.Text; var ma = Twitter.StatusUrlRegex.Matches(text).Cast<Match>() .Concat(Twitter.ThirdPartyStatusUrlRegex.Matches(text).Cast<Match>()); foreach (var _match in ma) { Int64 _statusId; if (Int64.TryParse(_match.Groups["StatusId"].Value, out _statusId)) { if (relPosts.ContainsKey(_statusId)) continue; PostClass p = null; var _post = TabInformations.GetInstance()[_statusId]; if (_post == null) { this.GetStatusApi(read, _statusId, ref p); } else { p = _post.Clone(); } if (p != null) relPosts.Add(p.StatusId, p); } } relPosts.Values.ToList().ForEach(p => { if (p.IsMe && !read && this._readOwnPost) p.IsRead = true; else p.IsRead = read; p.RelTabName = tab.TabName; TabInformations.GetInstance().AddPost(p); }); return rslt; }
public string GetRelatedResult(bool read, TabClass tab) { var rslt = string.Empty; var relPosts = new List<PostClass>(); if (tab.RelationTargetPost.TextFromApi.Contains("@") && tab.RelationTargetPost.InReplyToStatusId == 0) { //検索結果対応 var p = TabInformations.GetInstance()[tab.RelationTargetPost.StatusId]; if (p != null && p.InReplyToStatusId > 0) { tab.RelationTargetPost = p; } else { rslt = this.GetStatusApi(read, tab.RelationTargetPost.StatusId, ref p); if (!string.IsNullOrEmpty(rslt)) return rslt; tab.RelationTargetPost = p; } } relPosts.Add(tab.RelationTargetPost.Copy()); var tmpPost = relPosts[0]; do { rslt = this.GetRelatedResultsApi(read, tmpPost, tab, relPosts); if (!string.IsNullOrEmpty(rslt)) break; tmpPost = CheckReplyToPost(relPosts); } while (tmpPost != null); relPosts.ForEach(p => TabInformations.GetInstance().AddPost(p)); return rslt; }
public string GetSearch(bool read, TabClass tab, bool more) { if (MyCommon._endingFlag) return ""; HttpStatusCode res; var content = ""; long? maxId = null; long? sinceId = null; var count = 100; if (SettingCommon.Instance.UseAdditionalCount && SettingCommon.Instance.SearchCountApi != 0) { count = SettingCommon.Instance.SearchCountApi; } else { count = SettingCommon.Instance.CountApi; } if (more) { maxId = tab.OldestId - 1; } else { sinceId = tab.SinceId; } try { // TODO:一時的に40>100件に 件数変更UI作成の必要あり res = twCon.Search(tab.SearchWords, tab.SearchLang, count, maxId, sinceId, ref content); } catch(Exception ex) { return "Err:" + ex.Message; } switch (res) { case HttpStatusCode.BadRequest: return "Invalid query"; case HttpStatusCode.NotFound: return "Invalid query"; case HttpStatusCode.PaymentRequired: //API Documentには420と書いてあるが、該当コードがないので402にしてある return "Search API Limit?"; case HttpStatusCode.OK: break; default: return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")"; } if (!TabInformations.GetInstance().ContainsTab(tab)) return ""; return this.CreatePostsFromSearchJson(content, tab, read, count, ref tab.OldestId, more); }
public string GetStatusApi(bool read, Int64 id, TabClass tab) { PostClass post = null; var r = this.GetStatusApi(read, id, ref post); if (string.IsNullOrEmpty(r)) { if (tab != null) post.RelTabName = tab.TabName; //非同期アイコン取得&StatusDictionaryに追加 TabInformations.GetInstance().AddPost(post); } return r; }
public void OldestUnreadIndex_DisabledTest() { var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline }; // 未読表示無効 tab.UnreadManage = false; tab.AddPostToInnerStorage(new PostClass { StatusId = 100L, IsRead = false, // 未読 }); tab.AddSubmit(); tab.SortMode = ComparerMode.Id; tab.SortOrder = SortOrder.Ascending; tab.Sort(); Assert.Equal(-1, tab.OldestUnreadIndex); }
private string CreatePostsFromJson(string content, MyCommon.WORKERTYPE gType, TabClass tab, bool read, int count, ref long minimumId) { List<TwitterDataModel.Status> items; try { items = MyCommon.CreateDataFromJson<List<TwitterDataModel.Status>>(content); } catch(SerializationException ex) { MyCommon.TraceOut(ex.Message + Environment.NewLine + content); return "Json Parse Error(DataContractJsonSerializer)";; } catch(Exception ex) { MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content); return "Invalid Json!"; } foreach (var status in items) { PostClass post = null; post = CreatePostsFromStatusData(status); if (post == null) continue; if (minimumId > post.StatusId) minimumId = post.StatusId; //二重取得回避 lock (LockObj) { if (tab == null) { if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue; } else { if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue; } } //RT禁止ユーザーによるもの if (post.RetweetedId > 0 && this.noRTId.Contains(post.RetweetedByUserId)) continue; post.IsRead = read; if (post.IsMe && !read && _readOwnPost) post.IsRead = true; if (tab != null) post.RelTabName = tab.TabName; //非同期アイコン取得&StatusDictionaryに追加 TabInformations.GetInstance().AddPost(post); } return string.Empty; }
public void SetReadState_MarkAsUnreadTest() { var tab = new TabClass { TabType = MyCommon.TabUsageType.UserTimeline }; tab.UnreadManage = true; tab.AddPostToInnerStorage(new PostClass { StatusId = 100L, IsRead = true, // 既読 }); tab.AddSubmit(); Assert.Equal(0, tab.UnreadCount); tab.SetReadState(100L, false); // 未読にする Assert.Equal(1, tab.UnreadCount); }
private string GetRelatedResultsApi(bool read, PostClass post, TabClass tab, List<PostClass> relatedPosts) { if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return string.Empty; if (MyCommon._endingFlag) return string.Empty; HttpStatusCode res = HttpStatusCode.BadRequest; var content = string.Empty; try { if (post.RetweetedId > 0) { res = twCon.GetRelatedResults(post.RetweetedId, ref content); } else { res = twCon.GetRelatedResults(post.StatusId, ref content); } } catch(Exception ex) { return "Err:" + ex.Message; } switch (res) { case HttpStatusCode.OK: Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid; break; case HttpStatusCode.Unauthorized: Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid; return Properties.Resources.Unauthorized; case HttpStatusCode.BadRequest: return "Err:API Limits?"; default: return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")"; } List<TwitterDataModel.RelatedResult> items; try { items = MyCommon.CreateDataFromJson<List<TwitterDataModel.RelatedResult>>(content); } catch(SerializationException ex) { MyCommon.TraceOut(ex.Message + Environment.NewLine + content); return "Json Parse Error(DataContractJsonSerializer)"; } catch(Exception ex) { MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content); return "Invalid Json!"; } var targetItem = post; if (targetItem == null) { return string.Empty; } else { targetItem = targetItem.Copy(); } targetItem.RelTabName = tab.TabName; TabInformations.GetInstance().AddPost(targetItem); PostClass replyToItem = null; var replyToUserName = targetItem.InReplyToUser; if (targetItem.InReplyToStatusId > 0 && TabInformations.GetInstance()[targetItem.InReplyToStatusId] != null) { replyToItem = TabInformations.GetInstance()[targetItem.InReplyToStatusId].Copy(); replyToItem.IsRead = read; if (replyToItem.IsMe && !read && _readOwnPost) replyToItem.IsRead = true; replyToItem.RelTabName = tab.TabName; } var replyAdded = false; foreach (var relatedData in items) { foreach (var result in relatedData.Results) { var item = CreatePostsFromStatusData(result.Status); if (item == null) continue; if (targetItem.InReplyToStatusId == item.StatusId) { replyToItem = null; replyAdded = true; } item.IsRead = read; if (item.IsMe && !read && _readOwnPost) item.IsRead = true; if (tab != null) item.RelTabName = tab.TabName; //非同期アイコン取得&StatusDictionaryに追加 relatedPosts.Add(item); } } if (replyToItem != null) { relatedPosts.Add(replyToItem); } else if (targetItem.InReplyToStatusId > 0 && !replyAdded) { PostClass p = null; var rslt = string.Empty; rslt = GetStatusApi(read, targetItem.InReplyToStatusId, ref p); if (string.IsNullOrEmpty(rslt)) { p.IsRead = read; p.RelTabName = tab.TabName; relatedPosts.Add(p); } return rslt; } //発言者・返信先ユーザーの直近10発言取得 //var rslt = this.GetUserTimelineApi(read, 10, string.Empty, tab); //if (!string.IsNullOrEmpty(rslt)) return rslt; //if (!string.IsNullOrEmpty(replyToUserName)) // rslt = this.GetUserTimelineApi(read, 10, replyToUserName, tab); //} //return rslt; //MRTとかに対応のためツイート内にあるツイートを指すURLを取り込む var ma = Regex.Matches(tab.RelationTargetPost.Text, "title=\"https?://twitter.com/(#!/)?(?<ScreenName>[a-zA-Z0-9_]+)(/status(es)?/(?<StatusId>[0-9]+))\""); foreach (Match _match in ma) { Int64 _statusId; if (Int64.TryParse(_match.Groups["StatusId"].Value, out _statusId)) { PostClass p = null; var _post = TabInformations.GetInstance()[_statusId]; if (_post == null) { var result = this.GetStatusApi(read, _statusId, ref p); } else { p = _post.Copy(); } if (p != null) { p.IsRead = read; p.RelTabName = tab.TabName; relatedPosts.Add(p); } } } return string.Empty; }
public void AddFilter_Test() { var tab = new TabClass(); var filter = new PostFilterRule(); tab.AddFilter(filter); Assert.Equal(new[] { filter }, tab.FilterArray); Assert.True(tab.FilterModified); }