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