• R/O
  • HTTP
  • SSH
  • HTTPS

open-tween: Commit

開発に使用するリポジトリ


Commit MetaInfo

Revision5491484758a7b9d70b13ad4cbc824d89a8055ad4 (tree)
Time2018-05-13 03:55:47
AuthorKimura Youichi <kim.upsilon@bucy...>
CommiterKimura Youichi

Log Message

Merge branch 'quoted-status'

Change Summary

Incremental Difference

--- a/OpenTween.Tests/TwitterTest.cs
+++ b/OpenTween.Tests/TwitterTest.cs
@@ -110,6 +110,216 @@ namespace OpenTween
110110 }
111111
112112 [Fact]
113+ public void CreateAccessibleText_MediaAltTest()
114+ {
115+ var text = "https://t.co/hoge";
116+ var entities = new TwitterEntities
117+ {
118+ Media = new[]
119+ {
120+ new TwitterEntityMedia
121+ {
122+ Indices = new[] { 0, 17 },
123+ Url = "https://t.co/hoge",
124+ DisplayUrl = "pic.twitter.com/hoge",
125+ ExpandedUrl = "https://twitter.com/hoge/status/1234567890/photo/1",
126+ AltText = "代替テキスト",
127+ },
128+ },
129+ };
130+
131+ var expectedText = string.Format(Properties.Resources.ImageAltText, "代替テキスト");
132+
133+ Assert.Equal(expectedText, Twitter.CreateAccessibleText(text, entities, quotedStatus: null, quotedStatusLink: null));
134+ }
135+
136+ [Fact]
137+ public void CreateAccessibleText_MediaNoAltTest()
138+ {
139+ var text = "https://t.co/hoge";
140+ var entities = new TwitterEntities
141+ {
142+ Media = new[]
143+ {
144+ new TwitterEntityMedia
145+ {
146+ Indices = new[] { 0, 17 },
147+ Url = "https://t.co/hoge",
148+ DisplayUrl = "pic.twitter.com/hoge",
149+ ExpandedUrl = "https://twitter.com/hoge/status/1234567890/photo/1",
150+ AltText = null,
151+ },
152+ },
153+ };
154+
155+ var expectedText = "pic.twitter.com/hoge";
156+
157+ Assert.Equal(expectedText, Twitter.CreateAccessibleText(text, entities, quotedStatus: null, quotedStatusLink: null));
158+ }
159+
160+ [Fact]
161+ public void CreateAccessibleText_QuotedUrlTest()
162+ {
163+ var text = "https://t.co/hoge";
164+ var entities = new TwitterEntities
165+ {
166+ Urls = new[]
167+ {
168+ new TwitterEntityUrl
169+ {
170+ Indices = new[] { 0, 17 },
171+ Url = "https://t.co/hoge",
172+ DisplayUrl = "twitter.com/hoge/status/1…",
173+ ExpandedUrl = "https://twitter.com/hoge/status/1234567890",
174+ },
175+ },
176+ };
177+ var quotedStatus = new TwitterStatus
178+ {
179+ Id = 1234567890L,
180+ IdStr = "1234567890",
181+ User = new TwitterUser
182+ {
183+ Id = 1111,
184+ IdStr = "1111",
185+ ScreenName = "foo",
186+ },
187+ FullText = "test",
188+ };
189+
190+ var expectedText = string.Format(Properties.Resources.QuoteStatus_AccessibleText, "foo", "test");
191+
192+ Assert.Equal(expectedText, Twitter.CreateAccessibleText(text, entities, quotedStatus, quotedStatusLink: null));
193+ }
194+
195+ [Fact]
196+ public void CreateAccessibleText_QuotedUrlWithPermelinkTest()
197+ {
198+ var text = "hoge";
199+ var entities = new TwitterEntities();
200+ var quotedStatus = new TwitterStatus
201+ {
202+ Id = 1234567890L,
203+ IdStr = "1234567890",
204+ User = new TwitterUser
205+ {
206+ Id = 1111,
207+ IdStr = "1111",
208+ ScreenName = "foo",
209+ },
210+ FullText = "test",
211+ };
212+ var quotedStatusLink = new TwitterQuotedStatusPermalink
213+ {
214+ Url = "https://t.co/hoge",
215+ Display = "twitter.com/hoge/status/1…",
216+ Expanded = "https://twitter.com/hoge/status/1234567890",
217+ };
218+
219+ var expectedText = "hoge " + string.Format(Properties.Resources.QuoteStatus_AccessibleText, "foo", "test");
220+
221+ Assert.Equal(expectedText, Twitter.CreateAccessibleText(text, entities, quotedStatus, quotedStatusLink));
222+ }
223+
224+ [Fact]
225+ public void CreateAccessibleText_QuotedUrlNoReferenceTest()
226+ {
227+ var text = "https://t.co/hoge";
228+ var entities = new TwitterEntities
229+ {
230+ Urls = new[]
231+ {
232+ new TwitterEntityUrl
233+ {
234+ Indices = new[] { 0, 17 },
235+ Url = "https://t.co/hoge",
236+ DisplayUrl = "twitter.com/hoge/status/1…",
237+ ExpandedUrl = "https://twitter.com/hoge/status/1234567890",
238+ },
239+ },
240+ };
241+ var quotedStatus = (TwitterStatus)null;
242+
243+ var expectedText = "twitter.com/hoge/status/1…";
244+
245+ Assert.Equal(expectedText, Twitter.CreateAccessibleText(text, entities, quotedStatus, quotedStatusLink: null));
246+ }
247+
248+ [Fact]
249+ public void CreateHtmlAnchor_Test()
250+ {
251+ var text = "@twitterapi #BreakingMyTwitter https://t.co/mIJcSoVSK3";
252+ var entities = new TwitterEntities
253+ {
254+ UserMentions = new[]
255+ {
256+ new TwitterEntityMention { Indices = new[] { 0, 11 }, ScreenName = "twitterapi" },
257+ },
258+ Hashtags = new[]
259+ {
260+ new TwitterEntityHashtag { Indices = new[] { 12, 30 }, Text = "BreakingMyTwitter" },
261+ },
262+ Urls = new[]
263+ {
264+ new TwitterEntityUrl
265+ {
266+ Indices = new[] { 31, 54 },
267+ Url ="https://t.co/mIJcSoVSK3",
268+ DisplayUrl = "apps-of-a-feather.com",
269+ ExpandedUrl = "http://apps-of-a-feather.com/",
270+ },
271+ },
272+ };
273+
274+ var expectedHtml = @"<a class=""mention"" href=""https://twitter.com/twitterapi"">@twitterapi</a>"
275+ + @" <a class=""hashtag"" href=""https://twitter.com/search?q=%23BreakingMyTwitter"">#BreakingMyTwitter</a>"
276+ + @" <a href=""https://t.co/mIJcSoVSK3"" title=""https://t.co/mIJcSoVSK3"">apps-of-a-feather.com</a>";
277+
278+ Assert.Equal(expectedHtml, Twitter.CreateHtmlAnchor(text, entities, quotedStatusLink: null));
279+ }
280+
281+ [Fact]
282+ public void CreateHtmlAnchor_NicovideoTest()
283+ {
284+ var text = "sm9";
285+ var entities = new TwitterEntities();
286+
287+ var expectedHtml = @"<a href=""http://www.nicovideo.jp/watch/sm9"">sm9</a>";
288+
289+ Assert.Equal(expectedHtml, Twitter.CreateHtmlAnchor(text, entities, quotedStatusLink: null));
290+ }
291+
292+ [Fact]
293+ public void CreateHtmlAnchor_QuotedUrlWithPermelinkTest()
294+ {
295+ var text = "hoge";
296+ var entities = new TwitterEntities();
297+ var quotedStatus = new TwitterStatus
298+ {
299+ Id = 1234567890L,
300+ IdStr = "1234567890",
301+ User = new TwitterUser
302+ {
303+ Id = 1111,
304+ IdStr = "1111",
305+ ScreenName = "foo",
306+ },
307+ FullText = "test",
308+ };
309+ var quotedStatusLink = new TwitterQuotedStatusPermalink
310+ {
311+ Url = "https://t.co/hoge",
312+ Display = "twitter.com/hoge/status/1…",
313+ Expanded = "https://twitter.com/hoge/status/1234567890",
314+ };
315+
316+ var expectedHtml = @"hoge"
317+ + @" <a href=""https://t.co/hoge"" title=""https://t.co/hoge"">twitter.com/hoge/status/1…</a>";
318+
319+ Assert.Equal(expectedHtml, Twitter.CreateHtmlAnchor(text, entities, quotedStatusLink));
320+ }
321+
322+ [Fact]
113323 public void ParseSource_Test()
114324 {
115325 var sourceHtml = "<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>";
@@ -186,7 +396,21 @@ namespace OpenTween
186396 },
187397 };
188398
189- var statusIds = Twitter.GetQuoteTweetStatusIds(entities);
399+ var statusIds = Twitter.GetQuoteTweetStatusIds(entities, quotedStatusLink: null);
400+ Assert.Equal(new[] { 599261132361072640L }, statusIds);
401+ }
402+
403+ [Fact]
404+ public void GetQuoteTweetStatusIds_QuotedStatusLinkTest()
405+ {
406+ var entities = new TwitterEntities();
407+ var quotedStatusLink = new TwitterQuotedStatusPermalink
408+ {
409+ Url = "https://t.co/3HXq0LrbJb",
410+ Expanded = "https://twitter.com/kim_upsilon/status/599261132361072640",
411+ };
412+
413+ var statusIds = Twitter.GetQuoteTweetStatusIds(entities, quotedStatusLink);
190414 Assert.Equal(new[] { 599261132361072640L }, statusIds);
191415 }
192416
--- a/OpenTween/Resources/ChangeLog.txt
+++ b/OpenTween/Resources/ChangeLog.txt
@@ -2,6 +2,7 @@
22
33 ==== Ver 1.4.2-dev(xxxx/xx/xx)
44 * NEW: システムのタイムゾーンの変更を検知して、ツイートの投稿日時などの表示を新しいタイムゾーンに同期します
5+ * NEW: 5月中旬に予定されている引用ツイートに関する Twitter API の仕様変更に対応
56 * FIX: 動画のサムネイル画像に「URLをコピー」を行うとエラーが発生する不具合を修正
67
78 ==== Ver 1.4.1(2017/11/12)
--- a/OpenTween/Twitter.cs
+++ b/OpenTween/Twitter.cs
@@ -247,7 +247,7 @@ namespace OpenTween
247247 if (SettingManager.Common.UserstreamStartup) this.ReconnectUserStream();
248248 }
249249
250- public string PreProcessUrl(string orgData)
250+ internal static string PreProcessUrl(string orgData)
251251 {
252252 int posl1;
253253 var posl2 = 0;
@@ -820,16 +820,20 @@ namespace OpenTween
820820 }
821821 //HTMLに整形
822822 string textFromApi = post.TextFromApi;
823- post.Text = CreateHtmlAnchor(textFromApi, post.ReplyToList, entities, post.Media);
823+ var quotedStatusLink = (status.RetweetedStatus ?? status).QuotedStatusPermalink;
824+
825+ post.Text = CreateHtmlAnchor(textFromApi, entities, quotedStatusLink);
824826 post.TextFromApi = textFromApi;
825- post.TextFromApi = this.ReplaceTextFromApi(post.TextFromApi, entities);
827+ post.TextFromApi = this.ReplaceTextFromApi(post.TextFromApi, entities, quotedStatusLink);
826828 post.TextFromApi = WebUtility.HtmlDecode(post.TextFromApi);
827829 post.TextFromApi = post.TextFromApi.Replace("<3", "\u2661");
828- post.AccessibleText = this.CreateAccessibleText(textFromApi, entities, (status.RetweetedStatus ?? status).QuotedStatus);
830+ post.AccessibleText = CreateAccessibleText(textFromApi, entities, (status.RetweetedStatus ?? status).QuotedStatus, quotedStatusLink);
829831 post.AccessibleText = WebUtility.HtmlDecode(post.AccessibleText);
830832 post.AccessibleText = post.AccessibleText.Replace("<3", "\u2661");
831833
832- post.QuoteStatusIds = GetQuoteTweetStatusIds(entities)
834+ this.ExtractEntities(entities, post.ReplyToList, post.Media);
835+
836+ post.QuoteStatusIds = GetQuoteTweetStatusIds(entities, quotedStatusLink)
833837 .Where(x => x != post.StatusId && x != post.RetweetedId)
834838 .Distinct().ToArray();
835839
@@ -873,10 +877,13 @@ namespace OpenTween
873877 /// <summary>
874878 /// ツイートに含まれる引用ツイートのURLからステータスIDを抽出
875879 /// </summary>
876- public static IEnumerable<long> GetQuoteTweetStatusIds(IEnumerable<TwitterEntity> entities)
880+ public static IEnumerable<long> GetQuoteTweetStatusIds(IEnumerable<TwitterEntity> entities, TwitterQuotedStatusPermalink quotedStatusLink)
877881 {
878882 var urls = entities.OfType<TwitterEntityUrl>().Select(x => x.ExpandedUrl);
879883
884+ if (quotedStatusLink != null)
885+ urls = urls.Concat(new[] { quotedStatusLink.Expanded });
886+
880887 return GetQuoteTweetStatusIds(urls);
881888 }
882889
@@ -1181,16 +1188,19 @@ namespace OpenTween
11811188 //本文
11821189 var textFromApi = message.Text;
11831190 //HTMLに整形
1184- post.Text = CreateHtmlAnchor(textFromApi, post.ReplyToList, message.Entities, post.Media);
1185- post.TextFromApi = this.ReplaceTextFromApi(textFromApi, message.Entities);
1191+ post.Text = CreateHtmlAnchor(textFromApi, message.Entities, quotedStatusLink: null);
1192+ post.TextFromApi = this.ReplaceTextFromApi(textFromApi, message.Entities, quotedStatusLink: null);
11861193 post.TextFromApi = WebUtility.HtmlDecode(post.TextFromApi);
11871194 post.TextFromApi = post.TextFromApi.Replace("<3", "\u2661");
1188- post.AccessibleText = this.CreateAccessibleText(textFromApi, message.Entities, quoteStatus: null);
1195+ post.AccessibleText = CreateAccessibleText(textFromApi, message.Entities, quotedStatus: null, quotedStatusLink: null);
11891196 post.AccessibleText = WebUtility.HtmlDecode(post.AccessibleText);
11901197 post.AccessibleText = post.AccessibleText.Replace("<3", "\u2661");
11911198 post.IsFav = false;
11921199
1193- post.QuoteStatusIds = GetQuoteTweetStatusIds(message.Entities).Distinct().ToArray();
1200+ this.ExtractEntities(message.Entities, post.ReplyToList, post.Media);
1201+
1202+ post.QuoteStatusIds = GetQuoteTweetStatusIds(message.Entities, quotedStatusLink: null)
1203+ .Distinct().ToArray();
11941204
11951205 post.ExpandedUrls = message.Entities.OfType<TwitterEntityUrl>()
11961206 .Select(x => new PostClass.ExpandedUrlInfo(x.Url, x.ExpandedUrl))
@@ -1326,7 +1336,7 @@ namespace OpenTween
13261336 tab.OldestId = minimumId.Value;
13271337 }
13281338
1329- private string ReplaceTextFromApi(string text, TwitterEntities entities)
1339+ private string ReplaceTextFromApi(string text, TwitterEntities entities, TwitterQuotedStatusPermalink quotedStatusLink)
13301340 {
13311341 if (entities != null)
13321342 {
@@ -1345,10 +1355,14 @@ namespace OpenTween
13451355 }
13461356 }
13471357 }
1358+
1359+ if (quotedStatusLink != null)
1360+ text += " " + quotedStatusLink.Display;
1361+
13481362 return text;
13491363 }
13501364
1351- private string CreateAccessibleText(string text, TwitterEntities entities, TwitterStatus quoteStatus)
1365+ internal static string CreateAccessibleText(string text, TwitterEntities entities, TwitterStatus quotedStatus, TwitterQuotedStatusPermalink quotedStatusLink)
13521366 {
13531367 if (entities == null)
13541368 return text;
@@ -1357,19 +1371,19 @@ namespace OpenTween
13571371 {
13581372 foreach (var entity in entities.Urls)
13591373 {
1360- if (quoteStatus != null)
1374+ if (quotedStatus != null)
13611375 {
13621376 var matchStatusUrl = Twitter.StatusUrlRegex.Match(entity.ExpandedUrl);
1363- if (matchStatusUrl.Success && matchStatusUrl.Groups["StatusId"].Value == quoteStatus.IdStr)
1377+ if (matchStatusUrl.Success && matchStatusUrl.Groups["StatusId"].Value == quotedStatus.IdStr)
13641378 {
1365- var quoteText = this.CreateAccessibleText(quoteStatus.FullText, quoteStatus.MergedEntities, quoteStatus: null);
1366- text = text.Replace(entity.Url, string.Format(Properties.Resources.QuoteStatus_AccessibleText, quoteStatus.User.ScreenName, quoteText));
1379+ var quotedText = CreateAccessibleText(quotedStatus.FullText, quotedStatus.MergedEntities, quotedStatus: null, quotedStatusLink: null);
1380+ text = text.Replace(entity.Url, string.Format(Properties.Resources.QuoteStatus_AccessibleText, quotedStatus.User.ScreenName, quotedText));
1381+ continue;
13671382 }
13681383 }
1369- else if (!string.IsNullOrEmpty(entity.DisplayUrl))
1370- {
1384+
1385+ if (!string.IsNullOrEmpty(entity.DisplayUrl))
13711386 text = text.Replace(entity.Url, entity.DisplayUrl);
1372- }
13731387 }
13741388 }
13751389
@@ -1388,6 +1402,12 @@ namespace OpenTween
13881402 }
13891403 }
13901404
1405+ if (quotedStatusLink != null)
1406+ {
1407+ var quoteText = CreateAccessibleText(quotedStatus.FullText, quotedStatus.MergedEntities, quotedStatus: null, quotedStatusLink: null);
1408+ text += " " + string.Format(Properties.Resources.QuoteStatus_AccessibleText, quotedStatus.User.ScreenName, quoteText);
1409+ }
1410+
13911411 return text;
13921412 }
13931413
@@ -1546,7 +1566,7 @@ namespace OpenTween
15461566 }
15471567 }
15481568
1549- public string CreateHtmlAnchor(string text, List<Tuple<long, string>> AtList, TwitterEntities entities, List<MediaInfo> media)
1569+ private void ExtractEntities(TwitterEntities entities, List<Tuple<long, string>> AtList, List<MediaInfo> media)
15501570 {
15511571 if (entities != null)
15521572 {
@@ -1588,13 +1608,23 @@ namespace OpenTween
15881608 }
15891609 }
15901610 }
1611+ }
15911612
1613+ internal static string CreateHtmlAnchor(string text, TwitterEntities entities, TwitterQuotedStatusPermalink quotedStatusLink)
1614+ {
15921615 // PostClass.ExpandedUrlInfo を使用して非同期に URL 展開を行うためここでは expanded_url を使用しない
15931616 text = TweetFormatter.AutoLinkHtml(text, entities, keepTco: true);
15941617
15951618 text = Regex.Replace(text, "(^|[^a-zA-Z0-9_/&##@@>=.~])(sm|nm)([0-9]{1,10})", "$1<a href=\"http://www.nicovideo.jp/watch/$2$3\">$2$3</a>");
15961619 text = PreProcessUrl(text); //IDN置換
15971620
1621+ if (quotedStatusLink != null)
1622+ {
1623+ text += string.Format(" <a href=\"{0}\" title=\"{0}\">{1}</a>",
1624+ WebUtility.HtmlEncode(quotedStatusLink.Url),
1625+ WebUtility.HtmlEncode(quotedStatusLink.Display));
1626+ }
1627+
15981628 return text;
15991629 }
16001630
Show on old repository browser