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