OSDN Git Service

コンパイルできなくなっていたのを修正
[opentween/open-tween.git] / Tween / Twitter.vb
1 ' Tween - 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 ' All rights reserved.
8
9 ' This file is part of Tween.
10
11 ' This program is free software; you can redistribute it and/or modify it
12 ' under the terms of the GNU General Public License as published by the Free
13 ' Software Foundation; either version 3 of the License, or (at your option)
14 ' any later version.
15
16 ' This program is distributed in the hope that it will be useful, but
17 ' WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 ' or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 ' for more details. 
20
21 ' You should have received a copy of the GNU General Public License along
22 ' with this program. If not, see <http://www.gnu.org/licenses/>, or write to
23 ' the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
24 ' Boston, MA 02110-1301, USA.
25
26 Imports System.Diagnostics
27 Imports System.IO
28 Imports System.Linq
29 Imports System.Net
30 Imports System.Reflection.MethodBase
31 Imports System.Runtime.Serialization
32 Imports System.Runtime.Serialization.Json
33 Imports System.Text
34 Imports System.Text.RegularExpressions
35 Imports System.Threading
36 Imports System.Web
37 Imports System.Xml
38 Imports System.Xml.Linq
39
40 Public Class Twitter
41     Implements IDisposable
42
43     'Hashtag用正規表現
44     Private Const LATIN_ACCENTS As String = "\xc0-\xd6\xd8-\xf6\xf8-\xff"
45     Private Const NON_LATIN_HASHTAG_CHARS As String = "\u0400-\u04ff\u0500-\u0527\u1100-\u11ff\u3130-\u3185\uA960-\uA97F\uAC00-\uD7AF\uD7B0-\uD7FF"
46     'Private Const CJ_HASHTAG_CHARACTERS As String = "\u30A1-\u30FA\uFF66-\uFF9F\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\u3041-\u3096\u3400-\u4DBF\u4E00-\u9FFF\u20000-\u2A6DF\u2A700-\u2B73F\u2B740-\u2B81F\u2F800-\u2FA1F"
47     Private Const CJ_HASHTAG_CHARACTERS As String = "\u30A1-\u30FA\u30FC\u3005\uFF66-\uFF9F\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\u3041-\u309A\u3400-\u4DBF\p{IsCJKUnifiedIdeographs}"
48     Private Const HASHTAG_BOUNDARY As String = "^|$|\s|「|」|。|\.|!"
49     Private Const HASHTAG_ALPHA As String = "[a-z_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]"
50     Private Const HASHTAG_ALPHANUMERIC As String = "[a-z0-9_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]"
51     Private Const HASHTAG_TERMINATOR As String = "[^a-z0-9_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]"
52     Public Const HASHTAG As String = "(" + HASHTAG_BOUNDARY + ")(#|#)(" + HASHTAG_ALPHANUMERIC + "*" + HASHTAG_ALPHA + HASHTAG_ALPHANUMERIC + "*)(?=" + HASHTAG_TERMINATOR + "|" + HASHTAG_BOUNDARY + ")"
53     'URL正規表現
54     Private Const url_valid_domain As String = "(?<domain>(?:[^\p{P}\s][\.\-_](?=[^\p{P}\s])|[^\p{P}\s]){1,}\.[a-z]{2,}(?::[0-9]+)?)"
55     Private Const url_valid_general_path_chars As String = "[a-z0-9!*';:=+$/%#\[\]\-_,~]"
56     Private Const url_balance_parens As String = "(?:\(" + url_valid_general_path_chars + "+\))"
57     Private Const url_valid_url_path_ending_chars As String = "(?:[a-z0-9=_#/\-\+]+|" + url_balance_parens + ")"
58     Private Const pth As String = "(?:" + url_balance_parens +
59         "|@" + url_valid_general_path_chars + "+/" +
60         "|[.,]?" + url_valid_general_path_chars + "+" +
61         ")"
62     Private Const pth2 As String = "(/(?:" +
63         pth + "+" + url_valid_url_path_ending_chars + "|" +
64         pth + "+" + url_valid_url_path_ending_chars + "?|" +
65         url_valid_url_path_ending_chars +
66         ")?)?"
67     Private Const qry As String = "(?<query>\?[a-z0-9!*'();:&=+$/%#\[\]\-_.,~]*[a-z0-9_&=#])?"
68     Public Const rgUrl As String = "(?<before>(?:[^\""':!=#]|^|\:/))" +
69                                 "(?<url>(?<protocol>https?://)" +
70                                 url_valid_domain +
71                                 pth2 +
72                                 qry +
73                                 ")"
74     Delegate Sub GetIconImageDelegate(ByVal post As PostClass)
75     Private ReadOnly LockObj As New Object
76     Private followerId As New List(Of Long)
77     Private _GetFollowerResult As Boolean = False
78     Private noRTId As New List(Of Long)
79     Private _GetNoRetweetResult As Boolean = False
80
81     Private _followersCount As Integer = 0
82     Private _friendsCount As Integer = 0
83     Private _statusesCount As Integer = 0
84     Private _location As String = ""
85     Private _bio As String = ""
86     Private _protocol As String = "https://"
87
88     'プロパティからアクセスされる共通情報
89     Private _uname As String
90     Private _iconSz As Integer
91     Private _getIcon As Boolean
92     Private _dIcon As IDictionary(Of String, Image)
93
94     Private _tinyUrlResolve As Boolean
95     Private _restrictFavCheck As Boolean
96
97     Private _hubServer As String
98     Private _readOwnPost As Boolean
99     Private _hashList As New List(Of String)
100
101     '共通で使用する状態
102     Private _remainCountApi As Integer = -1
103
104     Private op As New Outputz
105     'max_idで古い発言を取得するために保持(lists分は個別タブで管理)
106     Private minHomeTimeline As Long = Long.MaxValue
107     Private minMentions As Long = Long.MaxValue
108     Private minDirectmessage As Long = Long.MaxValue
109     Private minDirectmessageSent As Long = Long.MaxValue
110
111     Private twCon As New HttpTwitter
112
113     Public Event UserIdChanged()
114
115     'Private _deletemessages As New List(Of PostClass)
116
117     Public Overloads Function Authenticate(ByVal username As String, ByVal password As String) As String
118
119         Dim res As HttpStatusCode
120         Dim content As String = ""
121
122         TwitterApiInfo.Initialize()
123         Try
124             res = twCon.AuthUserAndPass(username, password, content)
125         Catch ex As Exception
126             Return "Err:" + ex.Message
127         End Try
128
129         Select Case res
130             Case HttpStatusCode.OK
131                 Twitter.AccountState = ACCOUNT_STATE.Valid
132                 _uname = username.ToLower
133                 If AppendSettingDialog.Instance.UserstreamStartup Then Me.ReconnectUserStream()
134                 Return ""
135             Case HttpStatusCode.Unauthorized
136                 Twitter.AccountState = ACCOUNT_STATE.Invalid
137                 Dim errMsg As String = GetErrorMessageJson(content)
138                 If String.IsNullOrEmpty(errMsg) Then
139                     Return My.Resources.Unauthorized + Environment.NewLine + content
140                 Else
141                     Return "Auth error:" + errMsg
142                 End If
143             Case HttpStatusCode.Forbidden
144                 Dim errMsg As String = GetErrorMessageJson(content)
145                 If String.IsNullOrEmpty(errMsg) Then
146                     Return "Err:Forbidden"
147                 Else
148                     Return "Err:" + errMsg
149                 End If
150             Case Else
151                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
152         End Select
153
154     End Function
155
156     Public Function StartAuthentication(ByRef pinPageUrl As String) As String
157         'OAuth PIN Flow
158         Dim res As Boolean
159         Dim content As String = ""
160
161         TwitterApiInfo.Initialize()
162         Try
163             res = twCon.AuthGetRequestToken(pinPageUrl)
164         Catch ex As Exception
165             Return "Err:" + "Failed to access auth server."
166         End Try
167
168         Return ""
169     End Function
170
171     Public Overloads Function Authenticate(ByVal pinCode As String) As String
172
173         Dim res As HttpStatusCode
174         Dim content As String = ""
175
176         TwitterApiInfo.Initialize()
177         Try
178             res = twCon.AuthGetAccessToken(pinCode)
179         Catch ex As Exception
180             Return "Err:" + "Failed to access auth acc server."
181         End Try
182
183         Select Case res
184             Case HttpStatusCode.OK
185                 Twitter.AccountState = ACCOUNT_STATE.Valid
186                 _uname = Username.ToLower
187                 If AppendSettingDialog.Instance.UserstreamStartup Then Me.ReconnectUserStream()
188                 Google.GASender.GetInstance().TrackEventWithCategory("post", "authenticate", Me.UserId)
189                 Return ""
190             Case HttpStatusCode.Unauthorized
191                 Twitter.AccountState = ACCOUNT_STATE.Invalid
192                 Dim errMsg As String = GetErrorMessageJson(content)
193                 If String.IsNullOrEmpty(errMsg) Then
194                     Return "Check the PIN or retry." + Environment.NewLine + content
195                 Else
196                     Return "Auth error:" + errMsg
197                 End If
198             Case HttpStatusCode.Forbidden
199                 Dim errMsg As String = GetErrorMessageJson(content)
200                 If String.IsNullOrEmpty(errMsg) Then
201                     Return "Err:Forbidden"
202                 Else
203                     Return "Err:" + errMsg
204                 End If
205             Case Else
206                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
207         End Select
208
209     End Function
210
211     Public Sub ClearAuthInfo()
212         Twitter.AccountState = ACCOUNT_STATE.Invalid
213         TwitterApiInfo.Initialize()
214         twCon.ClearAuthInfo()
215     End Sub
216
217     Public Sub VerifyCredentials()
218         Dim res As HttpStatusCode
219         Dim content As String = ""
220
221         Try
222             res = twCon.VerifyCredentials(content)
223         Catch ex As Exception
224             Exit Sub
225         End Try
226
227         If res = HttpStatusCode.OK Then
228             Twitter.AccountState = ACCOUNT_STATE.Valid
229             Dim user As TwitterDataModel.User
230             Try
231                 user = CreateDataFromJson(Of TwitterDataModel.User)(content)
232             Catch ex As SerializationException
233                 Exit Sub
234             End Try
235             twCon.AuthenticatedUserId = user.Id
236         End If
237     End Sub
238
239     Private Function GetErrorMessageJson(ByVal content As String) As String
240         Try
241             If Not String.IsNullOrEmpty(content) Then
242                 Using jsonReader As XmlDictionaryReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(content), XmlDictionaryReaderQuotas.Max)
243                     Dim xElm As XElement = XElement.Load(jsonReader)
244                     If xElm.Element("error") IsNot Nothing Then
245                         Return xElm.Element("error").Value
246                     Else
247                         Return ""
248                     End If
249                 End Using
250             Else
251                 Return ""
252             End If
253         Catch ex As Exception
254             Return ""
255         End Try
256     End Function
257
258     Public Sub Initialize(ByVal token As String, ByVal tokenSecret As String, ByVal username As String, ByVal userId As Long)
259         'OAuth認証
260         If String.IsNullOrEmpty(token) OrElse String.IsNullOrEmpty(tokenSecret) OrElse String.IsNullOrEmpty(username) Then
261             Twitter.AccountState = ACCOUNT_STATE.Invalid
262         End If
263         TwitterApiInfo.Initialize()
264         twCon.Initialize(token, tokenSecret, username, userId)
265         _uname = username.ToLower
266         If AppendSettingDialog.Instance.UserstreamStartup Then Me.ReconnectUserStream()
267     End Sub
268
269     Public Function PreProcessUrl(ByVal orgData As String) As String
270         Dim posl1 As Integer
271         Dim posl2 As Integer = 0
272         'Dim IDNConveter As IdnMapping = New IdnMapping()
273         Dim href As String = "<a href="""
274
275         Do While True
276             If orgData.IndexOf(href, posl2, StringComparison.Ordinal) > -1 Then
277                 Dim urlStr As String = ""
278                 ' IDN展開
279                 posl1 = orgData.IndexOf(href, posl2, StringComparison.Ordinal)
280                 posl1 += href.Length
281                 posl2 = orgData.IndexOf("""", posl1, StringComparison.Ordinal)
282                 urlStr = orgData.Substring(posl1, posl2 - posl1)
283
284                 If Not urlStr.StartsWith("http://") AndAlso Not urlStr.StartsWith("https://") AndAlso Not urlStr.StartsWith("ftp://") Then
285                     Continue Do
286                 End If
287
288                 Dim replacedUrl As String = IDNDecode(urlStr)
289                 If replacedUrl Is Nothing Then Continue Do
290                 If replacedUrl = urlStr Then Continue Do
291
292                 orgData = orgData.Replace("<a href=""" + urlStr, "<a href=""" + replacedUrl)
293                 posl2 = 0
294             Else
295                 Exit Do
296             End If
297         Loop
298         Return orgData
299     End Function
300
301     Private Function GetPlainText(ByVal orgData As String) As String
302         Return HttpUtility.HtmlDecode(Regex.Replace(orgData, "(?<tagStart><a [^>]+>)(?<text>[^<]+)(?<tagEnd></a>)", "${text}"))
303     End Function
304
305     ' htmlの簡易サニタイズ(詳細表示に不要なタグの除去)
306
307     Private Function SanitizeHtml(ByVal orgdata As String) As String
308         Dim retdata As String = orgdata
309
310         retdata = Regex.Replace(retdata, "<(script|object|applet|image|frameset|fieldset|legend|style).*" & _
311             "</(script|object|applet|image|frameset|fieldset|legend|style)>", "", RegexOptions.IgnoreCase)
312
313         retdata = Regex.Replace(retdata, "<(frame|link|iframe|img)>", "", RegexOptions.IgnoreCase)
314
315         Return retdata
316     End Function
317
318     Private Function AdjustHtml(ByVal orgData As String) As String
319         Dim retStr As String = orgData
320         'Dim m As Match = Regex.Match(retStr, "<a [^>]+>[#|#](?<1>[a-zA-Z0-9_]+)</a>")
321         'While m.Success
322         '    SyncLock LockObj
323         '        _hashList.Add("#" + m.Groups(1).Value)
324         '    End SyncLock
325         '    m = m.NextMatch
326         'End While
327         retStr = Regex.Replace(retStr, "<a [^>]*href=""/", "<a href=""" + _protocol + "twitter.com/")
328         retStr = retStr.Replace("<a href=", "<a target=""_self"" href=")
329         retStr = retStr.Replace(vbLf, "<br>")
330
331         '半角スペースを置換(Thanks @anis774)
332         Dim ret As Boolean = False
333         Do
334             ret = EscapeSpace(retStr)
335         Loop While Not ret
336
337         Return SanitizeHtml(retStr)
338     End Function
339
340     Private Function EscapeSpace(ByRef html As String) As Boolean
341         '半角スペースを置換(Thanks @anis774)
342         Dim isTag As Boolean = False
343         For i As Integer = 0 To html.Length - 1
344             If html(i) = "<"c Then
345                 isTag = True
346             End If
347             If html(i) = ">"c Then
348                 isTag = False
349             End If
350
351             If (Not isTag) AndAlso (html(i) = " "c) Then
352                 html = html.Remove(i, 1)
353                 html = html.Insert(i, "&nbsp;")
354                 Return False
355             End If
356         Next
357         Return True
358     End Function
359
360     Private Structure PostInfo
361         Public CreatedAt As String
362         Public Id As String
363         Public Text As String
364         Public UserId As String
365         Public Sub New(ByVal Created As String, ByVal IdStr As String, ByVal txt As String, ByVal uid As String)
366             CreatedAt = Created
367             Id = IdStr
368             Text = txt
369             UserId = uid
370         End Sub
371         Public Shadows Function Equals(ByVal dst As PostInfo) As Boolean
372             If Me.CreatedAt = dst.CreatedAt AndAlso Me.Id = dst.Id AndAlso Me.Text = dst.Text AndAlso Me.UserId = dst.UserId Then
373                 Return True
374             Else
375                 Return False
376             End If
377         End Function
378     End Structure
379
380     Private Function IsPostRestricted(ByVal status As TwitterDataModel.Status) As Boolean
381         Static _prev As New PostInfo("", "", "", "")
382         Dim _current As New PostInfo("", "", "", "")
383
384         _current.CreatedAt = status.CreatedAt
385         _current.Id = status.IdStr
386         If status.Text Is Nothing Then
387             _current.Text = ""
388         Else
389             _current.Text = status.Text
390         End If
391         _current.UserId = status.User.IdStr
392
393         If _current.Equals(_prev) Then
394             Return True
395         End If
396         _prev.CreatedAt = _current.CreatedAt
397         _prev.Id = _current.Id
398         _prev.Text = _current.Text
399         _prev.UserId = _current.UserId
400
401         Return False
402     End Function
403
404     Public Function PostStatus(ByVal postStr As String, ByVal reply_to As Long) As String
405
406         If _endingFlag Then Return ""
407
408         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
409
410         postStr = postStr.Trim()
411
412         If Regex.Match(postStr, "^DM? +(?<id>[a-zA-Z0-9_]+) +(?<body>.+)", RegexOptions.IgnoreCase Or RegexOptions.Singleline).Success Then
413             Return SendDirectMessage(postStr)
414         End If
415
416         Google.GASender.GetInstance().TrackEventWithCategory("post", "status", Me.UserId)
417
418         Dim res As HttpStatusCode
419         Dim content As String = ""
420         Try
421             res = twCon.UpdateStatus(postStr, reply_to, content)
422         Catch ex As Exception
423             Return "Err:" + ex.Message
424         End Try
425
426         Select Case res
427             Case HttpStatusCode.OK
428                 Twitter.AccountState = ACCOUNT_STATE.Valid
429                 Dim status As TwitterDataModel.Status
430                 Try
431                     status = CreateDataFromJson(Of TwitterDataModel.Status)(content)
432                 Catch ex As SerializationException
433                     TraceOut(ex.Message + Environment.NewLine + content)
434                     Return "Err:Json Parse Error(DataContractJsonSerializer)"
435                 Catch ex As Exception
436                     TraceOut(ex, GetCurrentMethod.Name & " " & content)
437                     Return "Err:Invalid Json!"
438                 End Try
439                 _followersCount = status.User.FollowersCount
440                 _friendsCount = status.User.FriendsCount
441                 _statusesCount = status.User.StatusesCount
442                 _location = status.User.Location
443                 _bio = status.User.Description
444
445                 If IsPostRestricted(status) Then
446                     Return "OK:Delaying?"
447                 End If
448                 If op.Post(postStr.Length) Then
449                     Return ""
450                 Else
451                     Return "Outputz:Failed"
452                 End If
453             Case HttpStatusCode.NotFound
454                 Return ""
455             Case HttpStatusCode.Forbidden, HttpStatusCode.BadRequest
456                 Dim errMsg As String = GetErrorMessageJson(content)
457                 If String.IsNullOrEmpty(errMsg) Then
458                     Return "Warn:" + res.ToString
459                 Else
460                     Return "Warn:" + errMsg
461                 End If
462             Case HttpStatusCode.Conflict, _
463                 HttpStatusCode.ExpectationFailed, _
464                 HttpStatusCode.Gone, _
465                 HttpStatusCode.LengthRequired, _
466                 HttpStatusCode.MethodNotAllowed, _
467                 HttpStatusCode.NotAcceptable, _
468                 HttpStatusCode.NotFound, _
469                 HttpStatusCode.PaymentRequired, _
470                 HttpStatusCode.PreconditionFailed, _
471                 HttpStatusCode.RequestedRangeNotSatisfiable, _
472                 HttpStatusCode.RequestEntityTooLarge, _
473                 HttpStatusCode.RequestTimeout, _
474                 HttpStatusCode.RequestUriTooLong
475                 '仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
476                 Return "Warn:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
477             Case HttpStatusCode.Unauthorized
478                 Twitter.AccountState = ACCOUNT_STATE.Invalid
479                 Dim errMsg As String = GetErrorMessageJson(content)
480                 If String.IsNullOrEmpty(errMsg) Then
481                     Return My.Resources.Unauthorized
482                 Else
483                     Return "Auth err:" + errMsg
484                 End If
485             Case Else
486                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
487         End Select
488     End Function
489
490     Public Function PostStatusWithMedia(ByVal postStr As String, ByVal reply_to As Long, ByVal mediaFile As FileInfo) As String
491
492         If _endingFlag Then Return ""
493
494         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
495
496         postStr = postStr.Trim()
497
498         Google.GASender.GetInstance().TrackEventWithCategory("post", "status_with_media", Me.UserId)
499
500         Dim res As HttpStatusCode
501         Dim content As String = ""
502         Try
503             res = twCon.UpdateStatusWithMedia(postStr, reply_to, mediaFile, content)
504         Catch ex As Exception
505             Return "Err:" + ex.Message
506         End Try
507
508         Select Case res
509             Case HttpStatusCode.OK
510                 Twitter.AccountState = ACCOUNT_STATE.Valid
511                 Dim status As TwitterDataModel.Status
512                 Try
513                     status = CreateDataFromJson(Of TwitterDataModel.Status)(content)
514                 Catch ex As SerializationException
515                     TraceOut(ex.Message + Environment.NewLine + content)
516                     Return "Err:Json Parse Error(DataContractJsonSerializer)"
517                 Catch ex As Exception
518                     TraceOut(ex, GetCurrentMethod.Name & " " & content)
519                     Return "Err:Invalid Json!"
520                 End Try
521                 _followersCount = status.User.FollowersCount
522                 _friendsCount = status.User.FriendsCount
523                 _statusesCount = status.User.StatusesCount
524                 _location = status.User.Location
525                 _bio = status.User.Description
526
527                 If IsPostRestricted(status) Then
528                     Return "OK:Delaying?"
529                 End If
530                 If op.Post(postStr.Length) Then
531                     Return ""
532                 Else
533                     Return "Outputz:Failed"
534                 End If
535             Case HttpStatusCode.NotFound
536                 Return ""
537             Case HttpStatusCode.Forbidden, HttpStatusCode.BadRequest
538                 Dim errMsg As String = GetErrorMessageJson(content)
539                 If String.IsNullOrEmpty(errMsg) Then
540                     Return "Warn:" + res.ToString
541                 Else
542                     Return "Warn:" + errMsg
543                 End If
544             Case HttpStatusCode.Conflict, _
545                 HttpStatusCode.ExpectationFailed, _
546                 HttpStatusCode.Gone, _
547                 HttpStatusCode.LengthRequired, _
548                 HttpStatusCode.MethodNotAllowed, _
549                 HttpStatusCode.NotAcceptable, _
550                 HttpStatusCode.NotFound, _
551                 HttpStatusCode.PaymentRequired, _
552                 HttpStatusCode.PreconditionFailed, _
553                 HttpStatusCode.RequestedRangeNotSatisfiable, _
554                 HttpStatusCode.RequestEntityTooLarge, _
555                 HttpStatusCode.RequestTimeout, _
556                 HttpStatusCode.RequestUriTooLong
557                 '仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
558                 Return "Warn:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
559             Case HttpStatusCode.Unauthorized
560                 Twitter.AccountState = ACCOUNT_STATE.Invalid
561                 Dim errMsg As String = GetErrorMessageJson(content)
562                 If String.IsNullOrEmpty(errMsg) Then
563                     Return My.Resources.Unauthorized
564                 Else
565                     Return "Auth err:" + errMsg
566                 End If
567             Case Else
568                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
569         End Select
570     End Function
571
572     Public Function SendDirectMessage(ByVal postStr As String) As String
573
574         If _endingFlag Then Return ""
575
576         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
577         If Not TwitterApiInfo.IsDirectMessagePermission Then Return "Auth Err:try to re-authorization."
578
579         Google.GASender.GetInstance().TrackEventWithCategory("post", "direct_message", Me.UserId)
580
581         postStr = postStr.Trim()
582
583         Dim res As HttpStatusCode
584         Dim content As String = ""
585
586         Dim mc As Match = Regex.Match(postStr, "^DM? +(?<id>[a-zA-Z0-9_]+) +(?<body>.+)", RegexOptions.IgnoreCase Or RegexOptions.Singleline)
587
588         Try
589             res = twCon.SendDirectMessage(mc.Groups("body").Value, mc.Groups("id").Value, content)
590         Catch ex As Exception
591             Return "Err:" + ex.Message
592         End Try
593
594         Select Case res
595             Case HttpStatusCode.OK
596                 Twitter.AccountState = ACCOUNT_STATE.Valid
597                 Dim status As TwitterDataModel.Directmessage
598                 Try
599                     status = CreateDataFromJson(Of TwitterDataModel.Directmessage)(content)
600                 Catch ex As SerializationException
601                     TraceOut(ex.Message + Environment.NewLine + content)
602                     Return "Err:Json Parse Error(DataContractJsonSerializer)"
603                 Catch ex As Exception
604                     TraceOut(ex, GetCurrentMethod.Name & " " & content)
605                     Return "Err:Invalid Json!"
606                 End Try
607                 _followersCount = status.Sender.FollowersCount
608                 _friendsCount = status.Sender.FriendsCount
609                 _statusesCount = status.Sender.StatusesCount
610                 _location = status.Sender.Location
611                 _bio = status.Sender.Description
612
613                 If op.Post(postStr.Length) Then
614                     Return ""
615                 Else
616                     Return "Outputz:Failed"
617                 End If
618             Case HttpStatusCode.Forbidden, HttpStatusCode.BadRequest
619                 Dim errMsg As String = GetErrorMessageJson(content)
620                 If String.IsNullOrEmpty(errMsg) Then
621                     Return "Warn:" + res.ToString
622                 Else
623                     Return "Warn:" + errMsg
624                 End If
625             Case HttpStatusCode.Conflict, _
626                 HttpStatusCode.ExpectationFailed, _
627                 HttpStatusCode.Gone, _
628                 HttpStatusCode.LengthRequired, _
629                 HttpStatusCode.MethodNotAllowed, _
630                 HttpStatusCode.NotAcceptable, _
631                 HttpStatusCode.NotFound, _
632                 HttpStatusCode.PaymentRequired, _
633                 HttpStatusCode.PreconditionFailed, _
634                 HttpStatusCode.RequestedRangeNotSatisfiable, _
635                 HttpStatusCode.RequestEntityTooLarge, _
636                 HttpStatusCode.RequestTimeout, _
637                 HttpStatusCode.RequestUriTooLong
638                 '仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
639                 Return "Warn:" + res.ToString
640             Case HttpStatusCode.Unauthorized
641                 Twitter.AccountState = ACCOUNT_STATE.Invalid
642                 Dim errMsg As String = GetErrorMessageJson(content)
643                 If String.IsNullOrEmpty(errMsg) Then
644                     Return My.Resources.Unauthorized
645                 Else
646                     Return "Auth err:" + errMsg
647                 End If
648             Case Else
649                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
650         End Select
651     End Function
652
653     Public Function RemoveStatus(ByVal id As Long) As String
654         If _endingFlag Then Return ""
655
656         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
657
658         Google.GASender.GetInstance().TrackEventWithCategory("post", "destroy", Me.UserId)
659         Dim res As HttpStatusCode
660
661         Try
662             res = twCon.DestroyStatus(id)
663         Catch ex As Exception
664             Return "Err:" + ex.Message
665         End Try
666
667         Select Case res
668             Case HttpStatusCode.OK
669                 Twitter.AccountState = ACCOUNT_STATE.Valid
670                 Return ""
671             Case HttpStatusCode.Unauthorized
672                 Twitter.AccountState = ACCOUNT_STATE.Invalid
673                 Return My.Resources.Unauthorized
674             Case HttpStatusCode.NotFound
675                 Return ""
676             Case Else
677                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
678         End Select
679
680     End Function
681
682     Public Function PostRetweet(ByVal id As Long, ByVal read As Boolean) As String
683         If _endingFlag Then Return ""
684         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
685
686         'データ部分の生成
687         Dim target As Long = id
688         Dim post As PostClass = TabInformations.GetInstance.Item(id)
689         If post Is Nothing Then
690             Return "Err:Target isn't found."
691         End If
692         If TabInformations.GetInstance.Item(id).RetweetedId > 0 Then
693             target = TabInformations.GetInstance.Item(id).RetweetedId '再RTの場合は元発言をRT
694         End If
695
696         Google.GASender.GetInstance().TrackEventWithCategory("post", "retweet", Me.UserId)
697         Dim res As HttpStatusCode
698         Dim content As String = ""
699         Try
700             res = twCon.RetweetStatus(target, content)
701         Catch ex As Exception
702             Return "Err:" + ex.Message
703         End Try
704
705         Select Case res
706             Case HttpStatusCode.Unauthorized
707                 'Blockユーザーの発言をRTすると認証エラー返る
708                 'Twitter.AccountState = ACCOUNT_STATE.Invalid
709                 Return My.Resources.Unauthorized + " or blocked user."
710             Case Is <> HttpStatusCode.OK
711                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
712         End Select
713
714         Twitter.AccountState = ACCOUNT_STATE.Valid
715
716         Dim status As TwitterDataModel.Status
717         Try
718             status = CreateDataFromJson(Of TwitterDataModel.Status)(content)
719         Catch ex As SerializationException
720             TraceOut(ex.Message + Environment.NewLine + content)
721             Return "Err:Json Parse Error(DataContractJsonSerializer)"
722         Catch ex As Exception
723             TraceOut(ex, GetCurrentMethod.Name & " " & content)
724             Return "Err:Invalid Json!"
725         End Try
726
727         'ReTweetしたものをTLに追加
728         post = CreatePostsFromStatusData(status)
729         If post Is Nothing Then Return "Invalid Json!"
730
731         '二重取得回避
732         SyncLock LockObj
733             If TabInformations.GetInstance.ContainsKey(post.StatusId) Then Return ""
734         End SyncLock
735         'Retweet判定
736         If post.RetweetedId = 0 Then Return "Invalid Json!"
737         'ユーザー情報
738         post.IsMe = True
739
740         post.IsRead = read
741         post.IsOwl = False
742         If _readOwnPost Then post.IsRead = True
743         post.IsDm = False
744
745         TabInformations.GetInstance.AddPost(post)
746
747         Return ""
748     End Function
749
750     Public Function RemoveDirectMessage(ByVal id As Long, ByVal post As PostClass) As String
751         If _endingFlag Then Return ""
752
753         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
754         If Not TwitterApiInfo.IsDirectMessagePermission Then Return "Auth Err:try to re-authorization."
755
756         Google.GASender.GetInstance().TrackEventWithCategory("post", "destroy_direct_message", Me.UserId)
757         Dim res As HttpStatusCode
758
759         'If post.IsMe Then
760         '    _deletemessages.Add(post)
761         'End If
762         Try
763             res = twCon.DestroyDirectMessage(id)
764         Catch ex As Exception
765             Return "Err:" + ex.Message
766         End Try
767
768         Select Case res
769             Case HttpStatusCode.OK
770                 Twitter.AccountState = ACCOUNT_STATE.Valid
771                 Return ""
772             Case HttpStatusCode.Unauthorized
773                 Twitter.AccountState = ACCOUNT_STATE.Invalid
774                 Return My.Resources.Unauthorized
775             Case HttpStatusCode.NotFound
776                 Return ""
777             Case Else
778                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
779         End Select
780     End Function
781
782     Public Function PostFollowCommand(ByVal screenName As String) As String
783
784         If _endingFlag Then Return ""
785
786         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
787
788         Google.GASender.GetInstance().TrackEventWithCategory("post", "follow", Me.UserId)
789         Dim res As HttpStatusCode
790         Dim content As String = ""
791
792         Try
793             res = twCon.CreateFriendships(screenName, content)
794         Catch ex As Exception
795             Return "Err:" + ex.Message
796         End Try
797
798         Select Case res
799             Case HttpStatusCode.OK
800                 Twitter.AccountState = ACCOUNT_STATE.Valid
801                 Return ""
802             Case HttpStatusCode.Unauthorized
803                 Twitter.AccountState = ACCOUNT_STATE.Invalid
804                 Return My.Resources.Unauthorized
805             Case HttpStatusCode.Forbidden
806                 Dim errMsg As String = GetErrorMessageJson(content)
807                 If String.IsNullOrEmpty(errMsg) Then
808                     Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
809                 Else
810                     Return "Err:" + errMsg
811                 End If
812             Case Else
813                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
814         End Select
815     End Function
816
817     Public Function PostRemoveCommand(ByVal screenName As String) As String
818
819         If _endingFlag Then Return ""
820
821         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
822
823         Google.GASender.GetInstance().TrackEventWithCategory("post", "destroy_friendships", Me.UserId)
824         Dim res As HttpStatusCode
825         Dim content As String = ""
826
827         Try
828             res = twCon.DestroyFriendships(screenName, content)
829         Catch ex As Exception
830             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
831         End Try
832
833         Select Case res
834             Case HttpStatusCode.OK
835                 Twitter.AccountState = ACCOUNT_STATE.Valid
836                 Return ""
837             Case HttpStatusCode.Unauthorized
838                 Twitter.AccountState = ACCOUNT_STATE.Invalid
839                 Return My.Resources.Unauthorized
840             Case HttpStatusCode.Forbidden
841                 Dim errMsg As String = GetErrorMessageJson(content)
842                 If String.IsNullOrEmpty(errMsg) Then
843                     Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
844                 Else
845                     Return "Err:" + errMsg
846                 End If
847             Case Else
848                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
849         End Select
850     End Function
851
852     Public Function PostCreateBlock(ByVal screenName As String) As String
853
854         If _endingFlag Then Return ""
855
856         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
857
858         Google.GASender.GetInstance().TrackEventWithCategory("post", "block", Me.UserId)
859         Dim res As HttpStatusCode
860         Dim content As String = ""
861
862         Try
863             res = twCon.CreateBlock(screenName, content)
864         Catch ex As Exception
865             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
866         End Try
867
868         Select Case res
869             Case HttpStatusCode.OK
870                 Twitter.AccountState = ACCOUNT_STATE.Valid
871                 Return ""
872             Case HttpStatusCode.Unauthorized
873                 Twitter.AccountState = ACCOUNT_STATE.Invalid
874                 Return My.Resources.Unauthorized
875             Case HttpStatusCode.Forbidden
876                 Dim errMsg As String = GetErrorMessageJson(content)
877                 If String.IsNullOrEmpty(errMsg) Then
878                     Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
879                 Else
880                     Return "Err:" + errMsg
881                 End If
882             Case Else
883                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
884         End Select
885     End Function
886
887     Public Function PostDestroyBlock(ByVal screenName As String) As String
888
889         If _endingFlag Then Return ""
890
891         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
892
893         Google.GASender.GetInstance().TrackEventWithCategory("post", "destroy_block", Me.UserId)
894         Dim res As HttpStatusCode
895         Dim content As String = ""
896
897         Try
898             res = twCon.DestroyBlock(screenName, content)
899         Catch ex As Exception
900             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
901         End Try
902
903         Select Case res
904             Case HttpStatusCode.OK
905                 Twitter.AccountState = ACCOUNT_STATE.Valid
906                 Return ""
907             Case HttpStatusCode.Unauthorized
908                 Twitter.AccountState = ACCOUNT_STATE.Invalid
909                 Return My.Resources.Unauthorized
910             Case HttpStatusCode.Forbidden
911                 Dim errMsg As String = GetErrorMessageJson(content)
912                 If String.IsNullOrEmpty(errMsg) Then
913                     Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
914                 Else
915                     Return "Err:" + errMsg
916                 End If
917             Case Else
918                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
919         End Select
920     End Function
921
922     Public Function PostReportSpam(ByVal screenName As String) As String
923
924         If _endingFlag Then Return ""
925
926         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
927
928         Google.GASender.GetInstance().TrackEventWithCategory("post", "spam", Me.UserId)
929         Dim res As HttpStatusCode
930         Dim content As String = ""
931
932         Try
933             res = twCon.ReportSpam(screenName, content)
934         Catch ex As Exception
935             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
936         End Try
937
938         Select Case res
939             Case HttpStatusCode.OK
940                 Twitter.AccountState = ACCOUNT_STATE.Valid
941                 Return ""
942             Case HttpStatusCode.Unauthorized
943                 Twitter.AccountState = ACCOUNT_STATE.Invalid
944                 Return My.Resources.Unauthorized
945             Case HttpStatusCode.Forbidden
946                 Dim errMsg As String = GetErrorMessageJson(content)
947                 If String.IsNullOrEmpty(errMsg) Then
948                     Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
949                 Else
950                     Return "Err:" + errMsg
951                 End If
952             Case Else
953                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
954         End Select
955     End Function
956
957     Public Function GetFriendshipInfo(ByVal screenName As String, ByRef isFollowing As Boolean, ByRef isFollowed As Boolean) As String
958
959         If _endingFlag Then Return ""
960
961         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
962
963         Google.GASender.GetInstance().TrackPage("/friendships", Me.UserId)
964         Dim res As HttpStatusCode
965         Dim content As String = ""
966         Try
967             res = twCon.ShowFriendships(_uname, screenName, content)
968         Catch ex As Exception
969             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
970         End Try
971
972         Select Case res
973             Case HttpStatusCode.OK
974                 Try
975                     Dim relation = CreateDataFromJson(Of TwitterDataModel.Relationship)(content)
976                     isFollowing = relation.Relationship.Source.Following
977                     isFollowed = relation.Relationship.Source.FollowedBy
978                     Return ""
979                 Catch ex As SerializationException
980                     TraceOut(ex.Message + Environment.NewLine + content)
981                     Return "Err:Json Parse Error(DataContractJsonSerializer)"
982                 Catch ex As Exception
983                     TraceOut(ex, GetCurrentMethod.Name & " " & content)
984                     Return "Err:Invalid Json!"
985                 End Try
986             Case HttpStatusCode.BadRequest
987                 Return "Err:API Limits?"
988             Case HttpStatusCode.Unauthorized
989                 Twitter.AccountState = ACCOUNT_STATE.Invalid
990                 Return My.Resources.Unauthorized
991             Case Else
992                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
993         End Select
994     End Function
995
996     Public Function GetUserInfo(ByVal screenName As String, ByRef user As TwitterDataModel.User) As String
997
998         If _endingFlag Then Return ""
999
1000         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
1001
1002         Google.GASender.GetInstance().TrackPage("/showuser", Me.UserId)
1003         Dim res As HttpStatusCode
1004         Dim content As String = ""
1005         user = Nothing
1006         Try
1007             res = twCon.ShowUserInfo(screenName, content)
1008         Catch ex As Exception
1009             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
1010         End Try
1011
1012         Select Case res
1013             Case HttpStatusCode.OK
1014                 Twitter.AccountState = ACCOUNT_STATE.Valid
1015                 Try
1016                     user = CreateDataFromJson(Of TwitterDataModel.User)(content)
1017                 Catch ex As SerializationException
1018                     TraceOut(ex.Message + Environment.NewLine + content)
1019                     Return "Err:Json Parse Error(DataContractJsonSerializer)"
1020                 Catch ex As Exception
1021                     TraceOut(ex, GetCurrentMethod.Name & " " & content)
1022                     Return "Err:Invalid Json!"
1023                 End Try
1024                 Return ""
1025             Case HttpStatusCode.BadRequest
1026                 Return "Err:API Limits?"
1027             Case HttpStatusCode.Unauthorized
1028                 Twitter.AccountState = ACCOUNT_STATE.Invalid
1029                 Dim errMsg As String = GetErrorMessageJson(content)
1030                 If String.IsNullOrEmpty(errMsg) Then
1031                     Return My.Resources.Unauthorized
1032                 Else
1033                     Return "Auth err:" + errMsg
1034                 End If
1035             Case Else
1036                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
1037         End Select
1038     End Function
1039
1040     Public Function GetStatus_Retweeted_Count(ByVal StatusId As Long, ByRef retweeted_count As Integer) As String
1041
1042         If _endingFlag Then Return ""
1043
1044         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
1045
1046         Google.GASender.GetInstance().TrackPage("/retweet_count", Me.UserId)
1047         Dim res As HttpStatusCode
1048         Dim content As String = ""
1049         Dim xmlBuf As String = ""
1050
1051         retweeted_count = 0
1052
1053         ' 注:dev.twitter.comに記述されているcountパラメータは間違い。100が正しい
1054         For i As Integer = 1 To 100
1055
1056             Try
1057                 res = twCon.Statusid_retweeted_by_ids(StatusId, 100, i, content)
1058             Catch ex As Exception
1059                 Return "Err:" + ex.Message
1060             End Try
1061
1062             Select Case res
1063                 Case HttpStatusCode.OK
1064                     Try
1065                         Dim ids As Int64() = CreateDataFromJson(Of Int64())(content)
1066                         retweeted_count += ids.Length
1067                         If ids.Length < 100 Then Exit For
1068                     Catch ex As SerializationException
1069                         retweeted_count = -1
1070                         TraceOut(ex.Message + Environment.NewLine + content)
1071                         Return "Err:Json Parse Error(DataContractJsonSerializer)"
1072                     Catch ex As Exception
1073                         retweeted_count = -1
1074                         TraceOut(ex, GetCurrentMethod.Name & " " & content)
1075                         Return "Err:Invalid Json!"
1076                     End Try
1077                 Case HttpStatusCode.BadRequest
1078                     retweeted_count = -1
1079                     Return "Err:API Limits?"
1080                 Case HttpStatusCode.Unauthorized
1081                     retweeted_count = -1
1082                     Twitter.AccountState = ACCOUNT_STATE.Invalid
1083                     Return My.Resources.Unauthorized
1084                 Case Else
1085                     retweeted_count = -1
1086                     Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
1087             End Select
1088         Next
1089         Return ""
1090     End Function
1091
1092     Public Function PostFavAdd(ByVal id As Long) As String
1093         If _endingFlag Then Return ""
1094
1095         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
1096
1097         Google.GASender.GetInstance().TrackEventWithCategory("post", "favorites", Me.UserId)
1098         Dim res As HttpStatusCode
1099         Dim content As String = ""
1100         Try
1101             res = twCon.CreateFavorites(id, content)
1102         Catch ex As Exception
1103             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
1104         End Try
1105
1106         Select Case res
1107             Case HttpStatusCode.OK
1108                 Twitter.AccountState = ACCOUNT_STATE.Valid
1109                 If Not _restrictFavCheck Then Return ""
1110             Case HttpStatusCode.Unauthorized
1111                 Twitter.AccountState = ACCOUNT_STATE.Invalid
1112                 Return My.Resources.Unauthorized
1113             Case HttpStatusCode.Forbidden
1114                 Dim errMsg As String = GetErrorMessageJson(content)
1115                 If String.IsNullOrEmpty(errMsg) Then
1116                     Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
1117                 Else
1118                     Return "Err:" + errMsg
1119                 End If
1120             Case Else
1121                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
1122         End Select
1123
1124         'http://twitter.com/statuses/show/id.xml APIを発行して本文を取得
1125
1126         'Dim content As String = ""
1127         content = ""
1128         Try
1129             res = twCon.ShowStatuses(id, content)
1130         Catch ex As Exception
1131             Return "Err:" + ex.Message
1132         End Try
1133
1134         Select Case res
1135             Case HttpStatusCode.OK
1136                 Twitter.AccountState = ACCOUNT_STATE.Valid
1137                 Dim status As TwitterDataModel.Status
1138                 Try
1139                     status = CreateDataFromJson(Of TwitterDataModel.Status)(content)
1140                 Catch ex As SerializationException
1141                     TraceOut(ex.Message + Environment.NewLine + content)
1142                     Return "Err:Json Parse Error(DataContractJsonSerializer)"
1143                 Catch ex As Exception
1144                     TraceOut(ex, GetCurrentMethod.Name & " " & content)
1145                     Return "Err:Invalid Json!"
1146                 End Try
1147                 If status.Favorited Then
1148                     Return ""
1149                 Else
1150                     Return "NG(Restricted?)"
1151                 End If
1152             Case HttpStatusCode.Unauthorized
1153                 Twitter.AccountState = ACCOUNT_STATE.Invalid
1154                 Return My.Resources.Unauthorized
1155             Case HttpStatusCode.BadRequest
1156                 Return "Err:API Limits?"
1157             Case Else
1158                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
1159         End Select
1160
1161     End Function
1162
1163     Public Function PostFavRemove(ByVal id As Long) As String
1164         If _endingFlag Then Return ""
1165
1166         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
1167
1168         Google.GASender.GetInstance().TrackEventWithCategory("post", "destroy_favorites", Me.UserId)
1169         Dim res As HttpStatusCode
1170         Dim content As String = ""
1171         Try
1172             res = twCon.DestroyFavorites(id, content)
1173         Catch ex As Exception
1174             Return "Err:" + ex.Message
1175         End Try
1176
1177         Select Case res
1178             Case HttpStatusCode.OK
1179                 Twitter.AccountState = ACCOUNT_STATE.Valid
1180                 Return ""
1181             Case HttpStatusCode.Unauthorized
1182                 Twitter.AccountState = ACCOUNT_STATE.Invalid
1183                 Return My.Resources.Unauthorized
1184             Case HttpStatusCode.Forbidden
1185                 Dim errMsg As String = GetErrorMessageJson(content)
1186                 If String.IsNullOrEmpty(errMsg) Then
1187                     Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
1188                 Else
1189                     Return "Err:" + errMsg
1190                 End If
1191             Case Else
1192                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
1193         End Select
1194     End Function
1195
1196     Public Function PostUpdateProfile(ByVal name As String, ByVal url As String, ByVal location As String, ByVal description As String) As String
1197         If _endingFlag Then Return ""
1198
1199         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
1200
1201         Google.GASender.GetInstance().TrackEventWithCategory("post", "update_profile", Me.UserId)
1202         Dim res As HttpStatusCode
1203         Dim content As String = ""
1204         Try
1205             res = twCon.UpdateProfile(name, url, location, description, content)
1206         Catch ex As Exception
1207             Return "Err:" + ex.Message
1208         End Try
1209
1210         Select Case res
1211             Case HttpStatusCode.OK
1212                 Twitter.AccountState = ACCOUNT_STATE.Valid
1213                 Return ""
1214             Case HttpStatusCode.Unauthorized
1215                 Twitter.AccountState = ACCOUNT_STATE.Invalid
1216                 Return My.Resources.Unauthorized
1217             Case HttpStatusCode.Forbidden
1218                 Dim errMsg As String = GetErrorMessageJson(content)
1219                 If String.IsNullOrEmpty(errMsg) Then
1220                     Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
1221                 Else
1222                     Return "Err:" + errMsg
1223                 End If
1224             Case Else
1225                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
1226         End Select
1227     End Function
1228
1229     Public Function PostUpdateProfileImage(ByVal filename As String) As String
1230         If _endingFlag Then Return ""
1231
1232         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
1233
1234         Google.GASender.GetInstance().TrackEventWithCategory("post", "update_profile_image", Me.UserId)
1235         Dim res As HttpStatusCode
1236         Dim content As String = ""
1237         Try
1238             res = twCon.UpdateProfileImage(New FileInfo(filename), content)
1239         Catch ex As Exception
1240             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
1241         End Try
1242
1243         Select Case res
1244             Case HttpStatusCode.OK
1245                 Twitter.AccountState = ACCOUNT_STATE.Valid
1246                 Return ""
1247             Case HttpStatusCode.Unauthorized
1248                 Twitter.AccountState = ACCOUNT_STATE.Invalid
1249                 Return My.Resources.Unauthorized
1250             Case HttpStatusCode.Forbidden
1251                 Dim errMsg As String = GetErrorMessageJson(content)
1252                 If String.IsNullOrEmpty(errMsg) Then
1253                     Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
1254                 Else
1255                     Return "Err:" + errMsg
1256                 End If
1257             Case Else
1258                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
1259         End Select
1260     End Function
1261
1262     Public ReadOnly Property Username() As String
1263         Get
1264             Return twCon.AuthenticatedUsername
1265         End Get
1266     End Property
1267
1268     Public ReadOnly Property UserId As Long
1269         Get
1270             Return twCon.AuthenticatedUserId
1271         End Get
1272     End Property
1273
1274     Public ReadOnly Property Password() As String
1275         Get
1276             Return twCon.Password
1277         End Get
1278     End Property
1279
1280     Private Shared _accountState As ACCOUNT_STATE = ACCOUNT_STATE.Valid
1281     Public Shared Property AccountState() As ACCOUNT_STATE
1282         Get
1283             Return _accountState
1284         End Get
1285         Set(ByVal value As ACCOUNT_STATE)
1286             _accountState = value
1287         End Set
1288     End Property
1289
1290     Public WriteOnly Property GetIcon() As Boolean
1291         Set(ByVal value As Boolean)
1292             _getIcon = value
1293         End Set
1294     End Property
1295
1296     Public WriteOnly Property TinyUrlResolve() As Boolean
1297         Set(ByVal value As Boolean)
1298             _tinyUrlResolve = value
1299         End Set
1300     End Property
1301
1302     Public WriteOnly Property RestrictFavCheck() As Boolean
1303         Set(ByVal value As Boolean)
1304             _restrictFavCheck = value
1305         End Set
1306     End Property
1307
1308     Public WriteOnly Property IconSize() As Integer
1309         Set(ByVal value As Integer)
1310             _iconSz = value
1311         End Set
1312     End Property
1313
1314 #Region "バージョンアップ"
1315     Public Function GetVersionInfo() As String
1316         Dim content As String = ""
1317         If Not (New HttpVarious).GetData("http://tween.sourceforge.jp/version.txt?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), Nothing, content, GetUserAgentString()) Then
1318             Throw New Exception("GetVersionInfo Failed")
1319         End If
1320         Return content
1321     End Function
1322
1323     Public Function GetTweenBinary(ByVal strVer As String) As String
1324         Google.GASender.GetInstance().TrackPage("/newversion", Me.UserId)
1325         Try
1326             '本体
1327             If Not (New HttpVarious).GetDataToFile("http://tween.sourceforge.jp/Tween" + strVer + ".gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), _
1328                                                 Path.Combine(MyCommon.settingPath, "TweenNew.exe")) Then
1329                 Return "Err:Download failed"
1330             End If
1331             '英語リソース
1332             If Not Directory.Exists(Path.Combine(MyCommon.settingPath, "en")) Then
1333                 Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, "en"))
1334             End If
1335             If Not (New HttpVarious).GetDataToFile("http://tween.sourceforge.jp/TweenResEn" + strVer + ".gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), _
1336                                                 Path.Combine(Path.Combine(MyCommon.settingPath, "en"), "Tween.resourcesNew.dll")) Then
1337                 Return "Err:Download failed"
1338             End If
1339             'その他言語圏のリソース。取得失敗しても継続
1340             'UIの言語圏のリソース
1341             Dim curCul As String = ""
1342             If Not Thread.CurrentThread.CurrentUICulture.IsNeutralCulture Then
1343                 Dim idx As Integer = Thread.CurrentThread.CurrentUICulture.Name.LastIndexOf("-"c)
1344                 If idx > -1 Then
1345                     curCul = Thread.CurrentThread.CurrentUICulture.Name.Substring(0, idx)
1346                 Else
1347                     curCul = Thread.CurrentThread.CurrentUICulture.Name
1348                 End If
1349             Else
1350                 curCul = Thread.CurrentThread.CurrentUICulture.Name
1351             End If
1352             If Not String.IsNullOrEmpty(curCul) AndAlso curCul <> "en" AndAlso curCul <> "ja" Then
1353                 If Not Directory.Exists(Path.Combine(MyCommon.settingPath, curCul)) Then
1354                     Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, curCul))
1355                 End If
1356                 If Not (New HttpVarious).GetDataToFile("http://tween.sourceforge.jp/TweenRes" + curCul + strVer + ".gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), _
1357                                                     Path.Combine(Path.Combine(MyCommon.settingPath, curCul), "Tween.resourcesNew.dll")) Then
1358                     'Return "Err:Download failed"
1359                 End If
1360             End If
1361             'スレッドの言語圏のリソース
1362             Dim curCul2 As String
1363             If Not Thread.CurrentThread.CurrentCulture.IsNeutralCulture Then
1364                 Dim idx As Integer = Thread.CurrentThread.CurrentCulture.Name.LastIndexOf("-"c)
1365                 If idx > -1 Then
1366                     curCul2 = Thread.CurrentThread.CurrentCulture.Name.Substring(0, idx)
1367                 Else
1368                     curCul2 = Thread.CurrentThread.CurrentCulture.Name
1369                 End If
1370             Else
1371                 curCul2 = Thread.CurrentThread.CurrentCulture.Name
1372             End If
1373             If Not String.IsNullOrEmpty(curCul2) AndAlso curCul2 <> "en" AndAlso curCul2 <> curCul Then
1374                 If Not Directory.Exists(Path.Combine(MyCommon.settingPath, curCul2)) Then
1375                     Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, curCul2))
1376                 End If
1377                 If Not (New HttpVarious).GetDataToFile("http://tween.sourceforge.jp/TweenRes" + curCul2 + strVer + ".gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), _
1378                                                 Path.Combine(Path.Combine(MyCommon.settingPath, curCul2), "Tween.resourcesNew.dll")) Then
1379                     'Return "Err:Download failed"
1380                 End If
1381             End If
1382
1383             'アップデータ
1384             If Not (New HttpVarious).GetDataToFile("http://tween.sourceforge.jp/TweenUp3.gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), _
1385                                                 Path.Combine(MyCommon.settingPath, "TweenUp3.exe")) Then
1386                 Return "Err:Download failed"
1387             End If
1388             'シリアライザDLL
1389             If Not (New HttpVarious).GetDataToFile("http://tween.sourceforge.jp/TweenDll" + strVer + ".gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), _
1390                                                 Path.Combine(MyCommon.settingPath, "TweenNew.XmlSerializers.dll")) Then
1391                 Return "Err:Download failed"
1392             End If
1393             Return ""
1394         Catch ex As Exception
1395             Return "Err:Download failed"
1396         End Try
1397     End Function
1398 #End Region
1399
1400     Public Property DetailIcon() As IDictionary(Of String, Image)
1401         Get
1402             Return _dIcon
1403         End Get
1404         Set(ByVal value As IDictionary(Of String, Image))
1405             _dIcon = value
1406         End Set
1407     End Property
1408
1409     Public Property ReadOwnPost() As Boolean
1410         Get
1411             Return _readOwnPost
1412         End Get
1413         Set(ByVal value As Boolean)
1414             _readOwnPost = value
1415         End Set
1416     End Property
1417
1418     Public ReadOnly Property FollowersCount() As Integer
1419         Get
1420             Return _followersCount
1421         End Get
1422     End Property
1423
1424     Public ReadOnly Property FriendsCount() As Integer
1425         Get
1426             Return _friendsCount
1427         End Get
1428     End Property
1429
1430     Public ReadOnly Property StatusesCount() As Integer
1431         Get
1432             Return _statusesCount
1433         End Get
1434     End Property
1435
1436     Public ReadOnly Property Location() As String
1437         Get
1438             Return _location
1439         End Get
1440     End Property
1441
1442     Public ReadOnly Property Bio() As String
1443         Get
1444             Return _bio
1445         End Get
1446     End Property
1447
1448     Public WriteOnly Property UseSsl() As Boolean
1449         Set(ByVal value As Boolean)
1450             HttpTwitter.UseSsl = value
1451             If value Then
1452                 _protocol = "https://"
1453             Else
1454                 _protocol = "http://"
1455             End If
1456         End Set
1457     End Property
1458
1459     Public Function GetTimelineApi(ByVal read As Boolean, _
1460                             ByVal gType As WORKERTYPE, _
1461                             ByVal more As Boolean, _
1462                             ByVal startup As Boolean) As String
1463
1464         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
1465
1466         If _endingFlag Then Return ""
1467
1468         Dim res As HttpStatusCode
1469         Dim content As String = ""
1470         Dim count As Integer = AppendSettingDialog.Instance.CountApi
1471         If gType = WORKERTYPE.Reply Then count = AppendSettingDialog.Instance.CountApiReply()
1472         If AppendSettingDialog.Instance.UseAdditionalCount Then
1473             If more AndAlso AppendSettingDialog.Instance.MoreCountApi <> 0 Then
1474                 count = AppendSettingDialog.Instance.MoreCountApi
1475             ElseIf startup AndAlso AppendSettingDialog.Instance.FirstCountApi <> 0 AndAlso gType = WORKERTYPE.Timeline Then
1476                 count = AppendSettingDialog.Instance.FirstCountApi
1477             End If
1478         End If
1479         Try
1480             If gType = WORKERTYPE.Timeline Then
1481                 If more Then
1482                     res = twCon.HomeTimeline(count, Me.minHomeTimeline, 0, content)
1483                 Else
1484                     res = twCon.HomeTimeline(count, 0, 0, content)
1485                 End If
1486             Else
1487                 If more Then
1488                     res = twCon.Mentions(count, Me.minMentions, 0, content)
1489                 Else
1490                     res = twCon.Mentions(count, 0, 0, content)
1491                 End If
1492             End If
1493         Catch ex As Exception
1494             Return "Err:" + ex.Message
1495         End Try
1496         Select Case res
1497             Case HttpStatusCode.OK
1498                 Twitter.AccountState = ACCOUNT_STATE.Valid
1499             Case HttpStatusCode.Unauthorized
1500                 Twitter.AccountState = ACCOUNT_STATE.Invalid
1501                 Return My.Resources.Unauthorized
1502             Case HttpStatusCode.BadRequest
1503                 Return "Err:API Limits?"
1504             Case Else
1505                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
1506         End Select
1507
1508         If gType = WORKERTYPE.Timeline Then
1509             Return CreatePostsFromJson(content, gType, Nothing, read, count, Me.minHomeTimeline)
1510         Else
1511             Return CreatePostsFromJson(content, gType, Nothing, read, count, Me.minMentions)
1512         End If
1513     End Function
1514
1515     Public Function GetUserTimelineApi(ByVal read As Boolean,
1516                                        ByVal count As Integer,
1517                                        ByVal userName As String,
1518                                        ByVal tab As TabClass,
1519                                        ByVal more As Boolean) As String
1520
1521         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
1522
1523         If _endingFlag Then Return ""
1524
1525         Dim res As HttpStatusCode
1526         Dim content As String = ""
1527
1528         If count = 0 Then count = 20
1529         Try
1530             If String.IsNullOrEmpty(userName) Then
1531                 Dim target As String = tab.User
1532                 If target Is Nothing Then Return ""
1533                 res = twCon.UserTimeline(0, target, count, 0, 0, content)
1534             Else
1535                 If more Then
1536                     res = twCon.UserTimeline(0, userName, count, tab.OldestId, 0, content)
1537                 Else
1538                     res = twCon.UserTimeline(0, userName, count, 0, 0, content)
1539                 End If
1540             End If
1541         Catch ex As Exception
1542             Return "Err:" + ex.Message
1543         End Try
1544         Select Case res
1545             Case HttpStatusCode.OK
1546                 Twitter.AccountState = ACCOUNT_STATE.Valid
1547             Case HttpStatusCode.Unauthorized
1548                 Twitter.AccountState = ACCOUNT_STATE.Valid
1549                 Return "Err:@" + userName + "'s Tweets are protected."
1550             Case HttpStatusCode.BadRequest
1551                 Return "Err:API Limits?"
1552             Case Else
1553                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
1554         End Select
1555
1556         Dim items As List(Of TwitterDataModel.Status)
1557         Try
1558             items = CreateDataFromJson(Of List(Of TwitterDataModel.Status))(content)
1559         Catch ex As SerializationException
1560             TraceOut(ex.Message + Environment.NewLine + content)
1561             Return "Json Parse Error(DataContractJsonSerializer)"
1562         Catch ex As Exception
1563             TraceOut(ex, GetCurrentMethod.Name & " " & content)
1564             Return "Invalid Json!"
1565         End Try
1566
1567         For Each status As TwitterDataModel.Status In items
1568             Dim item As PostClass = CreatePostsFromStatusData(status)
1569             If item Is Nothing Then Continue For
1570             If item.StatusId < tab.OldestId Then tab.OldestId = item.StatusId
1571             item.IsRead = read
1572             If item.IsMe AndAlso Not read AndAlso _readOwnPost Then item.IsRead = True
1573             If tab IsNot Nothing Then item.RelTabName = tab.TabName
1574             '非同期アイコン取得&StatusDictionaryに追加
1575             TabInformations.GetInstance.AddPost(item)
1576         Next
1577
1578         Return ""
1579     End Function
1580
1581     Public Function GetStatusApi(ByVal read As Boolean,
1582                                        ByVal id As Int64,
1583                                        ByRef post As PostClass) As String
1584         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
1585
1586         If _endingFlag Then Return ""
1587
1588         Google.GASender.GetInstance().TrackPage("/showstatus", Me.UserId)
1589         Dim res As HttpStatusCode
1590         Dim content As String = ""
1591
1592         Try
1593             res = twCon.ShowStatuses(id, content)
1594         Catch ex As Exception
1595             Return "Err:" + ex.Message
1596         End Try
1597         Select Case res
1598             Case HttpStatusCode.OK
1599                 Twitter.AccountState = ACCOUNT_STATE.Valid
1600             Case HttpStatusCode.Unauthorized
1601                 Twitter.AccountState = ACCOUNT_STATE.Invalid
1602                 Return My.Resources.Unauthorized
1603             Case HttpStatusCode.BadRequest
1604                 Return "Err:API Limits?"
1605             Case HttpStatusCode.Forbidden
1606                 Return "Err:Protected user's tweet"
1607             Case Else
1608                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
1609         End Select
1610
1611         Dim status As TwitterDataModel.Status
1612         Try
1613             status = CreateDataFromJson(Of TwitterDataModel.Status)(content)
1614         Catch ex As SerializationException
1615             TraceOut(ex.Message + Environment.NewLine + content)
1616             Return "Json Parse Error(DataContractJsonSerializer)"
1617         Catch ex As Exception
1618             TraceOut(ex, GetCurrentMethod.Name & " " & content)
1619             Return "Invalid Json!"
1620         End Try
1621
1622         Dim item As PostClass = CreatePostsFromStatusData(status)
1623         If item Is Nothing Then Return "Err:Can't create post"
1624         item.IsRead = read
1625         If item.IsMe AndAlso Not read AndAlso _readOwnPost Then item.IsRead = True
1626
1627         post = item
1628         Return ""
1629     End Function
1630
1631     Public Function GetStatusApi(ByVal read As Boolean,
1632                                        ByVal id As Int64,
1633                                        ByVal tab As TabClass) As String
1634         Dim post As PostClass = Nothing
1635         Dim r As String = Me.GetStatusApi(read, id, post)
1636
1637         If r = "" Then
1638             If tab IsNot Nothing Then post.RelTabName = tab.TabName
1639             '非同期アイコン取得&StatusDictionaryに追加
1640             TabInformations.GetInstance.AddPost(post)
1641         End If
1642
1643         Return r
1644     End Function
1645
1646     Private Function CreatePostsFromStatusData(ByVal status As TwitterDataModel.Status) As PostClass
1647         Dim post As New PostClass
1648         Dim entities As TwitterDataModel.Entities
1649
1650         post.StatusId = status.Id
1651         If status.RetweetedStatus IsNot Nothing Then
1652             Dim retweeted As TwitterDataModel.RetweetedStatus = status.RetweetedStatus
1653
1654             post.CreatedAt = DateTimeParse(retweeted.CreatedAt)
1655
1656             'Id
1657             post.RetweetedId = retweeted.Id
1658             '本文
1659             post.TextFromApi = retweeted.Text
1660             entities = retweeted.Entities
1661             'Source取得(htmlの場合は、中身を取り出し)
1662             post.Source = retweeted.Source
1663             'Reply先
1664             Long.TryParse(retweeted.InReplyToStatusId, post.InReplyToStatusId)
1665             post.InReplyToUser = retweeted.InReplyToScreenName
1666             Long.TryParse(status.InReplyToUserId, post.InReplyToUserId)
1667             post.IsFav = TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites).Contains(post.RetweetedId)
1668             If retweeted.Geo IsNot Nothing Then post.PostGeo = New PostClass.StatusGeo() With {.Lat = retweeted.Geo.Coordinates(0), .Lng = retweeted.Geo.Coordinates(1)}
1669
1670             '以下、ユーザー情報
1671             Dim user As TwitterDataModel.User = retweeted.User
1672
1673             If user.ScreenName Is Nothing OrElse status.User.ScreenName Is Nothing Then Return Nothing
1674
1675             post.UserId = user.Id
1676             post.ScreenName = user.ScreenName
1677             post.Nickname = user.Name.Trim()
1678             post.ImageUrl = user.ProfileImageUrl
1679             post.IsProtect = user.Protected
1680
1681             'Retweetした人
1682             post.RetweetedBy = status.User.ScreenName
1683             post.RetweetedByUserId = status.User.Id
1684             post.IsMe = post.RetweetedBy.ToLower.Equals(_uname)
1685         Else
1686             post.CreatedAt = DateTimeParse(status.CreatedAt)
1687             '本文
1688             post.TextFromApi = status.Text
1689             entities = status.Entities
1690             'Source取得(htmlの場合は、中身を取り出し)
1691             post.Source = status.Source
1692             Long.TryParse(status.InReplyToStatusId, post.InReplyToStatusId)
1693             post.InReplyToUser = status.InReplyToScreenName
1694             Long.TryParse(status.InReplyToUserId, post.InReplyToUserId)
1695
1696             post.IsFav = status.Favorited
1697             If status.Geo IsNot Nothing Then post.PostGeo = New PostClass.StatusGeo() With {.Lat = status.Geo.Coordinates(0), .Lng = status.Geo.Coordinates(1)}
1698
1699             '以下、ユーザー情報
1700             Dim user As TwitterDataModel.User = status.User
1701
1702             If user.ScreenName Is Nothing Then Return Nothing
1703
1704             post.UserId = user.Id
1705             post.ScreenName = user.ScreenName
1706             post.Nickname = user.Name.Trim()
1707             post.ImageUrl = user.ProfileImageUrl
1708             post.IsProtect = user.Protected
1709             post.IsMe = post.ScreenName.ToLower.Equals(_uname)
1710         End If
1711         'HTMLに整形
1712         post.Text = CreateHtmlAnchor(post.TextFromApi, post.ReplyToList, entities, post.Media)
1713         post.TextFromApi = Me.ReplaceTextFromApi(post.TextFromApi, entities)
1714         post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi)
1715         post.TextFromApi = post.TextFromApi.Replace("<3", "♡")
1716
1717         'Source整形
1718         CreateSource(post)
1719
1720         post.IsReply = post.ReplyToList.Contains(_uname)
1721         post.IsExcludeReply = False
1722
1723         If post.IsMe Then
1724             post.IsOwl = False
1725         Else
1726             If followerId.Count > 0 Then post.IsOwl = Not followerId.Contains(post.UserId)
1727         End If
1728
1729         post.IsDm = False
1730         Return post
1731     End Function
1732
1733     Private Function CreatePostsFromJson(ByVal content As String, ByVal gType As WORKERTYPE, ByVal tab As TabClass, ByVal read As Boolean, ByVal count As Integer, ByRef minimumId As Long) As String
1734         Dim items As List(Of TwitterDataModel.Status)
1735         Try
1736             items = CreateDataFromJson(Of List(Of TwitterDataModel.Status))(content)
1737         Catch ex As SerializationException
1738             TraceOut(ex.Message + Environment.NewLine + content)
1739             Return "Json Parse Error(DataContractJsonSerializer)"
1740         Catch ex As Exception
1741             TraceOut(ex, GetCurrentMethod.Name & " " & content)
1742             Return "Invalid Json!"
1743         End Try
1744
1745         For Each status As TwitterDataModel.Status In items
1746             Dim post As PostClass = Nothing
1747             post = CreatePostsFromStatusData(status)
1748             If post Is Nothing Then Continue For
1749
1750             If minimumId > post.StatusId Then minimumId = post.StatusId
1751             '二重取得回避
1752             SyncLock LockObj
1753                 If tab Is Nothing Then
1754                     If TabInformations.GetInstance.ContainsKey(post.StatusId) Then Continue For
1755                 Else
1756                     If TabInformations.GetInstance.ContainsKey(post.StatusId, tab.TabName) Then Continue For
1757                 End If
1758             End SyncLock
1759
1760             'RT禁止ユーザーによるもの
1761             If post.RetweetedId > 0 AndAlso Me.noRTId.Contains(post.RetweetedByUserId) Then Continue For
1762
1763             post.IsRead = read
1764             If post.IsMe AndAlso Not read AndAlso _readOwnPost Then post.IsRead = True
1765
1766             If tab IsNot Nothing Then post.RelTabName = tab.TabName
1767             '非同期アイコン取得&StatusDictionaryに追加
1768             TabInformations.GetInstance.AddPost(post)
1769         Next
1770
1771         Return ""
1772     End Function
1773
1774     Private Function CreatePostsFromPhoenixSearch(ByVal content As String, ByVal gType As WORKERTYPE, ByVal tab As TabClass, ByVal read As Boolean, ByVal count As Integer, ByRef minimumId As Long, ByRef nextPageQuery As String) As String
1775         Dim items As TwitterDataModel.SearchResult
1776         Try
1777             items = CreateDataFromJson(Of TwitterDataModel.SearchResult)(content)
1778         Catch ex As SerializationException
1779             TraceOut(ex.Message + Environment.NewLine + content)
1780             Return "Json Parse Error(DataContractJsonSerializer)"
1781         Catch ex As Exception
1782             TraceOut(ex, GetCurrentMethod.Name & " " & content)
1783             Return "Invalid Json!"
1784         End Try
1785
1786         nextPageQuery = items.NextPage
1787
1788         For Each status As TwitterDataModel.Status In items.Statuses
1789             Dim post As PostClass = Nothing
1790             post = CreatePostsFromStatusData(status)
1791             If post Is Nothing Then Continue For
1792
1793             If minimumId > post.StatusId Then minimumId = post.StatusId
1794             '二重取得回避
1795             SyncLock LockObj
1796                 If tab Is Nothing Then
1797                     If TabInformations.GetInstance.ContainsKey(post.StatusId) Then Continue For
1798                 Else
1799                     If TabInformations.GetInstance.ContainsKey(post.StatusId, tab.TabName) Then Continue For
1800                 End If
1801             End SyncLock
1802
1803             post.IsRead = read
1804             If post.IsMe AndAlso Not read AndAlso _readOwnPost Then post.IsRead = True
1805
1806             If tab IsNot Nothing Then post.RelTabName = tab.TabName
1807             '非同期アイコン取得&StatusDictionaryに追加
1808             TabInformations.GetInstance.AddPost(post)
1809         Next
1810
1811         Return If(String.IsNullOrEmpty(items.ErrMsg), "", "Err:" + items.ErrMsg)
1812     End Function
1813
1814     Public Overloads Function GetListStatus(ByVal read As Boolean, _
1815                             ByVal tab As TabClass, _
1816                             ByVal more As Boolean, _
1817                             ByVal startup As Boolean) As String
1818
1819         If _endingFlag Then Return ""
1820
1821         Dim res As HttpStatusCode
1822         Dim content As String = ""
1823         Dim page As Integer = 0
1824         Dim count As Integer
1825         If AppendSettingDialog.Instance.UseAdditionalCount Then
1826             count = AppendSettingDialog.Instance.ListCountApi
1827             If count = 0 Then
1828                 If more AndAlso AppendSettingDialog.Instance.MoreCountApi <> 0 Then
1829                     count = AppendSettingDialog.Instance.MoreCountApi
1830                 ElseIf startup AndAlso AppendSettingDialog.Instance.FirstCountApi <> 0 Then
1831                     count = AppendSettingDialog.Instance.FirstCountApi
1832                 Else
1833                     count = AppendSettingDialog.Instance.CountApi
1834                 End If
1835             End If
1836         Else
1837             count = AppendSettingDialog.Instance.CountApi
1838         End If
1839         Try
1840             If more Then
1841                 res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, tab.OldestId, 0, AppendSettingDialog.Instance.IsListStatusesIncludeRts, content)
1842             Else
1843                 res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, 0, 0, AppendSettingDialog.Instance.IsListStatusesIncludeRts, content)
1844             End If
1845         Catch ex As Exception
1846             Return "Err:" + ex.Message
1847         End Try
1848         Select Case res
1849             Case HttpStatusCode.OK
1850                 Twitter.AccountState = ACCOUNT_STATE.Valid
1851             Case HttpStatusCode.Unauthorized
1852                 Twitter.AccountState = ACCOUNT_STATE.Invalid
1853                 Return My.Resources.Unauthorized
1854             Case HttpStatusCode.BadRequest
1855                 Return "Err:API Limits?"
1856             Case Else
1857                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
1858         End Select
1859
1860         Return CreatePostsFromJson(content, WORKERTYPE.List, tab, read, count, tab.OldestId)
1861     End Function
1862
1863
1864     Private Function CheckReplyToPost(ByVal relPosts As List(Of PostClass)) As PostClass
1865         Dim tmpPost As PostClass = relPosts(0)
1866         Dim lastPost As PostClass = Nothing
1867         Do While tmpPost IsNot Nothing
1868             If tmpPost.InReplyToStatusId = 0 Then Return Nothing
1869             lastPost = tmpPost
1870             Dim replyToPost = From p In relPosts
1871                              Where p.StatusId = tmpPost.InReplyToStatusId
1872                              Select p
1873             tmpPost = replyToPost.FirstOrDefault()
1874         Loop
1875         Return lastPost
1876     End Function
1877
1878     Public Function GetRelatedResult(ByVal read As Boolean, ByVal tab As TabClass) As String
1879         Google.GASender.GetInstance().TrackPage("/related_statuses", Me.UserId)
1880         Dim rslt As String = ""
1881         Dim relPosts As New List(Of PostClass)
1882         If tab.RelationTargetPost.TextFromApi.Contains("@") AndAlso tab.RelationTargetPost.InReplyToStatusId = 0 Then
1883             '検索結果対応
1884             Dim p As PostClass = TabInformations.GetInstance.Item(tab.RelationTargetPost.StatusId)
1885             If p IsNot Nothing AndAlso p.InReplyToStatusId > 0 Then
1886                 tab.RelationTargetPost = p
1887             Else
1888                 rslt = Me.GetStatusApi(read, tab.RelationTargetPost.StatusId, p)
1889                 If Not String.IsNullOrEmpty(rslt) Then Return rslt
1890                 tab.RelationTargetPost = p
1891             End If
1892         End If
1893         relPosts.Add(tab.RelationTargetPost.Copy)
1894         Dim tmpPost As PostClass = relPosts(0)
1895         Do
1896             rslt = Me.GetRelatedResultsApi(read, tmpPost, tab, relPosts)
1897             If Not String.IsNullOrEmpty(rslt) Then Exit Do
1898             tmpPost = CheckReplyToPost(relPosts)
1899         Loop While tmpPost IsNot Nothing
1900
1901         relPosts.ForEach(Sub(p) TabInformations.GetInstance.AddPost(p))
1902         Return rslt
1903     End Function
1904
1905     Private Function GetRelatedResultsApi(ByVal read As Boolean,
1906                                          ByVal post As PostClass,
1907                                          ByVal tab As TabClass,
1908                                          ByVal relatedPosts As List(Of PostClass)) As String
1909
1910         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
1911
1912         If _endingFlag Then Return ""
1913
1914         Dim res As HttpStatusCode
1915         Dim content As String = ""
1916         Try
1917             If post.RetweetedId > 0 Then
1918                 res = twCon.GetRelatedResults(post.RetweetedId, content)
1919             Else
1920                 res = twCon.GetRelatedResults(post.StatusId, content)
1921             End If
1922         Catch ex As Exception
1923             Return "Err:" + ex.Message
1924         End Try
1925         Select Case res
1926             Case HttpStatusCode.OK
1927                 Twitter.AccountState = ACCOUNT_STATE.Valid
1928             Case HttpStatusCode.Unauthorized
1929                 Twitter.AccountState = ACCOUNT_STATE.Invalid
1930                 Return My.Resources.Unauthorized
1931             Case HttpStatusCode.BadRequest
1932                 Return "Err:API Limits?"
1933             Case Else
1934                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
1935         End Select
1936
1937         Dim items As List(Of TwitterDataModel.RelatedResult)
1938         Try
1939             items = CreateDataFromJson(Of List(Of TwitterDataModel.RelatedResult))(content)
1940         Catch ex As SerializationException
1941             TraceOut(ex.Message + Environment.NewLine + content)
1942             Return "Json Parse Error(DataContractJsonSerializer)"
1943         Catch ex As Exception
1944             TraceOut(ex, GetCurrentMethod.Name & " " & content)
1945             Return "Invalid Json!"
1946         End Try
1947
1948         Dim targetItem As PostClass = post
1949         If targetItem Is Nothing Then
1950             Return ""
1951         Else
1952             targetItem = targetItem.Copy()
1953         End If
1954         targetItem.RelTabName = tab.TabName
1955         TabInformations.GetInstance.AddPost(targetItem)
1956
1957         Dim replyToItem As PostClass = Nothing
1958         Dim replyToUserName As String = targetItem.InReplyToUser
1959         If targetItem.InReplyToStatusId > 0 AndAlso TabInformations.GetInstance.Item(targetItem.InReplyToStatusId) IsNot Nothing Then
1960             replyToItem = TabInformations.GetInstance.Item(targetItem.InReplyToStatusId).Copy
1961             replyToItem.IsRead = read
1962             If replyToItem.IsMe AndAlso Not read AndAlso _readOwnPost Then replyToItem.IsRead = True
1963             replyToItem.RelTabName = tab.TabName
1964         End If
1965
1966         Dim replyAdded As Boolean = False
1967         For Each relatedData As TwitterDataModel.RelatedResult In items
1968             For Each result As TwitterDataModel.RelatedTweet In relatedData.Results
1969                 Dim item As PostClass = CreatePostsFromStatusData(result.Status)
1970                 If item Is Nothing Then Continue For
1971                 If targetItem.InReplyToStatusId = item.StatusId Then
1972                     replyToItem = Nothing
1973                     replyAdded = True
1974                 End If
1975                 item.IsRead = read
1976                 If item.IsMe AndAlso Not read AndAlso _readOwnPost Then item.IsRead = True
1977                 If tab IsNot Nothing Then item.RelTabName = tab.TabName
1978                 '非同期アイコン取得&StatusDictionaryに追加
1979                 relatedPosts.Add(item)
1980             Next
1981         Next
1982         If replyToItem IsNot Nothing Then
1983             relatedPosts.Add(replyToItem)
1984         ElseIf targetItem.InReplyToStatusId > 0 AndAlso Not replyAdded Then
1985             Dim p As PostClass = Nothing
1986             Dim rslt As String = ""
1987             rslt = GetStatusApi(read, targetItem.InReplyToStatusId, p)
1988             If String.IsNullOrEmpty(rslt) Then
1989                 p.IsRead = read
1990                 p.RelTabName = tab.TabName
1991                 relatedPosts.Add(p)
1992             End If
1993             Return rslt
1994         End If
1995
1996         '発言者・返信先ユーザーの直近10発言取得
1997         'Dim rslt As String = Me.GetUserTimelineApi(read, 10, "", tab)
1998         'If Not String.IsNullOrEmpty(rslt) Then Return rslt
1999         'If Not String.IsNullOrEmpty(replyToUserName) Then
2000         '    rslt = Me.GetUserTimelineApi(read, 10, replyToUserName, tab)
2001         'End If
2002         'Return rslt
2003
2004
2005         'MRTとかに対応のためツイート内にあるツイートを指すURLを取り込む
2006         Dim ma As MatchCollection = Regex.Matches(tab.RelationTargetPost.Text, "href=""https?://twitter.com/(#!/)?(?<ScreenName>[a-zA-Z0-9_]+)(/status(es)?/(?<StatusId>[0-9]+))""")
2007         For Each _match As Match In ma
2008             Dim _statusId As Int64
2009             If Int64.TryParse(_match.Groups("StatusId").Value, _statusId) Then
2010                 Dim p As PostClass = Nothing
2011                 Dim _post As PostClass = TabInformations.GetInstance.Item(_statusId)
2012                 If _post Is Nothing Then
2013                     Dim rslt = Me.GetStatusApi(read, _statusId, p)
2014                 Else
2015                     p = _post.Copy
2016                 End If
2017                 If p IsNot Nothing Then
2018                     p.IsRead = read
2019                     p.RelTabName = tab.TabName
2020                     relatedPosts.Add(p)
2021                 End If
2022             End If
2023         Next
2024         Return ""
2025     End Function
2026
2027     Public Function GetSearch(ByVal read As Boolean, _
2028                         ByVal tab As TabClass, _
2029                         ByVal more As Boolean) As String
2030
2031         If _endingFlag Then Return ""
2032
2033         Dim res As HttpStatusCode
2034         Dim content As String = ""
2035         Dim page As Integer = 0
2036         Dim sinceId As Long = 0
2037         Dim count As Integer = 100
2038         If AppendSettingDialog.Instance.UseAdditionalCount AndAlso
2039             AppendSettingDialog.Instance.SearchCountApi <> 0 Then
2040             count = AppendSettingDialog.Instance.SearchCountApi
2041         Else
2042             count = AppendSettingDialog.Instance.CountApi
2043         End If
2044         If more Then
2045             page = tab.GetSearchPage(count)
2046         Else
2047             sinceId = tab.SinceId
2048         End If
2049
2050         Try
2051             ' TODO:一時的に40>100件に 件数変更UI作成の必要あり
2052             res = twCon.Search(tab.SearchWords, tab.SearchLang, count, page, sinceId, content)
2053         Catch ex As Exception
2054             Return "Err:" + ex.Message
2055         End Try
2056         Select Case res
2057             Case HttpStatusCode.BadRequest
2058                 Return "Invalid query"
2059             Case HttpStatusCode.NotFound
2060                 Return "Invalid query"
2061             Case HttpStatusCode.PaymentRequired 'API Documentには420と書いてあるが、該当コードがないので402にしてある
2062                 Return "Search API Limit?"
2063             Case HttpStatusCode.OK
2064             Case Else
2065                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
2066         End Select
2067
2068         If Not TabInformations.GetInstance.ContainsTab(tab) Then Return ""
2069         content = Regex.Replace(content, "[\x00-\x1f-[\x0a\x0d]]+", " ")
2070         Dim xdoc As New XmlDocument
2071         Try
2072             xdoc.LoadXml(content)
2073         Catch ex As Exception
2074             TraceOut(ex, GetCurrentMethod.Name & " " & content)
2075             Return "Invalid ATOM!"
2076         End Try
2077         Dim nsmgr As New XmlNamespaceManager(xdoc.NameTable)
2078         nsmgr.AddNamespace("search", "http://www.w3.org/2005/Atom")
2079         nsmgr.AddNamespace("twitter", "http://api.twitter.com/")
2080         nsmgr.AddNamespace("georss", "http://www.georss.org/georss")
2081         For Each xentryNode As XmlNode In xdoc.DocumentElement.SelectNodes("/search:feed/search:entry", nsmgr)
2082             Dim xentry As XmlElement = CType(xentryNode, XmlElement)
2083             Dim post As New PostClass
2084             Try
2085                 post.StatusId = Long.Parse(xentry.Item("id").InnerText.Split(":"c)(2))
2086                 If TabInformations.GetInstance.ContainsKey(post.StatusId, tab.TabName) Then Continue For
2087                 post.CreatedAt = DateTime.Parse(xentry.Item("published").InnerText)
2088                 '本文
2089                 post.TextFromApi = xentry.Item("title").InnerText
2090                 'Source取得(htmlの場合は、中身を取り出し)
2091                 post.Source = xentry.Item("twitter:source").InnerText
2092                 post.InReplyToStatusId = 0
2093                 post.InReplyToUser = ""
2094                 post.InReplyToUserId = 0
2095                 post.IsFav = False
2096
2097                 ' Geoが勝手に付加されるバグがいっこうに修正されないので暫定的にGeo情報を無視する
2098                 If xentry.Item("twitter:geo").HasChildNodes Then
2099                     Dim pnt As String() = CType(xentry.SelectSingleNode("twitter:geo/georss:point", nsmgr), XmlElement).InnerText.Split(" "c)
2100                     post.PostGeo = New PostClass.StatusGeo With {.Lat = Double.Parse(pnt(0)), .Lng = Double.Parse(pnt(1))}
2101                 End If
2102
2103                 '以下、ユーザー情報
2104                 Dim xUentry As XmlElement = CType(xentry.SelectSingleNode("./search:author", nsmgr), XmlElement)
2105                 post.UserId = 0
2106                 post.ScreenName = xUentry.Item("name").InnerText.Split(" "c)(0).Trim
2107                 post.Nickname = xUentry.Item("name").InnerText.Substring(post.ScreenName.Length).Trim
2108                 If post.Nickname.Length > 2 Then
2109                     post.Nickname = post.Nickname.Substring(1, post.Nickname.Length - 2)
2110                 Else
2111                     post.Nickname = post.ScreenName
2112                 End If
2113                 post.ImageUrl = CType(xentry.SelectSingleNode("./search:link[@type='image/png']", nsmgr), XmlElement).GetAttribute("href")
2114                 post.IsProtect = False
2115                 post.IsMe = post.ScreenName.ToLower.Equals(_uname)
2116
2117                 'HTMLに整形
2118                 post.Text = CreateHtmlAnchor(HttpUtility.HtmlEncode(post.TextFromApi), post.ReplyToList, post.Media)
2119                 post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi)
2120                 'Source整形
2121                 CreateSource(post)
2122
2123                 post.IsRead = read
2124                 post.IsReply = post.ReplyToList.Contains(_uname)
2125                 post.IsExcludeReply = False
2126
2127                 post.IsOwl = False
2128                 If post.IsMe AndAlso Not read AndAlso _readOwnPost Then post.IsRead = True
2129                 post.IsDm = False
2130                 post.RelTabName = tab.TabName
2131                 If Not more AndAlso post.StatusId > tab.SinceId Then tab.SinceId = post.StatusId
2132             Catch ex As Exception
2133                 TraceOut(ex, GetCurrentMethod.Name & " " & content)
2134                 Continue For
2135             End Try
2136
2137             'Me._dIcon.Add(post.ImageUrl, Nothing)
2138             TabInformations.GetInstance.AddPost(post)
2139
2140         Next
2141
2142         '' TODO
2143         '' 遡るための情報max_idやnext_pageの情報を保持する
2144
2145 #If 0 Then
2146         Dim xNode As XmlNode = xdoc.DocumentElement.SelectSingleNode("/search:feed/twitter:warning", nsmgr)
2147
2148         If xNode IsNot Nothing Then
2149             Return "Warn:" + xNode.InnerText + "(" + GetCurrentMethod.Name + ")"
2150         End If
2151 #End If
2152
2153         Return ""
2154     End Function
2155
2156     Public Function GetPhoenixSearch(ByVal read As Boolean, _
2157                             ByVal tab As TabClass, _
2158                             ByVal more As Boolean) As String
2159
2160         If _endingFlag Then Return ""
2161
2162         Dim res As HttpStatusCode
2163         Dim content As String = ""
2164         Dim page As Integer = 0
2165         Dim sinceId As Long = 0
2166         Dim count As Integer = 100
2167         Dim querystr As String = ""
2168         If AppendSettingDialog.Instance.UseAdditionalCount AndAlso
2169             AppendSettingDialog.Instance.SearchCountApi <> 0 Then
2170             count = AppendSettingDialog.Instance.SearchCountApi
2171         End If
2172         If more Then
2173             page = tab.GetSearchPage(count)
2174             If Not String.IsNullOrEmpty(tab.NextPageQuery) Then
2175                 querystr = tab.NextPageQuery
2176             End If
2177         Else
2178             sinceId = tab.SinceId
2179         End If
2180
2181         Try
2182             If String.IsNullOrEmpty(querystr) Then
2183                 res = twCon.PhoenixSearch(tab.SearchWords, tab.SearchLang, count, page, sinceId, content)
2184             Else
2185                 res = twCon.PhoenixSearch(querystr, content)
2186             End If
2187         Catch ex As Exception
2188             Return "Err:" + ex.Message
2189         End Try
2190         Select Case res
2191             Case HttpStatusCode.BadRequest
2192                 Return "Invalid query"
2193             Case HttpStatusCode.NotFound
2194                 Return "Invalid query"
2195             Case HttpStatusCode.PaymentRequired 'API Documentには420と書いてあるが、該当コードがないので402にしてある
2196                 Return "Search API Limit?"
2197             Case HttpStatusCode.OK
2198             Case Else
2199                 Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
2200         End Select
2201
2202         If Not TabInformations.GetInstance.ContainsTab(tab) Then Return ""
2203
2204         '' TODO
2205         '' 遡るための情報max_idやnext_pageの情報を保持する
2206
2207         Return CreatePostsFromPhoenixSearch(content, WORKERTYPE.PublicSearch, tab, read, count, tab.OldestId, tab.NextPageQuery)
2208     End Function
2209
2210     Private Function CreateDirectMessagesFromJson(ByVal content As String, ByVal gType As WORKERTYPE, ByVal read As Boolean) As String
2211         Dim item As List(Of TwitterDataModel.Directmessage)
2212         Try
2213             If gType = WORKERTYPE.UserStream Then
2214                 Dim itm As List(Of TwitterDataModel.DirectmessageEvent) = CreateDataFromJson(Of List(Of TwitterDataModel.DirectmessageEvent))(content)
2215                 item = New List(Of TwitterDataModel.Directmessage)
2216                 For Each dat As TwitterDataModel.DirectmessageEvent In itm
2217                     item.Add(dat.Directmessage)
2218                 Next
2219             Else
2220                 item = CreateDataFromJson(Of List(Of TwitterDataModel.Directmessage))(content)
2221             End If
2222         Catch ex As SerializationException
2223             TraceOut(ex.Message + Environment.NewLine + content)
2224             Return "Json Parse Error(DataContractJsonSerializer)"
2225         Catch ex As Exception
2226             TraceOut(ex, GetCurrentMethod.Name & " " & content)
2227             Return "Invalid Json!"
2228         End Try
2229
2230         For Each message As TwitterDataModel.Directmessage In item
2231             Dim post As New PostClass
2232             Try
2233                 post.StatusId = message.Id
2234                 If gType <> WORKERTYPE.UserStream Then
2235                     If gType = WORKERTYPE.DirectMessegeRcv Then
2236                         If minDirectmessage > post.StatusId Then minDirectmessage = post.StatusId
2237                     Else
2238                         If minDirectmessageSent > post.StatusId Then minDirectmessageSent = post.StatusId
2239                     End If
2240                 End If
2241
2242                 '二重取得回避
2243                 SyncLock LockObj
2244                     If TabInformations.GetInstance.GetTabByType(TabUsageType.DirectMessage).Contains(post.StatusId) Then Continue For
2245                 End SyncLock
2246                 'sender_id
2247                 'recipient_id
2248                 post.CreatedAt = DateTimeParse(message.CreatedAt)
2249                 '本文
2250                 post.TextFromApi = message.Text
2251                 'HTMLに整形
2252                 post.Text = CreateHtmlAnchor(post.TextFromApi, post.ReplyToList, post.Media)
2253                 post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi)
2254                 post.TextFromApi = post.TextFromApi.Replace("<3", "♡")
2255                 post.IsFav = False
2256
2257                 '以下、ユーザー情報
2258                 Dim user As TwitterDataModel.User
2259                 If gType = WORKERTYPE.UserStream Then
2260                     If twCon.AuthenticatedUsername.Equals(message.Recipient.ScreenName, StringComparison.CurrentCultureIgnoreCase) Then
2261                         user = message.Sender
2262                         post.IsMe = False
2263                         post.IsOwl = True
2264                     Else
2265                         user = message.Recipient
2266                         post.IsMe = True
2267                         post.IsOwl = False
2268                     End If
2269                 Else
2270                     If gType = WORKERTYPE.DirectMessegeRcv Then
2271                         user = message.Sender
2272                         post.IsMe = False
2273                         post.IsOwl = True
2274                     Else
2275                         user = message.Recipient
2276                         post.IsMe = True
2277                         post.IsOwl = False
2278                     End If
2279                 End If
2280
2281                 post.UserId = user.Id
2282                 post.ScreenName = user.ScreenName
2283                 post.Nickname = user.Name.Trim()
2284                 post.ImageUrl = user.ProfileImageUrl
2285                 post.IsProtect = user.Protected
2286             Catch ex As Exception
2287                 TraceOut(ex, GetCurrentMethod.Name & " " & content)
2288                 MessageBox.Show("Parse Error(CreateDirectMessagesFromJson)")
2289                 Continue For
2290             End Try
2291
2292             post.IsRead = read
2293             If post.IsMe AndAlso Not read AndAlso _readOwnPost Then post.IsRead = True
2294             post.IsReply = False
2295             post.IsExcludeReply = False
2296             post.IsDm = True
2297
2298             TabInformations.GetInstance.AddPost(post)
2299         Next
2300
2301         Return ""
2302
2303     End Function
2304
2305     Public Function GetDirectMessageApi(ByVal read As Boolean, _
2306                             ByVal gType As WORKERTYPE, _
2307                             ByVal more As Boolean) As String
2308         If _endingFlag Then Return ""
2309
2310         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
2311         If Not TwitterApiInfo.IsDirectMessagePermission Then Return "Auth Err:try to re-authorization."
2312
2313         Dim res As HttpStatusCode
2314         Dim content As String = ""
2315
2316         Try
2317             If gType = WORKERTYPE.DirectMessegeRcv Then
2318                 If more Then
2319                     res = twCon.DirectMessages(20, minDirectmessage, 0, content)
2320                 Else
2321                     res = twCon.DirectMessages(20, 0, 0, content)
2322                 End If
2323             Else
2324                 If more Then
2325                     res = twCon.DirectMessagesSent(20, minDirectmessageSent, 0, content)
2326                 Else
2327                     res = twCon.DirectMessagesSent(20, 0, 0, content)
2328                 End If
2329             End If
2330         Catch ex As Exception
2331             Return "Err:" + ex.Message
2332         End Try
2333
2334         Select Case res
2335             Case HttpStatusCode.OK
2336                 Twitter.AccountState = ACCOUNT_STATE.Valid
2337             Case HttpStatusCode.Unauthorized
2338                 Twitter.AccountState = ACCOUNT_STATE.Invalid
2339                 Return My.Resources.Unauthorized
2340             Case HttpStatusCode.BadRequest
2341                 Return "Err:API Limits?"
2342             Case Else
2343                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
2344         End Select
2345
2346         Return CreateDirectMessagesFromJson(content, gType, read)
2347     End Function
2348
2349     Public Function GetFavoritesApi(ByVal read As Boolean, _
2350                         ByVal gType As WORKERTYPE) As String
2351
2352         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
2353
2354         If _endingFlag Then Return ""
2355
2356         Dim res As HttpStatusCode
2357         Dim content As String = ""
2358         Dim count As Integer = AppendSettingDialog.Instance.CountApi
2359         If AppendSettingDialog.Instance.UseAdditionalCount AndAlso
2360             AppendSettingDialog.Instance.FavoritesCountApi <> 0 Then
2361             count = AppendSettingDialog.Instance.FavoritesCountApi
2362         End If
2363         Try
2364             res = twCon.Favorites(count, content)
2365         Catch ex As Exception
2366             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
2367         End Try
2368
2369         Select Case res
2370             Case HttpStatusCode.OK
2371                 Twitter.AccountState = ACCOUNT_STATE.Valid
2372             Case HttpStatusCode.Unauthorized
2373                 Twitter.AccountState = ACCOUNT_STATE.Invalid
2374                 Return My.Resources.Unauthorized
2375             Case HttpStatusCode.BadRequest
2376                 Return "Err:API Limits?"
2377             Case Else
2378                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
2379         End Select
2380
2381         Dim serializer As New DataContractJsonSerializer(GetType(List(Of TwitterDataModel.Status)))
2382         Dim item As List(Of TwitterDataModel.Status)
2383
2384         Try
2385             item = CreateDataFromJson(Of List(Of TwitterDataModel.Status))(content)
2386         Catch ex As SerializationException
2387             TraceOut(ex.Message + Environment.NewLine + content)
2388             Return "Json Parse Error(DataContractJsonSerializer)"
2389         Catch ex As Exception
2390             TraceOut(ex, GetCurrentMethod.Name & " " & content)
2391             Return "Invalid Json!"
2392         End Try
2393
2394         For Each status As TwitterDataModel.Status In item
2395             Dim post As New PostClass
2396             Dim entities As TwitterDataModel.Entities
2397
2398             Try
2399                 post.StatusId = status.Id
2400                 '二重取得回避
2401                 SyncLock LockObj
2402                     If TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites).Contains(post.StatusId) Then Continue For
2403                 End SyncLock
2404                 'Retweet判定
2405                 If status.RetweetedStatus IsNot Nothing Then
2406                     Dim retweeted As TwitterDataModel.RetweetedStatus = status.RetweetedStatus
2407                     post.CreatedAt = DateTimeParse(retweeted.CreatedAt)
2408
2409                     'Id
2410                     post.RetweetedId = post.StatusId
2411                     '本文
2412                     post.TextFromApi = retweeted.Text
2413                     entities = retweeted.Entities
2414                     'Source取得(htmlの場合は、中身を取り出し)
2415                     post.Source = retweeted.Source
2416                     'Reply先
2417                     Long.TryParse(retweeted.InReplyToStatusId, post.InReplyToStatusId)
2418                     post.InReplyToUser = retweeted.InReplyToScreenName
2419                     Long.TryParse(retweeted.InReplyToUserId, post.InReplyToUserId)
2420                     post.IsFav = retweeted.Favorited
2421
2422                     '以下、ユーザー情報
2423                     Dim user As TwitterDataModel.User = retweeted.User
2424                     post.UserId = user.Id
2425                     post.ScreenName = user.ScreenName
2426                     post.Nickname = user.Name.Trim()
2427                     post.ImageUrl = user.ProfileImageUrl
2428                     post.IsProtect = user.Protected
2429
2430                     'Retweetした人
2431                     post.RetweetedBy = status.User.ScreenName
2432                     post.IsMe = post.RetweetedBy.ToLower.Equals(_uname)
2433                 Else
2434                     post.CreatedAt = DateTimeParse(status.CreatedAt)
2435
2436                     '本文
2437                     post.TextFromApi = status.Text
2438                     entities = status.Entities
2439                     'Source取得(htmlの場合は、中身を取り出し)
2440                     post.Source = status.Source
2441                     Long.TryParse(status.InReplyToStatusId, post.InReplyToStatusId)
2442                     post.InReplyToUser = status.InReplyToScreenName
2443                     Long.TryParse(status.InReplyToUserId, post.InReplyToUserId)
2444
2445                     post.IsFav = status.Favorited
2446
2447                     '以下、ユーザー情報
2448                     Dim user As TwitterDataModel.User = status.User
2449                     post.UserId = user.Id
2450                     post.ScreenName = user.ScreenName
2451                     post.Nickname = user.Name.Trim()
2452                     post.ImageUrl = user.ProfileImageUrl
2453                     post.IsProtect = user.Protected
2454                     post.IsMe = post.ScreenName.ToLower.Equals(_uname)
2455                 End If
2456                 'HTMLに整形
2457                 post.Text = CreateHtmlAnchor(post.TextFromApi, post.ReplyToList, entities, post.Media)
2458                 post.TextFromApi = Me.ReplaceTextFromApi(post.TextFromApi, entities)
2459                 post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi)
2460                 post.TextFromApi = post.TextFromApi.Replace("<3", "♡")
2461                 'Source整形
2462                 CreateSource(post)
2463
2464                 post.IsRead = read
2465                 post.IsReply = post.ReplyToList.Contains(_uname)
2466                 post.IsExcludeReply = False
2467
2468                 If post.IsMe Then
2469                     post.IsOwl = False
2470                 Else
2471                     If followerId.Count > 0 Then post.IsOwl = Not followerId.Contains(post.UserId)
2472                 End If
2473
2474                 post.IsDm = False
2475             Catch ex As Exception
2476                 TraceOut(ex, GetCurrentMethod.Name & " " & content)
2477                 Continue For
2478             End Try
2479
2480             TabInformations.GetInstance.AddPost(post)
2481
2482         Next
2483
2484         Return ""
2485     End Function
2486
2487     Private Function ReplaceTextFromApi(ByVal text As String, ByVal entities As TwitterDataModel.Entities) As String
2488         If entities IsNot Nothing Then
2489             If entities.Urls IsNot Nothing Then
2490                 For Each m In entities.Urls
2491                     If Not String.IsNullOrEmpty(m.DisplayUrl) Then text = text.Replace(m.Url, m.DisplayUrl)
2492                 Next
2493             End If
2494             If entities.Media IsNot Nothing Then
2495                 For Each m In entities.Media
2496                     If Not String.IsNullOrEmpty(m.DisplayUrl) Then text = text.Replace(m.Url, m.DisplayUrl)
2497                 Next
2498             End If
2499         End If
2500         Return text
2501     End Function
2502
2503     Public Function GetFollowersApi() As String
2504         If _endingFlag Then Return ""
2505         Dim cursor As Long = -1
2506         Dim tmpFollower As New List(Of Long)(followerId)
2507
2508         followerId.Clear()
2509         Do
2510             Dim ret As String = FollowerApi(cursor)
2511             If Not String.IsNullOrEmpty(ret) Then
2512                 followerId.Clear()
2513                 followerId.AddRange(tmpFollower)
2514                 _GetFollowerResult = False
2515                 Return ret
2516             End If
2517         Loop While cursor > 0
2518
2519         TabInformations.GetInstance.RefreshOwl(followerId)
2520
2521         _GetFollowerResult = True
2522         Return ""
2523     End Function
2524
2525     Public ReadOnly Property GetFollowersSuccess() As Boolean
2526         Get
2527             Return _GetFollowerResult
2528         End Get
2529     End Property
2530
2531     Private Function FollowerApi(ByRef cursor As Long) As String
2532         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
2533
2534         Dim res As HttpStatusCode
2535         Dim content As String = ""
2536         Try
2537             res = twCon.FollowerIds(cursor, content)
2538         Catch ex As Exception
2539             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
2540         End Try
2541
2542         Select Case res
2543             Case HttpStatusCode.OK
2544                 Twitter.AccountState = ACCOUNT_STATE.Valid
2545             Case HttpStatusCode.Unauthorized
2546                 Twitter.AccountState = ACCOUNT_STATE.Invalid
2547                 Return My.Resources.Unauthorized
2548             Case HttpStatusCode.BadRequest
2549                 Return "Err:API Limits?"
2550             Case Else
2551                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
2552         End Select
2553
2554         Try
2555             Dim followers = CreateDataFromJson(Of TwitterDataModel.Ids)(content)
2556             followerId.AddRange(followers.Id)
2557             cursor = followers.NextCursor
2558             Return ""
2559         Catch ex As SerializationException
2560             TraceOut(ex.Message + Environment.NewLine + content)
2561             Return "Err:Json Parse Error(DataContractJsonSerializer)"
2562         Catch ex As Exception
2563             TraceOut(ex, GetCurrentMethod.Name & " " & content)
2564             Return "Err:Invalid Json!"
2565         End Try
2566     End Function
2567
2568     Public Function GetNoRetweetIdsApi() As String
2569         If _endingFlag Then Return ""
2570         Dim cursor As Long = -1
2571         Dim tmpIds As New List(Of Long)(noRTId)
2572
2573         noRTId.Clear()
2574         Do
2575             Dim ret As String = NoRetweetApi(cursor)
2576             If Not String.IsNullOrEmpty(ret) Then
2577                 noRTId.Clear()
2578                 noRTId.AddRange(tmpIds)
2579                 _GetNoRetweetResult = False
2580                 Return ret
2581             End If
2582         Loop While cursor > 0
2583
2584         _GetNoRetweetResult = True
2585         Return ""
2586     End Function
2587
2588     Private Function NoRetweetApi(ByRef cursor As Long) As String
2589         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
2590
2591         Dim res As HttpStatusCode
2592         Dim content As String = ""
2593         Try
2594             res = twCon.NoRetweetIds(cursor, content)
2595         Catch ex As Exception
2596             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
2597         End Try
2598
2599         Select Case res
2600             Case HttpStatusCode.OK
2601                 Twitter.AccountState = ACCOUNT_STATE.Valid
2602             Case HttpStatusCode.Unauthorized
2603                 Twitter.AccountState = ACCOUNT_STATE.Invalid
2604                 Return My.Resources.Unauthorized
2605             Case HttpStatusCode.BadRequest
2606                 Return "Err:API Limits?"
2607             Case Else
2608                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
2609         End Select
2610
2611         Try
2612             Dim ids = CreateDataFromJson(Of Long())(content)
2613             noRTId.AddRange(ids)
2614             cursor = 0  '0より小さければ何でも良い。
2615             Return ""
2616         Catch ex As SerializationException
2617             TraceOut(ex.Message + Environment.NewLine + content)
2618             Return "Err:Json Parse Error(DataContractJsonSerializer)"
2619         Catch ex As Exception
2620             TraceOut(ex, GetCurrentMethod.Name & " " & content)
2621             Return "Err:Invalid Json!"
2622         End Try
2623     End Function
2624
2625     Public ReadOnly Property GetNoRetweetSuccess() As Boolean
2626         Get
2627             Return _GetNoRetweetResult
2628         End Get
2629     End Property
2630
2631     Public Function ConfigurationApi() As String
2632         Dim res As HttpStatusCode
2633         Dim content As String = ""
2634         Try
2635             res = twCon.GetConfiguration(content)
2636         Catch ex As Exception
2637             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
2638         End Try
2639
2640         Select Case res
2641             Case HttpStatusCode.OK
2642                 Twitter.AccountState = ACCOUNT_STATE.Valid
2643             Case HttpStatusCode.Unauthorized
2644                 Twitter.AccountState = ACCOUNT_STATE.Invalid
2645                 Return My.Resources.Unauthorized
2646             Case HttpStatusCode.BadRequest
2647                 Return "Err:API Limits?"
2648             Case Else
2649                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
2650         End Select
2651
2652         Try
2653             AppendSettingDialog.Instance.TwitterConfiguration = CreateDataFromJson(Of TwitterDataModel.Configuration)(content)
2654             Return ""
2655         Catch ex As SerializationException
2656             TraceOut(ex.Message + Environment.NewLine + content)
2657             Return "Err:Json Parse Error(DataContractJsonSerializer)"
2658         Catch ex As Exception
2659             TraceOut(ex, GetCurrentMethod.Name & " " & content)
2660             Return "Err:Invalid Json!"
2661         End Try
2662     End Function
2663
2664     Public Function GetListsApi() As String
2665         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
2666
2667         Dim res As HttpStatusCode
2668         Dim content As String = ""
2669         Dim cursor As Long = -1
2670
2671         Dim lists As New List(Of ListElement)
2672         Do
2673             Try
2674                 res = twCon.GetLists(Me.Username, cursor, content)
2675             Catch ex As Exception
2676                 Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
2677             End Try
2678
2679             Select Case res
2680                 Case HttpStatusCode.OK
2681                     Twitter.AccountState = ACCOUNT_STATE.Valid
2682                 Case HttpStatusCode.Unauthorized
2683                     Twitter.AccountState = ACCOUNT_STATE.Invalid
2684                     Return My.Resources.Unauthorized
2685                 Case HttpStatusCode.BadRequest
2686                     Return "Err:API Limits?"
2687                 Case Else
2688                     Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
2689             End Select
2690
2691             Try
2692                 Dim lst = CreateDataFromJson(Of TwitterDataModel.Lists)(content)
2693                 lists.AddRange(From le In lst.Lists Select New ListElement(le, Me))
2694                 cursor = lst.NextCursor
2695             Catch ex As SerializationException
2696                 TraceOut(ex.Message + Environment.NewLine + content)
2697                 Return "Err:Json Parse Error(DataContractJsonSerializer)"
2698             Catch ex As Exception
2699                 TraceOut(ex, GetCurrentMethod.Name & " " & content)
2700                 Return "Err:Invalid Json!"
2701             End Try
2702         Loop While cursor <> 0
2703
2704         cursor = -1
2705         content = ""
2706         Do
2707             Try
2708                 res = twCon.GetListsSubscriptions(Me.Username, cursor, content)
2709             Catch ex As Exception
2710                 Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
2711             End Try
2712
2713             Select Case res
2714                 Case HttpStatusCode.OK
2715                     Twitter.AccountState = ACCOUNT_STATE.Valid
2716                 Case HttpStatusCode.Unauthorized
2717                     Twitter.AccountState = ACCOUNT_STATE.Invalid
2718                     Return My.Resources.Unauthorized
2719                 Case HttpStatusCode.BadRequest
2720                     Return "Err:API Limits?"
2721                 Case Else
2722                     Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
2723             End Select
2724
2725             Try
2726                 Dim lst = CreateDataFromJson(Of TwitterDataModel.Lists)(content)
2727                 lists.AddRange(From le In lst.Lists Select New ListElement(le, Me))
2728                 cursor = lst.NextCursor
2729             Catch ex As SerializationException
2730                 TraceOut(ex.Message + Environment.NewLine + content)
2731                 Return "Err:Json Parse Error(DataContractJsonSerializer)"
2732             Catch ex As Exception
2733                 TraceOut(ex, GetCurrentMethod.Name & " " & content)
2734                 Return "Err:Invalid Json!"
2735             End Try
2736         Loop While cursor <> 0
2737
2738         TabInformations.GetInstance.SubscribableLists = lists
2739         Return ""
2740     End Function
2741
2742     Public Function DeleteList(ByVal list_id As String) As String
2743         Dim res As HttpStatusCode
2744         Dim content As String = ""
2745
2746         Google.GASender.GetInstance().TrackEventWithCategory("post", "destroy_list", Me.UserId)
2747         Try
2748             res = twCon.DeleteListID(Me.Username, list_id, content)
2749         Catch ex As Exception
2750             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
2751         End Try
2752
2753         Select Case res
2754             Case HttpStatusCode.OK
2755                 Twitter.AccountState = ACCOUNT_STATE.Valid
2756             Case HttpStatusCode.Unauthorized
2757                 Twitter.AccountState = ACCOUNT_STATE.Invalid
2758                 Return My.Resources.Unauthorized
2759             Case HttpStatusCode.BadRequest
2760                 Return "Err:API Limits?"
2761             Case Else
2762                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
2763         End Select
2764
2765         Return ""
2766     End Function
2767
2768     Public Function EditList(ByVal list_id As String, ByVal new_name As String, ByVal isPrivate As Boolean, ByVal description As String, ByRef list As ListElement) As String
2769         Google.GASender.GetInstance().TrackEventWithCategory("get", "update_list", Me.UserId)
2770         Dim res As HttpStatusCode
2771         Dim content As String = ""
2772
2773         Try
2774             res = twCon.UpdateListID(Me.Username, list_id, new_name, isPrivate, description, content)
2775         Catch ex As Exception
2776             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
2777         End Try
2778
2779         Select Case res
2780             Case HttpStatusCode.OK
2781                 Twitter.AccountState = ACCOUNT_STATE.Valid
2782             Case HttpStatusCode.Unauthorized
2783                 Twitter.AccountState = ACCOUNT_STATE.Invalid
2784                 Return My.Resources.Unauthorized
2785             Case HttpStatusCode.BadRequest
2786                 Return "Err:API Limits?"
2787             Case Else
2788                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
2789         End Select
2790
2791         Try
2792             Dim le = CreateDataFromJson(Of TwitterDataModel.ListElementData)(content)
2793             Dim newList As New ListElement(le, Me)
2794             list.Description = newList.Description
2795             list.Id = newList.Id
2796             list.IsPublic = newList.IsPublic
2797             list.MemberCount = newList.MemberCount
2798             list.Name = newList.Name
2799             list.SubscriberCount = newList.SubscriberCount
2800             list.Slug = newList.Slug
2801             list.Nickname = newList.Nickname
2802             list.Username = newList.Username
2803             list.UserId = newList.UserId
2804             Return ""
2805         Catch ex As SerializationException
2806             TraceOut(ex.Message + Environment.NewLine + content)
2807             Return "Err:Json Parse Error(DataContractJsonSerializer)"
2808         Catch ex As Exception
2809             TraceOut(ex, GetCurrentMethod.Name & " " & content)
2810             Return "Err:Invalid Json!"
2811         End Try
2812
2813     End Function
2814
2815     Public Function GetListMembers(ByVal list_id As String, ByVal lists As List(Of UserInfo), ByRef cursor As Long) As String
2816         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
2817
2818         Dim res As HttpStatusCode
2819         Dim content As String = ""
2820
2821         'Do
2822         Try
2823             res = twCon.GetListMembers(Me.Username, list_id, cursor, content)
2824         Catch ex As Exception
2825             Return "Err:" + ex.Message
2826         End Try
2827
2828         Select Case res
2829             Case HttpStatusCode.OK
2830                 Twitter.AccountState = ACCOUNT_STATE.Valid
2831             Case HttpStatusCode.Unauthorized
2832                 Twitter.AccountState = ACCOUNT_STATE.Invalid
2833                 Return My.Resources.Unauthorized
2834             Case HttpStatusCode.BadRequest
2835                 Return "Err:API Limits?"
2836             Case Else
2837                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
2838         End Select
2839
2840         Try
2841             Dim users = CreateDataFromJson(Of TwitterDataModel.Users)(content)
2842             Array.ForEach(Of TwitterDataModel.User)(
2843                 users.users,
2844                 New Action(Of TwitterDataModel.User)(Sub(u)
2845                                                          lists.Add(New UserInfo(u))
2846                                                      End Sub))
2847             cursor = users.NextCursor
2848             Return ""
2849         Catch ex As SerializationException
2850             TraceOut(ex.Message + Environment.NewLine + content)
2851             Return "Err:Json Parse Error(DataContractJsonSerializer)"
2852         Catch ex As Exception
2853             TraceOut(ex, GetCurrentMethod.Name & " " & content)
2854             Return "Err:Invalid Json!"
2855         End Try
2856
2857         Return ""
2858     End Function
2859
2860     Public Function CreateListApi(ByVal listName As String, ByVal isPrivate As Boolean, ByVal description As String) As String
2861         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
2862
2863         Google.GASender.GetInstance().TrackEventWithCategory("post", "create_list", Me.UserId)
2864         Dim res As HttpStatusCode
2865         Dim content As String = ""
2866
2867         Try
2868             res = twCon.CreateLists(listName, isPrivate, description, content)
2869         Catch ex As Exception
2870             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
2871         End Try
2872
2873         Select Case res
2874             Case HttpStatusCode.OK
2875                 Twitter.AccountState = ACCOUNT_STATE.Valid
2876             Case HttpStatusCode.Unauthorized
2877                 Twitter.AccountState = ACCOUNT_STATE.Invalid
2878                 Return My.Resources.Unauthorized
2879             Case HttpStatusCode.BadRequest
2880                 Return "Err:API Limits?"
2881             Case Else
2882                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
2883         End Select
2884
2885         Try
2886             Dim le = CreateDataFromJson(Of TwitterDataModel.ListElementData)(content)
2887             TabInformations.GetInstance().SubscribableLists.Add(New ListElement(le, Me))
2888             Return ""
2889         Catch ex As SerializationException
2890             TraceOut(ex.Message + Environment.NewLine + content)
2891             Return "Err:Json Parse Error(DataContractJsonSerializer)"
2892         Catch ex As Exception
2893             TraceOut(ex, GetCurrentMethod.Name & " " & content)
2894             Return "Err:Invalid Json!"
2895         End Try
2896     End Function
2897
2898     Public Function ContainsUserAtList(ByVal listId As String, ByVal user As String, ByRef value As Boolean) As String
2899         value = False
2900
2901         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
2902
2903         Dim res As HttpStatusCode
2904         Dim content As String = ""
2905
2906         Try
2907             res = Me.twCon.ShowListMember(listId, user, content)
2908         Catch ex As Exception
2909             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
2910         End Try
2911
2912         Select Case res
2913             Case HttpStatusCode.OK
2914                 Twitter.AccountState = ACCOUNT_STATE.Valid
2915             Case HttpStatusCode.Unauthorized
2916                 Twitter.AccountState = ACCOUNT_STATE.Invalid
2917                 Return My.Resources.Unauthorized
2918             Case HttpStatusCode.BadRequest
2919                 Return "Err:API Limits?"
2920             Case HttpStatusCode.NotFound
2921                 value = False
2922                 Return ""
2923             Case Else
2924                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
2925         End Select
2926
2927         Try
2928             Dim u = CreateDataFromJson(Of TwitterDataModel.User)(content)
2929             value = True
2930             Return ""
2931         Catch ex As Exception
2932             value = False
2933             Return ""
2934         End Try
2935     End Function
2936
2937     Public Function AddUserToList(ByVal listId As String, ByVal user As String) As String
2938         Dim content As String = ""
2939         Dim res As HttpStatusCode
2940
2941         Google.GASender.GetInstance().TrackEventWithCategory("post", "add_user_to_list", Me.UserId)
2942         Try
2943             res = twCon.CreateListMembers(listId, user, content)
2944         Catch ex As Exception
2945             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
2946         End Try
2947
2948         Select Case res
2949             Case HttpStatusCode.OK
2950                 Twitter.AccountState = ACCOUNT_STATE.Valid
2951             Case HttpStatusCode.Unauthorized
2952                 Twitter.AccountState = ACCOUNT_STATE.Invalid
2953                 Return My.Resources.Unauthorized
2954             Case HttpStatusCode.BadRequest
2955                 Return "Err:" + GetErrorMessageJson(content)
2956             Case Else
2957                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
2958         End Select
2959
2960         Return ""
2961     End Function
2962
2963     Public Function RemoveUserToList(ByVal listId As String, ByVal user As String) As String
2964         Google.GASender.GetInstance().TrackEventWithCategory("post", "remove_user_from_list", Me.UserId)
2965
2966         Dim content As String = ""
2967         Dim res As HttpStatusCode
2968
2969         Try
2970             res = twCon.DeleteListMembers(listId, user, content)
2971         Catch ex As Exception
2972             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
2973         End Try
2974
2975         Select Case res
2976             Case HttpStatusCode.OK
2977                 Twitter.AccountState = ACCOUNT_STATE.Valid
2978             Case HttpStatusCode.Unauthorized
2979                 Twitter.AccountState = ACCOUNT_STATE.Invalid
2980                 Return My.Resources.Unauthorized
2981             Case HttpStatusCode.BadRequest
2982                 Return "Err:" + GetErrorMessageJson(content)
2983             Case Else
2984                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
2985         End Select
2986
2987         Return ""
2988     End Function
2989
2990     Private Class range
2991         Public Property fromIndex As Integer
2992         Public Property toIndex As Integer
2993         Public Sub New(ByVal fromIndex As Integer, ByVal toIndex As Integer)
2994             Me.fromIndex = fromIndex
2995             Me.toIndex = toIndex
2996         End Sub
2997     End Class
2998     Public Function CreateHtmlAnchor(ByVal Text As String, ByVal AtList As List(Of String), ByVal media As Dictionary(Of String, String)) As String
2999         If Text Is Nothing Then Return Nothing
3000         Dim retStr As String = Text.Replace("&gt;", "<<<<<tweenだいなり>>>>>").Replace("&lt;", "<<<<<tweenしょうなり>>>>>")
3001         'uriの正規表現
3002         'Const url_valid_domain As String = "(?<domain>(?:[^\p{P}\s][\.\-_](?=[^\p{P}\s])|[^\p{P}\s]){1,}\.[a-z]{2,}(?::[0-9]+)?)"
3003         'Const url_valid_general_path_chars As String = "[a-z0-9!*';:=+$/%#\[\]\-_,~]"
3004         'Const url_balance_parens As String = "(?:\(" + url_valid_general_path_chars + "+\))"
3005         'Const url_valid_url_path_ending_chars As String = "(?:[a-z0-9=_#/\-\+]+|" + url_balance_parens + ")"
3006         'Const pth As String = "(?:" + url_balance_parens +
3007         '    "|@" + url_valid_general_path_chars + "+/" +
3008         '    "|[.,]?" + url_valid_general_path_chars + "+" +
3009         '    ")"
3010         'Const pth2 As String = "(/(?:" +
3011         '    pth + "+" + url_valid_url_path_ending_chars + "|" +
3012         '    pth + "+" + url_valid_url_path_ending_chars + "?|" +
3013         '    url_valid_url_path_ending_chars +
3014         '    ")?)?"
3015         'Const qry As String = "(?<query>\?[a-z0-9!*'();:&=+$/%#\[\]\-_.,~]*[a-z0-9_&=#])?"
3016         'Const rgUrl As String = "(?<before>(?:[^\""':!=#]|^|\:/))" +
3017         '                            "(?<url>(?<protocol>https?://)" +
3018         '                            url_valid_domain +
3019         '                            pth2 +
3020         '                            qry +
3021         '                            ")"
3022         'Const rgUrl As String = "(?<before>(?:[^\""':!=#]|^|\:/))" +
3023         '                            "(?<url>(?<protocol>https?://|www\.)" +
3024         '                            url_valid_domain +
3025         '                            pth2 +
3026         '                            qry +
3027         '                            ")"
3028         '絶対パス表現のUriをリンクに置換
3029         retStr = Regex.Replace(retStr,
3030                                rgUrl,
3031                                New MatchEvaluator(Function(mu As Match)
3032                                                       Dim sb As New StringBuilder(mu.Result("${before}<a href="""))
3033                                                       'If mu.Result("${protocol}").StartsWith("w", StringComparison.OrdinalIgnoreCase) Then
3034                                                       '    sb.Append("http://")
3035                                                       'End If
3036                                                       Dim url As String = mu.Result("${url}")
3037                                                       Dim title As String = ShortUrl.ResolveMedia(url, True)
3038                                                       If url <> title Then
3039                                                           title = ShortUrl.ResolveMedia(title, False)
3040                                                       End If
3041                                                       sb.Append(url + """ title=""" + title + """>").Append(url).Append("</a>")
3042                                                       If media IsNot Nothing AndAlso Not media.ContainsKey(url) Then media.Add(url, title)
3043                                                       Return sb.ToString
3044                                                   End Function),
3045                                RegexOptions.IgnoreCase)
3046
3047         '@先をリンクに置換(リスト)
3048         retStr = Regex.Replace(retStr,
3049                                "(^|[^a-zA-Z0-9_/])([@@]+)([a-zA-Z0-9_]{1,20}/[a-zA-Z][a-zA-Z0-9\p{IsLatin-1Supplement}\-]{0,79})",
3050                                "$1$2<a href=""/$3"">$3</a>")
3051
3052         Dim m As Match = Regex.Match(retStr, "(^|[^a-zA-Z0-9_])[@@]([a-zA-Z0-9_]{1,20})")
3053         While m.Success
3054             If Not AtList.Contains(m.Result("$2").ToLower) Then AtList.Add(m.Result("$2").ToLower)
3055             m = m.NextMatch
3056         End While
3057         '@先をリンクに置換
3058         retStr = Regex.Replace(retStr,
3059                                "(^|[^a-zA-Z0-9_/])([@@])([a-zA-Z0-9_]{1,20})",
3060                                "$1$2<a href=""/$3"">$3</a>")
3061
3062         'ハッシュタグを抽出し、リンクに置換
3063         Dim anchorRange As New List(Of range)
3064         For i As Integer = 0 To retStr.Length - 1
3065             Dim index As Integer = retStr.IndexOf("<a ", i)
3066             If index > -1 AndAlso index < retStr.Length Then
3067                 i = index
3068                 Dim toIndex As Integer = retStr.IndexOf("</a>", index)
3069                 If toIndex > -1 Then
3070                     anchorRange.Add(New range(index, toIndex + 3))
3071                     i = toIndex
3072                 End If
3073             End If
3074         Next
3075         'retStr = Regex.Replace(retStr,
3076         '                       "(^|[^a-zA-Z0-9/&])([##])([0-9a-zA-Z_]*[a-zA-Z_]+[a-zA-Z0-9_\xc0-\xd6\xd8-\xf6\xf8-\xff]*)",
3077         '                       New MatchEvaluator(Function(mh As Match)
3078         '                                              For Each rng As range In anchorRange
3079         '                                                  If mh.Index >= rng.fromIndex AndAlso
3080         '                                                   mh.Index <= rng.toIndex Then Return mh.Result("$0")
3081         '                                              Next
3082         '                                              If IsNumeric(mh.Result("$3")) Then Return mh.Result("$0")
3083         '                                              SyncLock LockObj
3084         '                                                  _hashList.Add("#" + mh.Result("$3"))
3085         '                                              End SyncLock
3086         '                                              Return mh.Result("$1") + "<a href=""" & _protocol & "twitter.com/search?q=%23" + mh.Result("$3") + """>" + mh.Result("$2$3") + "</a>"
3087         '                                          End Function),
3088         '                                      RegexOptions.IgnoreCase)
3089         retStr = Regex.Replace(retStr,
3090                                HASHTAG,
3091                                New MatchEvaluator(Function(mh As Match)
3092                                                       For Each rng As range In anchorRange
3093                                                           If mh.Index >= rng.fromIndex AndAlso
3094                                                            mh.Index <= rng.toIndex Then Return mh.Result("$0")
3095                                                       Next
3096                                                       SyncLock LockObj
3097                                                           _hashList.Add("#" + mh.Result("$3"))
3098                                                       End SyncLock
3099                                                       Return mh.Result("$1") + "<a href=""" & _protocol & "twitter.com/search?q=%23" + mh.Result("$3") + """>" + mh.Result("$2$3") + "</a>"
3100                                                   End Function),
3101                                               RegexOptions.IgnoreCase)
3102
3103
3104         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>")
3105
3106         retStr = retStr.Replace("<<<<<tweenだいなり>>>>>", "&gt;").Replace("<<<<<tweenしょうなり>>>>>", "&lt;")
3107
3108         'retStr = AdjustHtml(ShortUrl.Resolve(PreProcessUrl(retStr), True)) 'IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
3109         retStr = AdjustHtml(PreProcessUrl(retStr)) 'IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
3110         Return retStr
3111     End Function
3112
3113     Private Class EntityInfo
3114         Public Property StartIndex As Integer
3115         Public Property EndIndex As Integer
3116         Public Property Text As String
3117         Public Property Html As String
3118         Public Property Display As String
3119     End Class
3120     Public Function CreateHtmlAnchor(ByRef Text As String, ByVal AtList As List(Of String), ByVal entities As TwitterDataModel.Entities, ByVal media As Dictionary(Of String, String)) As String
3121         Dim ret As String = Text
3122
3123         If entities IsNot Nothing Then
3124             Dim etInfo As New SortedList(Of Integer, EntityInfo)
3125             'URL
3126             If entities.Urls IsNot Nothing Then
3127                 For Each ent In entities.Urls
3128                     If String.IsNullOrEmpty(ent.DisplayUrl) Then
3129                         etInfo.Add(ent.Indices(0),
3130                                    New EntityInfo With {.StartIndex = ent.Indices(0),
3131                                                         .EndIndex = ent.Indices(1),
3132                                                         .Text = ent.Url,
3133                                                         .Html = "<a href=""" + ent.Url + """>" + ent.Url + "</a>"})
3134                     Else
3135                         Dim expanded As String = ShortUrl.ResolveMedia(ent.ExpandedUrl, False)
3136                         etInfo.Add(ent.Indices(0),
3137                                    New EntityInfo With {.StartIndex = ent.Indices(0),
3138                                                         .EndIndex = ent.Indices(1),
3139                                                         .Text = ent.Url,
3140                                                         .Html = "<a href=""" + ent.Url + """ title=""" + expanded + """>" + ent.DisplayUrl + "</a>",
3141                                                         .Display = ent.DisplayUrl})
3142                         If media IsNot Nothing AndAlso Not media.ContainsKey(ent.Url) Then media.Add(ent.Url, expanded)
3143                     End If
3144                 Next
3145             End If
3146             If entities.Hashtags IsNot Nothing Then
3147                 For Each ent In entities.Hashtags
3148                     Dim hash As String = Text.Substring(ent.Indices(0), ent.Indices(1) - ent.Indices(0))
3149                     etInfo.Add(ent.Indices(0),
3150                                New EntityInfo With {.StartIndex = ent.Indices(0),
3151                                                     .EndIndex = ent.Indices(1),
3152                                                     .Text = hash,
3153                                                     .Html = "<a href=""" & _protocol & "twitter.com/search?q=%23" + ent.Text + """>" + hash + "</a>"})
3154                     SyncLock LockObj
3155                         _hashList.Add("#" + ent.Text)
3156                     End SyncLock
3157                 Next
3158             End If
3159             If entities.UserMentions IsNot Nothing Then
3160                 For Each ent In entities.UserMentions
3161                     Dim screenName As String = Text.Substring(ent.Indices(0) + 1, ent.Indices(1) - ent.Indices(0) - 1)
3162                     etInfo.Add(ent.Indices(0) + 1,
3163                                New EntityInfo With {.StartIndex = ent.Indices(0) + 1,
3164                                                     .EndIndex = ent.Indices(1),
3165                                                     .Text = ent.ScreenName,
3166                                                     .Html = "<a href=""/" + ent.ScreenName + """>" + screenName + "</a>"})
3167                     If Not AtList.Contains(ent.ScreenName.ToLower) Then AtList.Add(ent.ScreenName.ToLower)
3168                 Next
3169             End If
3170             If entities.Media IsNot Nothing Then
3171                 For Each ent In entities.Media
3172                     If ent.Type = "photo" Then
3173                         etInfo.Add(ent.Indices(0),
3174                                    New EntityInfo With {.StartIndex = ent.Indices(0),
3175                                                         .EndIndex = ent.Indices(1),
3176                                                         .Text = ent.Url,
3177                                                         .Html = "<a href=""" + ent.Url + """ title=""" + ent.ExpandedUrl + """>" + ent.DisplayUrl + "</a>",
3178                                                         .Display = ent.DisplayUrl})
3179                         If media IsNot Nothing AndAlso Not media.ContainsKey(ent.Url) Then media.Add(ent.Url, ent.MediaUrl)
3180                     End If
3181                 Next
3182             End If
3183             If etInfo.Count > 0 Then
3184                 Dim idx As Integer = 0
3185                 ret = ""
3186                 For Each et In etInfo
3187                     ret += Text.Substring(idx, et.Key - idx) + et.Value.Html
3188                     idx = et.Value.EndIndex
3189                 Next
3190                 ret += Text.Substring(idx)
3191             End If
3192         End If
3193
3194         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>")
3195         ret = AdjustHtml(ShortUrl.Resolve(PreProcessUrl(ret), False)) 'IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
3196
3197         Return ret
3198     End Function
3199
3200     'Source整形
3201     Private Sub CreateSource(ByRef post As PostClass)
3202         If post.Source.StartsWith("<") Then
3203             If Not post.Source.Contains("</a>") Then
3204                 post.Source += "</a>"
3205             End If
3206             Dim mS As Match = Regex.Match(post.Source, ">(?<source>.+)<")
3207             If mS.Success Then
3208                 post.SourceHtml = String.Copy(ShortUrl.Resolve(PreProcessUrl(post.Source), False))
3209                 post.Source = HttpUtility.HtmlDecode(mS.Result("${source}"))
3210             Else
3211                 post.Source = ""
3212                 post.SourceHtml = ""
3213             End If
3214         Else
3215             If post.Source = "web" Then
3216                 post.SourceHtml = My.Resources.WebSourceString
3217             ElseIf post.Source = "Keitai Mail" Then
3218                 post.SourceHtml = My.Resources.KeitaiMailSourceString
3219             Else
3220                 post.SourceHtml = String.Copy(post.Source)
3221             End If
3222         End If
3223     End Sub
3224
3225     Public Function GetInfoApi(ByVal info As ApiInfo) As Boolean
3226         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return True
3227
3228         If _endingFlag Then Return True
3229
3230         Dim res As HttpStatusCode
3231         Dim content As String = ""
3232         Try
3233             res = twCon.RateLimitStatus(content)
3234         Catch ex As Exception
3235             TwitterApiInfo.Initialize()
3236             Return False
3237         End Try
3238
3239         If res <> HttpStatusCode.OK Then Return False
3240
3241         Try
3242             Dim limit = CreateDataFromJson(Of TwitterDataModel.RateLimitStatus)(content)
3243             Dim arg As New ApiInformationChangedEventArgs
3244             arg.ApiInfo.MaxCount = limit.HourlyLimit
3245             arg.ApiInfo.RemainCount = limit.RemainingHits
3246             arg.ApiInfo.ResetTime = DateTimeParse(limit.RestTime)
3247             arg.ApiInfo.ResetTimeInSeconds = limit.RestTimeInSeconds
3248             If info IsNot Nothing Then
3249                 arg.ApiInfo.UsingCount = info.UsingCount
3250
3251                 info.MaxCount = arg.ApiInfo.MaxCount
3252                 info.RemainCount = arg.ApiInfo.RemainCount
3253                 info.ResetTime = arg.ApiInfo.ResetTime
3254                 info.ResetTimeInSeconds = arg.ApiInfo.ResetTimeInSeconds
3255             End If
3256
3257             RaiseEvent ApiInformationChanged(Me, arg)
3258             TwitterApiInfo.WriteBackEventArgs(arg)
3259             Return True
3260         Catch ex As Exception
3261             TraceOut(ex, GetCurrentMethod.Name & " " & content)
3262             TwitterApiInfo.Initialize()
3263             Return False
3264         End Try
3265     End Function
3266     Public Function GetBlockUserIds() As String
3267         If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
3268
3269         Dim res As HttpStatusCode
3270         Dim content As String = ""
3271
3272         Try
3273             res = twCon.GetBlockUserIds(content)
3274         Catch ex As Exception
3275             Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
3276         End Try
3277
3278         Select Case res
3279             Case HttpStatusCode.OK
3280                 Twitter.AccountState = ACCOUNT_STATE.Valid
3281             Case HttpStatusCode.Unauthorized
3282                 Twitter.AccountState = ACCOUNT_STATE.Invalid
3283                 Return My.Resources.Unauthorized
3284             Case HttpStatusCode.BadRequest
3285                 Return "Err:API Limits?"
3286             Case Else
3287                 Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
3288         End Select
3289
3290         Try
3291             Dim Ids = CreateDataFromJson(Of Long())(content)
3292             TabInformations.GetInstance.BlockIds.AddRange(Ids)
3293             Return ("")
3294         Catch ex As SerializationException
3295             TraceOut(ex.Message + Environment.NewLine + content)
3296             Return "Err:Json Parse Error(DataContractJsonSerializer)"
3297         Catch ex As Exception
3298             TraceOut(ex, GetCurrentMethod.Name & " " & content)
3299             Return "Err:Invalid Json!"
3300         End Try
3301
3302     End Function
3303
3304     Public Function GetHashList() As String()
3305         Dim hashArray As String()
3306         SyncLock LockObj
3307             hashArray = _hashList.ToArray
3308             _hashList.Clear()
3309         End SyncLock
3310         Return hashArray
3311     End Function
3312
3313     Public ReadOnly Property AccessToken() As String
3314         Get
3315             Return twCon.AccessToken
3316         End Get
3317     End Property
3318
3319     Public ReadOnly Property AccessTokenSecret() As String
3320         Get
3321             Return twCon.AccessTokenSecret
3322         End Get
3323     End Property
3324
3325     Public Event ApiInformationChanged(ByVal sender As Object, ByVal e As ApiInformationChangedEventArgs)
3326
3327     Private Sub Twitter_ApiInformationChanged(ByVal sender As Object, ByVal e As ApiInformationChangedEventArgs) Handles Me.ApiInformationChanged
3328     End Sub
3329
3330 #Region "UserStream"
3331     Public Property TrackWord As String = ""
3332     Public Property AllAtReply As Boolean = False
3333
3334     Public Event NewPostFromStream()
3335     Public Event UserStreamStarted()
3336     Public Event UserStreamStopped()
3337     Public Event UserStreamGetFriendsList()
3338     Public Event PostDeleted(ByVal id As Long)
3339     Public Event UserStreamEventReceived(ByVal eventType As FormattedEvent)
3340     Private _lastUserstreamDataReceived As DateTime
3341     Private WithEvents userStream As TwitterUserstream
3342
3343     Public Class FormattedEvent
3344         Public Property Eventtype As EVENTTYPE
3345         Public Property CreatedAt As DateTime
3346         Public Property [Event] As String
3347         Public Property Username As String
3348         Public Property Target As String
3349         Public Property Id As Int64
3350         Public Property IsMe As Boolean
3351     End Class
3352
3353     Public Property StoredEvent As New List(Of FormattedEvent)
3354
3355     Private Class EventTypeTableElement
3356         Public Name As String
3357         Public Type As EVENTTYPE
3358
3359         Public Sub New(ByVal name As String, ByVal type As EVENTTYPE)
3360             Me.Name = name
3361             Me.Type = type
3362         End Sub
3363     End Class
3364
3365     Private EventTable As EventTypeTableElement() = {
3366         New EventTypeTableElement("favorite", EVENTTYPE.Favorite), _
3367         New EventTypeTableElement("unfavorite", EVENTTYPE.Unfavorite), _
3368         New EventTypeTableElement("follow", EVENTTYPE.Follow), _
3369         New EventTypeTableElement("list_member_added", EVENTTYPE.ListMemberAdded), _
3370         New EventTypeTableElement("list_member_removed", EVENTTYPE.ListMemberRemoved), _
3371         New EventTypeTableElement("block", EVENTTYPE.Block), _
3372         New EventTypeTableElement("unblock", EVENTTYPE.Unblock), _
3373         New EventTypeTableElement("user_update", EVENTTYPE.UserUpdate), _
3374         New EventTypeTableElement("deleted", EVENTTYPE.Deleted), _
3375         New EventTypeTableElement("list_created", EVENTTYPE.ListCreated), _
3376         New EventTypeTableElement("list_updated", EVENTTYPE.ListUpdated)
3377     }
3378
3379     Public Function EventNameToEventType(ByVal EventName As String) As EVENTTYPE
3380         Return (From tbl In EventTable Where tbl.Name.Equals(EventName) Select tbl.Type).FirstOrDefault()
3381     End Function
3382
3383     Public ReadOnly Property IsUserstreamDataReceived As Boolean
3384         Get
3385             Return Now.Subtract(Me._lastUserstreamDataReceived).TotalSeconds < 31
3386         End Get
3387     End Property
3388
3389     Private Sub userStream_StatusArrived(ByVal line As String) Handles userStream.StatusArrived
3390         Me._lastUserstreamDataReceived = Now
3391         If String.IsNullOrEmpty(line) Then Exit Sub
3392
3393         Dim isDm As Boolean = False
3394
3395         Try
3396             Using jsonReader As XmlDictionaryReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(line), XmlDictionaryReaderQuotas.Max)
3397                 Dim xElm As XElement = XElement.Load(jsonReader)
3398                 If xElm.Element("friends") IsNot Nothing Then
3399                     Debug.Print("friends")
3400                     Exit Sub
3401                 ElseIf xElm.Element("delete") IsNot Nothing Then
3402                     Debug.Print("delete")
3403                     Dim id As Int64
3404                     If xElm.Element("delete").Element("direct_message") IsNot Nothing AndAlso
3405                         xElm.Element("delete").Element("direct_message").Element("id") IsNot Nothing Then
3406                         id = CLng(xElm.Element("delete").Element("direct_message").Element("id").Value)
3407                         RaiseEvent PostDeleted(id)
3408                     ElseIf xElm.Element("delete").Element("status") IsNot Nothing AndAlso
3409                         xElm.Element("delete").Element("status").Element("id") IsNot Nothing Then
3410                         id = CLng(xElm.Element("delete").Element("status").Element("id").Value)
3411                         RaiseEvent PostDeleted(id)
3412                     Else
3413                         TraceOut("delete:" + line)
3414                         Exit Sub
3415                     End If
3416                     For i As Integer = Me.StoredEvent.Count - 1 To 0 Step -1
3417                         Dim sEvt As FormattedEvent = Me.StoredEvent(i)
3418                         If sEvt.Id = id AndAlso (sEvt.Event = "favorite" OrElse sEvt.Event = "unfavorite") Then
3419                             Me.StoredEvent.RemoveAt(i)
3420                         End If
3421                     Next
3422                     Exit Sub
3423                 ElseIf xElm.Element("limit") IsNot Nothing Then
3424                     Debug.Print(line)
3425                     Exit Sub
3426                 ElseIf xElm.Element("event") IsNot Nothing Then
3427                     Debug.Print("event: " + xElm.Element("event").Value)
3428                     CreateEventFromJson(line)
3429                     Exit Sub
3430                 ElseIf xElm.Element("direct_message") IsNot Nothing Then
3431                     Debug.Print("direct_message")
3432                     isDm = True
3433                 ElseIf xElm.Element("scrub_geo") IsNot Nothing Then
3434                     Try
3435                         TabInformations.GetInstance.ScrubGeoReserve(Long.Parse(xElm.Element("scrub_geo").Element("user_id").Value),
3436                                                                     Long.Parse(xElm.Element("scrub_geo").Element("up_to_status_id").Value))
3437                     Catch ex As Exception
3438                         TraceOut("scrub_geo:" + line)
3439                     End Try
3440                     Exit Sub
3441                 End If
3442             End Using
3443
3444             Dim res As New StringBuilder
3445             res.Length = 0
3446             res.Append("[")
3447             res.Append(line)
3448             res.Append("]")
3449
3450             If isDm Then
3451                 CreateDirectMessagesFromJson(res.ToString, WORKERTYPE.UserStream, False)
3452             Else
3453                 CreatePostsFromJson(res.ToString, WORKERTYPE.Timeline, Nothing, False, Nothing, Nothing)
3454             End If
3455         Catch ex As NullReferenceException
3456             TraceOut("NullRef StatusArrived: " + line)
3457         End Try
3458
3459         RaiseEvent NewPostFromStream()
3460     End Sub
3461
3462     Private Sub CreateEventFromJson(ByVal content As String)
3463         Dim eventData As TwitterDataModel.EventData = Nothing
3464         Try
3465             eventData = CreateDataFromJson(Of TwitterDataModel.EventData)(content)
3466         Catch ex As SerializationException
3467             TraceOut(ex, "Event Serialize Exception!" + Environment.NewLine + content)
3468         Catch ex As Exception
3469             TraceOut(ex, "Event Exception!" + Environment.NewLine + content)
3470         End Try
3471
3472         Dim evt As New FormattedEvent
3473         evt.CreatedAt = DateTimeParse(eventData.CreatedAt)
3474         evt.Event = eventData.Event
3475         evt.Username = eventData.Source.ScreenName
3476         evt.IsMe = evt.Username.ToLower().Equals(Me.Username.ToLower())
3477         evt.Eventtype = EventNameToEventType(evt.Event)
3478         Select Case eventData.Event
3479             Case "follow"
3480                 If eventData.Target.ScreenName.ToLower.Equals(_uname) Then
3481                     If Not Me.followerId.Contains(eventData.Source.Id) Then Me.followerId.Add(eventData.Source.Id)
3482                 Else
3483                     Exit Sub    'Block後のUndoをすると、SourceとTargetが逆転したfollowイベントが帰ってくるため。
3484                 End If
3485                 evt.Target = ""
3486             Case "favorite", "unfavorite"
3487                 evt.Target = "@" + eventData.TargetObject.User.ScreenName + ":" + HttpUtility.HtmlDecode(eventData.TargetObject.Text)
3488                 evt.Id = eventData.TargetObject.Id
3489                 If TabInformations.GetInstance.ContainsKey(eventData.TargetObject.Id) Then
3490                     Dim post As PostClass = TabInformations.GetInstance.Item(eventData.TargetObject.Id)
3491                     If eventData.Event = "favorite" Then
3492                         If evt.Username.ToLower.Equals(_uname) Then
3493                             post.IsFav = True
3494                             TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites).Add(post.StatusId, post.IsRead, False)
3495                         Else
3496                             post.FavoritedCount += 1
3497                             If Not TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites).Contains(post.StatusId) Then
3498                                 If TweenMain.GetInstance().FavEventChangeUnread AndAlso post.IsRead Then
3499                                     post.IsRead = False
3500                                 End If
3501                                 TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites).Add(post.StatusId, post.IsRead, False)
3502                             Else
3503                                 If TweenMain.GetInstance().FavEventChangeUnread Then
3504                                     TabInformations.GetInstance.SetRead(False, TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites).TabName, TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites).IndexOf(post.StatusId))
3505                                 End If
3506                             End If
3507                         End If
3508                     Else
3509                         If evt.Username.ToLower.Equals(_uname) Then
3510                             post.IsFav = False
3511                         Else
3512                             post.FavoritedCount -= 1
3513                             If post.FavoritedCount < 0 Then post.FavoritedCount = 0
3514                         End If
3515                     End If
3516                 End If
3517             Case "list_member_added", "list_member_removed", "list_updated"
3518                 evt.Target = eventData.TargetObject.FullName
3519             Case "block"
3520                 If Not TabInformations.GetInstance.BlockIds.Contains(eventData.Target.Id) Then TabInformations.GetInstance.BlockIds.Add(eventData.Target.Id)
3521                 evt.Target = ""
3522             Case "unblock"
3523                 If TabInformations.GetInstance.BlockIds.Contains(eventData.Target.Id) Then TabInformations.GetInstance.BlockIds.Remove(eventData.Target.Id)
3524                 evt.Target = ""
3525             Case "user_update"
3526                 evt.Target = ""
3527             Case "list_created"
3528                 evt.Target = ""
3529             Case Else
3530                 TraceOut("Unknown Event:" + evt.Event + Environment.NewLine + content)
3531         End Select
3532         Me.StoredEvent.Insert(0, evt)
3533         RaiseEvent UserStreamEventReceived(evt)
3534     End Sub
3535
3536     Private Sub userStream_Started() Handles userStream.Started
3537         Google.GASender.GetInstance().TrackPage("/userstream", Me.UserId)
3538         RaiseEvent UserStreamStarted()
3539     End Sub
3540
3541     Private Sub userStream_Stopped() Handles userStream.Stopped
3542         RaiseEvent UserStreamStopped()
3543     End Sub
3544
3545     Public ReadOnly Property UserStreamEnabled As Boolean
3546         Get
3547             Return If(userStream Is Nothing, False, userStream.Enabled)
3548         End Get
3549     End Property
3550
3551     Public Sub StartUserStream()
3552         If userStream IsNot Nothing Then
3553             StopUserStream()
3554         End If
3555         userStream = New TwitterUserstream(twCon)
3556         userStream.Start(Me.AllAtReply, Me.TrackWord)
3557     End Sub
3558
3559     Public Sub StopUserStream()
3560         If userStream IsNot Nothing Then userStream.Dispose()
3561         userStream = Nothing
3562         If Not _endingFlag Then RaiseEvent UserStreamStopped()
3563     End Sub
3564
3565     Public Sub ReconnectUserStream()
3566         If userStream IsNot Nothing Then
3567             Me.StartUserStream()
3568         End If
3569     End Sub
3570
3571     Private Class TwitterUserstream
3572         Implements IDisposable
3573
3574         Public Event StatusArrived(ByVal status As String)
3575         Public Event Stopped()
3576         Public Event Started()
3577         Private twCon As HttpTwitter
3578
3579         Private _streamThread As Thread
3580         Private _streamActive As Boolean
3581
3582         Private _allAtreplies As Boolean = False
3583         Private _trackwords As String = ""
3584
3585         Public Sub New(ByVal twitterConnection As HttpTwitter)
3586             twCon = DirectCast(twitterConnection.Clone(), HttpTwitter)
3587         End Sub
3588
3589         Public Sub Start(ByVal allAtReplies As Boolean, ByVal trackwords As String)
3590             Me.AllAtReplies = allAtReplies
3591             Me.TrackWords = trackwords
3592             _streamActive = True
3593             If _streamThread IsNot Nothing AndAlso _streamThread.IsAlive Then Exit Sub
3594             _streamThread = New Thread(AddressOf UserStreamLoop)
3595             _streamThread.Name = "UserStreamReceiver"
3596             _streamThread.IsBackground = True
3597             _streamThread.Start()
3598         End Sub
3599
3600         Public ReadOnly Property Enabled() As Boolean
3601             Get
3602                 Return _streamActive
3603             End Get
3604         End Property
3605
3606         Public Property AllAtReplies As Boolean
3607             Get
3608                 Return _allAtreplies
3609             End Get
3610             Set(ByVal value As Boolean)
3611                 _allAtreplies = value
3612             End Set
3613         End Property
3614
3615         Public Property TrackWords As String
3616             Get
3617                 Return _trackwords
3618             End Get
3619             Set(ByVal value As String)
3620                 _trackwords = value
3621             End Set
3622         End Property
3623
3624         Private Sub UserStreamLoop()
3625             Dim st As Stream = Nothing
3626             Dim sr As StreamReader = Nothing
3627             Dim sleepSec As Integer = 0
3628             Do
3629                 Try
3630                     If Not MyCommon.IsNetworkAvailable() Then
3631                         Thread.Sleep(30 * 1000)
3632                         Continue Do
3633                     End If
3634
3635                     RaiseEvent Started()
3636                     Dim res As HttpStatusCode = twCon.UserStream(st, _allAtreplies, _trackwords, GetUserAgentString())
3637
3638                     Select Case res
3639                         Case HttpStatusCode.OK
3640                             Twitter.AccountState = ACCOUNT_STATE.Valid
3641                         Case HttpStatusCode.Unauthorized
3642                             Twitter.AccountState = ACCOUNT_STATE.Invalid
3643                             sleepSec = 120
3644                             Continue Do
3645                     End Select
3646
3647                     If st Is Nothing Then
3648                         sleepSec = 30
3649                         'TraceOut("Stop:stream is Nothing")
3650                         Continue Do
3651                     End If
3652
3653                     sr = New StreamReader(st)
3654
3655                     Do While _streamActive AndAlso Not sr.EndOfStream AndAlso Twitter.AccountState = ACCOUNT_STATE.Valid
3656                         RaiseEvent StatusArrived(sr.ReadLine())
3657                         'Me.LastTime = Now
3658                     Loop
3659
3660                     If sr.EndOfStream OrElse Twitter.AccountState = ACCOUNT_STATE.Invalid Then
3661                         sleepSec = 30
3662                         'TraceOut("Stop:EndOfStream")
3663                         Continue Do
3664                     End If
3665                     Exit Do
3666                 Catch ex As WebException
3667                     If ex.Status = WebExceptionStatus.Timeout Then
3668                         sleepSec = 30                        'TraceOut("Stop:Timeout")
3669                     ElseIf ex.Response IsNot Nothing AndAlso CType(ex.Response, HttpWebResponse).StatusCode = 420 Then
3670                         'TraceOut("Stop:Connection Limit")
3671                         Exit Do
3672                     Else
3673                         sleepSec = 30
3674                         'TraceOut("Stop:WebException " & ex.Status.ToString)
3675                     End If
3676                 Catch ex As ThreadAbortException
3677                     Exit Do
3678                 Catch ex As IOException
3679                     sleepSec = 30
3680                     'TraceOut("Stop:IOException with Active." + Environment.NewLine + ex.Message)
3681                 Catch ex As ArgumentException
3682                     'System.ArgumentException: ストリームを読み取れませんでした。
3683                     'サーバー側もしくは通信経路上で切断された場合?タイムアウト頻発後発生
3684                     sleepSec = 30
3685                     TraceOut(ex, "Stop:ArgumentException")
3686                 Catch ex As Exception
3687                     TraceOut("Stop:Exception." + Environment.NewLine + ex.Message)
3688                     ExceptionOut(ex)
3689                     sleepSec = 30
3690                 Finally
3691                     If _streamActive Then RaiseEvent Stopped()
3692                     twCon.RequestAbort()
3693                     If sr IsNot Nothing Then sr.Close()
3694                     If st IsNot Nothing Then st.Close()
3695                     If sleepSec > 0 Then
3696                         Dim ms As Integer = 0
3697                         Do While _streamActive AndAlso ms < sleepSec * 1000
3698                             Thread.Sleep(500)
3699                             ms += 500
3700                         Loop
3701                     End If
3702                     sleepSec = 0
3703                 End Try
3704             Loop While Me._streamActive
3705
3706             If _streamActive Then RaiseEvent Stopped()
3707             TraceOut("Stop:EndLoop")
3708         End Sub
3709
3710 #Region "IDisposable Support"
3711         Private disposedValue As Boolean ' 重複する呼び出しを検出するには
3712
3713         ' IDisposable
3714         Protected Overridable Sub Dispose(ByVal disposing As Boolean)
3715             If Not Me.disposedValue Then
3716                 If disposing Then
3717                     ' TODO: マネージ状態を破棄します (マネージ オブジェクト)。
3718                     _streamActive = False
3719                     If _streamThread IsNot Nothing AndAlso _streamThread.IsAlive Then
3720                         _streamThread.Abort()
3721                     End If
3722                 End If
3723
3724                 ' TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下の Finalize() をオーバーライドします。
3725                 ' TODO: 大きなフィールドを null に設定します。
3726             End If
3727             Me.disposedValue = True
3728         End Sub
3729
3730         ' TODO: 上の Dispose(ByVal disposing As Boolean) にアンマネージ リソースを解放するコードがある場合にのみ、Finalize() をオーバーライドします。
3731         'Protected Overrides Sub Finalize()
3732         '    ' このコードを変更しないでください。クリーンアップ コードを上の Dispose(ByVal disposing As Boolean) に記述します。
3733         '    Dispose(False)
3734         '    MyBase.Finalize()
3735         'End Sub
3736
3737         ' このコードは、破棄可能なパターンを正しく実装できるように Visual Basic によって追加されました。
3738         Public Sub Dispose() Implements IDisposable.Dispose
3739             ' このコードを変更しないでください。クリーンアップ コードを上の Dispose(ByVal disposing As Boolean) に記述します。
3740             Dispose(True)
3741             GC.SuppressFinalize(Me)
3742         End Sub
3743 #End Region
3744
3745     End Class
3746 #End Region
3747
3748 #Region "IDisposable Support"
3749     Private disposedValue As Boolean ' 重複する呼び出しを検出するには
3750
3751     ' IDisposable
3752     Protected Overridable Sub Dispose(ByVal disposing As Boolean)
3753         If Not Me.disposedValue Then
3754             If disposing Then
3755                 ' TODO: マネージ状態を破棄します (マネージ オブジェクト)。
3756                 Me.StopUserStream()
3757             End If
3758
3759             ' TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下の Finalize() をオーバーライドします。
3760             ' TODO: 大きなフィールドを null に設定します。
3761         End If
3762         Me.disposedValue = True
3763     End Sub
3764
3765     ' TODO: 上の Dispose(ByVal disposing As Boolean) にアンマネージ リソースを解放するコードがある場合にのみ、Finalize() をオーバーライドします。
3766     'Protected Overrides Sub Finalize()
3767     '    ' このコードを変更しないでください。クリーンアップ コードを上の Dispose(ByVal disposing As Boolean) に記述します。
3768     '    Dispose(False)
3769     '    MyBase.Finalize()
3770     'End Sub
3771
3772     ' このコードは、破棄可能なパターンを正しく実装できるように Visual Basic によって追加されました。
3773     Public Sub Dispose() Implements IDisposable.Dispose
3774         ' このコードを変更しないでください。クリーンアップ コードを上の Dispose(ByVal disposing As Boolean) に記述します。
3775         Dispose(True)
3776         GC.SuppressFinalize(Me)
3777     End Sub
3778 #End Region
3779
3780 End Class