root/lang/vb2005/Tween/Tween/Twitter.vb @ 31904

Revision 31904, 207.0 kB (checked in by kiri_feather, 4 years ago)

設定保存失敗時のメッセージ変更とリトライ

  • Property svn:mime-type set to text/plain
Line 
1' Tween - Client of Twitter
2' Copyright © 2007-2009 kiri_feather (@kiri_feather) <kiri_feather@gmail.com>
3'           © 2008-2009 Moz (@syo68k) <http://iddy.jp/profile/moz/>
4'           © 2008-2009 takeshik (@takeshik) <http://www.takeshik.org/>
5' All rights reserved.
6'
7' This file is part of Tween.
8'
9' This program is free software; you can redistribute it and/or modify it
10' under the terms of the GNU General Public License as published by the Free
11' Software Foundation; either version 3 of the License, or (at your option)
12' any later version.
13'
14' This program is distributed in the hope that it will be useful, but
15' WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16' or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17' for more details.
18'
19' You should have received a copy of the GNU General Public License along
20' with this program. If not, see <http://www.gnu.org/licenses/>, or write to
21' the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
22' Boston, MA 02110-1301, USA.
23
24Imports System.Web
25Imports System.Xml
26Imports System.Text
27Imports System.Threading
28Imports System.IO
29Imports System.Text.RegularExpressions
30Imports System.Globalization
31
32Public Module Twitter
33    Delegate Sub GetIconImageDelegate(ByVal post As PostClass)
34    Delegate Function GetTimelineDelegate(ByVal page As Integer, _
35                                ByVal read As Boolean, _
36                                ByRef endPage As Integer, _
37                                ByVal gType As WORKERTYPE, _
38                                ByRef getDM As Boolean) As String
39    Delegate Function GetDirectMessageDelegate(ByVal page As Integer, _
40                                    ByVal read As Boolean, _
41                                    ByVal endPage As Integer, _
42                                    ByVal gType As WORKERTYPE) As String
43    Private ReadOnly LockObj As New Object
44    Private GetTmSemaphore As New Threading.Semaphore(8, 8)
45
46    Private follower As New Collections.Specialized.StringCollection
47    Private followerId As New List(Of Long)
48    Private tmpFollower As New Collections.Specialized.StringCollection
49
50    'プロパティからアクセスされる共通情報
51    Private _uid As String
52    Private _pwd As String
53    Private _proxyType As ProxyTypeEnum
54    Private _proxyAddress As String
55    Private _proxyPort As Integer
56    Private _proxyUser As String
57    Private _proxyPassword As String
58
59    Private _nextThreshold As Integer
60    Private _nextPages As Integer
61
62    Private _iconSz As Integer
63    Private _getIcon As Boolean
64    Private _lIcon As ImageList
65    Private _dIcon As Dictionary(Of String, Image)
66
67    Private _tinyUrlResolve As Boolean
68    Private _restrictFavCheck As Boolean
69    Private _useAPI As Boolean
70
71    Private _hubServer As String
72    Private _defaultTimeOut As Integer      ' MySocketクラスへ渡すタイムアウト待ち時間(秒単位 ミリ秒への換算はMySocketクラス側で行う)
73    Private _countApi As Integer
74    Private _usePostMethod As Boolean
75    Private _ApiMethod As MySocket.REQ_TYPE
76
77    '共通で使用する状態
78    Private _authKey As String              'StatusUpdate、発言削除で使用
79    Private _authKeyDM As String              'DM送信、DM削除で使用
80    Private _signed As Boolean
81    Private _infoTwitter As String = ""
82    Private _dmCount As Integer
83    Private _getDm As Boolean
84
85    Private _ShortUrlService() As String = { _
86            "http://tinyurl.com/", _
87            "http://is.gd/", _
88            "http://snipurl.com/", _
89            "http://snurl.com/", _
90            "http://nsfw.in/", _
91            "http://qurlyq.com/", _
92            "http://dwarfurl.com/", _
93            "http://icanhaz.com/", _
94            "http://tiny.cc/", _
95            "http://urlenco.de/", _
96            "http://bit.ly/", _
97            "http://piurl.com/", _
98            "http://linkbee.com/", _
99            "http://traceurl.com/", _
100            "http://twurl.nl/", _
101            "http://cli.gs/", _
102            "http://rubyurl.com/", _
103            "http://budurl.com/", _
104            "http://ff.im/" _
105        }
106
107    Private Const _baseUrlStr As String = "twitter.com"
108    Private Const _loginPath As String = "/sessions"
109    Private Const _homePath As String = "/home"
110    Private Const _replyPath As String = "/replies"
111    Private Const _DMPathRcv As String = "/direct_messages"
112    Private Const _DMPathSnt As String = "/direct_messages/sent"
113    Private Const _DMDestroyPath As String = "/direct_messages/destroy/"
114    Private Const _StDestroyPath As String = "/status/destroy/"
115    Private Const _uidHeader As String = "session[username_or_email]="
116    Private Const _pwdHeader As String = "session[password]="
117    Private Const _pageQry As String = "?page="
118    Private Const _statusHeader As String = "status="
119    Private Const _statusUpdatePath As String = "/status/update?page=1&tab=home"
120    Private Const _statusUpdatePathAPI As String = "/statuses/update.xml"
121    Private Const _linkToOld As String = "class=""section_links"" rel=""prev"""
122    Private Const _postFavAddPath As String = "/favorites/create/"
123    Private Const _postFavRemovePath As String = "/favorites/destroy/"
124    Private Const _authKeyHeader As String = "authenticity_token="
125    Private Const _parseLink1 As String = "<a href="""
126    Private Const _parseLink2 As String = """>"
127    Private Const _parseLink3 As String = "</a>"
128    Private Const _GetFollowers As String = "/statuses/followers.xml"
129    Private Const _ShowStatus As String = "/statuses/show/"
130
131    '''Wedata対応
132    Private Const wedataUrl As String = "http://wedata.net/databases/Tween/items.json"
133
134    Private Function SignIn() As String
135        If _endingFlag Then Return ""
136
137        'ユーザー情報からデータ部分の生成
138        Dim account As String = ""
139
140        SyncLock LockObj
141            If _signed Then Return ""
142
143            '未認証
144            _signed = False
145
146            MySocket.ResetCookie()
147
148            Dim resStatus As String = ""
149            Dim resMsg As String = ""
150
151            resMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + "/", resStatus, MySocket.REQ_TYPE.ReqGET), String)
152            If resMsg.Length = 0 Then
153                Return "SignIn -> " + resStatus
154            End If
155            Dim authToken As String = ""
156            Dim rg As New Regex("authenticity_token"" type=""hidden"" value=""(?<auth>[a-z0-9]+)""")
157            Dim m As Match = rg.Match(resMsg)
158            If m.Success Then
159                authToken = m.Result("${auth}")
160            Else
161                Return "SignIn -> Can't get token."
162            End If
163
164            account = _authKeyHeader + authToken + "&" + _uidHeader + _uid + "&" + _pwdHeader + _pwd + "&" + "remember_me=1"
165
166            resMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + _loginPath, resStatus, MySocket.REQ_TYPE.ReqPOST, account), String)
167            If resStatus.StartsWith("OK") Then
168                'OK (username/passwordが合致しない)
169                Dim msg As String = resStatus
170                If resMsg.Contains("Wrong Username/Email and password combination.") Then
171                    msg = "Wrong Username or password."
172                Else
173                    '未知の応答(May be required Chapta)
174                    msg = "Wrong Username or password. Try from web."
175                End If
176                Return "SignIn Failed -> " + msg
177            ElseIf resMsg.Contains("https://twitter.com/account/locked") Then   '302 FOUND
178                Dim msg As String = "You account is Locked Out."
179                Return "SignIn Failed -> " + msg
180            ElseIf resMsg.Contains("https://twitter.com:443/") Then '302 FOUND
181                'OK
182            ElseIf resMsg.StartsWith("Err:") Then
183                ' その他プロトコルエラー
184                Return "SignIn Failed -> " + resMsg
185            Else
186                '応答がOK でありサインインできていない場合の未知の応答
187                TraceOut(True, "SignIn Failed." + vbCrLf + "resStatus:" + resStatus + vbCrLf + "resMsg:" + vbCrLf + resMsg)
188                Return "SignIn Failed -> " + "Unknown problems."
189            End If
190
191            _signed = True
192            Return ""
193        End SyncLock
194    End Function
195
196    Public Function GetTimeline(ByVal page As Integer, _
197                                ByVal read As Boolean, _
198                                ByRef endPage As Integer, _
199                                ByVal gType As WORKERTYPE, _
200                                ByRef getDM As Boolean) As String
201
202        If endPage = 0 Then
203            '通常モード
204            Dim epage As Integer = page
205            GetTmSemaphore.WaitOne()
206            Dim trslt As String = ""
207            trslt = GetTimelineThread(page, read, epage, gType, getDM)
208            If trslt.Length > 0 Then Return trslt
209            page += 1
210            If epage < page OrElse gType = WORKERTYPE.Reply Then Return ""
211            endPage = epage
212        End If
213        '起動時モード or 通常モードの読み込み継続 -> 複数ページ同時取得
214        Dim num As Integer = endPage - page
215        Dim ar(num) As IAsyncResult
216        Dim dlgt(num) As GetTimelineDelegate
217
218        For idx As Integer = 0 To num
219            dlgt(idx) = New GetTimelineDelegate(AddressOf GetTimelineThread)
220            GetTmSemaphore.WaitOne()
221            ar(idx) = dlgt(idx).BeginInvoke(page + idx, read, endPage + idx, gType, getDM, Nothing, Nothing)
222        Next
223        Dim rslt As String = ""
224        For idx As Integer = 0 To num
225            Dim epage As Integer = 0
226            Dim dm As Boolean = False
227            Dim trslt As String = ""
228            Try
229                trslt = dlgt(idx).EndInvoke(epage, dm, ar(idx))
230            Catch ex As Exception
231                '最後までendinvoke回す(ゾンビ化回避)
232                ExceptionOut(ex)
233                rslt = "GetTimelineErr"
234            End Try
235            If trslt.Length > 0 AndAlso rslt.Length = 0 Then rslt = trslt
236            If dm Then getDM = True
237        Next
238        Return rslt
239    End Function
240
241    Private Function GetTimelineThread(ByVal page As Integer, _
242                                ByVal read As Boolean, _
243                                ByRef endPage As Integer, _
244                                ByVal gType As WORKERTYPE, _
245                                ByRef getDM As Boolean) As String
246        Try
247            If _endingFlag Then Return ""
248
249            Dim retMsg As String = ""
250            Dim resStatus As String = ""
251
252            Static redirectToTimeline As String = ""
253            Static redirectToReply As String = ""
254
255            If _signed = False Then
256                retMsg = SignIn()
257                If retMsg.Length > 0 Then
258                    Return retMsg
259                End If
260            End If
261
262            If _endingFlag Then Return ""
263
264            'リクエストメッセージを作成する
265            Dim pageQuery As String
266
267            If page = 1 Then
268                pageQuery = ""
269            Else
270                pageQuery = _pageQry + page.ToString
271            End If
272
273RETRY:
274            If gType = WORKERTYPE.Timeline Then
275                If redirectToTimeline = "" Then
276                    retMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + _homePath + pageQuery, resStatus), String)
277                    If resStatus.StartsWith("Found") Then
278                        redirectToTimeline = resStatus.Substring(6)
279                        If redirectToTimeline.Contains("?") Then
280                            redirectToTimeline = redirectToTimeline.Remove(redirectToTimeline.IndexOf("?"))
281                        End If
282                        GoTo RETRY
283                    End If
284                Else
285                    retMsg = DirectCast(CreateSocket.GetWebResponse(redirectToTimeline + pageQuery, resStatus), String)
286                End If
287            Else
288                If redirectToReply = "" Then
289                    retMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + _replyPath + pageQuery, resStatus), String)
290                    If resStatus.StartsWith("Found") Then
291                        redirectToReply = resStatus.Substring(6)
292                        If redirectToReply.Contains("?") Then
293                            redirectToReply = redirectToReply.Remove(redirectToReply.IndexOf("?"))
294                        End If
295                        GoTo RETRY
296                    End If
297                Else
298                    retMsg = DirectCast(CreateSocket.GetWebResponse(redirectToReply + pageQuery, resStatus), String)
299                End If
300            End If
301
302            If retMsg.Length = 0 Then
303                _signed = False
304                Return resStatus
305            End If
306
307            ' tr 要素の class 属性を消去
308            Do
309                Dim idx As Integer = retMsg.IndexOf(_removeClass, StringComparison.Ordinal)
310                If idx = -1 Then Exit Do
311                Dim idx2 As Integer = retMsg.IndexOf("""", idx + _removeClass.Length, StringComparison.Ordinal) - idx + 1 - 3
312                If idx2 > 0 Then retMsg = retMsg.Remove(idx + 3, idx2)
313            Loop
314
315            If _endingFlag Then Return ""
316
317            '各メッセージに分割可能か?
318            Dim strSepTmp As String
319            If gType = WORKERTYPE.Timeline Then
320                strSepTmp = _splitPostRecent
321            Else
322                strSepTmp = _splitPost
323            End If
324
325            Dim pos1 As Integer
326            Dim pos2 As Integer
327
328            pos1 = retMsg.IndexOf(strSepTmp, StringComparison.Ordinal)
329            If pos1 = -1 Then
330                '0件 or 取得失敗
331                _signed = False
332                Return "GetTimeline -> Err: tweets count is 0."
333            End If
334
335            Dim strSep() As String = {strSepTmp}
336            Dim posts() As String = retMsg.Split(strSep, StringSplitOptions.RemoveEmptyEntries)
337            Dim intCnt As Integer = 0
338            Dim listCnt As Integer = 0
339            SyncLock LockObj
340                listCnt = TabInformations.GetInstance.ItemCount
341            End SyncLock
342            Dim dlgt(20) As GetIconImageDelegate
343            Dim ar(20) As IAsyncResult
344            Dim arIdx As Integer = -1
345
346            For Each strPost As String In posts
347                intCnt += 1
348
349                If intCnt = 1 Then
350                    If page = 1 And gType = WORKERTYPE.Timeline Then
351                        ''siv取得
352                        'pos1 = strPost.IndexOf(_getSiv, 0)
353                        'If pos1 > 0 Then
354                        '    pos2 = strPost.IndexOf(_getSivTo, pos1 + _getSiv.Length)
355                        '    If pos2 > -1 Then
356                        '        _authSiv = strPost.Substring(pos1 + _getSiv.Length, pos2 - pos1 - _getSiv.Length)
357                        '    Else
358                        '        '取得失敗
359                        '        _signed = False
360                        '        Return "GetTimeline -> Err: Can't get Siv."
361                        '    End If
362                        'Else
363                        '    '取得失敗
364                        '    _signed = False
365                        '    Return "GetTimeline -> Err: Can't get Siv."
366                        'End If
367
368                        'AuthKeyの取得
369                        If GetAuthKey(retMsg) < 0 Then
370                            _signed = False
371                            Return "GetTimeline -> Err: Can't get auth token."
372                        End If
373
374                        'TwitterInfoの取得
375                        pos1 = retMsg.IndexOf(_getInfoTwitter, StringComparison.Ordinal)
376                        If pos1 > -1 Then
377                            pos2 = retMsg.IndexOf(_getInfoTwitterTo, pos1, StringComparison.Ordinal)
378                            If pos2 > -1 Then
379                                _infoTwitter = retMsg.Substring(pos1 + _getInfoTwitter.Length, pos2 - pos1 - _getInfoTwitter.Length)
380                            Else
381                                _infoTwitter = ""
382                            End If
383                        Else
384                            _infoTwitter = ""
385                        End If
386                    End If
387                Else
388
389                    Dim post As New PostClass
390
391                    Try
392                        'Get ID
393                        pos1 = 0
394                        pos2 = strPost.IndexOf(_statusIdTo, 0, StringComparison.Ordinal)
395                        post.Id = Long.Parse(HttpUtility.HtmlDecode(strPost.Substring(0, pos2)))
396                    Catch ex As Exception
397                        _signed = False
398                        TraceOut("TM-ID:" + strPost)
399                        Return "GetTimeline -> Err: Can't get ID."
400                    End Try
401                    'Get Name
402                    Try
403                        pos1 = strPost.IndexOf(_parseName, pos2, StringComparison.Ordinal)
404                        pos2 = strPost.IndexOf(_parseNameTo, pos1, StringComparison.Ordinal)
405                        post.Name = HttpUtility.HtmlDecode(strPost.Substring(pos1 + _parseName.Length, pos2 - pos1 - _parseName.Length))
406                    Catch ex As Exception
407                        _signed = False
408                        TraceOut("TM-Name:" + strPost)
409                        Return "GetTimeline -> Err: Can't get Name."
410                    End Try
411                    'Get Nick
412                    '''バレンタイン対応
413                    If strPost.IndexOf("twitter.com/images/heart.png", pos2, StringComparison.Ordinal) > -1 Then
414                        post.Nickname = post.Name
415                    Else
416                        Try
417                            pos1 = strPost.IndexOf(_parseNick, pos2, StringComparison.Ordinal)
418                            pos2 = strPost.IndexOf(_parseNickTo, pos1 + _parseNick.Length, StringComparison.Ordinal)
419                            post.Nickname = HttpUtility.HtmlDecode(strPost.Substring(pos1 + _parseNick.Length, pos2 - pos1 - _parseNick.Length))
420                        Catch ex As Exception
421                            _signed = False
422                            TraceOut("TM-Nick:" + strPost)
423                            Return "GetTimeline -> Err: Can't get Nick."
424                        End Try
425                    End If
426
427                    '二重取得回避
428                    SyncLock LockObj
429                        If TabInformations.GetInstance.ContainsKey(post.Id) Then Continue For
430                    End SyncLock
431
432                    Dim orgData As String = ""
433                    'バレンタイン
434                    If strPost.IndexOf("<form action=""/status/update"" id=""heartForm", 0, StringComparison.Ordinal) > -1 Then
435                        Try
436                            pos1 = strPost.IndexOf("<strong>", 0, StringComparison.Ordinal)
437                            pos2 = strPost.IndexOf("</strong>", pos1, StringComparison.Ordinal)
438                            orgData = strPost.Substring(pos1 + 8, pos2 - pos1 - 8)
439                        Catch ex As Exception
440                            _signed = False
441                            TraceOut("TM-VBody:" + strPost)
442                            Return "GetTimeline -> Err: Can't get Valentine body."
443                        End Try
444                    End If
445
446
447                    'Get ImagePath
448                    Try
449                        pos1 = strPost.IndexOf(_parseImg, pos2, StringComparison.Ordinal)
450                        pos2 = strPost.IndexOf(_parseImgTo, pos1 + _parseImg.Length, StringComparison.Ordinal)
451                        post.ImageUrl = HttpUtility.HtmlDecode(strPost.Substring(pos1 + _parseImg.Length, pos2 - pos1 - _parseImg.Length))
452                    Catch ex As Exception
453                        _signed = False
454                        TraceOut("TM-Img:" + strPost)
455                        Return "GetTimeline -> Err: Can't get ImagePath."
456                    End Try
457
458                    'Protect
459                    If strPost.IndexOf(_isProtect, pos2, StringComparison.Ordinal) > -1 Then
460                        post.IsProtect = True
461                    End If
462
463                    'Get Message
464                    pos1 = strPost.IndexOf(_parseMsg1, pos2, StringComparison.Ordinal)
465                    If pos1 < 0 Then
466                        'Valentine対応その2
467                        Try
468                            If strPost.IndexOf("<div id=""doyouheart", pos2, StringComparison.Ordinal) > -1 Then
469                                'バレンタイン
470                                orgData += " <3 you! Do you <3 "
471                                pos1 = strPost.IndexOf("<a href", pos2, StringComparison.Ordinal)
472                                pos2 = strPost.IndexOf("?", pos1, StringComparison.Ordinal)
473                                orgData += strPost.Substring(pos1, pos2 - pos1 + 1)
474                            Else
475                                pos1 = strPost.IndexOf(_parseProtectMsg1, pos2, StringComparison.Ordinal)
476                                If pos1 = -1 Then
477                                    'バレンタイン
478                                    orgData += " <3 's "
479                                    pos1 = strPost.IndexOf("<a href", pos2, StringComparison.Ordinal)
480                                    If pos1 > -1 Then
481                                        pos2 = strPost.IndexOf("!", pos1, StringComparison.Ordinal)
482                                        orgData += strPost.Substring(pos1, pos2 - pos1 + 1)
483                                    End If
484                                Else
485                                    'プロテクトメッセージ
486                                    pos2 = strPost.IndexOf(_parseProtectMsg2, pos1, StringComparison.Ordinal)
487                                    orgData = strPost.Substring(pos1 + _parseProtectMsg1.Length, pos2 - pos1 - _parseProtectMsg1.Length).Trim()
488                                End If
489                            End If
490                        Catch ex As Exception
491                            _signed = False
492                            TraceOut("TM-VBody2:" + strPost)
493                            Return "GetTimeline -> Err: Can't get Valentine body2."
494                        End Try
495                    Else
496                        '通常メッセージ
497                        Try
498                            pos2 = strPost.IndexOf(_parseMsg2, pos1, StringComparison.Ordinal)
499                            orgData = strPost.Substring(pos1 + _parseMsg1.Length, pos2 - pos1 - _parseMsg1.Length).Trim()
500                        Catch ex As Exception
501                            _signed = False
502                            TraceOut("TM-Body:" + strPost)
503                            Return "GetTimeline -> Err: Can't get body."
504                        End Try
505                        '原文リンク削除
506                        orgData = Regex.Replace(orgData, "<a href=""https://twitter\.com/" + post.Name + "/status/[0-9]+"">\.\.\.</a>$", "")
507                        'ハート変換
508                        orgData = orgData.Replace("&lt;3", "♡")
509                    End If
510
511                    'URL前処理(IDNデコードなど)
512                    orgData = PreProcessUrl(orgData)
513
514                    '短縮URL解決処理(orgData書き換え)
515                    orgData = ShortUrlResolve(orgData)
516
517                    '表示用にhtml整形
518                    post.OriginalData = AdjustHtml(orgData)
519
520                    '単純テキストの取り出し(リンクタグ除去)
521                    Try
522                        post.Data = GetPlainText(orgData)
523                    Catch ex As Exception
524                        _signed = False
525                        TraceOut("TM-Link:" + strPost)
526                        Return "GetTimeline -> Err: Can't parse links."
527                    End Try
528
529                    ' Imageタグ除去(ハロウィン)
530                    Dim ImgTag As New Regex("<img src=.*?/>", RegexOptions.IgnoreCase)
531                    If ImgTag.IsMatch(post.Data) Then post.Data = ImgTag.Replace(post.Data, "<img>")
532
533                    'Get Date
534#If 0 Then
535                    Try
536                        pos1 = strPost.IndexOf(_parseDate, pos2, StringComparison.Ordinal)
537                        pos2 = strPost.IndexOf(_parseDateTo, pos1 + _parseDate.Length, StringComparison.Ordinal)
538                        post.PDate = DateTime.ParseExact(strPost.Substring(pos1 + _parseDate.Length, pos2 - pos1 - _parseDate.Length), "yyyy'-'MM'-'dd'T'HH':'mm':'sszzz", System.Globalization.DateTimeFormatInfo.InvariantInfo, Globalization.DateTimeStyles.None)
539                    Catch ex As Exception
540                        _signed = False
541                        TraceOut("TM-Date:" + strPost)
542                        Return "GetTimeline -> Err: Can't get date."
543                    End Try
544#Else
545                    '取得できなくなったため暫定対応(2/26)
546                    post.PDate = Now()
547#End If
548
549
550                    'from Sourceの取得
551                    Try
552                        pos1 = strPost.IndexOf(_parseSourceFrom, pos2, StringComparison.Ordinal)
553                        If pos1 > -1 Then
554                            pos1 = strPost.IndexOf(_parseSource2, pos1 + 19, StringComparison.Ordinal)
555                            pos2 = strPost.IndexOf(_parseSourceTo, pos1 + 2, StringComparison.Ordinal)
556                            post.Source = HttpUtility.HtmlDecode(strPost.Substring(pos1 + 2, pos2 - pos1 - 2))
557                        Else
558                            post.Source = "Web"
559                        End If
560                    Catch ex As Exception
561                        _signed = False
562                        TraceOut("TM-Src:" + strPost)
563                        Return "GetTimeline -> Err: Can't get src."
564                    End Try
565
566                    'Get Reply(in_reply_to_user/id)
567                    Dim rg As New Regex("<a href=""https?:\/\/twitter\.com\/(?<name>[a-zA-Z0-9_]+)\/status\/(?<id>[0-9]+)"">(?:in reply to |u8fd4u4fe1: )")
568                    Dim m As Match = rg.Match(strPost)
569                    If m.Success Then
570                        post.InReplyToUser = m.Result("${name}")
571                        post.InReplyToId = Long.Parse(m.Result("${id}"))
572                        post.IsReply = post.InReplyToUser.Equals(_uid, StringComparison.OrdinalIgnoreCase)
573                    End If
574
575                    '@先リスト作成
576                    rg = New Regex("@<a href=""\/(?<1>[a-zA-Z0-9_]+)[^a-zA-Z0-9_]")
577                    m = rg.Match(orgData)
578                    While m.Success
579                        post.ReplyToList.Add(m.Groups(1).Value.ToLower())
580                        m = m.NextMatch
581                    End While
582                    If Not post.IsReply Then post.IsReply = post.ReplyToList.Contains(_uid)
583
584                    If gType = WORKERTYPE.Reply Then post.IsReply = True
585
586                    'Get Fav
587                    pos1 = strPost.IndexOf(_parseStar, pos2, StringComparison.Ordinal)
588                    If pos1 > -1 Then
589                        Try
590                            pos2 = strPost.IndexOf(_parseStarTo, pos1 + _parseStar.Length, StringComparison.Ordinal)
591                            If strPost.Substring(pos1 + _parseStar.Length, pos2 - pos1 - _parseStar.Length) = _parseStarEmpty Then
592                                post.IsFav = False
593                            Else
594                                post.IsFav = True
595                            End If
596                        Catch ex As Exception
597                            _signed = False
598                            TraceOut("TM-Fav:" + strPost)
599                            Return "GetTimeline -> Err: Can't get fav status."
600                        End Try
601                    Else
602                        post.IsFav = False
603                    End If
604
605
606                    If _endingFlag Then Return ""
607
608                    post.IsMe = post.Name.Equals(_uid, StringComparison.OrdinalIgnoreCase)
609                    SyncLock LockObj
610                        If follower.Count > 1 Then
611                            post.IsOwl = Not follower.Contains(post.Name.ToLower())
612                        Else
613                            post.IsOwl = False
614                        End If
615                    End SyncLock
616                    post.IsRead = read
617
618                    arIdx += 1
619                    dlgt(arIdx) = New GetIconImageDelegate(AddressOf GetIconImage)
620                    ar(arIdx) = dlgt(arIdx).BeginInvoke(post, Nothing, Nothing)
621
622                End If
623
624                'テスト実装:DMカウント取得
625                If intCnt = posts.Length AndAlso gType = WORKERTYPE.Timeline AndAlso page = 1 Then
626                    pos1 = strPost.IndexOf(_parseDMcountFrom, pos2, StringComparison.Ordinal)
627                    If pos1 > -1 Then
628                        Try
629                            pos2 = strPost.IndexOf(_parseDMcountTo, pos1 + _parseDMcountFrom.Length, StringComparison.Ordinal)
630                            Dim dmCnt As Integer = Integer.Parse(strPost.Substring(pos1 + _parseDMcountFrom.Length, pos2 - pos1 - _parseDMcountFrom.Length))
631                            If dmCnt > _dmCount Then
632                                _dmCount = dmCnt
633                                _getDm = True
634                            End If
635                        Catch ex As Exception
636                            Return "GetTimeline -> Err: Can't get DM count."
637                        End Try
638                    End If
639                End If
640                getDM = _getDm
641            Next
642
643            For i As Integer = 0 To arIdx
644                Try
645                    dlgt(i).EndInvoke(ar(i))
646                Catch ex As Exception
647                    '最後までendinvoke回す(ゾンビ化回避)
648                    ExceptionOut(ex)
649                End Try
650            Next
651
652            SyncLock LockObj
653                If page = 1 AndAlso (TabInformations.GetInstance.ItemCount - listCnt) >= _nextThreshold Then
654                    '新着が閾値の件数以上なら、次のページも念のため読み込み
655                    endPage = _nextPages + 1
656                End If
657            End SyncLock
658
659            Return ""
660        Finally
661            GetTmSemaphore.Release()
662        End Try
663    End Function
664
665    Public Function GetDirectMessage(ByVal page As Integer, _
666                                    ByVal read As Boolean, _
667                                    ByVal endPage As Integer, _
668                                    ByVal gType As WORKERTYPE) As String
669        If endPage = 0 Then
670            '通常モード(DMはモード関係なし)
671            endPage = 1
672        End If
673        '起動時モード
674        Dim num As Integer = endPage - page
675        Dim ar(num) As IAsyncResult
676        Dim dlgt(num) As GetDirectMessageDelegate
677
678        For idx As Integer = 0 To num
679            gType = WORKERTYPE.DirectMessegeRcv
680            dlgt(idx) = New GetDirectMessageDelegate(AddressOf GetDirectMessageThread)
681            GetTmSemaphore.WaitOne()
682            ar(idx) = dlgt(idx).BeginInvoke(page + idx, read, endPage + idx, gType, Nothing, Nothing)
683        Next
684        Dim rslt As String = ""
685        For idx As Integer = 0 To num
686            Dim trslt As String = ""
687            Try
688                trslt = dlgt(idx).EndInvoke(ar(idx))
689            Catch ex As Exception
690                '最後までendinvoke回す(ゾンビ化回避)
691                ExceptionOut(ex)
692                rslt = "GetDirectMessageErr"
693            End Try
694            If trslt.Length > 0 AndAlso rslt.Length = 0 Then rslt = trslt
695        Next
696        For idx As Integer = 0 To num
697            gType = WORKERTYPE.DirectMessegeSnt
698            dlgt(idx) = New GetDirectMessageDelegate(AddressOf GetDirectMessageThread)
699            GetTmSemaphore.WaitOne()
700            ar(idx) = dlgt(idx).BeginInvoke(page + idx, read, endPage + idx, gType, Nothing, Nothing)
701        Next
702        For idx As Integer = 0 To num
703            Dim trslt As String = ""
704            Try
705                trslt = dlgt(idx).EndInvoke(ar(idx))
706            Catch ex As Exception
707                '最後までendinvoke回す(ゾンビ化回避)
708                ExceptionOut(ex)
709                rslt = "GetDirectMessageErr"
710            End Try
711            If trslt.Length > 0 AndAlso rslt.Length = 0 Then rslt = trslt
712        Next
713        Return rslt
714    End Function
715
716    Private Function GetDirectMessageThread(ByVal page As Integer, _
717                                    ByVal read As Boolean, _
718                                    ByVal endPage As Integer, _
719                                    ByVal gType As WORKERTYPE) As String
720        Try
721            If _endingFlag Then Return ""
722
723            Dim retMsg As String = ""
724            Dim resStatus As String = ""
725
726            Static redirectToDmRcv As String = ""
727            Static redirectToDmSnd As String = ""
728
729            _getDm = False
730
731            If _signed = False Then
732                retMsg = SignIn()
733                If retMsg.Length > 0 Then
734                    Return retMsg
735                End If
736            End If
737
738            If _endingFlag Then Return ""
739
740            'リクエストメッセージを作成する
741            Dim pageQuery As String = _pageQry + page.ToString
742RETRY:
743            If gType = WORKERTYPE.DirectMessegeRcv Then
744                If redirectToDmRcv = "" Then
745                    retMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + _DMPathRcv + pageQuery, resStatus), String)
746                    If resStatus.StartsWith("Found") Then
747                        redirectToDmRcv = resStatus.Substring(6)
748                        If redirectToDmRcv.Contains("?") Then
749                            redirectToDmRcv = redirectToDmRcv.Remove(redirectToDmRcv.IndexOf("?"))
750                        End If
751                        GoTo RETRY
752                    End If
753                Else
754                    retMsg = DirectCast(CreateSocket.GetWebResponse(redirectToDmRcv + pageQuery, resStatus), String)
755                End If
756            Else
757                If redirectToDmSnd = "" Then
758                    retMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + _DMPathSnt + pageQuery, resStatus), String)
759                    If resStatus.StartsWith("Found") Then
760                        redirectToDmSnd = resStatus.Substring(6)
761                        If redirectToDmSnd.Contains("?") Then
762                            redirectToDmSnd = redirectToDmSnd.Remove(redirectToDmSnd.IndexOf("?"))
763                        End If
764                        GoTo RETRY
765                    End If
766                Else
767                    retMsg = DirectCast(CreateSocket.GetWebResponse(redirectToDmSnd + pageQuery, resStatus), String)
768                End If
769            End If
770
771            If retMsg.Length = 0 Then
772                _signed = False
773                Return resStatus
774            End If
775
776            ' tr 要素の class 属性を消去
777            Do
778                Dim idx As Integer = retMsg.IndexOf(_removeClass, StringComparison.Ordinal)
779                If idx = -1 Then Exit Do
780                Dim idx2 As Integer = retMsg.IndexOf("""", idx + _removeClass.Length, StringComparison.Ordinal) - idx + 1 - 3
781                If idx2 > 0 Then retMsg = retMsg.Remove(idx + 3, idx2)
782            Loop
783
784            If _endingFlag Then Return ""
785
786            ''AuthKeyの取得
787            'If GetAuthKeyDM(retMsg) < 0 Then
788            '    _signed = False
789            '    Return "GetDirectMessage -> Err: Busy(1)"
790            'End If
791
792            Dim pos1 As Integer
793            Dim pos2 As Integer
794
795            '各メッセージに分割可能か?
796            pos1 = retMsg.IndexOf(_splitDM, StringComparison.Ordinal)
797            If pos1 = -1 Then
798                '0件(メッセージなし。エラーの場合もありうるが判別できないので正常として戻す)
799                Return ""
800            End If
801
802            Dim strSep() As String = {_splitDM}
803            Dim posts() As String = retMsg.Split(strSep, StringSplitOptions.RemoveEmptyEntries)
804            Dim intCnt As Integer = 0   'カウンタ
805            Dim listCnt As Integer = 0
806            SyncLock LockObj
807                listCnt = TabInformations.GetInstance.ItemCount
808            End SyncLock
809            Dim dlgt(20) As GetIconImageDelegate
810            Dim ar(20) As IAsyncResult
811            Dim arIdx As Integer = -1
812
813            For Each strPost As String In posts
814                intCnt += 1
815
816                If intCnt > 1 Then  '1件目はヘッダなので無視
817                    'Dim lItem As New MyListItem
818                    Dim post As New PostClass()
819
820                    'Get ID
821                    Try
822                        pos1 = 0
823                        pos2 = strPost.IndexOf("""", 0, StringComparison.Ordinal)
824                        post.Id = Long.Parse(HttpUtility.HtmlDecode(strPost.Substring(0, pos2)))
825                    Catch ex As Exception
826                        _signed = False
827                        TraceOut("DM-ID:" + strPost)
828                        Return "GetDirectMessage -> Err: Can't get ID"
829                    End Try
830
831                    'Get Name
832                    Try
833                        pos1 = strPost.IndexOf(_parseName, pos2, StringComparison.Ordinal)
834                        pos2 = strPost.IndexOf(_parseNameTo, pos1, StringComparison.Ordinal)
835                        post.Name = HttpUtility.HtmlDecode(strPost.Substring(pos1 + _parseName.Length, pos2 - pos1 - _parseName.Length))
836                    Catch ex As Exception
837                        _signed = False
838                        TraceOut("DM-Name:" + strPost)
839                        Return "GetDirectMessage -> Err: Can't get Name"
840                    End Try
841
842                    'Get Nick
843                    Try
844                        pos1 = strPost.IndexOf(_parseNick, pos2, StringComparison.Ordinal)
845                        pos2 = strPost.IndexOf(_parseNickTo, pos1 + _parseNick.Length, StringComparison.Ordinal)
846                        post.Nickname = HttpUtility.HtmlDecode(strPost.Substring(pos1 + _parseNick.Length, pos2 - pos1 - _parseNick.Length))
847                    Catch ex As Exception
848                        _signed = False
849                        TraceOut("DM-Nick:" + strPost)
850                        Return "GetDirectMessage -> Err: Can't get Nick."
851                    End Try
852
853                    SyncLock LockObj
854                        If TabInformations.GetInstance.ContainsKey(post.Id) Then Continue For
855                    End SyncLock
856
857                    'Get ImagePath
858                    Try
859                        pos1 = strPost.IndexOf(_parseImg, pos2, StringComparison.Ordinal)
860                        pos2 = strPost.IndexOf(_parseImgTo, pos1 + _parseImg.Length, StringComparison.Ordinal)
861                        post.ImageUrl = HttpUtility.HtmlDecode(strPost.Substring(pos1 + _parseImg.Length, pos2 - pos1 - _parseImg.Length))
862                    Catch ex As Exception
863                        _signed = False
864                        TraceOut("DM-Img:" + strPost)
865                        Return "GetDirectMessage -> Err: Can't get ImagePath"
866                    End Try
867
868                    'Get Protect
869                    Try
870                        pos1 = strPost.IndexOf(_isProtect, pos2, StringComparison.Ordinal)
871                        If pos1 > -1 Then post.IsProtect = True
872                    Catch ex As Exception
873                        _signed = False
874                        TraceOut("DM-Protect:" + strPost)
875                        Return "GetDirectMessage -> Err: Can't get Protect"
876                    End Try
877
878                    Dim orgData As String = ""
879
880                    'Get Message
881                    Try
882                        pos1 = strPost.IndexOf(_parseDM1, pos2, StringComparison.Ordinal)
883                        If pos1 > -1 Then
884                            pos2 = strPost.IndexOf(_parseDM2, pos1, StringComparison.Ordinal)
885                            orgData = strPost.Substring(pos1 + _parseDM1.Length, pos2 - pos1 - _parseDM1.Length).Trim()
886                        Else
887                            pos1 = strPost.IndexOf(_parseDM11, pos2, StringComparison.Ordinal)
888                            pos2 = strPost.IndexOf(_parseDM2, pos1, StringComparison.Ordinal)
889                            orgData = strPost.Substring(pos1 + _parseDM11.Length, pos2 - pos1 - _parseDM11.Length).Trim()
890                        End If
891                        orgData = Regex.Replace(orgData, "<a href=""https://twitter\.com/" + post.Name + "/status/[0-9]+"">\.\.\.</a>$", "")
892                        orgData = orgData.Replace("&lt;3", "♡")
893                    Catch ex As Exception
894                        _signed = False
895                        TraceOut("DM-Body:" + strPost)
896                        Return "GetDirectMessage -> Err: Can't get body"
897                    End Try
898
899                    'URL前処理(IDNデコードなど)
900                    orgData = PreProcessUrl(orgData)
901
902                    '短縮URL解決処理(orgData書き換え)
903                    orgData = ShortUrlResolve(orgData)
904
905                    '表示用にhtml整形
906                    post.OriginalData = AdjustHtml(orgData)
907
908                    '単純テキストの取り出し(リンクタグ除去)
909                    Try
910                        post.Data = GetPlainText(orgData)
911                    Catch ex As Exception
912                        _signed = False
913                        TraceOut("DM-Link:" + strPost)
914                        Return "GetDirectMessage -> Err: Can't parse links"
915                    End Try
916
917#If 0 Then
918                'Get Date
919                Try
920                    pos1 = strPost.IndexOf(_parseDate, pos2, StringComparison.Ordinal)
921                    pos2 = strPost.IndexOf(_parseDateTo, pos1 + _parseDate.Length, StringComparison.Ordinal)
922                    post.PDate = DateTime.ParseExact(strPost.Substring(pos1 + _parseDate.Length, pos2 - pos1 - _parseDate.Length), "yyyy'-'MM'-'dd'T'HH':'mm':'sszzz", System.Globalization.DateTimeFormatInfo.InvariantInfo, Globalization.DateTimeStyles.None)
923                Catch ex As Exception
924                    _signed = False
925                    TraceOut("DM-Date:" + strPost)
926                    Return "GetDirectMessage -> Err: Can't get date."
927                End Try
928#Else
929                    '取得できなくなったため暫定対応(2/26)
930                    post.PDate = Now()
931#End If
932
933                    'Get Fav
934                    'pos1 = strPost.IndexOf(_parseStar, pos2)
935                    'pos2 = strPost.IndexOf("""", pos1 + _parseStar.Length)
936                    'If strPost.Substring(pos1 + _parseStar.Length, pos2 - pos1 - _parseStar.Length) = "empty" Then
937                    '    lItem.Fav = False
938                    'Else
939                    '    lItem.Fav = True
940                    'End If
941                    post.IsFav = False
942
943
944                    If _endingFlag Then Return ""
945
946                    '受信DMかの判定で使用
947                    If gType = WORKERTYPE.DirectMessegeRcv Then
948                        post.IsOwl = False
949                    Else
950                        post.IsOwl = True
951                    End If
952
953                    post.IsRead = read
954                    post.IsDm = True
955
956                    'Imageの取得
957                    arIdx += 1
958                    dlgt(arIdx) = New GetIconImageDelegate(AddressOf GetIconImage)
959                    ar(arIdx) = dlgt(arIdx).BeginInvoke(post, Nothing, Nothing)
960                End If
961            Next
962
963            For i As Integer = 0 To arIdx
964                Try
965                    dlgt(i).EndInvoke(ar(i))
966                Catch ex As Exception
967                    ExceptionOut(ex)
968                End Try
969            Next
970
971            Return ""
972
973        Finally
974            GetTmSemaphore.Release()
975        End Try
976    End Function
977
978    Private Function PreProcessUrl(ByVal orgData As String) As String
979        Dim posl1 As Integer
980        Dim posl2 As Integer = 0
981        Dim IDNConveter As IdnMapping = New IdnMapping()
982        Dim href As String = "<a href="""
983
984        Do While True
985            If orgData.IndexOf(href, posl2, StringComparison.Ordinal) > -1 Then
986                Dim urlStr As String = ""
987                ' IDN展開
988                posl1 = orgData.IndexOf(href, posl2, StringComparison.Ordinal)
989                posl1 += href.Length
990                posl2 = orgData.IndexOf("""", posl1, StringComparison.Ordinal)
991                urlStr = orgData.Substring(posl1, posl2 - posl1)
992
993                If Not urlStr.StartsWith("http://") AndAlso Not urlStr.StartsWith("https://") AndAlso Not urlStr.StartsWith("ftp://") Then
994                    Continue Do
995                End If
996
997                Dim replacedUrl As String = IDNDecode(urlStr)
998                If replacedUrl Is Nothing Then Continue Do
999                If replacedUrl = urlStr Then Continue Do
1000
1001                orgData = orgData.Replace("<a href=""" + urlStr, "<a href=""" + replacedUrl)
1002                posl2 = 0
1003            Else
1004                Exit Do
1005            End If
1006        Loop
1007        Return orgData
1008    End Function
1009
1010    Private Function ShortUrlResolve(ByVal orgData As String) As String
1011        If _tinyUrlResolve Then
1012            For Each svc As String In _ShortUrlService
1013                Dim posl1 As Integer
1014                Dim posl2 As Integer = 0
1015
1016                Do While True
1017                    If orgData.IndexOf("<a href=""" + svc, posl2, StringComparison.Ordinal) > -1 Then
1018                        Dim urlStr As String = ""
1019                        Try
1020                            posl1 = orgData.IndexOf("<a href=""" + svc, posl2, StringComparison.Ordinal)
1021                            posl1 = orgData.IndexOf(svc, posl1, StringComparison.Ordinal)
1022                            posl2 = orgData.IndexOf("""", posl1, StringComparison.Ordinal)
1023                            urlStr = orgData.Substring(posl1, posl2 - posl1)
1024                            Dim Response As String = ""
1025                            Dim retUrlStr As String = ""
1026                            retUrlStr = DirectCast(CreateSocket.GetWebResponse(urlStr, Response, MySocket.REQ_TYPE.ReqGETForwardTo), String)
1027                            If retUrlStr.Length > 0 Then
1028                                If Not retUrlStr.StartsWith("http") Then Exit Do
1029                                orgData = orgData.Replace("<a href=""" + urlStr, "<a href=""" + urlEncodeMultibyteChar(retUrlStr))
1030                                posl2 = 0   '置換した場合は頭から再探索(複数同時置換での例外対応)
1031                            End If
1032                        Catch ex As Exception
1033                            '_signed = False
1034                            'Return "GetTimeline -> Err: Can't get tinyurl."
1035                        End Try
1036                    Else
1037                        Exit Do
1038                    End If
1039                Loop
1040            Next
1041        End If
1042        Return orgData
1043    End Function
1044
1045    Private Function GetPlainText(ByVal orgData As String) As String
1046        Dim retStr As String
1047
1048        '単純テキストの取り出し(リンクタグ除去)
1049        If orgData.IndexOf(_parseLink1, StringComparison.Ordinal) = -1 Then
1050            retStr = HttpUtility.HtmlDecode(orgData)
1051        Else
1052            Dim posl1 As Integer
1053            Dim posl2 As Integer
1054            Dim posl3 As Integer = 0
1055
1056            retStr = ""
1057
1058            posl3 = 0
1059            Do While True
1060                posl1 = orgData.IndexOf(_parseLink1, posl3, StringComparison.Ordinal)
1061                If posl1 = -1 Then Exit Do
1062
1063                If (posl3 + _parseLink3.Length <> posl1) Or posl3 = 0 Then
1064                    If posl3 <> 0 Then
1065                        retStr += HttpUtility.HtmlDecode(orgData.Substring(posl3 + _parseLink3.Length, posl1 - posl3 - _parseLink3.Length))
1066                    Else
1067                        retStr += HttpUtility.HtmlDecode(orgData.Substring(0, posl1))
1068                    End If
1069                End If
1070                posl2 = orgData.IndexOf(_parseLink2, posl1, StringComparison.Ordinal)
1071                posl3 = orgData.IndexOf(_parseLink3, posl2, StringComparison.Ordinal)
1072                retStr += HttpUtility.HtmlDecode(orgData.Substring(posl2 + _parseLink2.Length, posl3 - posl2 - _parseLink2.Length))
1073            Loop
1074            retStr += HttpUtility.HtmlDecode(orgData.Substring(posl3 + _parseLink3.Length))
1075        End If
1076
1077        Return retStr
1078    End Function
1079
1080    Private Function AdjustHtml(ByVal orgData As String) As String
1081        Dim retStr As String = orgData
1082        retStr = retStr.Replace("<a href=""/", "<a href=""https://twitter.com/")
1083        retStr = retStr.Replace("<a href=", "<a target=""_self"" href=")
1084        retStr = retStr.Replace(vbLf, "<br>")
1085        Return retStr
1086    End Function
1087
1088    Private Sub GetIconImage(ByVal post As PostClass)
1089        If Not _getIcon Then
1090            post.ImageIndex = -1
1091            TabInformations.GetInstance.AddPost(post)
1092            Exit Sub
1093        End If
1094
1095        SyncLock LockObj
1096            post.ImageIndex = _lIcon.Images.IndexOfKey(post.ImageUrl)
1097        End SyncLock
1098
1099        If post.ImageIndex > -1 Then
1100            TabInformations.GetInstance.AddPost(post)
1101            Exit Sub
1102        End If
1103
1104        Dim resStatus As String = ""
1105        Dim img As Image = DirectCast(CreateSocket.GetWebResponse(post.ImageUrl, resStatus, MySocket.REQ_TYPE.ReqGETBinary), System.Drawing.Image)
1106        If img Is Nothing Then
1107            post.ImageIndex = -1
1108            TabInformations.GetInstance.AddPost(post)
1109            Exit Sub
1110        End If
1111
1112        If _endingFlag Then Exit Sub
1113
1114        Dim bmp2 As New Bitmap(_iconSz, _iconSz)
1115        Using g As Graphics = Graphics.FromImage(bmp2)
1116            g.InterpolationMode = Drawing2D.InterpolationMode.High
1117            g.DrawImage(img, 0, 0, _iconSz, _iconSz)
1118        End Using
1119
1120        SyncLock LockObj
1121            post.ImageIndex = _lIcon.Images.IndexOfKey(post.ImageUrl)
1122            If post.ImageIndex = -1 Then
1123                _dIcon.Add(post.ImageUrl, img)  '詳細表示用ディクショナリに追加
1124                _lIcon.Images.Add(post.ImageUrl, bmp2)
1125                post.ImageIndex = _lIcon.Images.IndexOfKey(post.ImageUrl)
1126            End If
1127        End SyncLock
1128        TabInformations.GetInstance.AddPost(post)
1129    End Sub
1130
1131    Private Function GetAuthKey(ByVal resMsg As String) As Integer
1132        Dim pos1 As Integer
1133        Dim pos2 As Integer
1134
1135        pos1 = resMsg.IndexOf(_getAuthKey, StringComparison.Ordinal)
1136        If pos1 < 0 Then
1137            'データ不正?
1138            Return -7
1139        End If
1140        pos2 = resMsg.IndexOf(_getAuthKeyTo, pos1 + _getAuthKey.Length, StringComparison.Ordinal)
1141        If pos2 > -1 Then
1142            _authKey = resMsg.Substring(pos1 + _getAuthKey.Length, pos2 - pos1 - _getAuthKey.Length)
1143        Else
1144            Return -7
1145        End If
1146
1147        Return 0
1148    End Function
1149
1150    Private Function GetAuthKeyDM(ByVal resMsg As String) As Integer
1151        Dim pos1 As Integer
1152        Dim pos2 As Integer
1153
1154        pos1 = resMsg.IndexOf(_getAuthKey, StringComparison.Ordinal)
1155        If pos1 < 0 Then
1156            'データ不正?
1157            Return -7
1158        End If
1159        pos2 = resMsg.IndexOf("""", pos1 + _getAuthKey.Length, StringComparison.Ordinal)
1160        _authKeyDM = resMsg.Substring(pos1 + _getAuthKey.Length, pos2 - pos1 - _getAuthKey.Length)
1161
1162        Return 0
1163    End Function
1164
1165    Private Structure PostInfo
1166        Public CreatedAt As String
1167        Public Id As String
1168        Public Text As String
1169        Public UserId As String
1170        Public Sub New(ByVal Created As String, ByVal IdStr As String, ByVal txt As String, ByVal uid As String)
1171            CreatedAt = Created
1172            Id = IdStr
1173            Text = txt
1174            UserId = uid
1175        End Sub
1176        Public Shadows Function Equals(ByVal dst As PostInfo) As Boolean
1177            If Me.CreatedAt = dst.CreatedAt AndAlso Me.Id = dst.Id AndAlso Me.Text = dst.Text AndAlso Me.UserId = dst.UserId Then
1178                Return True
1179            Else
1180                Return False
1181            End If
1182        End Function
1183    End Structure
1184
1185    Private Function IsPostRestricted(ByRef resMsg As String) As Boolean
1186        Static _prev As New PostInfo("", "", "", "")
1187        Dim _current As New PostInfo("", "", "", "")
1188
1189
1190        Dim xd As XmlDocument = New XmlDocument()
1191        Try
1192            xd.LoadXml(resMsg)
1193            _current.CreatedAt = xd.SelectSingleNode("/status/created_at/text()").Value
1194            _current.Id = xd.SelectSingleNode("/status/id/text()").Value
1195            _current.Text = xd.SelectSingleNode("/status/text/text()").Value
1196            _current.UserId = xd.SelectSingleNode("/status/user/id/text()").Value
1197
1198            If _current.Equals(_prev) Then
1199                Return True
1200            End If
1201            _prev.CreatedAt = _current.CreatedAt
1202            _prev.Id = _current.Id
1203            _prev.Text = _current.Text
1204            _prev.UserId = _current.UserId
1205        Catch ex As XmlException
1206            Return False
1207        End Try
1208
1209        Return False
1210    End Function
1211
1212    Public Function PostStatus(ByVal postStr As String, ByVal reply_to As Long) As String
1213
1214        If _endingFlag Then Return ""
1215
1216        postStr = postStr.Trim()
1217
1218        'データ部分の生成
1219        Dim dataStr As String
1220        If reply_to = 0 Then
1221            dataStr = _statusHeader + HttpUtility.UrlEncode(postStr) + "&source=Tween"
1222        Else
1223            dataStr = _statusHeader + HttpUtility.UrlEncode(postStr) + "&source=Tween" + "&in_reply_to_status_id=" + HttpUtility.UrlEncode(reply_to.ToString)
1224        End If
1225
1226        Dim resStatus As String = ""
1227        Dim resMsg As String = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + _statusUpdatePathAPI, resStatus, MySocket.REQ_TYPE.ReqPOSTAPI, dataStr), String)
1228
1229        If resStatus.StartsWith("OK") Then
1230            If Not postStr.StartsWith("D ", StringComparison.OrdinalIgnoreCase) AndAlso _
1231                    Not postStr.StartsWith("DM ", StringComparison.OrdinalIgnoreCase) AndAlso _
1232                    IsPostRestricted(resMsg) Then
1233                Return "Err:POST規制?"
1234            End If
1235            resStatus = Outputz.Post(CreateSocket, postStr.Length)
1236            If resStatus.Length > 0 Then
1237                Return "Outputz:" + resStatus
1238            Else
1239                Return ""
1240            End If
1241        Else
1242            Return resStatus
1243        End If
1244    End Function
1245
1246    Public Function RemoveStatus(ByVal id As Long) As String
1247        If _endingFlag Then Return ""
1248
1249        'データ部分の生成
1250        Dim resStatus As String = ""
1251        Dim resMsg As String = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + _StDestroyPath + id.ToString + ".xml", resStatus, MySocket.REQ_TYPE.ReqPOSTAPI), String)
1252
1253        If resMsg.StartsWith("<?xml") = False OrElse resStatus.StartsWith("OK") = False Then
1254            Return resStatus
1255        End If
1256
1257        Return ""
1258    End Function
1259
1260    Public Function RemoveDirectMessage(ByVal id As Long) As String
1261        If _endingFlag Then Return ""
1262
1263        'データ部分の生成
1264        Dim dataStr As String = _authKeyHeader + HttpUtility.UrlEncode(_authKey)
1265        Dim resStatus As String = ""
1266        Dim resMsg As String = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + _DMDestroyPath + id.ToString + ".xml", resStatus, MySocket.REQ_TYPE.ReqPOSTAPI), String)
1267
1268        If resMsg.StartsWith("<?xml") = False OrElse resStatus.StartsWith("OK") = False Then
1269            Return resStatus
1270        End If
1271
1272        Return ""
1273    End Function
1274
1275    ' Contributed by shuyoko <http://twitter.com/shuyoko> BEGIN:
1276    Public Function GetBlackFavId(ByVal id As Long, ByRef blackid As Long) As String
1277        Dim dataStr As String = _authKeyHeader + HttpUtility.UrlEncode(_authKey)
1278        Dim resStatus As String = ""
1279        Dim resMsg As String = DirectCast(CreateSocket.GetWebResponse("http://blavotter.hocha.org/blackfav/getblack.php?format=simple&id=" + id.ToString(), resStatus, MySocket.REQ_TYPE.ReqGET), String)
1280
1281        If resStatus.StartsWith("OK") = False Then
1282            Return resStatus
1283        End If
1284
1285        blackid = Long.Parse(resMsg)
1286
1287        Return ""
1288
1289    End Function
1290    ' Contributed by shuyoko <http://twitter.com/shuyoko> END.
1291
1292    Public Function PostFavAdd(ByVal id As Long) As String
1293        If _endingFlag Then Return ""
1294
1295        'データ部分の生成
1296        'Dim dataStr As String = _authKeyHeader + HttpUtility.UrlEncode(_authKey)
1297        Dim resStatus As String = ""
1298        Dim resMsg As String = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + _postFavAddPath + id.ToString() + ".xml", resStatus, MySocket.REQ_TYPE.ReqPOSTAPI), String)
1299
1300        If resStatus.StartsWith("OK") = False Then
1301            Return resStatus
1302        End If
1303
1304        If _restrictFavCheck = False Then Return ""
1305
1306        'http://twitter.com/statuses/show/id.xml APIを発行して本文を取得
1307
1308        resMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + _ShowStatus + id.ToString() + ".xml", resStatus, MySocket.REQ_TYPE.ReqPOSTEncodeProtoVer2), String)
1309
1310        Try
1311            Using rd As Xml.XmlTextReader = New Xml.XmlTextReader(New System.IO.StringReader(resMsg))
1312                rd.Read()
1313                While rd.EOF = False
1314                    If rd.IsStartElement("favorited") Then
1315                        If rd.ReadElementContentAsBoolean() = True Then
1316                            Return ""  '正常にふぁぼれている
1317                        Else
1318                            Return "NG(Restricted?)"  '正常応答なのにふぁぼれてないので制限っぽい
1319                        End If
1320                    Else
1321                        rd.Read()
1322                    End If
1323                End While
1324                rd.Close()
1325            End Using
1326        Catch ex As XmlException
1327            '
1328        End Try
1329
1330        Return ""
1331    End Function
1332
1333    Public Function PostFavRemove(ByVal id As Long) As String
1334        If _endingFlag Then Return ""
1335
1336        'データ部分の生成
1337        'Dim dataStr As String = _authKeyHeader + HttpUtility.UrlEncode(_authKey)
1338        Dim resStatus As String = ""
1339        Dim resMsg As String = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + _postFavRemovePath + id.ToString() + ".xml", resStatus, MySocket.REQ_TYPE.ReqPOSTAPI), String)
1340
1341        If resStatus.StartsWith("OK") = False Then
1342            Return resStatus
1343        End If
1344
1345        Return ""
1346    End Function
1347
1348#Region "follower取得"
1349    Delegate Function GetFollowersDelegate(ByVal Query As Integer) As String
1350    Private semaphore As Threading.Semaphore = Nothing
1351    Private threadNum As Integer = 0
1352    Private _threadErr As Boolean = False
1353
1354    Private Function GetFollowersMethod(ByVal Query As Integer) As String
1355        Dim resStatus As String = ""
1356        Dim resMsg As String = ""
1357
1358        Try
1359            resMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + _GetFollowers + _pageQry + Query.ToString, resStatus, MySocket.REQ_TYPE.ReqPOSTAPI), String)
1360            If resStatus.StartsWith("OK") = False Then
1361                _threadErr = True
1362                Return resStatus
1363            End If
1364            Using rd As Xml.XmlTextReader = New Xml.XmlTextReader(New System.IO.StringReader(resMsg))
1365                Dim lc As Integer = 0
1366                rd.Read()
1367                While rd.EOF = False
1368                    If rd.IsStartElement("screen_name") Then
1369                        Dim tmp As String = rd.ReadElementString("screen_name").ToLower()
1370                        SyncLock LockObj
1371                            If Not tmpFollower.Contains(tmp) Then
1372                                tmpFollower.Add(tmp)
1373                            End If
1374                        End SyncLock
1375                        lc += 1
1376                    Else
1377                        rd.Read()
1378                    End If
1379                End While
1380            End Using
1381        Catch ex As XmlException
1382            _threadErr = True
1383            TraceOut("NG(XmlException)")
1384            Return "NG(XmlException)"
1385        End Try
1386
1387        Return ""
1388    End Function
1389
1390    Private Sub GetFollowersCallback(ByVal ar As IAsyncResult)
1391        Dim dlgt As GetFollowersDelegate = DirectCast(ar.AsyncState, GetFollowersDelegate)
1392
1393        Try
1394            Dim ret As String = dlgt.EndInvoke(ar)
1395            If Not ret.Equals("") AndAlso Not _threadErr Then
1396                TraceOut(ret)
1397                _threadErr = True
1398            End If
1399        Catch ex As Exception
1400            _threadErr = True
1401            ExceptionOut(ex)
1402        Finally
1403            semaphore.Release()                     ' セマフォから出る
1404            Interlocked.Decrement(threadNum)        ' スレッド数カウンタを-1
1405        End Try
1406
1407    End Sub
1408
1409    ' キャッシュの検証と読み込み -1を渡した場合は読み込みのみ行う(APIエラーでFollowersCountが取得できなかったとき)
1410    Private Function ValidateCache(ByVal _FollowersCount As Integer) As Integer
1411        Dim CacheFileName As String = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "FollowersCache")
1412
1413        If Not File.Exists(CacheFileName) Then
1414            ' 存在しない場合はそのまま帰る
1415            Return _FollowersCount
1416        End If
1417
1418        Dim serializer As Xml.Serialization.XmlSerializer = New Xml.Serialization.XmlSerializer(tmpFollower.GetType())
1419
1420        Try
1421            Using fs As New IO.FileStream(CacheFileName, FileMode.Open)
1422                tmpFollower = CType(serializer.Deserialize(fs), Specialized.StringCollection)
1423                If Not tmpFollower(0).Equals(_uid.ToLower()) Then
1424                    ' 別IDの場合はキャッシュ破棄して読み直し
1425                    tmpFollower.Clear()
1426                    tmpFollower.Add(_uid.ToLower())
1427                    Return _FollowersCount
1428                End If
1429            End Using
1430        Catch ex As XmlException
1431            ' 不正なxmlの場合は読み直し
1432            tmpFollower.Clear()
1433            tmpFollower.Add(_uid.ToLower())
1434            Return _FollowersCount
1435        End Try
1436
1437        If _FollowersCount = -1 Then Return tmpFollower.Count
1438
1439        If (_FollowersCount + 1) = tmpFollower.Count Then
1440            '変動がないので読み込みの必要なし
1441            Return 0
1442        ElseIf (_FollowersCount + 1) < tmpFollower.Count Then
1443            '減っている場合はどこが抜けているのかわからないので全部破棄して読み直し
1444            tmpFollower.Clear()
1445            tmpFollower.Add(_uid.ToLower())
1446            Return _FollowersCount
1447        End If
1448
1449        ' 増えた場合は差分だけ読む
1450
1451        Return _FollowersCount - tmpFollower.Count
1452
1453    End Function
1454
1455    Private Sub UpdateCache()
1456        Dim CacheFileName As String = Path.Combine(Path.GetDirectoryName(Application.ExecutablePath), "FollowersCache")
1457
1458        Dim serializer As Xml.Serialization.XmlSerializer = New Xml.Serialization.XmlSerializer(follower.GetType())
1459
1460        Using fs As New IO.FileStream(CacheFileName, FileMode.Create)
1461            serializer.Serialize(fs, follower)
1462        End Using
1463
1464    End Sub
1465
1466    Public Function GetFollowers(ByVal CacheInvalidate As Boolean) As String
1467#If DEBUG Then
1468        Dim sw As New System.Diagnostics.Stopwatch
1469        sw.Start()
1470#End If
1471        Dim resStatus As String = ""
1472        Dim resMsg As String = ""
1473        Dim i As Integer = 0
1474        Dim DelegateInstance As GetFollowersDelegate = New GetFollowersDelegate(AddressOf GetFollowersMethod)
1475        Dim threadMax As Integer = 4            ' 最大スレッド数
1476        Dim followersCount As Integer = 0
1477
1478        Interlocked.Exchange(threadNum, 0)      ' スレッド数カウンタ初期化
1479        _threadErr = False
1480        follower.Clear()
1481        tmpFollower.Clear()
1482        follower.Add(_uid.ToLower())
1483        tmpFollower.Add(_uid.ToLower())
1484
1485        resMsg = DirectCast(CreateSocket.GetWebResponse("https://twitter.com/users/show/" + _uid + ".xml", resStatus, MySocket.REQ_TYPE.ReqPOSTAPI), String)
1486        Dim xd As XmlDocument = New XmlDocument()
1487        Try
1488            xd.LoadXml(resMsg)
1489            followersCount = Integer.Parse(xd.SelectSingleNode("/user/followers_count/text()").Value)
1490        Catch ex As XmlException
1491            If CacheInvalidate OrElse ValidateCache(-1) < 0 Then
1492                ' FollowersカウントがAPIで取得できず、なおかつキャッシュから読めなかった
1493                SyncLock LockObj
1494                    follower.Clear()
1495                    follower.Add(_uid.ToLower())
1496                End SyncLock
1497                Return "NG"
1498            Else
1499                'キャッシュを読み出せたのでキャッシュを使う
1500                SyncLock LockObj
1501                    follower = tmpFollower
1502                End SyncLock
1503                Return ""
1504            End If
1505        End Try
1506
1507        Dim tmp As Integer
1508
1509        If CacheInvalidate Then
1510            tmp = followersCount
1511        Else
1512            tmp = ValidateCache(followersCount)
1513        End If
1514
1515
1516        If tmp <> 0 Then
1517            i = (tmp + 100) \ 100 - 1 ' Followersカウント取得しページ単位に切り上げる
1518        Else
1519            ' キャッシュの件数に変化がなかった
1520#If DEBUG Then
1521            sw.Stop()
1522            Console.WriteLine(sw.ElapsedMilliseconds)
1523#End If
1524            SyncLock LockObj
1525                follower = tmpFollower
1526            End SyncLock
1527            Return ""
1528        End If
1529
1530
1531        semaphore = New System.Threading.Semaphore(threadMax, threadMax) 'スレッド最大数
1532
1533        For cnt As Integer = 0 To i
1534            If _endingFlag Then Exit For
1535            semaphore.WaitOne()                     'セマフォ取得 threadMax以上ならここでブロックされる
1536            Interlocked.Increment(threadNum)        'スレッド数カウンタを+1
1537            DelegateInstance.BeginInvoke(cnt + 1, New System.AsyncCallback(AddressOf GetFollowersCallback), DelegateInstance)
1538        Next
1539
1540        '全てのスレッドの終了を待つ(スレッド数カウンタが0になるまで待機)
1541        Do
1542            Thread.Sleep(50)
1543        Loop Until Interlocked.Add(threadNum, 0) = 0
1544
1545        semaphore.Close()
1546
1547        If _endingFlag Then Return ""
1548
1549        ' エラーが発生しているならFollowersリストクリア
1550
1551        If _threadErr Then
1552            ' エラーが発生しているならFollowersリストクリア
1553            SyncLock LockObj
1554                follower.Clear()
1555                follower.Add(_uid.ToLower())
1556            End SyncLock
1557            Return "NG"
1558        End If
1559
1560        follower = tmpFollower
1561        If Not _endingFlag Then UpdateCache()
1562
1563#If DEBUG Then
1564        sw.Stop()
1565        Console.WriteLine(sw.ElapsedMilliseconds)
1566#End If
1567
1568        Return ""
1569    End Function
1570
1571#End Region
1572
1573    Public Sub RefreshOwl()
1574        If follower.Count > 1 Then TabInformations.GetInstance.RefreshOwl(follower)
1575    End Sub
1576
1577    Public Property Username() As String
1578        Get
1579            Return _uid
1580        End Get
1581        Set(ByVal value As String)
1582            _uid = value.ToLower
1583            _signed = False
1584        End Set
1585    End Property
1586
1587    Public Property Password() As String
1588        Get
1589            Return _pwd
1590        End Get
1591        Set(ByVal value As String)
1592            _pwd = value
1593            _signed = False
1594        End Set
1595    End Property
1596
1597    Public Property NextThreshold() As Integer
1598        Get
1599            Return _nextThreshold
1600        End Get
1601        Set(ByVal value As Integer)
1602            _nextThreshold = value
1603        End Set
1604    End Property
1605
1606    Public Property NextPages() As Integer
1607        Get
1608            Return _nextPages
1609        End Get
1610        Set(ByVal value As Integer)
1611            _nextPages = value
1612        End Set
1613    End Property
1614
1615    Public ReadOnly Property InfoTwitter() As String
1616        Get
1617            Return _infoTwitter
1618        End Get
1619    End Property
1620
1621    Public Property UseAPI() As Boolean
1622        Get
1623            Return _useAPI
1624        End Get
1625        Set(ByVal value As Boolean)
1626            _useAPI = value
1627        End Set
1628    End Property
1629
1630    Public Property HubServer() As String
1631        Get
1632            Return _hubServer
1633        End Get
1634        Set(ByVal value As String)
1635            _hubServer = value
1636        End Set
1637    End Property
1638
1639    Public Sub GetWedata()
1640        Dim resStatus As String = ""
1641        Dim resMsg As String = ""
1642
1643        resMsg = DirectCast(CreateSocket.GetWebResponse(wedataUrl, resStatus, timeOut:=10 * 1000), String) 'タイムアウト時間を10秒に設定
1644        If resMsg.Length = 0 Then Exit Sub
1645
1646        Dim rs As New System.IO.StringReader(resMsg)
1647
1648        Dim mode As Integer = 0 '0:search name 1:search data 2:read data
1649        Dim name As String = ""
1650
1651        'ストリームの末端まで繰り返す
1652        Dim ln As String
1653        While rs.Peek() > -1
1654            ln = rs.ReadLine
1655
1656            Select Case mode
1657                Case 0
1658                    If ln.StartsWith("    ""name"": ") Then
1659                        name = ln.Substring(13, ln.Length - 2 - 13)
1660                        mode += 1
1661                    End If
1662                Case 1
1663                    If ln = "    ""data"": {" Then
1664                        mode += 1
1665                    End If
1666                Case 2
1667                    If ln = "    }," Then
1668                        mode = 0
1669                    Else
1670                        If ln.EndsWith(",") Then ln = ln.Substring(0, ln.Length - 1)
1671                        Select Case name
1672                            Case "SplitPostReply"
1673                                If ln.StartsWith("      ""tagfrom"": """) Then
1674                                    _splitPost = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1675                                End If
1676                            Case "SplitPostRecent"
1677                                If ln.StartsWith("      ""tagfrom"": """) Then
1678                                    _splitPostRecent = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1679                                End If
1680                            Case "StatusID"
1681                                If ln.StartsWith("      ""tagto"": """) Then
1682                                    _statusIdTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1683                                End If
1684                            Case "IsProtect"
1685                                If ln.StartsWith("      ""tagfrom"": """) Then
1686                                    _isProtect = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1687                                End If
1688                            Case "IsReply"
1689                                If ln.StartsWith("      ""tagfrom"": """) Then
1690                                    _isReplyEng = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1691                                End If
1692                                If ln.StartsWith("      ""tagfrom2"": """) Then
1693                                    _isReplyJpn = ln.Substring(19, ln.Length - 1 - 19).Replace("\", "")
1694                                End If
1695                                If ln.StartsWith("      ""tagto"": """) Then
1696                                    _isReplyTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1697                                End If
1698                            Case "GetStar"
1699                                If ln.StartsWith("      ""tagfrom"": """) Then
1700                                    _parseStar = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1701                                End If
1702                                If ln.StartsWith("      ""tagfrom2"": """) Then
1703                                    _parseStarEmpty = ln.Substring(19, ln.Length - 1 - 19).Replace("\", "")
1704                                End If
1705                                If ln.StartsWith("      ""tagto"": """) Then
1706                                    _parseStarTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1707                                End If
1708                            Case "Follower"
1709                                If ln.StartsWith("      ""tagfrom"": """) Then
1710                                    _followerList = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1711                                End If
1712                                If ln.StartsWith("      ""tagfrom2"": """) Then
1713                                    _followerMbr1 = ln.Substring(19, ln.Length - 1 - 19).Replace("\", "")
1714                                End If
1715                                If ln.StartsWith("      ""tagfrom3"": """) Then
1716                                    _followerMbr2 = ln.Substring(19, ln.Length - 1 - 19).Replace("\", "")
1717                                End If
1718                                If ln.StartsWith("      ""tagto"": """) Then
1719                                    _followerMbr3 = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1720                                End If
1721                            Case "SplitDM"
1722                                If ln.StartsWith("      ""tagfrom"": """) Then
1723                                    _splitDM = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1724                                End If
1725                            Case "GetMsgDM"
1726                                If ln.StartsWith("      ""tagfrom"": """) Then
1727                                    _parseDM1 = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1728                                End If
1729                                If ln.StartsWith("      ""tagfrom2"": """) Then
1730                                    _parseDM11 = ln.Substring(19, ln.Length - 1 - 19).Replace("\", "")
1731                                End If
1732                                If ln.StartsWith("      ""tagto"": """) Then
1733                                    _parseDM2 = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1734                                End If
1735                            Case "GetDate"
1736                                If ln.StartsWith("      ""tagfrom"": """) Then
1737                                    _parseDate = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1738                                End If
1739                                If ln.StartsWith("      ""tagto"": """) Then
1740                                    _parseDateTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1741                                End If
1742                            Case "GetMsg"
1743                                If ln.StartsWith("      ""tagfrom"": """) Then
1744                                    _parseMsg1 = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1745                                End If
1746                                If ln.StartsWith("      ""tagto"": """) Then
1747                                    _parseMsg2 = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1748                                End If
1749                            Case "GetImagePath"
1750                                If ln.StartsWith("      ""tagfrom"": """) Then
1751                                    _parseImg = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1752                                End If
1753                                If ln.StartsWith("      ""tagto"": """) Then
1754                                    _parseImgTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1755                                End If
1756                            Case "GetNick"
1757                                If ln.StartsWith("      ""tagfrom"": """) Then
1758                                    _parseNick = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1759                                End If
1760                                If ln.StartsWith("      ""tagto"": """) Then
1761                                    _parseNickTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1762                                End If
1763                            Case "GetName"
1764                                If ln.StartsWith("      ""tagfrom"": """) Then
1765                                    _parseName = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1766                                End If
1767                                If ln.StartsWith("      ""tagto"": """) Then
1768                                    _parseNameTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1769                                End If
1770                                'Case "GetSiv"
1771                                '    If ln.StartsWith("      ""tagfrom"": """) Then
1772                                '        _getSiv = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1773                                '    End If
1774                                '    If ln.StartsWith("      ""tagto"": """) Then
1775                                '        _getSivTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1776                                '    End If
1777                            Case "AuthKey"
1778                                If ln.StartsWith("      ""tagfrom"": """) Then
1779                                    _getAuthKey = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1780                                End If
1781                                If ln.StartsWith("      ""tagto"": """) Then
1782                                    _getAuthKeyTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1783                                End If
1784                            Case "InfoTwitter"
1785                                If ln.StartsWith("      ""tagfrom"": """) Then
1786                                    _getInfoTwitter = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1787                                End If
1788                                If ln.StartsWith("      ""tagto"": """) Then
1789                                    _getInfoTwitterTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1790                                End If
1791                            Case "GetProtectMsg"
1792                                If ln.StartsWith("      ""tagfrom"": """) Then
1793                                    _parseProtectMsg1 = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1794                                End If
1795                                If ln.StartsWith("      ""tagto"": """) Then
1796                                    _parseProtectMsg2 = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1797                                End If
1798                            Case "GetDMCount"
1799                                If ln.StartsWith("      ""tagfrom"": """) Then
1800                                    _parseDMcountFrom = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1801                                End If
1802                                If ln.StartsWith("      ""tagto"": """) Then
1803                                    _parseDMcountTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1804                                End If
1805                            Case "GetSource"
1806                                If ln.StartsWith("      ""tagfrom"": """) Then
1807                                    _parseSourceFrom = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1808                                End If
1809                                If ln.StartsWith("      ""tagfrom2"": """) Then
1810                                    _parseSource2 = ln.Substring(19, ln.Length - 1 - 19).Replace("\", "")
1811                                End If
1812                                If ln.StartsWith("      ""tagto"": """) Then
1813                                    _parseSource2 = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
1814                                End If
1815                            Case "RemoveClass"
1816                                If ln.StartsWith("      ""tagfrom"": """) Then
1817                                    _removeClass = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
1818                                End If
1819                        End Select
1820                    End If
1821            End Select
1822        End While
1823
1824        rs.Close()
1825
1826#If DEBUG Then
1827        GenerateAnalyzeKey()
1828#End If
1829    End Sub
1830
1831    Public WriteOnly Property GetIcon() As Boolean
1832        Set(ByVal value As Boolean)
1833            _getIcon = value
1834        End Set
1835    End Property
1836
1837    Public WriteOnly Property TinyUrlResolve() As Boolean
1838        Set(ByVal value As Boolean)
1839            _tinyUrlResolve = value
1840        End Set
1841    End Property
1842
1843    Public WriteOnly Property ProxyType() As ProxyTypeEnum
1844        Set(ByVal value As ProxyTypeEnum)
1845            _proxyType = value
1846        End Set
1847    End Property
1848
1849    Public WriteOnly Property ProxyAddress() As String
1850        Set(ByVal value As String)
1851            _proxyAddress = value
1852        End Set
1853    End Property
1854
1855    Public WriteOnly Property ProxyPort() As Integer
1856        Set(ByVal value As Integer)
1857            _proxyPort = value
1858        End Set
1859    End Property
1860
1861    Public WriteOnly Property ProxyUser() As String
1862        Set(ByVal value As String)
1863            _proxyUser = value
1864        End Set
1865    End Property
1866
1867    Public WriteOnly Property ProxyPassword() As String
1868        Set(ByVal value As String)
1869            _proxyPassword = value
1870        End Set
1871    End Property
1872
1873    Public WriteOnly Property RestrictFavCheck() As Boolean
1874        Set(ByVal value As Boolean)
1875            _restrictFavCheck = value
1876        End Set
1877    End Property
1878
1879    Public WriteOnly Property IconSize() As Integer
1880        Set(ByVal value As Integer)
1881            _iconSz = value
1882        End Set
1883    End Property
1884
1885    Public Function MakeShortUrl(ByVal ConverterType As UrlConverter, ByVal SrcUrl As String) As String
1886        Dim ret As String = ""
1887        Dim resStatus As String = ""
1888        Dim src As String = SrcUrl
1889
1890        For Each svc As String In _ShortUrlService
1891            If SrcUrl.StartsWith(svc) Then
1892                Return "Can't convert"
1893            End If
1894        Next
1895
1896        SrcUrl = HttpUtility.UrlEncode(SrcUrl)
1897        Select Case ConverterType
1898            Case UrlConverter.TinyUrl       'tinyurl
1899                If SrcUrl.StartsWith("http") Then
1900                    If SrcUrl.StartsWith("http://tinyurl.com/") Then
1901                        Return "Can't convert"
1902                    End If
1903                    If "http://tinyurl.com/xxxxxx".Length > src.Length AndAlso Not src.Contains("?") AndAlso Not src.Contains("#") Then
1904                        ' 明らかに長くなると推測できる場合は圧縮しない
1905                        ret = src
1906                        Exit Select
1907                    End If
1908                    Try
1909                        ret = DirectCast(CreateSocket.GetWebResponse("http://tinyurl.com/api-create.php?url=" + SrcUrl, resStatus, MySocket.REQ_TYPE.ReqPOSTEncode), String)
1910                    Catch ex As Exception
1911                        Return "Can't convert"
1912                    End Try
1913                End If
1914                If Not ret.StartsWith("http://tinyurl.com/") Then
1915                    Return "Can't convert"
1916                End If
1917            Case UrlConverter.Isgd
1918                If SrcUrl.StartsWith("http") Then
1919                    If SrcUrl.StartsWith("http://is.gd/") Then
1920                        Return "Can't convert"
1921                    End If
1922                    If "http://is.gd/xxxx".Length > src.Length AndAlso Not src.Contains("?") AndAlso Not src.Contains("#") Then
1923                        ' 明らかに長くなると推測できる場合は圧縮しない
1924                        ret = src
1925                        Exit Select
1926                    End If
1927                    Try
1928                        ret = DirectCast(CreateSocket.GetWebResponse("http://is.gd/api.php?longurl=" + SrcUrl, resStatus, MySocket.REQ_TYPE.ReqPOSTEncode), String)
1929                    Catch ex As Exception
1930                        Return "Can't convert"
1931                    End Try
1932                End If
1933                If Not ret.StartsWith("http://is.gd/") Then
1934                    Return "Can't convert"
1935                End If
1936        End Select
1937
1938        If src.Length < ret.Length Then ret = src ' 圧縮の結果逆に長くなった場合は圧縮前のURLを返す
1939        Return ret
1940    End Function
1941
1942#Region "バージョンアップ"
1943    Public Function GetVersionInfo() As String
1944        Dim resStatus As String = ""
1945        Dim ret As String = DirectCast(CreateSocket.GetWebResponse("http://tween.sourceforge.jp/version2.txt?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), resStatus), String)
1946        If ret.Length = 0 Then Throw New Exception("GetVersionInfo: " + resStatus)
1947        Return ret
1948    End Function
1949
1950    Public Function GetTweenBinary(ByVal strVer As String) As String
1951        Dim resStatus As String = ""
1952        Dim ret As String = ""
1953        ret = DirectCast(CreateSocket.GetWebResponse("http://tween.sourceforge.jp/Tween" + strVer + ".gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), resStatus, MySocket.REQ_TYPE.ReqGETFile), String)
1954        If ret = "" OrElse resStatus.StartsWith("OK") Then
1955            '取得OKなら、続いてresources.dllダウンロード
1956            Return GetTweenResourcesDll(strVer)
1957        Else
1958            Return resStatus
1959        End If
1960    End Function
1961
1962    Public Function GetTweenUpBinary() As String
1963        Dim resStatus As String = ""
1964        Dim ret As String = DirectCast(CreateSocket.GetWebResponse("http://tween.sourceforge.jp/TweenUp.gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), resStatus, MySocket.REQ_TYPE.ReqGETFileUp), String)
1965        If ret = "" OrElse resStatus.StartsWith("OK") Then
1966            Return ""
1967        Else
1968            Return resStatus
1969        End If
1970    End Function
1971
1972    Public Function GetTweenResourcesDll(ByVal strver As String) As String
1973        Dim resStatus As String = ""
1974        Dim ret As String = DirectCast(CreateSocket.GetWebResponse("http://tween.sourceforge.jp/TweenRes" + strver + ".gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), resStatus, MySocket.REQ_TYPE.ReqGETFileRes), String)
1975        If ret = "" OrElse resStatus.StartsWith("OK") Then
1976            Return ""
1977        Else
1978            Return resStatus
1979        End If
1980    End Function
1981#End Region
1982
1983    Private Function CreateSocket() As MySocket
1984        Return New MySocket("UTF-8", _uid, _pwd, _proxyType, _proxyAddress, _proxyPort, _proxyUser, _proxyPassword, _defaultTimeOut)
1985    End Function
1986
1987    Public WriteOnly Property ListIcon() As ImageList
1988        Set(ByVal value As ImageList)
1989            _lIcon = value
1990        End Set
1991    End Property
1992
1993    Public WriteOnly Property DetailIcon() As Dictionary(Of String, Image)
1994        Set(ByVal value As Dictionary(Of String, Image))
1995            _dIcon = value
1996        End Set
1997    End Property
1998
1999    Public Property DefaultTimeOut() As Integer
2000        Get
2001            Return _defaultTimeOut
2002        End Get
2003        Set(ByVal value As Integer)
2004            _defaultTimeOut = value
2005        End Set
2006    End Property
2007
2008    Public WriteOnly Property CountApi() As Integer
2009        'API時の取得件数
2010        Set(ByVal value As Integer)
2011            _countApi = value
2012        End Set
2013    End Property
2014
2015    Public WriteOnly Property UsePostMethod() As Boolean
2016        Set(ByVal value As Boolean)
2017            _usePostMethod = value
2018            If value Then
2019                _ApiMethod = MySocket.REQ_TYPE.ReqPOSTAPI
2020            Else
2021                _ApiMethod = MySocket.REQ_TYPE.ReqGetAPI
2022            End If
2023        End Set
2024    End Property
2025
2026    Public Function GetTimelineApi(ByVal read As Boolean, _
2027                            ByVal gType As WORKERTYPE) As String
2028        If _endingFlag Then Return ""
2029
2030        Dim retMsg As String = ""
2031        Dim resStatus As String = ""
2032        'スレッド取得は行わず、countで調整
2033        'Const GET_COUNT As Integer = 60
2034        Const COUNT_QUERY As String = "count="
2035        Const FRIEND_PATH As String = "/statuses/friends_timeline.xml"
2036        Const REPLY_PATH As String = "/statuses/replies.xml"
2037
2038        If gType = WORKERTYPE.Timeline Then
2039            retMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + FRIEND_PATH + "?" + COUNT_QUERY + _countApi.ToString(), resStatus, _ApiMethod), String)
2040        Else
2041            retMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + REPLY_PATH + "?" + COUNT_QUERY + _countApi.ToString(), resStatus, _ApiMethod), String)
2042        End If
2043
2044        If retMsg = "" Then Return resStatus
2045
2046        Dim arIdx As Integer = -1
2047        Dim dlgt(_countApi) As GetIconImageDelegate    'countQueryに合わせる
2048        Dim ar(_countApi) As IAsyncResult              'countQueryに合わせる
2049        Dim xdoc As New XmlDocument
2050        Try
2051            xdoc.LoadXml(retMsg)
2052        Catch ex As Exception
2053            ExceptionOut(ex)
2054            TraceOut(True, retMsg)
2055            MessageBox.Show("不正なXMLです。(TL-LoadXml)")
2056            Return "Invalid XML!"
2057        End Try
2058
2059        For Each xentryNode As XmlNode In xdoc.DocumentElement.SelectNodes("./status")
2060            Dim xentry As XmlElement = CType(xentryNode, XmlElement)
2061            Dim post As New PostClass
2062            Try
2063                post.PDate = DateTime.ParseExact(xentry.Item("created_at").InnerText, "ddd MMM dd HH:mm:ss zzzz yyyy", System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.None)
2064                post.Id = Long.Parse(xentry.Item("id").InnerText)
2065                '二重取得回避
2066                SyncLock LockObj
2067                    If TabInformations.GetInstance.ContainsKey(post.Id) Then Continue For
2068                End SyncLock
2069                '本文
2070                post.Data = xentry.Item("text").InnerText
2071                'HTMLに整形
2072                post.OriginalData = CreateHtmlAnchor(post.Data, post.ReplyToList)
2073                post.Data = HttpUtility.HtmlDecode(post.Data)
2074                post.Data = post.Data.Replace("<3", "♡")
2075                'Source取得(htmlの場合は、中身を取り出し)
2076                post.Source = xentry.Item("source").InnerText
2077                If post.Source.StartsWith("<") Then
2078                    Dim rgS As New Regex(">(?<source>.+)<")
2079                    Dim mS As Match = rgS.Match(post.Source)
2080                    If mS.Success Then
2081                        post.Source = mS.Result("${source}")
2082                    End If
2083                End If
2084                Long.TryParse(xentry.Item("in_reply_to_status_id").InnerText, post.InReplyToId)
2085                post.InReplyToUser = xentry.Item("in_reply_to_screen_name").InnerText
2086                'in_reply_to_user_idを使うか?
2087                post.IsFav = Boolean.Parse(xentry.Item("favorited").InnerText)
2088
2089                '以下、ユーザー情報
2090                Dim xUentry As XmlElement = CType(xentry.SelectSingleNode("./user"), XmlElement)
2091                post.Uid = Long.Parse(xUentry.Item("id").InnerText)
2092                post.Name = xUentry.Item("screen_name").InnerText
2093                post.Nickname = xUentry.Item("name").InnerText
2094                post.ImageUrl = xUentry.Item("profile_image_url").InnerText
2095                post.IsProtect = Boolean.Parse(xUentry.Item("protected").InnerText)
2096                post.IsMe = post.Name.ToLower.Equals(_uid)
2097                post.IsRead = read
2098                If gType = WORKERTYPE.Timeline Then
2099                    post.IsReply = post.ReplyToList.Contains(_uid)
2100                Else
2101                    post.IsReply = True
2102                End If
2103
2104                If post.IsMe Then
2105                    post.IsOwl = False
2106                Else
2107                    If followerId.Count > 0 Then post.IsOwl = Not followerId.Contains(post.Uid)
2108                End If
2109
2110                post.IsDm = False
2111            Catch ex As Exception
2112                ExceptionOut(ex)
2113                TraceOut(True, retMsg)
2114                MessageBox.Show("不正なXMLです。(TL-Parse)")
2115                Continue For
2116            End Try
2117
2118            '非同期アイコン取得&StatusDictionaryに追加
2119            arIdx += 1
2120            dlgt(arIdx) = New GetIconImageDelegate(AddressOf GetIconImage)
2121            ar(arIdx) = dlgt(arIdx).BeginInvoke(post, Nothing, Nothing)
2122        Next
2123
2124        'アイコン取得完了待ち
2125        For i As Integer = 0 To arIdx
2126            Try
2127                dlgt(i).EndInvoke(ar(i))
2128            Catch ex As Exception
2129                '最後までendinvoke回す(ゾンビ化回避)
2130                ExceptionOut(ex)
2131            End Try
2132        Next
2133
2134        Return ""
2135    End Function
2136
2137    Public Function GetDirectMessageApi(ByVal read As Boolean, _
2138                            ByVal gType As WORKERTYPE) As String
2139        If _endingFlag Then Return ""
2140
2141        Dim retMsg As String = ""
2142        Dim resStatus As String = ""
2143        'スレッド取得は行わず、countで調整
2144        Const GET_COUNT As Integer = 20
2145        'Const COUNT_QUERY As String = "count="
2146        Const RECEIVE_PATH As String = "/direct_messages.xml"
2147        Const SENT_PATH As String = "/direct_messages/sent.xml"
2148
2149        If gType = WORKERTYPE.DirectMessegeRcv Then
2150            retMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + RECEIVE_PATH, resStatus, _ApiMethod), String)
2151        Else
2152            retMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + SENT_PATH, resStatus, _ApiMethod), String)
2153        End If
2154
2155        If retMsg = "" Then Return resStatus
2156
2157        Dim arIdx As Integer = -1
2158        Dim dlgt(GET_COUNT) As GetIconImageDelegate    'countQueryに合わせる
2159        Dim ar(GET_COUNT) As IAsyncResult              'countQueryに合わせる
2160        Dim xdoc As New XmlDocument
2161        Try
2162            xdoc.LoadXml(retMsg)
2163        Catch ex As Exception
2164            ExceptionOut(ex)
2165            TraceOut(True, retMsg)
2166            MessageBox.Show("不正なXMLです。(DM-LoadXml)")
2167            Return "Invalid XML!"
2168        End Try
2169
2170        For Each xentryNode As XmlNode In xdoc.DocumentElement.SelectNodes("./direct_message")
2171            Dim xentry As XmlElement = CType(xentryNode, XmlElement)
2172            Dim post As New PostClass
2173            Try
2174                post.Id = Long.Parse(xentry.Item("id").InnerText)
2175                '二重取得回避
2176                SyncLock LockObj
2177                    If TabInformations.GetInstance.ContainsKey(post.Id) Then Continue For
2178                End SyncLock
2179                'sender_id
2180                'recipient_id
2181                post.PDate = DateTime.ParseExact(xentry.Item("created_at").InnerText, "ddd MMM dd HH:mm:ss zzzz yyyy", System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.None)
2182                '本文
2183                post.Data = xentry.Item("text").InnerText
2184                'HTMLに整形
2185                post.OriginalData = CreateHtmlAnchor(post.Data, post.ReplyToList)
2186                post.Data = HttpUtility.HtmlDecode(post.Data)
2187                post.Data = post.Data.Replace("<3", "♡")
2188                post.IsFav = False
2189                '受信DMかの判定で使用
2190                If gType = WORKERTYPE.DirectMessegeRcv Then
2191                    post.IsOwl = False
2192                Else
2193                    post.IsOwl = True
2194                End If
2195
2196                '以下、ユーザー情報
2197                Dim xUentry As XmlElement
2198                If gType = WORKERTYPE.DirectMessegeRcv Then
2199                    xUentry = CType(xentry.SelectSingleNode("./sender"), XmlElement)
2200                    post.IsMe = False
2201                Else
2202                    xUentry = CType(xentry.SelectSingleNode("./recipient"), XmlElement)
2203                    post.IsMe = True
2204                End If
2205                post.Uid = Long.Parse(xUentry.Item("id").InnerText)
2206                post.Name = xUentry.Item("screen_name").InnerText
2207                post.Nickname = xUentry.Item("name").InnerText
2208                post.ImageUrl = xUentry.Item("profile_image_url").InnerText
2209                post.IsProtect = Boolean.Parse(xUentry.Item("protected").InnerText)
2210            Catch ex As Exception
2211                ExceptionOut(ex)
2212                TraceOut(True, retMsg)
2213                MessageBox.Show("不正なXMLです。(DM-Parse)")
2214                Continue For
2215            End Try
2216
2217            post.IsRead = read
2218            post.IsReply = False
2219            post.IsDm = True
2220
2221            '非同期アイコン取得&StatusDictionaryに追加
2222            arIdx += 1
2223            dlgt(arIdx) = New GetIconImageDelegate(AddressOf GetIconImage)
2224            ar(arIdx) = dlgt(arIdx).BeginInvoke(post, Nothing, Nothing)
2225        Next
2226
2227        'アイコン取得完了待ち
2228        For i As Integer = 0 To arIdx
2229            Try
2230                dlgt(i).EndInvoke(ar(i))
2231            Catch ex As Exception
2232                '最後までendinvoke回す(ゾンビ化回避)
2233                ExceptionOut(ex)
2234            End Try
2235        Next
2236
2237        Return ""
2238    End Function
2239
2240    Public Function GetFollowersApi() As String
2241        If _endingFlag Then Return ""
2242
2243        Dim retMsg As String = ""
2244        Dim resStatus As String = ""
2245        Const FOLLOWER_PATH As String = "/followers/ids.xml"
2246
2247        retMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + FOLLOWER_PATH, resStatus, _ApiMethod), String)
2248
2249        If retMsg = "" Then Return resStatus
2250
2251        Dim xdoc As New XmlDocument
2252        Try
2253            xdoc.LoadXml(retMsg)
2254        Catch ex As Exception
2255            ExceptionOut(ex)
2256            TraceOut(True, retMsg)
2257            MessageBox.Show("不正なXMLです。再取得してください。(FollowerApi-LoadXml)")
2258            Return "Invalid XML!"
2259        End Try
2260
2261        followerId.Clear()
2262        For Each xentryNode As XmlNode In xdoc.DocumentElement.SelectNodes("./id")
2263            Try
2264                followerId.Add(Long.Parse(xentryNode.InnerText))
2265            Catch ex As Exception
2266                ExceptionOut(ex)
2267                TraceOut(True, retMsg)
2268                MessageBox.Show("不正なXMLです。再取得してください。(FollowerApi-Parse)")
2269                Continue For
2270            End Try
2271        Next
2272
2273        Return ""
2274    End Function
2275
2276    Private Function CreateHtmlAnchor(ByVal Text As String, ByVal AtList As List(Of String)) As String
2277        'uriの正規表現
2278        Dim rgUrl As Regex = New Regex("(?<![0-9A-Za-z])(?:https?|shttp|ftps?)://(?:(?:[-_.!~*'()a-zA-Z0-9;:&=+$,]|%[0-9A-Fa-f" + _
2279                         "][0-9A-Fa-f])*@)?(?:(?:[a-zA-Z0-9](?:[-a-zA-Z0-9]*[a-zA-Z0-9])?\.)" + _
2280                         "*[a-zA-Z](?:[-a-zA-Z0-9]*[a-zA-Z0-9])?\.?|[0-9]+\.[0-9]+\.[0-9]+\." + _
2281                         "[0-9]+)(?::[0-9]*)?(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f]" + _
2282                         "[0-9A-Fa-f])*(?:;(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-" + _
2283                         "Fa-f])*)*(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f" + _
2284                         "])*(?:;(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*)*)" + _
2285                         "*)?(?:\?(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])" + _
2286                         "*)?(?:#(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*)?")
2287        Dim retStr As String = HttpUtility.HtmlEncode(Text)     '要検証(デコードされて取得されるので再エンコード)
2288        '絶対パス表現のUriをリンクに置換
2289        retStr = rgUrl.Replace(Text, "<a href=""$&"">$&</a>")
2290        '@返信を抽出し、@先リスト作成
2291        Dim rg As New Regex("(^|[ -/:-@[-^`{-~])@([a-zA-Z0-9_]{1,20})")
2292        Dim m As Match = rg.Match(retStr)
2293        While m.Success
2294            AtList.Add(m.Result("$2").ToLower)
2295            m = m.NextMatch
2296        End While
2297        '@先をリンクに置換
2298        retStr = rg.Replace(retStr, "$1@<a href=""/$2"">$2</a>")
2299        retStr = AdjustHtml(ShortUrlResolve(PreProcessUrl(retStr))) 'IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
2300        Return retStr
2301    End Function
2302
2303#Region "デバッグモード解析キー自動生成"
2304#If DEBUG Then
2305    Public Sub GenerateAnalyzeKey()
2306        '解析キー情報部分のソースをwedataから作成する
2307        '生成したソースはプロジェクトのディレクトリにコピーする
2308        Dim sw As New System.IO.StreamWriter(".\AnalyzeKey.vb", _
2309            False, _
2310            System.Text.Encoding.UTF8)
2311
2312        sw.WriteLine("Public Module AnalyzeKey")
2313        sw.WriteLine("'    このファイルはデバッグビルドのTweenにより自動作成されました   作成日時  " + DateAndTime.Now.ToString())
2314        sw.WriteLine("")
2315
2316        sw.WriteLine("    Public _splitPost As String = " + Chr(34) + _splitPost.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2317        sw.WriteLine("    Public _splitPostRecent As String = " + Chr(34) + _splitPostRecent.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2318        sw.WriteLine("    Public _statusIdTo As String = " + Chr(34) + _statusIdTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2319        sw.WriteLine("    Public _splitDM As String = " + Chr(34) + _splitDM.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2320        sw.WriteLine("    Public _parseName As String = " + Chr(34) + _parseName.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2321        sw.WriteLine("    Public _parseNameTo As String = " + Chr(34) + _parseNameTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2322        sw.WriteLine("    Public _parseNick As String = " + Chr(34) + _parseNick.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2323        sw.WriteLine("    Public _parseNickTo As String = " + Chr(34) + _parseNickTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2324        sw.WriteLine("    Public _parseImg As String = " + Chr(34) + _parseImg.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2325        sw.WriteLine("    Public _parseImgTo As String = " + Chr(34) + _parseImgTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2326        sw.WriteLine("    Public _parseMsg1 As String = " + Chr(34) + _parseMsg1.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2327        sw.WriteLine("    Public _parseMsg2 As String = " + Chr(34) + _parseMsg2.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2328        sw.WriteLine("    Public _parseDM1 As String = " + Chr(34) + _parseDM1.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2329        sw.WriteLine("    Public _parseDM11 As String = " + Chr(34) + _parseDM11.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2330        sw.WriteLine("    Public _parseDM2 As String = " + Chr(34) + _parseDM2.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2331        sw.WriteLine("    Public _parseDate As String = " + Chr(34) + _parseDate.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2332        sw.WriteLine("    Public _parseDateTo As String = " + Chr(34) + _parseDateTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2333        sw.WriteLine("    Public _getAuthKey As String = " + Chr(34) + _getAuthKey.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2334        sw.WriteLine("    Public _getAuthKeyTo As String = " + Chr(34) + _getAuthKeyTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2335        sw.WriteLine("    Public _parseStar As String = " + Chr(34) + _parseStar.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2336        sw.WriteLine("    Public _parseStarTo As String = " + Chr(34) + _parseStarTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2337        sw.WriteLine("    Public _parseStarEmpty As String = " + Chr(34) + _parseStarEmpty.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2338        sw.WriteLine("    Public _followerList As String = " + Chr(34) + _followerList.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2339        sw.WriteLine("    Public _followerMbr1 As String = " + Chr(34) + _followerMbr1.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2340        sw.WriteLine("    Public _followerMbr2 As String = " + Chr(34) + _followerMbr2.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2341        sw.WriteLine("    Public _followerMbr3 As String = " + Chr(34) + _followerMbr3.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2342        sw.WriteLine("    Public _getInfoTwitter As String = " + Chr(34) + _getInfoTwitter.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2343        sw.WriteLine("    Public _getInfoTwitterTo As String = " + Chr(34) + _getInfoTwitterTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2344        sw.WriteLine("    Public _isProtect As String = " + Chr(34) + _isProtect.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2345        sw.WriteLine("    Public _isReplyEng As String = " + Chr(34) + _isReplyEng.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2346        sw.WriteLine("    Public _isReplyJpn As String = " + Chr(34) + _isReplyJpn.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2347        sw.WriteLine("    Public _isReplyTo As String = " + Chr(34) + _isReplyTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2348        sw.WriteLine("    Public _parseProtectMsg1 As String = " + Chr(34) + _parseProtectMsg1.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2349        sw.WriteLine("    Public _parseProtectMsg2 As String = " + Chr(34) + _parseProtectMsg2.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2350        sw.WriteLine("    Public _parseDMcountFrom As String = " + Chr(34) + _parseDMcountFrom.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2351        sw.WriteLine("    Public _parseDMcountTo As String = " + Chr(34) + _parseDMcountTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2352        sw.WriteLine("    Public _parseSourceFrom As String = " + Chr(34) + _parseSourceFrom.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2353        sw.WriteLine("    Public _parseSource2 As String = " + Chr(34) + _parseSource2.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2354        sw.WriteLine("    Public _parseSourceTo As String = " + Chr(34) + _parseSourceTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2355        sw.WriteLine("    Public _removeClass As String = " + Chr(34) + _removeClass.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
2356        sw.WriteLine("End Module")
2357
2358        sw.Close()
2359        'MessageBox.Show("解析キー情報定義ファイル AnalyzeKey.vbを生成しました")
2360
2361    End Sub
2362#End If
2363#End Region
2364End Module
Note: See TracBrowser for help on using the browser.