1 ' Tween - Client of Twitter
2 ' Copyright (c) 2007-2009 kiri_feather (@kiri_feather) <kiri_feather@gmail.com>
3 ' (c) 2008-2009 Moz (@syo68k) <http://iddy.jp/profile/moz/>
4 ' (c) 2008-2009 takeshik (@takeshik) <http://www.takeshik.org/>
7 ' This file is part of Tween.
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)
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
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.
27 Imports System.Threading
29 Imports System.Text.RegularExpressions
30 Imports System.Globalization
31 Imports System.Diagnostics
34 Delegate Sub GetIconImageDelegate(ByVal post As PostClass)
35 Delegate Function GetTimelineDelegate(ByVal page As Integer, _
36 ByVal read As Boolean, _
37 ByRef endPage As Integer, _
38 ByVal gType As WORKERTYPE, _
39 ByRef getDM As Boolean) As String
40 Delegate Function GetDirectMessageDelegate(ByVal page As Integer, _
41 ByVal read As Boolean, _
42 ByVal endPage As Integer, _
43 ByVal gType As WORKERTYPE) As String
44 Private ReadOnly LockObj As New Object
45 Private GetTmSemaphore As New Threading.Semaphore(8, 8)
47 Private follower As New List(Of String)
48 Private followerId As New List(Of Long)
49 Private tmpFollower As New List(Of String)
51 Private _followersCount As Integer = 0
52 Private _friendsCount As Integer = 0
53 Private _statusesCount As Integer = 0
54 Private _location As String = ""
55 Private _bio As String = ""
56 Private _useSsl As Boolean = True
57 Private _protocol As String = "https://"
58 Private _bitlyId As String = ""
59 Private _bitlyKey As String = ""
62 Private _uid As String
63 Private _pwd As String
64 Private _proxyType As ProxyType
65 Private _proxyAddress As String
66 Private _proxyPort As Integer
67 Private _proxyUser As String
68 Private _proxyPassword As String
70 Private _nextThreshold As Integer
71 Private _nextPages As Integer
73 Private _iconSz As Integer
74 Private _getIcon As Boolean
75 Private _lIcon As ImageList
76 Private _dIcon As Dictionary(Of String, Image)
78 Private _tinyUrlResolve As Boolean
79 Private _restrictFavCheck As Boolean
80 Private _useAPI As Boolean
82 Private _hubServer As String
83 Private _defaultTimeOut As Integer ' MySocketクラスへ渡すタイムアウト待ち時間(秒単位 ミリ秒への換算はMySocketクラス側で行う)
84 Private _countApi As Integer
85 Private _usePostMethod As Boolean
86 Private _ApiMethod As MySocket.REQ_TYPE
87 Private _readOwnPost As Boolean
90 Private _authKey As String 'StatusUpdate、発言削除で使用
91 Private _authKeyDM As String 'DM送信、DM削除で使用
92 Private _signed As Boolean
93 Private _infoTwitter As String = ""
94 Private _dmCount As Integer
95 Private _getDm As Boolean
96 Private _remainCountApi As Integer = -1
98 Private _ShortUrlService() As String = { _
99 "http://tinyurl.com/", _
101 "http://snipurl.com/", _
102 "http://snurl.com/", _
104 "http://qurlyq.com/", _
105 "http://dwarfurl.com/", _
106 "http://icanhaz.com/", _
108 "http://urlenco.de/", _
110 "http://piurl.com/", _
111 "http://linkbee.com/", _
112 "http://traceurl.com/", _
113 "http://twurl.nl/", _
115 "http://rubyurl.com/", _
116 "http://budurl.com/", _
118 "http://twitthis.com/", _
120 "http://tumblr.com/", _
121 "http://www.qurl.com/", _
122 "http://digg.com/", _
124 "http://ustre.am/", _
126 "http://airme.us/", _
127 "http://qurl.com/", _
128 "http://bctiny.com/", _
133 Private Const _apiHost As String = "api."
134 Private Const _baseUrlStr As String = "twitter.com"
135 Private Const _loginPath As String = "/sessions"
136 Private Const _homePath As String = "/home"
137 Private Const _replyPath As String = "/replies"
138 Private Const _DMPathRcv As String = "/inbox"
139 Private Const _DMPathSnt As String = "/sent"
140 Private Const _DMDestroyPath As String = "/direct_messages/destroy/"
141 Private Const _StDestroyPath As String = "/statuses/destroy/"
142 Private Const _postRetweetPath As String = "/1/statuses/retweet/"
143 Private Const _uidHeader As String = "session[username_or_email]="
144 Private Const _pwdHeader As String = "session[password]="
145 Private Const _pageQry As String = "?page="
146 Private Const _cursorQry As String = "?cursor="
147 Private Const _statusHeader As String = "status="
148 Private Const _statusUpdatePathAPI As String = "/statuses/update.xml"
149 Private Const _linkToOld As String = "class=""section_links"" rel=""prev"""
150 Private Const _postFavAddPath As String = "/favorites/create/"
151 Private Const _postFavRemovePath As String = "/favorites/destroy/"
152 Private Const _authKeyHeader As String = "authenticity_token="
153 'Private Const _parseLink1 As String = "<a href="""
154 'Private Const _parseLink2 As String = """>"
155 'Private Const _parseLink3 As String = "</a>"
156 Private Const _GetFollowers As String = "/statuses/followers.xml"
157 Private Const _ShowStatus As String = "/statuses/show/"
158 Private Const _rateLimitStatus As String = "/account/rate_limit_status.xml"
161 Private Const wedataUrl As String = "http://wedata.net/databases/Tween/items.json"
163 Private Function SignIn() As String
164 If _endingFlag Then Return ""
167 Dim account As String = ""
168 Static skipCount As Integer = 0
171 If _signed Then Return ""
172 If Twitter.AccountState <> ACCOUNT_STATE.Valid AndAlso skipCount < 10 Then
174 Return "SignIn -> Check Username/Password in setting."
181 MySocket.ResetCookie()
183 Dim resStatus As String = ""
184 Dim resMsg As String = ""
186 '設定によらずログイン処理はhttps固定
187 resMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + "/login", resStatus, MySocket.REQ_TYPE.ReqGET), String)
188 If resMsg.Length = 0 Then
189 'Twitter.AccountState = ACCOUNT_STATE.Invalid
190 Return "SignIn -> " + resStatus
192 Dim authToken As String = ""
193 Dim rg As New Regex("authenticity_token"" type=""hidden"" value=""(?<auth>[a-z0-9]+)""")
194 Dim m As Match = rg.Match(resMsg)
196 authToken = m.Result("${auth}")
198 Return "SignIn -> Can't get token."
201 account = _authKeyHeader + authToken + "&" + _uidHeader + _uid + "&" + _pwdHeader + HttpUtility.UrlEncode(_pwd) + "&" + "remember_me=1"
204 resMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + _loginPath, resStatus, MySocket.REQ_TYPE.ReqPOST, account), String)
205 If resStatus.StartsWith("OK") Then
206 'OK (username/passwordが合致しない)
207 Dim msg As String = resStatus
208 If resMsg.Contains("Wrong Username/Email and password combination.") Then
209 msg = "Wrong Username or password."
211 '未知の応答(May be required Chapta)
212 msg = "Wrong Username or password. Try from web."
214 Twitter.AccountState = ACCOUNT_STATE.Invalid
215 Return "SignIn Failed -> " + msg
216 ElseIf resMsg.Contains("https://twitter.com/account/locked") Then '302 FOUND
217 Dim msg As String = "You account is Locked Out."
218 Twitter.AccountState = ACCOUNT_STATE.Invalid
219 Return "SignIn Failed -> " + msg
220 ElseIf resMsg.Contains("https://twitter.com:443/") Then '302 FOUND
222 ElseIf resMsg.Contains("https://twitter.com/") OrElse _
223 resMsg.Contains("http://twitter.com/") Then '302 FOUND
225 ElseIf resStatus.StartsWith("Err:") Then
227 Return "SignIn Failed -> " + resStatus
229 '応答がOK でありサインインできていない場合の未知の応答
230 'TraceOut(True, "SignIn Failed." + vbCrLf + "resStatus:" + resStatus + vbCrLf + "resMsg:" + vbCrLf + resMsg)
231 Twitter.AccountState = ACCOUNT_STATE.Invalid
232 Return "SignIn Failed -> " + "Unknown problems."
234 Twitter.AccountState = ACCOUNT_STATE.Valid
240 Public Function GetTimeline(ByVal page As Integer, _
241 ByVal read As Boolean, _
242 ByRef endPage As Integer, _
243 ByVal gType As WORKERTYPE, _
244 ByRef getDM As Boolean) As String
248 Dim epage As Integer = page
249 GetTmSemaphore.WaitOne()
250 Dim trslt As String = ""
251 trslt = GetTimelineThread(page, read, epage, gType, getDM)
252 If trslt.Length > 0 Then Return trslt
254 If epage < page OrElse gType = WORKERTYPE.Reply Then Return ""
257 '起動時モード or 通常モードの読み込み継続 -> 複数ページ同時取得
258 Dim num As Integer = endPage - page
259 Dim ar(num) As IAsyncResult
260 Dim dlgt(num) As GetTimelineDelegate
262 For idx As Integer = 0 To num
263 dlgt(idx) = New GetTimelineDelegate(AddressOf GetTimelineThread)
264 GetTmSemaphore.WaitOne()
265 ar(idx) = dlgt(idx).BeginInvoke(page + idx, read, endPage + idx, gType, getDM, Nothing, Nothing)
267 Dim rslt As String = ""
268 For idx As Integer = 0 To num
269 Dim epage As Integer = 0
270 Dim dm As Boolean = False
271 Dim trslt As String = ""
273 trslt = dlgt(idx).EndInvoke(epage, dm, ar(idx))
274 Catch ex As Exception
275 '最後までendinvoke回す(ゾンビ化回避)
276 ex.Data("IsTerminatePermission") = False
278 rslt = "GetTimelineErr"
280 If trslt.Length > 0 AndAlso rslt.Length = 0 Then rslt = trslt
281 If dm Then getDM = True
286 Private Function GetTimelineThread(ByVal page As Integer, _
287 ByVal read As Boolean, _
288 ByRef endPage As Integer, _
289 ByVal gType As WORKERTYPE, _
290 ByRef getDM As Boolean) As String
292 If _endingFlag Then Return ""
294 Dim retMsg As String = ""
295 Dim resStatus As String = ""
297 Static redirectToTimeline As String = ""
298 Static redirectToReply As String = ""
300 If _signed = False Then
302 If retMsg.Length > 0 Then
307 If _endingFlag Then Return ""
310 Dim pageQuery As String
315 pageQuery = _pageQry + page.ToString
318 If gType = WORKERTYPE.Timeline Then
319 retMsg = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + _homePath + pageQuery, resStatus, MySocket.REQ_TYPE.ReqGetApp), String)
321 retMsg = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + _replyPath + pageQuery, resStatus, MySocket.REQ_TYPE.ReqGetApp), String)
324 If retMsg.Length = 0 Then
330 retMsg = Regex.Replace(retMsg, "(?<tagStart><li)(?<cls>\s+class=""[^""]+"")", "${tagStart}")
333 ' Dim idx As Integer = retMsg.IndexOf(_removeClass, StringComparison.Ordinal)
334 ' If idx = -1 Then Exit Do
335 ' Dim idx2 As Integer = retMsg.IndexOf("""", idx + _removeClass.Length, StringComparison.Ordinal) - idx + 1 - 3
336 ' If idx2 > 0 Then retMsg = retMsg.Remove(idx + 3, idx2)
337 ' Catch ex As Exception
339 ' TraceOut("TM-Remove: " + retMsg)
340 ' Return "GetTimeline -> Err: Can't parse data."
344 If _endingFlag Then Return ""
347 Dim strSepTmp As String
348 If gType = WORKERTYPE.Timeline Then
349 strSepTmp = _splitPostRecent
351 strSepTmp = _splitPost
357 pos1 = retMsg.IndexOf(strSepTmp, StringComparison.Ordinal)
361 Return "GetTimeline -> Err: tweets count is 0."
364 Dim strSep() As String = {strSepTmp}
365 Dim posts() As String = retMsg.Split(strSep, StringSplitOptions.RemoveEmptyEntries)
366 Dim intCnt As Integer = 0
367 Dim listCnt As Integer = 0
369 listCnt = TabInformations.GetInstance.ItemCount
371 Dim dlgt(20) As GetIconImageDelegate
372 Dim ar(20) As IAsyncResult
373 Dim arIdx As Integer = -1
377 For Each strPost As String In posts
381 If page = 1 And gType = WORKERTYPE.Timeline Then
383 'pos1 = strPost.IndexOf(_getSiv, 0)
385 ' pos2 = strPost.IndexOf(_getSivTo, pos1 + _getSiv.Length)
387 ' _authSiv = strPost.Substring(pos1 + _getSiv.Length, pos2 - pos1 - _getSiv.Length)
391 ' Return "GetTimeline -> Err: Can't get Siv."
396 ' Return "GetTimeline -> Err: Can't get Siv."
400 If GetAuthKey(retMsg) < 0 Then
402 Return "GetTimeline -> Err: Can't get auth token."
406 pos1 = retMsg.IndexOf(_getInfoTwitter, StringComparison.Ordinal)
408 pos2 = retMsg.IndexOf(_getInfoTwitterTo, pos1, StringComparison.Ordinal)
410 _infoTwitter = retMsg.Substring(pos1 + _getInfoTwitter.Length, pos2 - pos1 - _getInfoTwitter.Length)
420 Dim post As New PostClass
422 pos1 = strPost.IndexOf("</ol>")
424 strPost = strPost.Substring(0, pos1)
430 pos2 = strPost.IndexOf(_statusIdTo, 0, StringComparison.Ordinal)
431 post.Id = Long.Parse(HttpUtility.HtmlDecode(strPost.Substring(0, pos2)))
432 Catch ex As Exception
434 TraceOut("TM-ID:" + strPost)
435 Return "GetTimeline -> Err: Can't get ID."
439 pos1 = strPost.IndexOf(_parseName, pos2, StringComparison.Ordinal)
440 pos2 = strPost.IndexOf(_parseNameTo, pos1, StringComparison.Ordinal)
441 post.Name = HttpUtility.HtmlDecode(strPost.Substring(pos1 + _parseName.Length, pos2 - pos1 - _parseName.Length))
442 Catch ex As Exception
444 TraceOut("TM-Name:" + strPost)
445 Return "GetTimeline -> Err: Can't get Name."
449 If strPost.IndexOf("twitter.com/images/heart.png", pos2, StringComparison.Ordinal) > -1 Then
450 post.Nickname = post.Name
453 pos1 = strPost.IndexOf(_parseNick, pos2, StringComparison.Ordinal)
454 pos2 = strPost.IndexOf(_parseNickTo, pos1 + _parseNick.Length, StringComparison.Ordinal)
455 post.Nickname = HttpUtility.HtmlDecode(strPost.Substring(pos1 + _parseNick.Length, pos2 - pos1 - _parseNick.Length))
456 Catch ex As Exception
458 TraceOut("TM-Nick:" + strPost)
459 Return "GetTimeline -> Err: Can't get Nick."
465 If TabInformations.GetInstance.ContainsKey(post.Id) Then Continue For
468 Dim orgData As String = ""
470 If strPost.IndexOf("<form action=""/status/update"" id=""heartForm", 0, StringComparison.Ordinal) > -1 Then
472 pos1 = strPost.IndexOf("<strong>", 0, StringComparison.Ordinal)
473 pos2 = strPost.IndexOf("</strong>", pos1, StringComparison.Ordinal)
474 orgData = strPost.Substring(pos1 + 8, pos2 - pos1 - 8)
475 Catch ex As Exception
477 TraceOut("TM-VBody:" + strPost)
478 Return "GetTimeline -> Err: Can't get Valentine body."
485 pos1 = strPost.IndexOf(_parseImg, pos2, StringComparison.Ordinal)
486 pos2 = strPost.IndexOf(_parseImgTo, pos1 + _parseImg.Length, StringComparison.Ordinal)
487 post.ImageUrl = HttpUtility.HtmlDecode(strPost.Substring(pos1 + _parseImg.Length, pos2 - pos1 - _parseImg.Length))
488 Catch ex As Exception
490 TraceOut("TM-Img:" + strPost)
491 Return "GetTimeline -> Err: Can't get ImagePath."
495 If strPost.IndexOf(_isProtect, pos2, StringComparison.Ordinal) > -1 Then
496 post.IsProtect = True
500 If strPost.IndexOf("class=""big-retweet-icon""") > -1 Then
501 rg = New Regex("class=""shared-content"".+<a href=""/(?<name>[a-zA-Z0-9_]+)""")
502 m = rg.Match(strPost)
504 post.RetweetedBy = m.Result("${name}")
506 post.RetweetedBy = ""
508 rg = New Regex("&in_reply_to_status_id=(?<id>[0-9]+)&in_reply_to=")
509 m = rg.Match(strPost)
511 post.RetweetedId = Long.Parse(m.Result("${id}"))
518 pos1 = strPost.IndexOf(_parseMsg1, pos2, StringComparison.Ordinal)
522 If strPost.IndexOf("<div id=""doyouheart", pos2, StringComparison.Ordinal) > -1 Then
524 orgData += " <3 you! Do you <3 "
525 pos1 = strPost.IndexOf("<a href", pos2, StringComparison.Ordinal)
526 pos2 = strPost.IndexOf("?", pos1, StringComparison.Ordinal)
527 orgData += strPost.Substring(pos1, pos2 - pos1 + 1)
529 pos1 = strPost.IndexOf(_parseProtectMsg1, pos2, StringComparison.Ordinal)
533 pos1 = strPost.IndexOf("<a href", pos2, StringComparison.Ordinal)
535 pos2 = strPost.IndexOf("!", pos1, StringComparison.Ordinal)
536 orgData += strPost.Substring(pos1, pos2 - pos1 + 1)
540 pos2 = strPost.IndexOf(_parseProtectMsg2, pos1, StringComparison.Ordinal)
541 orgData = strPost.Substring(pos1 + _parseProtectMsg1.Length, pos2 - pos1 - _parseProtectMsg1.Length).Trim()
544 Catch ex As Exception
546 TraceOut("TM-VBody2:" + strPost)
547 Return "GetTimeline -> Err: Can't get Valentine body2."
552 pos2 = strPost.IndexOf(_parseMsg2, pos1, StringComparison.Ordinal)
553 orgData = strPost.Substring(pos1 + _parseMsg1.Length, pos2 - pos1 - _parseMsg1.Length).Trim()
554 Catch ex As Exception
556 TraceOut("TM-Body:" + strPost)
557 Return "GetTimeline -> Err: Can't get body."
561 ' orgData = Regex.Replace(orgData, "<a href=""https://twitter\.com/" + post.Name + "/status/[0-9]+"">\.\.\.</a>$", "")
564 orgData = orgData.Replace("<3", "♡")
568 orgData = PreProcessUrl(orgData)
570 '短縮URL解決処理(orgData書き換え)
571 orgData = ShortUrlResolve(orgData)
574 post.OriginalData = AdjustHtml(orgData)
576 '単純テキストの取り出し(リンクタグ除去)
578 post.Data = GetPlainText(orgData)
579 Catch ex As Exception
581 TraceOut("TM-Link:" + strPost)
582 Return "GetTimeline -> Err: Can't parse links."
586 Dim ImgTag As New Regex("<img src=.*?/>", RegexOptions.IgnoreCase)
587 If ImgTag.IsMatch(post.Data) Then post.Data = ImgTag.Replace(post.Data, "<img>")
592 pos1 = strPost.IndexOf(_parseDate, pos2, StringComparison.Ordinal)
594 pos2 = strPost.IndexOf(_parseDateTo, pos1 + _parseDate.Length, StringComparison.Ordinal)
595 post.PDate = DateTime.ParseExact(strPost.Substring(pos1 + _parseDate.Length, pos2 - pos1 - _parseDate.Length), "ddd MMM dd HH':'mm':'ss zzzz yyyy", System.Globalization.DateTimeFormatInfo.InvariantInfo, Globalization.DateTimeStyles.None)
599 Catch ex As Exception
601 TraceOut("TM-Date:" + strPost)
602 Return "GetTimeline -> Err: Can't get date."
605 '取得できなくなったため暫定対応(2/26)
611 'ToDo: _parseSourceFromを正規表現へ。wedataからの取得へ変更(次版より)
612 rg = New Regex("<span>.+>(?<name>.+)</a>.*</span>")
613 m = rg.Match(strPost)
615 post.Source = m.Result("${name}")
620 ' pos1 = strPost.IndexOf(_parseSourceFrom, pos2, StringComparison.Ordinal)
621 ' If pos1 = -1 Then pos1 = strPost.IndexOf(_parseSourceFrom2, pos2, StringComparison.Ordinal)
623 ' pos1 = strPost.IndexOf(_parseSource2, pos1 + 19, StringComparison.Ordinal)
624 ' pos2 = strPost.IndexOf(_parseSourceTo, pos1 + 2, StringComparison.Ordinal)
625 ' post.Source = HttpUtility.HtmlDecode(strPost.Substring(pos1 + 2, pos2 - pos1 - 2))
627 ' post.Source = "Web"
629 'Catch ex As Exception
631 ' TraceOut("TM-Src:" + strPost)
632 ' Return "GetTimeline -> Err: Can't get src."
635 'Get Reply(in_reply_to_user/id)
636 'ToDo: _isReplyEngを正規表現へ。wedataからの取得へ変更(次版より)
637 rg = New Regex("<a href=""https?:\/\/twitter\.com\/(?<name>[a-zA-Z0-9_]+)\/status\/(?<id>[0-9]+)"">(in reply to )*\k<name>")
638 m = rg.Match(strPost)
640 post.InReplyToUser = m.Result("${name}")
641 post.InReplyToId = Long.Parse(m.Result("${id}"))
642 post.IsReply = post.InReplyToUser.Equals(_uid, StringComparison.OrdinalIgnoreCase)
646 rg = New Regex("@<a [^>]*href=""\/(?<1>[a-zA-Z0-9_]+)[^a-zA-Z0-9_]")
647 m = rg.Match(orgData)
649 post.ReplyToList.Add(m.Groups(1).Value.ToLower())
652 If Not post.IsReply Then post.IsReply = post.ReplyToList.Contains(_uid)
654 If gType = WORKERTYPE.Reply Then post.IsReply = True
657 If strPost.IndexOf("class=""fav-action fav""") > -1 Then
662 'pos1 = strPost.IndexOf(_parseStar, pos2, StringComparison.Ordinal)
665 ' pos2 = strPost.IndexOf(_parseStarTo, pos1 + _parseStar.Length, StringComparison.Ordinal)
666 ' If strPost.Substring(pos1 + _parseStar.Length, pos2 - pos1 - _parseStar.Length) = _parseStarEmpty Then
671 ' Catch ex As Exception
673 ' TraceOut("TM-Fav:" + strPost)
674 ' Return "GetTimeline -> Err: Can't get fav status."
680 If _endingFlag Then Return ""
682 post.IsMe = post.Name.Equals(_uid, StringComparison.OrdinalIgnoreCase)
684 If follower.Count > 1 Then
685 post.IsOwl = Not follower.Contains(post.Name.ToLower())
691 If post.IsMe AndAlso Not read AndAlso _readOwnPost Then post.IsRead = True
694 dlgt(arIdx) = New GetIconImageDelegate(AddressOf GetIconImage)
695 ar(arIdx) = dlgt(arIdx).BeginInvoke(post, Nothing, Nothing)
700 If intCnt = posts.Length AndAlso gType = WORKERTYPE.Timeline AndAlso page = 1 Then
701 pos1 = strPost.IndexOf(_parseDMcountFrom, pos2, StringComparison.Ordinal)
704 pos2 = strPost.IndexOf(_parseDMcountTo, pos1 + _parseDMcountFrom.Length, StringComparison.Ordinal)
705 Dim dmCnt As Integer = Integer.Parse(strPost.Substring(pos1 + _parseDMcountFrom.Length, pos2 - pos1 - _parseDMcountFrom.Length))
706 If dmCnt > _dmCount Then
710 Catch ex As Exception
711 Return "GetTimeline -> Err: Can't get DM count."
718 For i As Integer = 0 To arIdx
720 dlgt(i).EndInvoke(ar(i))
721 Catch ex As Exception
722 '最後までendinvoke回す(ゾンビ化回避)
723 ex.Data("IsTerminatePermission") = False
729 If page = 1 AndAlso (TabInformations.GetInstance.ItemCount - listCnt) >= _nextThreshold Then
730 '新着が閾値の件数以上なら、次のページも念のため読み込み
731 endPage = _nextPages + 1
737 GetTmSemaphore.Release()
741 Public Function GetDirectMessage(ByVal page As Integer, _
742 ByVal read As Boolean, _
743 ByVal endPage As Integer, _
744 ByVal gType As WORKERTYPE) As String
751 Dim num As Integer = endPage - page
752 Dim ar(num) As IAsyncResult
753 Dim dlgt(num) As GetDirectMessageDelegate
755 For idx As Integer = 0 To num
756 gType = WORKERTYPE.DirectMessegeRcv
757 dlgt(idx) = New GetDirectMessageDelegate(AddressOf GetDirectMessageThread)
758 GetTmSemaphore.WaitOne()
759 ar(idx) = dlgt(idx).BeginInvoke(page + idx, read, endPage + idx, gType, Nothing, Nothing)
761 Dim rslt As String = ""
762 For idx As Integer = 0 To num
763 Dim trslt As String = ""
765 trslt = dlgt(idx).EndInvoke(ar(idx))
766 Catch ex As Exception
767 '最後までendinvoke回す(ゾンビ化回避)
768 ex.Data("IsTerminatePermission") = False
770 rslt = "GetDirectMessageErr"
772 If trslt.Length > 0 AndAlso rslt.Length = 0 Then rslt = trslt
774 For idx As Integer = 0 To num
775 gType = WORKERTYPE.DirectMessegeSnt
776 dlgt(idx) = New GetDirectMessageDelegate(AddressOf GetDirectMessageThread)
777 GetTmSemaphore.WaitOne()
778 ar(idx) = dlgt(idx).BeginInvoke(page + idx, read, endPage + idx, gType, Nothing, Nothing)
780 For idx As Integer = 0 To num
781 Dim trslt As String = ""
783 trslt = dlgt(idx).EndInvoke(ar(idx))
784 Catch ex As Exception
785 '最後までendinvoke回す(ゾンビ化回避)
786 ex.Data("IsTerminatePermission") = False
788 rslt = "GetDirectMessageErr"
790 If trslt.Length > 0 AndAlso rslt.Length = 0 Then rslt = trslt
795 Private Function GetDirectMessageThread(ByVal page As Integer, _
796 ByVal read As Boolean, _
797 ByVal endPage As Integer, _
798 ByVal gType As WORKERTYPE) As String
800 If _endingFlag Then Return ""
802 Dim retMsg As String = ""
803 Dim resStatus As String = ""
805 Static redirectToDmRcv As String = ""
806 Static redirectToDmSnd As String = ""
810 If _signed = False Then
812 If retMsg.Length > 0 Then
817 If _endingFlag Then Return ""
820 Dim pageQuery As String = _pageQry + page.ToString
821 If gType = WORKERTYPE.DirectMessegeRcv Then
822 retMsg = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + _DMPathRcv + pageQuery, resStatus, MySocket.REQ_TYPE.ReqGetApp), String)
824 retMsg = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + _DMPathSnt + pageQuery, resStatus, MySocket.REQ_TYPE.ReqGetApp), String)
827 If retMsg.Length = 0 Then
833 retMsg = Regex.Replace(retMsg, "(?<tagStart><li)(?<cls>\s+class=""[^""]+"")", "${tagStart}")
836 ' Dim idx As Integer = retMsg.IndexOf(_removeClass, StringComparison.Ordinal)
837 ' If idx = -1 Then Exit Do
838 ' Dim idx2 As Integer = retMsg.IndexOf("""", idx + _removeClass.Length, StringComparison.Ordinal) - idx + 1 - 3
839 ' If idx2 > 0 Then retMsg = retMsg.Remove(idx + 3, idx2)
840 ' Catch ex As Exception
842 ' TraceOut("DM-Remove: " + retMsg)
843 ' Return "GetDm -> Err: Can't parse data."
847 If _endingFlag Then Return ""
850 'If GetAuthKeyDM(retMsg) < 0 Then
852 ' Return "GetDirectMessage -> Err: Busy(1)"
859 pos1 = retMsg.IndexOf(_splitDM, StringComparison.Ordinal)
861 '0件(メッセージなし。エラーの場合もありうるが判別できないので正常として戻す)
865 Dim strSep() As String = {_splitDM}
866 Dim posts() As String = retMsg.Split(strSep, StringSplitOptions.RemoveEmptyEntries)
867 Dim intCnt As Integer = 0 'カウンタ
868 Dim listCnt As Integer = 0
870 listCnt = TabInformations.GetInstance.ItemCount
872 Dim dlgt(20) As GetIconImageDelegate
873 Dim ar(20) As IAsyncResult
874 Dim arIdx As Integer = -1
876 For Each strPost As String In posts
879 If intCnt > 1 Then '1件目はヘッダなので無視
880 'Dim lItem As New MyListItem
881 Dim post As New PostClass()
883 pos1 = strPost.IndexOf("</ol>")
885 strPost = strPost.Substring(0, pos1)
891 pos2 = strPost.IndexOf("""", 0, StringComparison.Ordinal)
892 post.Id = Long.Parse(HttpUtility.HtmlDecode(strPost.Substring(0, pos2)))
893 Catch ex As Exception
895 TraceOut("DM-ID:" + strPost)
896 Return "GetDirectMessage -> Err: Can't get ID"
901 pos1 = strPost.IndexOf(_parseName, pos2, StringComparison.Ordinal)
902 pos2 = strPost.IndexOf(_parseNameTo, pos1, StringComparison.Ordinal)
903 post.Name = HttpUtility.HtmlDecode(strPost.Substring(pos1 + _parseName.Length, pos2 - pos1 - _parseName.Length))
904 Catch ex As Exception
906 TraceOut("DM-Name:" + strPost)
907 Return "GetDirectMessage -> Err: Can't get Name"
912 pos1 = strPost.IndexOf(_parseNick, pos2, StringComparison.Ordinal)
913 pos2 = strPost.IndexOf(_parseNickTo, pos1 + _parseNick.Length, StringComparison.Ordinal)
914 post.Nickname = HttpUtility.HtmlDecode(strPost.Substring(pos1 + _parseNick.Length, pos2 - pos1 - _parseNick.Length))
915 Catch ex As Exception
917 TraceOut("DM-Nick:" + strPost)
918 Return "GetDirectMessage -> Err: Can't get Nick."
922 If TabInformations.GetInstance.ContainsKey(post.Id) Then Continue For
927 pos1 = strPost.IndexOf(_parseImg, pos2, StringComparison.Ordinal)
928 pos2 = strPost.IndexOf(_parseImgTo, pos1 + _parseImg.Length, StringComparison.Ordinal)
929 post.ImageUrl = HttpUtility.HtmlDecode(strPost.Substring(pos1 + _parseImg.Length, pos2 - pos1 - _parseImg.Length))
930 Catch ex As Exception
932 TraceOut("DM-Img:" + strPost)
933 Return "GetDirectMessage -> Err: Can't get ImagePath"
938 pos1 = strPost.IndexOf(_isProtect, pos2, StringComparison.Ordinal)
939 If pos1 > -1 Then post.IsProtect = True
940 Catch ex As Exception
942 TraceOut("DM-Protect:" + strPost)
943 Return "GetDirectMessage -> Err: Can't get Protect"
946 Dim orgData As String = ""
950 pos1 = strPost.IndexOf(_parseDM1, pos2, StringComparison.Ordinal)
952 pos2 = strPost.IndexOf(_parseDM2, pos1, StringComparison.Ordinal)
953 orgData = strPost.Substring(pos1 + _parseDM1.Length, pos2 - pos1 - _parseDM1.Length).Trim()
955 pos1 = strPost.IndexOf(_parseDM11, pos2, StringComparison.Ordinal)
956 pos2 = strPost.IndexOf(_parseDM2, pos1, StringComparison.Ordinal)
957 orgData = strPost.Substring(pos1 + _parseDM11.Length, pos2 - pos1 - _parseDM11.Length).Trim()
959 'orgData = Regex.Replace(orgData, "<a href=""https://twitter\.com/" + post.Name + "/status/[0-9]+"">\.\.\.</a>$", "")
960 orgData = orgData.Replace("<3", "♡")
961 Catch ex As Exception
963 TraceOut("DM-Body:" + strPost)
964 Return "GetDirectMessage -> Err: Can't get body"
968 orgData = PreProcessUrl(orgData)
970 '短縮URL解決処理(orgData書き換え)
971 orgData = ShortUrlResolve(orgData)
974 post.OriginalData = AdjustHtml(orgData)
976 '単純テキストの取り出し(リンクタグ除去)
978 post.Data = GetPlainText(orgData)
979 Catch ex As Exception
981 TraceOut("DM-Link:" + strPost)
982 Return "GetDirectMessage -> Err: Can't parse links"
988 pos1 = strPost.IndexOf(_parseDate, pos2, StringComparison.Ordinal)
990 pos2 = strPost.IndexOf(_parseDateTo, pos1 + _parseDate.Length, StringComparison.Ordinal)
991 post.PDate = DateTime.ParseExact(strPost.Substring(pos1 + _parseDate.Length, pos2 - pos1 - _parseDate.Length), "ddd MMM dd HH':'mm':'ss zzzz yyyy", System.Globalization.DateTimeFormatInfo.InvariantInfo, Globalization.DateTimeStyles.None)
995 Catch ex As Exception
997 TraceOut("DM-Date:" + strPost)
998 Return "GetDirectMessage -> Err: Can't get date."
1001 '取得できなくなったため暫定対応(2/26)
1006 'pos1 = strPost.IndexOf(_parseStar, pos2)
1007 'pos2 = strPost.IndexOf("""", pos1 + _parseStar.Length)
1008 'If strPost.Substring(pos1 + _parseStar.Length, pos2 - pos1 - _parseStar.Length) = "empty" Then
1016 If _endingFlag Then Return ""
1019 If gType = WORKERTYPE.DirectMessegeRcv Then
1030 dlgt(arIdx) = New GetIconImageDelegate(AddressOf GetIconImage)
1031 ar(arIdx) = dlgt(arIdx).BeginInvoke(post, Nothing, Nothing)
1035 For i As Integer = 0 To arIdx
1037 dlgt(i).EndInvoke(ar(i))
1038 Catch ex As Exception
1039 ex.Data("IsTerminatePermission") = False
1047 GetTmSemaphore.Release()
1051 Public Function GetFavorites(ByVal page As Integer, _
1052 ByVal read As Boolean, _
1053 ByRef endPage As Integer, _
1054 ByVal gType As WORKERTYPE, _
1055 ByRef getDM As Boolean) As String
1057 GetTmSemaphore.WaitOne()
1059 If _endingFlag Then Return ""
1061 Dim retMsg As String = ""
1062 Dim resStatus As String = ""
1064 Static redirectToFav As String = ""
1065 Const FAV_PATH As String = "/favorites"
1067 If _signed = False Then
1069 If retMsg.Length > 0 Then
1074 If _endingFlag Then Return ""
1077 Dim pageQuery As String
1082 pageQuery = _pageQry + page.ToString
1085 retMsg = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + FAV_PATH + pageQuery, resStatus, MySocket.REQ_TYPE.ReqGetApp), String)
1087 If retMsg.Length = 0 Then
1092 ' tr 要素の class 属性を消去
1093 retMsg = Regex.Replace(retMsg, "(?<tagStart><li)(?<cls>\s+class=""[^""]+"")", "${tagStart}")
1096 ' Dim idx As Integer = retMsg.IndexOf(_removeClass, StringComparison.Ordinal)
1097 ' If idx = -1 Then Exit Do
1098 ' Dim idx2 As Integer = retMsg.IndexOf("""", idx + _removeClass.Length, StringComparison.Ordinal) - idx + 1 - 3
1099 ' If idx2 > 0 Then retMsg = retMsg.Remove(idx + 3, idx2)
1100 ' Catch ex As Exception
1102 ' TraceOut("GetFav-Remove: " + retMsg)
1103 ' Return "GetFav -> Err: Can't parse data."
1107 If _endingFlag Then Return ""
1110 Dim strSepTmp As String
1111 strSepTmp = _splitPostRecent
1116 pos1 = retMsg.IndexOf(strSepTmp, StringComparison.Ordinal)
1120 Return "GetTimeline -> Err: tweets count is 0."
1123 Dim strSep() As String = {strSepTmp}
1124 Dim posts() As String = retMsg.Split(strSep, StringSplitOptions.RemoveEmptyEntries)
1125 Dim intCnt As Integer = 0
1126 Dim listCnt As Integer = 0
1128 listCnt = TabInformations.GetInstance.ItemCount
1130 Dim dlgt(20) As GetIconImageDelegate
1131 Dim ar(20) As IAsyncResult
1132 Dim arIdx As Integer = -1
1136 For Each strPost As String In posts
1143 Dim post As New PostClass
1145 pos1 = strPost.IndexOf("</ol>")
1147 strPost = strPost.Substring(0, pos1)
1153 pos2 = strPost.IndexOf(_statusIdTo, 0, StringComparison.Ordinal)
1154 post.Id = Long.Parse(HttpUtility.HtmlDecode(strPost.Substring(0, pos2)))
1155 Catch ex As Exception
1157 TraceOut("TM-ID:" + strPost)
1158 Return "GetTimeline -> Err: Can't get ID."
1162 pos1 = strPost.IndexOf(_parseName, pos2, StringComparison.Ordinal)
1163 pos2 = strPost.IndexOf(_parseNameTo, pos1, StringComparison.Ordinal)
1164 post.Name = HttpUtility.HtmlDecode(strPost.Substring(pos1 + _parseName.Length, pos2 - pos1 - _parseName.Length))
1165 Catch ex As Exception
1167 TraceOut("TM-Name:" + strPost)
1168 Return "GetTimeline -> Err: Can't get Name."
1172 If strPost.IndexOf("twitter.com/images/heart.png", pos2, StringComparison.Ordinal) > -1 Then
1173 post.Nickname = post.Name
1176 pos1 = strPost.IndexOf(_parseNick, pos2, StringComparison.Ordinal)
1177 pos2 = strPost.IndexOf(_parseNickTo, pos1 + _parseNick.Length, StringComparison.Ordinal)
1178 post.Nickname = HttpUtility.HtmlDecode(strPost.Substring(pos1 + _parseNick.Length, pos2 - pos1 - _parseNick.Length))
1179 Catch ex As Exception
1181 TraceOut("TM-Nick:" + strPost)
1182 Return "GetTimeline -> Err: Can't get Nick."
1188 ' If TabInformations.GetInstance.ContainsKey(post.Id) Then Continue For
1191 Dim orgData As String = ""
1193 If strPost.IndexOf("<form action=""/status/update"" id=""heartForm", 0, StringComparison.Ordinal) > -1 Then
1195 pos1 = strPost.IndexOf("<strong>", 0, StringComparison.Ordinal)
1196 pos2 = strPost.IndexOf("</strong>", pos1, StringComparison.Ordinal)
1197 orgData = strPost.Substring(pos1 + 8, pos2 - pos1 - 8)
1198 Catch ex As Exception
1200 TraceOut("TM-VBody:" + strPost)
1201 Return "GetTimeline -> Err: Can't get Valentine body."
1208 pos1 = strPost.IndexOf(_parseImg, pos2, StringComparison.Ordinal)
1209 pos2 = strPost.IndexOf(_parseImgTo, pos1 + _parseImg.Length, StringComparison.Ordinal)
1210 post.ImageUrl = HttpUtility.HtmlDecode(strPost.Substring(pos1 + _parseImg.Length, pos2 - pos1 - _parseImg.Length))
1211 Catch ex As Exception
1213 TraceOut("TM-Img:" + strPost)
1214 Return "GetTimeline -> Err: Can't get ImagePath."
1218 If strPost.IndexOf(_isProtect, pos2, StringComparison.Ordinal) > -1 Then
1219 post.IsProtect = True
1223 If strPost.IndexOf("class=""big-retweet-icon""") > -1 Then
1224 rg = New Regex("class=""shared-content"".+<a href=""/(?<name>[a-zA-Z0-9_]+)""")
1225 m = rg.Match(strPost)
1227 post.RetweetedBy = m.Result("${name}")
1229 post.RetweetedBy = ""
1231 rg = New Regex("&in_reply_to_status_id=(?<id>[0-9]+)&in_reply_to=")
1232 m = rg.Match(strPost)
1234 post.RetweetedId = Long.Parse(m.Result("${id}"))
1236 post.RetweetedId = 0
1241 pos1 = strPost.IndexOf(_parseMsg1, pos2, StringComparison.Ordinal)
1245 If strPost.IndexOf("<div id=""doyouheart", pos2, StringComparison.Ordinal) > -1 Then
1247 orgData += " <3 you! Do you <3 "
1248 pos1 = strPost.IndexOf("<a href", pos2, StringComparison.Ordinal)
1249 pos2 = strPost.IndexOf("?", pos1, StringComparison.Ordinal)
1250 orgData += strPost.Substring(pos1, pos2 - pos1 + 1)
1252 pos1 = strPost.IndexOf(_parseProtectMsg1, pos2, StringComparison.Ordinal)
1255 orgData += " <3 's "
1256 pos1 = strPost.IndexOf("<a href", pos2, StringComparison.Ordinal)
1258 pos2 = strPost.IndexOf("!", pos1, StringComparison.Ordinal)
1259 orgData += strPost.Substring(pos1, pos2 - pos1 + 1)
1263 pos2 = strPost.IndexOf(_parseProtectMsg2, pos1, StringComparison.Ordinal)
1264 orgData = strPost.Substring(pos1 + _parseProtectMsg1.Length, pos2 - pos1 - _parseProtectMsg1.Length).Trim()
1267 Catch ex As Exception
1269 TraceOut("TM-VBody2:" + strPost)
1270 Return "GetTimeline -> Err: Can't get Valentine body2."
1275 pos2 = strPost.IndexOf(_parseMsg2, pos1, StringComparison.Ordinal)
1276 orgData = strPost.Substring(pos1 + _parseMsg1.Length, pos2 - pos1 - _parseMsg1.Length).Trim()
1277 Catch ex As Exception
1279 TraceOut("TM-Body:" + strPost)
1280 Return "GetTimeline -> Err: Can't get body."
1284 ' orgData = Regex.Replace(orgData, "<a href=""https://twitter\.com/" + post.Name + "/status/[0-9]+"">\.\.\.</a>$", "")
1287 orgData = orgData.Replace("<3", "♡")
1291 orgData = PreProcessUrl(orgData)
1293 '短縮URL解決処理(orgData書き換え)
1294 orgData = ShortUrlResolve(orgData)
1297 post.OriginalData = AdjustHtml(orgData)
1299 '単純テキストの取り出し(リンクタグ除去)
1301 post.Data = GetPlainText(orgData)
1302 Catch ex As Exception
1304 TraceOut("TM-Link:" + strPost)
1305 Return "GetTimeline -> Err: Can't parse links."
1309 Dim ImgTag As New Regex("<img src=.*?/>", RegexOptions.IgnoreCase)
1310 If ImgTag.IsMatch(post.Data) Then post.Data = ImgTag.Replace(post.Data, "<img>")
1315 pos1 = strPost.IndexOf(_parseDate, pos2, StringComparison.Ordinal)
1317 pos2 = strPost.IndexOf(_parseDateTo, pos1 + _parseDate.Length, StringComparison.Ordinal)
1318 post.PDate = DateTime.ParseExact(strPost.Substring(pos1 + _parseDate.Length, pos2 - pos1 - _parseDate.Length), "ddd MMM dd HH':'mm':'ss zzzz yyyy", System.Globalization.DateTimeFormatInfo.InvariantInfo, Globalization.DateTimeStyles.None)
1322 Catch ex As Exception
1324 TraceOut("TM-Date:" + strPost)
1325 Return "GetTimeline -> Err: Can't get date."
1328 '取得できなくなったため暫定対応(2/26)
1334 'ToDo: _parseSourceFromを正規表現へ。wedataからの取得へ変更(次版より)
1335 rg = New Regex("<span>.+>(?<name>.+)</a>.*</span>")
1336 m = rg.Match(strPost)
1338 post.Source = m.Result("${name}")
1343 ' pos1 = strPost.IndexOf(_parseSourceFrom, pos2, StringComparison.Ordinal)
1344 ' If pos1 = -1 Then pos1 = strPost.IndexOf(_parseSourceFrom2, pos2, StringComparison.Ordinal)
1346 ' pos1 = strPost.IndexOf(_parseSource2, pos1 + 19, StringComparison.Ordinal)
1347 ' pos2 = strPost.IndexOf(_parseSourceTo, pos1 + 2, StringComparison.Ordinal)
1348 ' post.Source = HttpUtility.HtmlDecode(strPost.Substring(pos1 + 2, pos2 - pos1 - 2))
1350 ' post.Source = "Web"
1352 'Catch ex As Exception
1354 ' TraceOut("TM-Src:" + strPost)
1355 ' Return "GetTimeline -> Err: Can't get src."
1358 'Get Reply(in_reply_to_user/id)
1359 'ToDo: _isReplyEngを正規表現へ。wedataからの取得へ変更(次版より)
1360 rg = New Regex("<a href=""https?:\/\/twitter\.com\/(?<name>[a-zA-Z0-9_]+)\/status\/(?<id>[0-9]+)"">(in reply to )*\k<name>")
1361 m = rg.Match(strPost)
1363 post.InReplyToUser = m.Result("${name}")
1364 post.InReplyToId = Long.Parse(m.Result("${id}"))
1365 post.IsReply = post.InReplyToUser.Equals(_uid, StringComparison.OrdinalIgnoreCase)
1369 rg = New Regex("@<a [^>]*href=""\/(?<1>[a-zA-Z0-9_]+)[^a-zA-Z0-9_]")
1370 m = rg.Match(orgData)
1372 post.ReplyToList.Add(m.Groups(1).Value.ToLower())
1375 If Not post.IsReply Then post.IsReply = post.ReplyToList.Contains(_uid)
1380 If _endingFlag Then Return ""
1382 post.IsMe = post.Name.Equals(_uid, StringComparison.OrdinalIgnoreCase)
1384 If follower.Count > 1 Then
1385 post.IsOwl = Not follower.Contains(post.Name.ToLower())
1393 dlgt(arIdx) = New GetIconImageDelegate(AddressOf GetIconImage)
1394 ar(arIdx) = dlgt(arIdx).BeginInvoke(post, Nothing, Nothing)
1400 For i As Integer = 0 To arIdx
1402 dlgt(i).EndInvoke(ar(i))
1403 Catch ex As Exception
1404 '最後までendinvoke回す(ゾンビ化回避)
1405 ex.Data("IsTerminatePermission") = False
1412 GetTmSemaphore.Release()
1416 Private Function PreProcessUrl(ByVal orgData As String) As String
1417 Dim posl1 As Integer
1418 Dim posl2 As Integer = 0
1419 Dim IDNConveter As IdnMapping = New IdnMapping()
1420 Dim href As String = "<a href="""
1423 If orgData.IndexOf(href, posl2, StringComparison.Ordinal) > -1 Then
1424 Dim urlStr As String = ""
1426 posl1 = orgData.IndexOf(href, posl2, StringComparison.Ordinal)
1427 posl1 += href.Length
1428 posl2 = orgData.IndexOf("""", posl1, StringComparison.Ordinal)
1429 urlStr = orgData.Substring(posl1, posl2 - posl1)
1431 If Not urlStr.StartsWith("http://") AndAlso Not urlStr.StartsWith("https://") AndAlso Not urlStr.StartsWith("ftp://") Then
1435 Dim replacedUrl As String = IDNDecode(urlStr)
1436 If replacedUrl Is Nothing Then Continue Do
1437 If replacedUrl = urlStr Then Continue Do
1439 orgData = orgData.Replace("<a href=""" + urlStr, "<a href=""" + replacedUrl)
1448 Private Function doShortUrlResolve(ByRef orgData As String) As Boolean
1449 Dim replaced As Boolean = False
1450 For Each _svc As String In _ShortUrlService
1451 Dim svc As String = _svc
1452 Dim posl1 As Integer
1453 Dim posl2 As Integer = 0
1456 If orgData.IndexOf("<a href=""" + svc, posl2, StringComparison.Ordinal) > -1 Then
1457 Dim urlStr As String = ""
1459 posl1 = orgData.IndexOf("<a href=""" + svc, posl2, StringComparison.Ordinal)
1460 posl1 = orgData.IndexOf(svc, posl1, StringComparison.Ordinal)
1461 posl2 = orgData.IndexOf("""", posl1, StringComparison.Ordinal)
1462 urlStr = New Uri(urlEncodeMultibyteChar(orgData.Substring(posl1, posl2 - posl1))).GetLeftPart(UriPartial.Path)
1463 Dim Response As String = ""
1464 Dim retUrlStr As String = ""
1465 Dim tmpurlStr As String = urlStr
1466 Dim SchemeAndDomain As Regex = New Regex("http://.+?/+?")
1467 Dim tmpSchemeAndDomain As String = ""
1468 For i As Integer = 0 To 4 'とりあえず5回試す
1469 retUrlStr = urlEncodeMultibyteChar(DirectCast(CreateSocket.GetWebResponse(tmpurlStr, Response, MySocket.REQ_TYPE.ReqGETForwardTo), String))
1470 If retUrlStr.Length > 0 Then
1471 ' 転送先URLが返された (まだ転送されるかもしれないので返値を引数にしてもう一度)
1472 ' 取得試行回数オーバーの場合は取得結果を転送先とする
1473 Dim scd As Match = SchemeAndDomain.Match(retUrlStr)
1474 If scd.Success AndAlso scd.Value <> svc Then
1477 tmpurlStr = retUrlStr
1481 If tmpurlStr <> urlStr Then
1482 '少なくとも一度以上転送されている (前回の結果を転送先とする)
1483 retUrlStr = tmpurlStr
1491 If retUrlStr.Length > 0 Then
1492 If Not retUrlStr.StartsWith("http") Then
1493 If retUrlStr.StartsWith("/") Then
1494 retUrlStr = urlEncodeMultibyteChar(svc + retUrlStr.Substring(1))
1495 ElseIf retUrlStr.StartsWith("data:") Then
1498 retUrlStr = urlEncodeMultibyteChar(retUrlStr.Insert(0, svc))
1501 retUrlStr = urlEncodeMultibyteChar(retUrlStr)
1503 orgData = orgData.Replace("<a href=""" + urlStr, "<a href=""" + retUrlStr)
1504 posl2 = 0 '置換した場合は頭から再探索(複数同時置換での例外対応)
1507 Catch ex As Exception
1509 'Return "GetTimeline -> Err: Can't get tinyurl."
1520 Private Sub doShortUrlResolve(ByRef orgData As String)
1521 'Dim replaced As Boolean = False
1523 'Dim posl1 As Integer
1524 'Dim posl2 As Integer = 0
1525 Static urlCache As New Specialized.StringDictionary()
1526 If urlCache.Count > 500 Then urlCache.Clear() '定期的にリセット
1528 Dim rx As New Regex("<a href=""(?<svc>http://.+?/)(?<path>[^""]+)""", RegexOptions.IgnoreCase)
1529 Dim m As MatchCollection = rx.Matches(orgData)
1530 Dim urlList As New List(Of String)
1531 For Each orgUrlMatch As Match In m
1532 Dim orgUrl As String = orgUrlMatch.Result("${svc}")
1533 Dim orgUrlPath As String = orgUrlMatch.Result("${path}")
1534 If Array.IndexOf(_ShortUrlService, orgUrl) > -1 AndAlso _
1535 Not urlList.Contains(orgUrl + orgUrlPath) Then
1536 urlList.Add(orgUrl + orgUrlPath)
1539 For Each orgUrl As String In urlList
1540 If urlCache.ContainsKey(orgUrl) Then
1542 orgData = orgData.Replace("<a href=""" + orgUrl + """", "<a href=""" + urlCache(orgUrl) + """")
1543 Catch ex As Exception
1548 'urlとして生成できない場合があるらしい
1549 Dim urlstr As String = New Uri(urlEncodeMultibyteChar(orgUrl)).GetLeftPart(UriPartial.Path)
1550 Dim Response As String = ""
1551 Dim retUrlStr As String = ""
1552 Dim tmpurlStr As String = urlstr
1553 retUrlStr = urlEncodeMultibyteChar(DirectCast(CreateSocket.GetWebResponse(tmpurlStr, Response, MySocket.REQ_TYPE.ReqGETForwardTo, timeOut:=5000), String))
1554 If retUrlStr.StartsWith("http") Then
1555 retUrlStr = retUrlStr.Replace("""", "%22") 'ダブルコーテーションがあるとURL終端と判断されるため、これだけ再エンコード
1556 orgData = orgData.Replace("<a href=""" + orgUrl + """", "<a href=""" + retUrlStr + """")
1557 urlCache.Add(orgUrl, retUrlStr)
1559 Catch ex As Exception
1565 'For Each ma As Match In m
1566 ' svc = ma.Result("${svc}")
1568 ' If orgData.IndexOf("<a href=""" + svc, posl2, StringComparison.Ordinal) > -1 Then
1569 ' Dim urlStr As String = ""
1571 ' posl1 = orgData.IndexOf("<a href=""" + svc, posl2, StringComparison.Ordinal)
1572 ' posl1 = orgData.IndexOf(svc, posl1, StringComparison.Ordinal)
1573 ' posl2 = orgData.IndexOf("""", posl1, StringComparison.Ordinal)
1574 ' urlStr = New Uri(urlEncodeMultibyteChar(orgData.Substring(posl1, posl2 - posl1))).GetLeftPart(UriPartial.Path)
1575 ' Dim Response As String = ""
1576 ' Dim retUrlStr As String = ""
1577 ' Dim tmpurlStr As String = urlStr
1578 ' Dim SchemeAndDomain As Regex = New Regex("http://.+?/+?")
1579 ' Dim tmpSchemeAndDomain As String = ""
1580 ' For i As Integer = 0 To 4 'とりあえず5回試す
1581 ' retUrlStr = urlEncodeMultibyteChar(DirectCast(CreateSocket.GetWebResponse(tmpurlStr, Response, MySocket.REQ_TYPE.ReqGETForwardTo, timeOut:=2000), String))
1582 ' If retUrlStr.Length > 0 Then
1583 ' ' 転送先URLが返された (まだ転送されるかもしれないので返値を引数にしてもう一度)
1584 ' ' 取得試行回数オーバーの場合は取得結果を転送先とする
1585 ' Dim scd As Match = SchemeAndDomain.Match(retUrlStr)
1586 ' If scd.Success AndAlso scd.Value <> svc Then
1589 ' tmpurlStr = retUrlStr
1593 ' If tmpurlStr <> urlStr Then
1594 ' '少なくとも一度以上転送されている (前回の結果を転送先とする)
1595 ' retUrlStr = tmpurlStr
1603 ' If retUrlStr.Length > 0 Then
1604 ' If Not retUrlStr.StartsWith("http") Then
1605 ' If retUrlStr.StartsWith("/") Then
1606 ' retUrlStr = urlEncodeMultibyteChar(svc + retUrlStr.Substring(1))
1607 ' ElseIf retUrlStr.StartsWith("data:") Then
1610 ' retUrlStr = urlEncodeMultibyteChar(retUrlStr.Insert(0, svc))
1613 ' retUrlStr = urlEncodeMultibyteChar(retUrlStr)
1615 ' orgData = orgData.Replace("<a href=""" + urlStr, "<a href=""" + retUrlStr)
1616 ' posl2 = 0 '置換した場合は頭から再探索(複数同時置換での例外対応)
1619 ' Catch ex As Exception
1621 ' 'Return "GetTimeline -> Err: Can't get tinyurl."
1631 Private Function ShortUrlResolve(ByVal orgData As String) As String
1632 If _tinyUrlResolve Then
1634 Static Dim sw As New Stopwatch()
1635 Static Dim c As Integer = 0
1638 doShortUrlResolve(orgData)
1641 'Loop While doShortUrlResolve(orgData)
1645 Console.WriteLine("ShortUrlResolve: " + c.ToString() + " / " + sw.Elapsed.ToString)
1651 Private Function GetPlainText(ByVal orgData As String) As String
1652 Return HttpUtility.HtmlDecode(Regex.Replace(orgData, "(?<tagStart><a [^>]+>)(?<text>[^<]+)(?<tagEnd></a>)", "${text}"))
1654 ''単純テキストの取り出し(リンクタグ除去)
1655 'If orgData.IndexOf(_parseLink1, StringComparison.Ordinal) = -1 Then
1656 ' retStr = HttpUtility.HtmlDecode(orgData)
1658 ' Dim posl1 As Integer
1659 ' Dim posl2 As Integer
1660 ' Dim posl3 As Integer = 0
1666 ' posl1 = orgData.IndexOf(_parseLink1, posl3, StringComparison.Ordinal)
1667 ' If posl1 = -1 Then Exit Do
1669 ' If (posl3 + _parseLink3.Length <> posl1) Or posl3 = 0 Then
1670 ' If posl3 <> 0 Then
1671 ' retStr += HttpUtility.HtmlDecode(orgData.Substring(posl3 + _parseLink3.Length, posl1 - posl3 - _parseLink3.Length))
1673 ' retStr += HttpUtility.HtmlDecode(orgData.Substring(0, posl1))
1676 ' posl2 = orgData.IndexOf(_parseLink2, posl1, StringComparison.Ordinal)
1677 ' posl3 = orgData.IndexOf(_parseLink3, posl2, StringComparison.Ordinal)
1678 ' retStr += HttpUtility.HtmlDecode(orgData.Substring(posl2 + _parseLink2.Length, posl3 - posl2 - _parseLink2.Length))
1680 ' retStr += HttpUtility.HtmlDecode(orgData.Substring(posl3 + _parseLink3.Length))
1686 ' htmlの簡易サニタイズ(詳細表示に不要なタグの除去)
1688 Private Function SanitizeHtml(ByVal orgdata As String) As String
1689 Dim retdata As String = orgdata
1691 ' <script ~ </script>
1692 Dim rx As Regex = New Regex( _
1693 "<(script|object|applet|image|frameset|fieldset|legend|style).*" & _
1694 "</(script|object|applet|image|frameset|fieldset|legend|style)>", RegexOptions.IgnoreCase)
1695 retdata = rx.Replace(retdata, "")
1698 rx = New Regex("<(frame|link|iframe|img)>", RegexOptions.IgnoreCase)
1699 retdata = rx.Replace(retdata, "")
1704 Private Function AdjustHtml(ByVal orgData As String) As String
1705 Dim retStr As String = orgData
1706 retStr = Regex.Replace(retStr, "<a [^>]*href=""/", "<a href=""" + _protocol + "twitter.com/")
1707 retStr = retStr.Replace("<a href=", "<a target=""_self"" href=")
1708 retStr = retStr.Replace(vbLf, "<br>")
1710 Return SanitizeHtml(retStr)
1713 Private Sub GetIconImage(ByVal post As PostClass)
1718 If Not _getIcon Then
1719 post.ImageIndex = -1
1720 TabInformations.GetInstance.AddPost(post)
1725 post.ImageIndex = _lIcon.Images.IndexOfKey(post.ImageUrl)
1728 If post.ImageIndex > -1 Then
1729 TabInformations.GetInstance.AddPost(post)
1733 Dim resStatus As String = ""
1734 img = DirectCast(CreateSocket.GetWebResponse(post.ImageUrl, resStatus, MySocket.REQ_TYPE.ReqGETBinary), System.Drawing.Image)
1735 If img Is Nothing Then
1736 post.ImageIndex = -1
1737 TabInformations.GetInstance.AddPost(post)
1741 If _endingFlag Then Exit Sub
1743 bmp2 = New Bitmap(_iconSz, _iconSz)
1744 Using g As Graphics = Graphics.FromImage(bmp2)
1745 g.InterpolationMode = Drawing2D.InterpolationMode.High
1746 g.DrawImage(img, 0, 0, _iconSz, _iconSz)
1751 post.ImageIndex = _lIcon.Images.IndexOfKey(post.ImageUrl)
1752 If post.ImageIndex = -1 Then
1754 Dim fd As New System.Drawing.Imaging.FrameDimension(img.FrameDimensionsList(0))
1755 Dim fd_count As Integer = img.GetFrameCount(fd)
1756 If fd_count > 1 Then
1758 img.SelectActiveFrame(fd, 1)
1759 _dIcon.Add(post.ImageUrl, img) '詳細表示用ディクショナリに追加
1760 Catch ex As Exception
1761 Dim bmp As New Bitmap(img)
1762 _dIcon.Add(post.ImageUrl, bmp) '詳細表示用ディクショナリに追加
1766 _dIcon.Add(post.ImageUrl, img) '詳細表示用ディクショナリに追加
1768 _lIcon.Images.Add(post.ImageUrl, bmp2)
1769 post.ImageIndex = _lIcon.Images.IndexOfKey(post.ImageUrl)
1770 Catch ex As InvalidOperationException
1771 'タイミングにより追加できない場合がある?(キー重複ではない)
1772 post.ImageIndex = -1
1776 TabInformations.GetInstance.AddPost(post)
1777 Catch ex As ArgumentException
1786 Private Function GetAuthKey(ByVal resMsg As String) As Integer
1790 pos1 = resMsg.IndexOf(_getAuthKey, StringComparison.Ordinal)
1795 pos2 = resMsg.IndexOf(_getAuthKeyTo, pos1 + _getAuthKey.Length, StringComparison.Ordinal)
1797 _authKey = resMsg.Substring(pos1 + _getAuthKey.Length, pos2 - pos1 - _getAuthKey.Length)
1805 Private Function GetAuthKeyDM(ByVal resMsg As String) As Integer
1809 pos1 = resMsg.IndexOf(_getAuthKey, StringComparison.Ordinal)
1814 pos2 = resMsg.IndexOf("""", pos1 + _getAuthKey.Length, StringComparison.Ordinal)
1815 _authKeyDM = resMsg.Substring(pos1 + _getAuthKey.Length, pos2 - pos1 - _getAuthKey.Length)
1820 Private Structure PostInfo
1821 Public CreatedAt As String
1823 Public Text As String
1824 Public UserId As String
1825 Public Sub New(ByVal Created As String, ByVal IdStr As String, ByVal txt As String, ByVal uid As String)
1831 Public Shadows Function Equals(ByVal dst As PostInfo) As Boolean
1832 If Me.CreatedAt = dst.CreatedAt AndAlso Me.Id = dst.Id AndAlso Me.Text = dst.Text AndAlso Me.UserId = dst.UserId Then
1840 Private Function IsPostRestricted(ByRef resMsg As String) As Boolean
1841 Static _prev As New PostInfo("", "", "", "")
1842 Dim _current As New PostInfo("", "", "", "")
1845 Dim xd As XmlDocument = New XmlDocument()
1848 _current.CreatedAt = xd.SelectSingleNode("/status/created_at/text()").Value
1849 _current.Id = xd.SelectSingleNode("/status/id/text()").Value
1850 _current.Text = xd.SelectSingleNode("/status/text/text()").Value
1851 _current.UserId = xd.SelectSingleNode("/status/user/id/text()").Value
1853 If _current.Equals(_prev) Then
1856 _prev.CreatedAt = _current.CreatedAt
1857 _prev.Id = _current.Id
1858 _prev.Text = _current.Text
1859 _prev.UserId = _current.UserId
1860 Catch ex As XmlException
1867 Public Function PostStatus(ByVal postStr As String, ByVal reply_to As Long) As String
1869 If _endingFlag Then Return ""
1871 If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
1873 postStr = postStr.Trim()
1876 Dim dataStr As String
1877 If reply_to = 0 Then
1878 dataStr = _statusHeader + HttpUtility.UrlEncode(postStr) + "&source=Tween"
1880 dataStr = _statusHeader + HttpUtility.UrlEncode(postStr) + "&source=Tween" + "&in_reply_to_status_id=" + HttpUtility.UrlEncode(reply_to.ToString)
1883 Dim resStatus As String = ""
1884 Dim resMsg As String = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + _statusUpdatePathAPI, resStatus, MySocket.REQ_TYPE.ReqPOSTAPI, dataStr), String)
1886 If resStatus.StartsWith("OK") Then
1887 Dim xd As XmlDocument = New XmlDocument()
1890 Dim xNode As XmlNode = Nothing
1891 xNode = xd.SelectSingleNode("/status/user/followers_count/text()")
1892 If xNode IsNot Nothing Then _followersCount = Integer.Parse(xNode.Value)
1893 xNode = xd.SelectSingleNode("/status/user/friends_count/text()")
1894 If xNode IsNot Nothing Then _friendsCount = Integer.Parse(xNode.Value)
1895 xNode = xd.SelectSingleNode("/status/user/statuses_count/text()")
1896 If xNode IsNot Nothing Then _statusesCount = Integer.Parse(xNode.Value)
1897 xNode = xd.SelectSingleNode("/status/user/location/text()")
1898 If xNode IsNot Nothing Then _location = xNode.Value
1899 xNode = xd.SelectSingleNode("/status/user/description/text()")
1900 If xNode IsNot Nothing Then _bio = xNode.Value
1901 Catch ex As Exception
1904 If Not postStr.StartsWith("D ", StringComparison.OrdinalIgnoreCase) AndAlso _
1905 Not postStr.StartsWith("DM ", StringComparison.OrdinalIgnoreCase) AndAlso _
1906 IsPostRestricted(resMsg) Then
1907 Return "OK:Delaying?"
1909 resStatus = Outputz.Post(CreateSocket, postStr.Length)
1910 If resStatus.Length > 0 Then
1911 Return "Outputz:" + resStatus
1915 ElseIf resStatus.StartsWith("Err: Forbidden") Then
1916 Return "Err:Forbidden(Update Limits?)"
1918 If resStatus.StartsWith("Err: Unauthorized") Then
1919 Twitter.AccountState = ACCOUNT_STATE.Invalid
1920 Return "Check your Username/Password."
1927 Public Function RemoveStatus(ByVal id As Long) As String
1928 If _endingFlag Then Return ""
1930 If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
1933 Dim resStatus As String = ""
1934 Dim resMsg As String = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + _StDestroyPath + id.ToString + ".xml", resStatus, MySocket.REQ_TYPE.ReqPOSTAPI), String)
1936 If resMsg.StartsWith("<?xml") = False OrElse resStatus.StartsWith("OK") = False Then
1937 If resStatus.StartsWith("Err: Unauthorized") Then
1938 Twitter.AccountState = ACCOUNT_STATE.Invalid
1939 Return "Check your Username/Password."
1948 Public Function PostRetweet(ByVal id As Long) As String
1949 If _endingFlag Then Return ""
1950 If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
1953 Dim target As Long = id
1954 If TabInformations.GetInstance.Item(id).RetweetedId > 0 Then
1955 target = TabInformations.GetInstance.Item(id).RetweetedId
1957 Dim resStatus As String = ""
1958 Dim resMsg As String = DirectCast(CreateSocket.GetWebResponse(_protocol + _apiHost + _hubServer + _postRetweetPath + target.ToString + ".xml", resStatus, MySocket.REQ_TYPE.ReqPOSTAPI), String)
1960 If resMsg.StartsWith("<?xml") = False OrElse resStatus.StartsWith("OK") = False Then
1961 If resStatus.StartsWith("Err: Unauthorized") Then
1962 Twitter.AccountState = ACCOUNT_STATE.Invalid
1963 Return "Check your Username/Password."
1972 Public Function RemoveDirectMessage(ByVal id As Long) As String
1973 If _endingFlag Then Return ""
1975 If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
1978 Dim dataStr As String = _authKeyHeader + HttpUtility.UrlEncode(_authKey)
1979 Dim resStatus As String = ""
1980 Dim resMsg As String = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + _DMDestroyPath + id.ToString + ".xml", resStatus, MySocket.REQ_TYPE.ReqPOSTAPI), String)
1982 If resMsg.StartsWith("<?xml") = False OrElse resStatus.StartsWith("OK") = False Then
1983 If resStatus.StartsWith("Err: Unauthorized") Then
1984 Twitter.AccountState = ACCOUNT_STATE.Invalid
1985 Return "Check your Username/Password."
1994 Public Function PostFollowCommand(ByVal id As String) As String
1996 If _endingFlag Then Return ""
1998 If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
2000 Const PATH_FOLLOW As String = "/friendships/create.xml?screen_name="
2002 Dim resStatus As String = ""
2003 Dim resMsg As String = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + PATH_FOLLOW + id, resStatus, MySocket.REQ_TYPE.ReqPOSTAPI), String)
2005 If Not resStatus.StartsWith("OK") Then
2006 If resStatus.StartsWith("Err: Unauthorized") Then
2007 Twitter.AccountState = ACCOUNT_STATE.Invalid
2008 Return "Check your Username/Password."
2017 Public Function PostRemoveCommand(ByVal id As String) As String
2019 If _endingFlag Then Return ""
2021 If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
2023 Const PATH_REMOVE As String = "/friendships/destroy.xml?screen_name="
2025 Dim resStatus As String = ""
2026 Dim resMsg As String = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + PATH_REMOVE + id, resStatus, MySocket.REQ_TYPE.ReqPOSTAPI), String)
2028 If Not resStatus.StartsWith("OK") Then
2029 If resStatus.StartsWith("Err: Unauthorized") Then
2030 Twitter.AccountState = ACCOUNT_STATE.Invalid
2031 Return "Check your Username/Password."
2040 Public Function GetFriendshipInfo(ByVal id As String) As String
2042 If _endingFlag Then Return ""
2044 If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
2046 Const PATH_FRIENDSHIP As String = "/friendships/show.xml?source_screen_name="
2047 Const QUERY_TARGET As String = "&target_screen_name="
2049 Dim resStatus As String = ""
2050 Dim resMsg As String = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + PATH_FRIENDSHIP + _uid + QUERY_TARGET + id, resStatus, MySocket.REQ_TYPE.ReqGetAPI), String)
2052 If Not resStatus.StartsWith("OK") Then
2053 If resStatus.StartsWith("Err: Unauthorized") Then
2054 Twitter.AccountState = ACCOUNT_STATE.Invalid
2055 Return "Check your Username/Password."
2060 Dim xdoc As New XmlDocument
2061 Dim result As String = ""
2063 xdoc.LoadXml(resMsg)
2064 Dim isFollowing As Boolean = Boolean.Parse(xdoc.SelectSingleNode("/relationship/source/following").InnerText)
2065 Dim isFollowed As Boolean = Boolean.Parse(xdoc.SelectSingleNode("/relationship/source/followed_by").InnerText)
2067 result = "Following " + id + "." + System.Environment.NewLine
2069 result = "NOT follwing them." + System.Environment.NewLine
2072 result += "Followed by " + id + "."
2074 result += "NOT followed by " + id + "."
2076 result = "Ok. The results are below..." + System.Environment.NewLine + result
2077 Catch ex As Exception
2078 result = "Err: Invalid XML."
2084 ' Contributed by shuyoko <http://twitter.com/shuyoko> BEGIN:
2085 Public Function GetBlackFavId(ByVal id As Long, ByRef blackid As Long) As String
2086 Dim dataStr As String = _authKeyHeader + HttpUtility.UrlEncode(_authKey)
2087 Dim resStatus As String = ""
2088 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)
2090 If resStatus.StartsWith("OK") = False Then
2094 blackid = Long.Parse(resMsg)
2099 ' Contributed by shuyoko <http://twitter.com/shuyoko> END.
2101 Public Function PostFavAdd(ByVal id As Long) As String
2102 If _endingFlag Then Return ""
2105 If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
2108 'Dim dataStr As String = _authKeyHeader + HttpUtility.UrlEncode(_authKey)
2109 Dim resStatus As String = ""
2110 Dim resMsg As String = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + _postFavAddPath + id.ToString() + ".xml", resStatus, MySocket.REQ_TYPE.ReqPOSTAPI), String)
2112 If resStatus.StartsWith("OK") = False Then
2113 If resStatus.StartsWith("Err: Unauthorized") Then
2114 Twitter.AccountState = ACCOUNT_STATE.Invalid
2115 Return "Check your Username/Password."
2121 If _restrictFavCheck = False Then Return ""
2123 'http://twitter.com/statuses/show/id.xml APIを発行して本文を取得
2125 resMsg = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + _ShowStatus + id.ToString() + ".xml", resStatus, MySocket.REQ_TYPE.ReqGetAPI), String)
2128 Using rd As Xml.XmlTextReader = New Xml.XmlTextReader(New System.IO.StringReader(resMsg))
2130 While rd.EOF = False
2131 If rd.IsStartElement("favorited") Then
2132 If rd.ReadElementContentAsBoolean() = True Then
2133 Return "" '正常にふぁぼれている
2135 Return "NG(Restricted?)" '正常応答なのにふぁぼれてないので制限っぽい
2143 Catch ex As XmlException
2150 Public Function PostFavRemove(ByVal id As Long) As String
2151 If _endingFlag Then Return ""
2154 If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
2157 'Dim dataStr As String = _authKeyHeader + HttpUtility.UrlEncode(_authKey)
2158 Dim resStatus As String = ""
2159 Dim resMsg As String = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + _postFavRemovePath + id.ToString() + ".xml", resStatus, MySocket.REQ_TYPE.ReqPOSTAPI), String)
2161 If resStatus.StartsWith("OK") = False Then
2162 If resStatus.StartsWith("Err: Unauthorized") Then
2163 Twitter.AccountState = ACCOUNT_STATE.Invalid
2164 Return "Check your Username/Password."
2173 #Region "follower取得"
2174 'Delegate Function GetFollowersDelegate(ByVal Query As Integer) As String
2175 'Private semaphore As Threading.Semaphore = Nothing
2176 'Private threadNum As Integer = 0
2177 Private _threadErr As Boolean = False
2179 Private Function GetFollowersMethod() As String
2180 Dim resStatus As String = ""
2181 Dim resMsg As String = ""
2182 Dim lineCount As Integer = 0
2183 Dim page As Long = -1
2186 If _endingFlag Then Exit Do
2187 resMsg = DirectCast(CreateSocket.GetWebResponse("https://" + _hubServer + _GetFollowers + _cursorQry + page.ToString, resStatus, MySocket.REQ_TYPE.ReqGetAPI), String)
2188 If resStatus.StartsWith("OK") = False Then
2193 Using rd As Xml.XmlTextReader = New Xml.XmlTextReader(New System.IO.StringReader(resMsg))
2196 While rd.EOF = False
2197 If rd.IsStartElement("screen_name") Then
2198 Dim tmp As String = rd.ReadElementString("screen_name").ToLower()
2200 If Not tmpFollower.Contains(tmp) Then
2201 tmpFollower.Add(tmp)
2205 ElseIf rd.IsStartElement("next_cursor") Then
2206 page = Long.Parse(rd.ReadElementString("next_cursor"))
2207 If page = 0 Then Exit Do
2214 Catch ex As Exception
2216 TraceOut("NG(XmlException)")
2217 Return "NG(XmlException)"
2219 Loop While lineCount > 0
2224 'Private Sub GetFollowersCallback(ByVal ar As IAsyncResult)
2225 ' Dim dlgt As GetFollowersDelegate = DirectCast(ar.AsyncState, GetFollowersDelegate)
2228 ' Dim ret As String = dlgt.EndInvoke(ar)
2229 ' If Not ret.Equals("") AndAlso Not _threadErr Then
2233 ' Catch ex As Exception
2235 ' ex.Data("IsTerminatePermission") = False
2238 ' GetTmSemaphore.Release() ' セマフォから出る
2239 ' Interlocked.Decrement(threadNum) ' スレッド数カウンタを-1
2244 ' キャッシュの検証と読み込み -1を渡した場合は読み込みのみ行う(APIエラーでFollowersCountが取得できなかったとき)
2245 Private Function ValidateCache() As Integer
2249 Dim setting As SettingFollower = SettingFollower.Load()
2250 follower = setting.Follower
2251 If follower.Count = 0 OrElse Not follower(0).Equals(_uid.ToLower()) Then
2252 ' 別IDの場合はキャッシュ破棄して読み直し
2255 Catch ex As XmlException
2258 Catch ex As InvalidOperationException
2263 'If _FollowersCount = -1 Then Return tmpFollower.Count
2264 Return follower.Count
2266 'If (_FollowersCount + 1) = tmpFollower.Count Then
2269 'ElseIf (_FollowersCount + 1) < tmpFollower.Count Then
2270 ' '減っている場合はどこが抜けているのかわからないので全部破棄して読み直し
2271 ' tmpFollower.Clear()
2272 ' tmpFollower.Add(_uid.ToLower())
2273 ' Return _FollowersCount
2278 'Return _FollowersCount - tmpFollower.Count
2282 Private Sub UpdateCache()
2283 Dim setting As New SettingFollower(follower)
2287 Public Function GetFollowers(ByVal CacheInvalidate As Boolean) As String
2289 Dim sw As New System.Diagnostics.Stopwatch
2293 If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
2295 'Dim resStatus As String = ""
2296 'Dim resMsg As String = ""
2297 'Dim i As Integer = 0
2298 'Dim DelegateInstance As GetFollowersDelegate = New GetFollowersDelegate(AddressOf GetFollowersMethod)
2299 'Dim threadMax As Integer = 4 ' 最大スレッド数
2300 'Dim followersCount As Integer = 0
2302 'Interlocked.Exchange(threadNum, 0) ' スレッド数カウンタ初期化
2306 'follower.Add(_uid.ToLower())
2307 tmpFollower.Add(_uid.ToLower())
2309 'resMsg = DirectCast(CreateSocket.GetWebResponse("https://twitter.com/users/show/" + _uid + ".xml", resStatus, MySocket.REQ_TYPE.ReqGetAPI), String)
2310 'If resMsg = "" Then
2311 ' If resStatus.StartsWith("Err: BadRequest") Then
2312 ' Return "Maybe, the requests reached API limit."
2313 ' ElseIf resStatus.StartsWith("Err: Unauthorized") Then
2314 ' Twitter.AccountState = ACCOUNT_STATE.Invalid
2315 ' Return "Check your Username/Password."
2321 'Dim xd As XmlDocument = New XmlDocument()
2323 ' xd.LoadXml(resMsg)
2324 ' followersCount = Integer.Parse(xd.SelectSingleNode("/user/followers_count/text()").Value)
2325 'Catch ex As Exception
2326 ' 'If CacheInvalidate OrElse ValidateCache(-1) < 0 Then
2327 ' If ValidateCache(-1) < 0 Then
2328 ' ' FollowersカウントがAPIで取得できず、なおかつキャッシュから読めなかった
2331 ' follower.Add(_uid.ToLower())
2333 ' Return "Can't get followers_count and invalid cache."
2335 ' 'キャッシュを読み出せたのでキャッシュを使う
2337 ' follower = tmpFollower
2345 ''If CacheInvalidate Then
2346 'tmp = followersCount
2348 ''tmp = ValidateCache(followersCount)
2353 ' i = (tmp + 100) \ 100 ' Followersカウント取得しページ単位に切り上げる。1ページ余分に読む
2355 ' ' ' キャッシュの件数に変化がなかった
2358 ' ' Console.WriteLine(sw.ElapsedMilliseconds)
2360 ' ' SyncLock LockObj
2361 ' ' follower = tmpFollower
2364 ' Return "" 'ユーザー情報のフォロワー数が0
2368 ''semaphore = New System.Threading.Semaphore(threadMax, threadMax) 'スレッド最大数
2370 'For cnt As Integer = 0 To i
2371 ' If _endingFlag Then Exit For
2372 ' 'semaphore.WaitOne() 'セマフォ取得 threadMax以上ならここでブロックされる
2373 ' GetTmSemaphore.WaitOne()
2374 ' 'Interlocked.Increment(threadNum) 'スレッド数カウンタを+1
2375 ' 'DelegateInstance.BeginInvoke(cnt + 1, New System.AsyncCallback(AddressOf GetFollowersCallback), DelegateInstance)
2376 ' Dim ret As String = GetFollowersMethod(cnt + 1)
2377 ' 'Interlocked.Decrement(threadNum) 'スレッド数カウンタを-1
2378 ' GetTmSemaphore.Release()
2379 ' If _threadErr Then Exit For
2382 '''全てのスレッドの終了を待つ(スレッド数カウンタが0になるまで待機)
2385 ''Loop Until Interlocked.Add(threadNum, 0) = 0
2389 Dim ret As String = GetFollowersMethod()
2390 If _endingFlag Then Return ""
2393 If ValidateCache() > 0 Then
2395 For Each name As String In tmpFollower
2396 If Not follower.Contains(name) Then follower.Add(name)
2399 If Not _endingFlag AndAlso follower.Count > 1 Then UpdateCache()
2400 Return "Can't get followers. Use cache."
2402 ' エラーが発生しているならFollowersリストクリア
2405 follower.Add(_uid.ToLower())
2407 Return "Can't get followers."
2412 follower = tmpFollower
2414 If Not _endingFlag AndAlso follower.Count > 1 Then UpdateCache()
2418 'Console.WriteLine(sw.ElapsedMilliseconds)
2426 Public Sub RefreshOwl()
2427 TabInformations.GetInstance.RefreshOwl(follower)
2430 Public Property Username() As String
2434 Set(ByVal value As String)
2435 _uid = value.ToLower
2440 Public Property Password() As String
2444 Set(ByVal value As String)
2450 Private _accountState As ACCOUNT_STATE = ACCOUNT_STATE.Valid
2451 Public Property AccountState() As ACCOUNT_STATE
2453 Return _accountState
2455 Set(ByVal value As ACCOUNT_STATE)
2456 _accountState = value
2460 Public Property NextThreshold() As Integer
2462 Return _nextThreshold
2464 Set(ByVal value As Integer)
2465 _nextThreshold = value
2469 Public Property NextPages() As Integer
2473 Set(ByVal value As Integer)
2478 Public ReadOnly Property InfoTwitter() As String
2484 Public Property UseAPI() As Boolean
2488 Set(ByVal value As Boolean)
2493 Public Property HubServer() As String
2497 Set(ByVal value As String)
2502 Public Sub GetWedata()
2503 Dim resStatus As String = ""
2504 Dim resMsg As String = ""
2506 resMsg = DirectCast(CreateSocket.GetWebResponse(wedataUrl, resStatus, timeOut:=10 * 1000), String) 'タイムアウト時間を10秒に設定
2507 If resMsg.Length = 0 Then Exit Sub
2509 Dim rs As New System.IO.StringReader(resMsg)
2511 Dim mode As Integer = 0 '0:search name 1:search data 2:read data
2512 Dim name As String = ""
2516 While rs.Peek() > -1
2521 If ln.StartsWith(" ""name"": ") Then
2522 name = ln.Substring(13, ln.Length - 2 - 13)
2526 If ln = " ""data"": {" Then
2533 If ln.EndsWith(",") Then ln = ln.Substring(0, ln.Length - 1)
2535 Case "SplitPostReply"
2536 If ln.StartsWith(" ""tagfrom"": """) Then
2537 _splitPost = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2539 Case "SplitPostRecent"
2540 If ln.StartsWith(" ""tagfrom"": """) Then
2541 _splitPostRecent = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2544 If ln.StartsWith(" ""tagto"": """) Then
2545 _statusIdTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2548 If ln.StartsWith(" ""tagfrom"": """) Then
2549 _isProtect = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2552 If ln.StartsWith(" ""tagfrom"": """) Then
2553 _isReplyEng = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2555 If ln.StartsWith(" ""tagfrom2"": """) Then
2556 _isReplyJpn = ln.Substring(19, ln.Length - 1 - 19).Replace("\", "")
2558 If ln.StartsWith(" ""tagto"": """) Then
2559 _isReplyTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2562 ' If ln.StartsWith(" ""tagfrom"": """) Then
2563 ' _parseStar = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2565 ' If ln.StartsWith(" ""tagfrom2"": """) Then
2566 ' _parseStarEmpty = ln.Substring(19, ln.Length - 1 - 19).Replace("\", "")
2568 ' If ln.StartsWith(" ""tagto"": """) Then
2569 ' _parseStarTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2572 If ln.StartsWith(" ""tagfrom"": """) Then
2573 _followerList = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2575 If ln.StartsWith(" ""tagfrom2"": """) Then
2576 _followerMbr1 = ln.Substring(19, ln.Length - 1 - 19).Replace("\", "")
2578 If ln.StartsWith(" ""tagfrom3"": """) Then
2579 _followerMbr2 = ln.Substring(19, ln.Length - 1 - 19).Replace("\", "")
2581 If ln.StartsWith(" ""tagto"": """) Then
2582 _followerMbr3 = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2585 If ln.StartsWith(" ""tagfrom"": """) Then
2586 _splitDM = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2589 If ln.StartsWith(" ""tagfrom"": """) Then
2590 _parseDM1 = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2592 If ln.StartsWith(" ""tagfrom2"": """) Then
2593 _parseDM11 = ln.Substring(19, ln.Length - 1 - 19).Replace("\", "")
2595 If ln.StartsWith(" ""tagto"": """) Then
2596 _parseDM2 = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2599 If ln.StartsWith(" ""tagfrom"": """) Then
2600 _parseDate = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2602 If ln.StartsWith(" ""tagto"": """) Then
2603 _parseDateTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2606 If ln.StartsWith(" ""tagfrom"": """) Then
2607 _parseMsg1 = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2609 If ln.StartsWith(" ""tagto"": """) Then
2610 _parseMsg2 = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2613 If ln.StartsWith(" ""tagfrom"": """) Then
2614 _parseImg = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2616 If ln.StartsWith(" ""tagto"": """) Then
2617 _parseImgTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2620 If ln.StartsWith(" ""tagfrom"": """) Then
2621 _parseNick = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2623 If ln.StartsWith(" ""tagto"": """) Then
2624 _parseNickTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2627 If ln.StartsWith(" ""tagfrom"": """) Then
2628 _parseName = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2630 If ln.StartsWith(" ""tagto"": """) Then
2631 _parseNameTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2634 ' If ln.StartsWith(" ""tagfrom"": """) Then
2635 ' _getSiv = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2637 ' If ln.StartsWith(" ""tagto"": """) Then
2638 ' _getSivTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2641 If ln.StartsWith(" ""tagfrom"": """) Then
2642 _getAuthKey = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2644 If ln.StartsWith(" ""tagto"": """) Then
2645 _getAuthKeyTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2648 If ln.StartsWith(" ""tagfrom"": """) Then
2649 _getInfoTwitter = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2651 If ln.StartsWith(" ""tagto"": """) Then
2652 _getInfoTwitterTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2654 Case "GetProtectMsg"
2655 If ln.StartsWith(" ""tagfrom"": """) Then
2656 _parseProtectMsg1 = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2658 If ln.StartsWith(" ""tagto"": """) Then
2659 _parseProtectMsg2 = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2662 If ln.StartsWith(" ""tagfrom"": """) Then
2663 _parseDMcountFrom = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2665 If ln.StartsWith(" ""tagto"": """) Then
2666 _parseDMcountTo = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2669 If ln.StartsWith(" ""tagfrom"": """) Then
2670 _parseSourceFrom = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2672 If ln.StartsWith(" ""tagfrom2"": """) Then
2673 _parseSource2 = ln.Substring(19, ln.Length - 1 - 19).Replace("\", "")
2675 If ln.StartsWith(" ""tagto"": """) Then
2676 _parseSource2 = ln.Substring(16, ln.Length - 1 - 16).Replace("\", "")
2679 If ln.StartsWith(" ""tagfrom"": """) Then
2680 _removeClass = ln.Substring(18, ln.Length - 1 - 18).Replace("\", "")
2690 GenerateAnalyzeKey()
2694 Public WriteOnly Property GetIcon() As Boolean
2695 Set(ByVal value As Boolean)
2700 Public WriteOnly Property TinyUrlResolve() As Boolean
2701 Set(ByVal value As Boolean)
2702 _tinyUrlResolve = value
2706 Public WriteOnly Property SelectedProxyType() As ProxyType
2707 Set(ByVal value As ProxyType)
2712 Public WriteOnly Property ProxyAddress() As String
2713 Set(ByVal value As String)
2714 _proxyAddress = value
2718 Public WriteOnly Property ProxyPort() As Integer
2719 Set(ByVal value As Integer)
2724 Public WriteOnly Property ProxyUser() As String
2725 Set(ByVal value As String)
2730 Public WriteOnly Property ProxyPassword() As String
2731 Set(ByVal value As String)
2732 _proxyPassword = value
2736 Public WriteOnly Property RestrictFavCheck() As Boolean
2737 Set(ByVal value As Boolean)
2738 _restrictFavCheck = value
2742 Public WriteOnly Property IconSize() As Integer
2743 Set(ByVal value As Integer)
2748 Public Function MakeShortUrl(ByVal ConverterType As UrlConverter, ByVal SrcUrl As String) As String
2749 Dim ret As String = ""
2750 Dim resStatus As String = ""
2751 Dim src As String = urlEncodeMultibyteChar(SrcUrl)
2753 For Each svc As String In _ShortUrlService
2754 If SrcUrl.StartsWith(svc) Then
2755 Return "Can't convert"
2759 SrcUrl = HttpUtility.UrlEncode(SrcUrl)
2760 Select Case ConverterType
2761 Case UrlConverter.TinyUrl 'tinyurl
2762 If SrcUrl.StartsWith("http") Then
2763 If "http://tinyurl.com/xxxxxx".Length > src.Length AndAlso Not src.Contains("?") AndAlso Not src.Contains("#") Then
2764 ' 明らかに長くなると推測できる場合は圧縮しない
2769 ret = DirectCast(CreateSocket.GetWebResponse("http://tinyurl.com/api-create.php?url=" + SrcUrl, resStatus, MySocket.REQ_TYPE.ReqPOSTEncode), String)
2770 Catch ex As Exception
2771 Return "Can't convert"
2774 If Not ret.StartsWith("http://tinyurl.com/") Then
2775 Return "Can't convert"
2777 Case UrlConverter.Isgd
2778 If SrcUrl.StartsWith("http") Then
2779 If "http://is.gd/xxxx".Length > src.Length AndAlso Not src.Contains("?") AndAlso Not src.Contains("#") Then
2780 ' 明らかに長くなると推測できる場合は圧縮しない
2785 ret = DirectCast(CreateSocket.GetWebResponse("http://is.gd/api.php?longurl=" + SrcUrl, resStatus, MySocket.REQ_TYPE.ReqPOSTEncode), String)
2786 Catch ex As Exception
2787 Return "Can't convert"
2790 If Not ret.StartsWith("http://is.gd/") Then
2791 Return "Can't convert"
2793 Case UrlConverter.Twurl
2794 If SrcUrl.StartsWith("http") Then
2795 If "http://twurl.nl/xxxxxx".Length > src.Length AndAlso Not src.Contains("?") AndAlso Not src.Contains("#") Then
2796 ' 明らかに長くなると推測できる場合は圧縮しない
2801 ret = DirectCast(CreateSocket.GetWebResponse("http://tweetburner.com/links", resStatus, MySocket.REQ_TYPE.ReqPOSTEncode, "link[url]=" + SrcUrl), String)
2802 Catch ex As Exception
2803 Return "Can't convert"
2806 If Not ret.StartsWith("http://twurl.nl/") Then
2807 Return "Can't convert"
2809 Case UrlConverter.Unu
2810 If SrcUrl.StartsWith("http") Then
2811 If "http://u.nu/xxxx".Length > src.Length AndAlso Not src.Contains("?") AndAlso Not src.Contains("#") Then
2812 ' 明らかに長くなると推測できる場合は圧縮しない
2817 ret = DirectCast(CreateSocket.GetWebResponse("http://u.nu/unu-api-simple?url=" + SrcUrl, resStatus, MySocket.REQ_TYPE.ReqPOSTEncode), String)
2818 Catch ex As Exception
2819 Return "Can't convert"
2822 If Not ret.StartsWith("http://u.nu") Then
2823 Return "Can't convert"
2825 Case UrlConverter.Bitly, UrlConverter.Jmp
2826 Dim BitlyLogin As String = "tweenapi"
2827 Dim BitlyApiKey As String = "R_c5ee0e30bdfff88723c4457cc331886b"
2828 If _bitlyId <> "" Then
2829 BitlyLogin = _bitlyId
2830 BitlyApiKey = _bitlyKey
2832 Const BitlyApiVersion As String = "2.0.1"
2833 If SrcUrl.StartsWith("http") Then
2834 If "http://bit.ly/xxxx".Length > src.Length AndAlso Not src.Contains("?") AndAlso Not src.Contains("#") Then
2835 ' 明らかに長くなると推測できる場合は圧縮しない
2840 Dim req As String = "http://api.bit.ly/shorten?version=" + BitlyApiVersion + _
2841 "&login=" + BitlyLogin + _
2842 "&apiKey=" + BitlyApiKey + _
2843 "&longUrl=" + SrcUrl
2844 If BitlyLogin <> "tweenapi" Then req += "&history=1"
2845 ret = DirectCast(CreateSocket.GetWebResponse(req, resStatus, MySocket.REQ_TYPE.ReqPOSTEncode), String)
2846 Dim rx As Regex = New Regex("""shortUrl"": ""(?<ShortUrl>.*?)""")
2847 If rx.Match(ret).Success Then
2848 ret = rx.Match(ret).Groups("ShortUrl").Value
2850 Catch ex As Exception
2851 Return "Can't convert"
2854 If Not ret.StartsWith("http://bit.ly") Then
2855 Return "Can't convert"
2857 If ConverterType = UrlConverter.Jmp Then ret = ret.Replace("bit.ly", "j.mp")
2860 Dim ch As Char() = {ControlChars.Cr, ControlChars.Lf}
2861 ret = ret.TrimEnd(ch)
2862 If src.Length < ret.Length Then ret = src ' 圧縮の結果逆に長くなった場合は圧縮前のURLを返す
2867 Public Function GetVersionInfo() As String
2868 Dim resStatus As String = ""
2869 Dim ret As String = DirectCast(CreateSocket.GetWebResponse("http://tween.sourceforge.jp/version2.txt?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), resStatus), String)
2870 If ret.Length = 0 Then Throw New Exception("GetVersionInfo: " + resStatus)
2874 Public Function GetTweenBinary(ByVal strVer As String) As String
2875 Dim resStatus As String = ""
2876 Dim ret As String = ""
2877 ret = DirectCast(CreateSocket.GetWebResponse("http://tween.sourceforge.jp/Tween" + strVer + ".gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), resStatus, MySocket.REQ_TYPE.ReqGETFile), String)
2878 If ret = "" OrElse resStatus.StartsWith("OK") Then
2879 '取得OKなら、続いてresources.dllダウンロード
2880 ret = GetTweenResourcesDll(strVer)
2882 Return GetTweenDll(strVer)
2891 Public Function GetTweenUpBinary() As String
2892 Dim resStatus As String = ""
2893 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)
2894 If ret = "" OrElse resStatus.StartsWith("OK") Then
2901 Public Function GetTweenResourcesDll(ByVal strver As String) As String
2902 Dim resStatus As String = ""
2903 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)
2904 If ret = "" OrElse resStatus.StartsWith("OK") Then
2911 Public Function GetTweenDll(ByVal strVer As String) As String
2912 Dim resStatus As String = ""
2913 Dim ret As String = ""
2914 ret = DirectCast(CreateSocket.GetWebResponse("http://tween.sourceforge.jp/TweenDll" + strVer + ".gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), resStatus, MySocket.REQ_TYPE.ReqGETFileDll), String)
2915 If ret = "" OrElse resStatus.StartsWith("OK") Then
2924 Private Function CreateSocket() As MySocket
2925 Return New MySocket("UTF-8", _uid, _pwd, _proxyType, _proxyAddress, _proxyPort, _proxyUser, _proxyPassword, _defaultTimeOut)
2928 Public WriteOnly Property ListIcon() As ImageList
2929 Set(ByVal value As ImageList)
2934 Public WriteOnly Property DetailIcon() As Dictionary(Of String, Image)
2935 Set(ByVal value As Dictionary(Of String, Image))
2940 Public Property DefaultTimeOut() As Integer
2942 Return _defaultTimeOut
2944 Set(ByVal value As Integer)
2945 _defaultTimeOut = value
2949 Public WriteOnly Property CountApi() As Integer
2951 Set(ByVal value As Integer)
2956 Public WriteOnly Property UsePostMethod() As Boolean
2957 Set(ByVal value As Boolean)
2958 _usePostMethod = False
2960 'POSTメソッドが弾かれるためGETに固定(2009/4/9)
2962 _ApiMethod = MySocket.REQ_TYPE.ReqPOSTAPI
2964 _ApiMethod = MySocket.REQ_TYPE.ReqGetAPI
2967 _ApiMethod = MySocket.REQ_TYPE.ReqGetAPI
2972 Public Property ReadOwnPost() As Boolean
2976 Set(ByVal value As Boolean)
2977 _readOwnPost = value
2981 Public ReadOnly Property FollowersCount() As Integer
2983 Return _followersCount
2987 Public ReadOnly Property FriendsCount() As Integer
2989 Return _friendsCount
2993 Public ReadOnly Property StatusesCount() As Integer
2995 Return _statusesCount
2999 Public ReadOnly Property Location() As String
3005 Public ReadOnly Property Bio() As String
3011 Public WriteOnly Property UseSsl() As Boolean
3012 Set(ByVal value As Boolean)
3015 _protocol = "https://"
3017 _protocol = "http://"
3022 Public WriteOnly Property BitlyId() As String
3023 Set(ByVal value As String)
3028 Public WriteOnly Property BitlyKey() As String
3029 Set(ByVal value As String)
3034 Public Function GetTimelineApi(ByVal read As Boolean, _
3035 ByVal gType As WORKERTYPE) As String
3037 If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
3039 If _endingFlag Then Return ""
3041 Dim retMsg As String = ""
3042 Dim resStatus As String = ""
3043 Dim sck As MySocket = CreateSocket()
3044 'スレッド取得は行わず、countで調整
3045 Const COUNT_QUERY As String = "count="
3046 Const FRIEND_PATH As String = "/statuses/home_timeline.xml"
3047 Const REPLY_PATH As String = "/statuses/mentions.xml"
3049 If gType = WORKERTYPE.Timeline Then
3050 retMsg = DirectCast(sck.GetWebResponse(_protocol + _hubServer + FRIEND_PATH + "?" + COUNT_QUERY + _countApi.ToString(), resStatus, _ApiMethod), String)
3052 retMsg = DirectCast(sck.GetWebResponse(_protocol + _hubServer + REPLY_PATH + "?" + COUNT_QUERY + _countApi.ToString(), resStatus, _ApiMethod), String)
3056 If resStatus.StartsWith("Err: BadRequest") Then
3057 Return "Maybe, the requests reached API limit."
3058 ElseIf resStatus.StartsWith("Err: Unauthorized") Then
3059 Twitter.AccountState = ACCOUNT_STATE.Invalid
3060 Return "Check your Username/Password."
3066 If gType = WORKERTYPE.Timeline Then Debug.WriteLine(retMsg)
3068 Dim arIdx As Integer = -1
3069 Dim dlgt(_countApi) As GetIconImageDelegate 'countQueryに合わせる
3070 Dim ar(_countApi) As IAsyncResult 'countQueryに合わせる
3071 Dim xdoc As New XmlDocument
3073 xdoc.LoadXml(retMsg)
3074 Catch ex As Exception
3076 'MessageBox.Show("不正なXMLです。(TL-LoadXml)")
3077 Return "Invalid XML!"
3080 For Each xentryNode As XmlNode In xdoc.DocumentElement.SelectNodes("./status")
3081 Dim xentry As XmlElement = CType(xentryNode, XmlElement)
3082 Dim post As New PostClass
3084 post.Id = Long.Parse(xentry.Item("id").InnerText)
3087 If TabInformations.GetInstance.ContainsKey(post.Id) Then Continue For
3090 Dim xRnode As XmlNode = xentry.SelectSingleNode("./retweeted_status")
3091 If xRnode IsNot Nothing Then
3092 Dim xRentry As XmlElement = CType(xRnode, XmlElement)
3093 post.PDate = DateTime.ParseExact(xRentry.Item("created_at").InnerText, "ddd MMM dd HH:mm:ss zzzz yyyy", System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.None)
3095 post.RetweetedId = Long.Parse(xRentry.Item("id").InnerText)
3097 post.Data = xRentry.Item("text").InnerText
3098 'Source取得(htmlの場合は、中身を取り出し)
3099 post.Source = xRentry.Item("source").InnerText
3101 Long.TryParse(xRentry.Item("in_reply_to_status_id").InnerText, post.InReplyToId)
3102 post.InReplyToUser = xRentry.Item("in_reply_to_screen_name").InnerText
3103 post.IsFav = TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites).Contains(post.RetweetedId)
3106 Dim xRUentry As XmlElement = CType(xRentry.SelectSingleNode("./user"), XmlElement)
3107 post.Uid = Long.Parse(xRUentry.Item("id").InnerText)
3108 post.Name = xRUentry.Item("screen_name").InnerText
3109 post.Nickname = xRUentry.Item("name").InnerText
3110 post.ImageUrl = xRUentry.Item("profile_image_url").InnerText
3111 post.IsProtect = Boolean.Parse(xRUentry.Item("protected").InnerText)
3112 post.IsMe = post.Name.ToLower.Equals(_uid)
3115 Dim xUentry As XmlElement = CType(xentry.SelectSingleNode("./user"), XmlElement)
3116 post.RetweetedBy = xUentry.Item("screen_name").InnerText
3118 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)
3120 post.Data = xentry.Item("text").InnerText
3121 'Source取得(htmlの場合は、中身を取り出し)
3122 post.Source = xentry.Item("source").InnerText
3123 Long.TryParse(xentry.Item("in_reply_to_status_id").InnerText, post.InReplyToId)
3124 post.InReplyToUser = xentry.Item("in_reply_to_screen_name").InnerText
3125 'in_reply_to_user_idを使うか?
3126 post.IsFav = Boolean.Parse(xentry.Item("favorited").InnerText)
3129 Dim xUentry As XmlElement = CType(xentry.SelectSingleNode("./user"), XmlElement)
3130 post.Uid = Long.Parse(xUentry.Item("id").InnerText)
3131 post.Name = xUentry.Item("screen_name").InnerText
3132 post.Nickname = xUentry.Item("name").InnerText
3133 post.ImageUrl = xUentry.Item("profile_image_url").InnerText
3134 post.IsProtect = Boolean.Parse(xUentry.Item("protected").InnerText)
3135 post.IsMe = post.Name.ToLower.Equals(_uid)
3138 post.OriginalData = CreateHtmlAnchor(post.Data, post.ReplyToList)
3139 post.Data = HttpUtility.HtmlDecode(post.Data)
3140 post.Data = post.Data.Replace("<3", "♡")
3142 If post.Source.StartsWith("<") Then
3143 Dim rgS As New Regex(">(?<source>.+)<")
3144 Dim mS As Match = rgS.Match(post.Source)
3146 post.Source = mS.Result("${source}")
3151 If gType = WORKERTYPE.Timeline Then
3152 post.IsReply = post.ReplyToList.Contains(_uid)
3160 If followerId.Count > 0 Then post.IsOwl = Not followerId.Contains(post.Uid)
3162 If post.IsMe AndAlso Not read AndAlso _readOwnPost Then post.IsRead = True
3165 Catch ex As Exception
3167 'MessageBox.Show("不正なXMLです。(TL-Parse)")
3171 '非同期アイコン取得&StatusDictionaryに追加
3173 dlgt(arIdx) = New GetIconImageDelegate(AddressOf GetIconImage)
3174 ar(arIdx) = dlgt(arIdx).BeginInvoke(post, Nothing, Nothing)
3178 For i As Integer = 0 To arIdx
3180 dlgt(i).EndInvoke(ar(i))
3181 Catch ex As Exception
3182 '最後までendinvoke回す(ゾンビ化回避)
3183 ex.Data("IsTerminatePermission") = False
3188 If _ApiMethod = MySocket.REQ_TYPE.ReqGetAPI Then _remainCountApi = sck.RemainCountApi
3193 Public Function GetSearch(ByVal read As Boolean, _
3194 ByVal tabName As String) As String
3196 If _endingFlag Then Return ""
3198 Dim retMsg As String = ""
3199 Dim resStatus As String = ""
3200 Dim sck As MySocket = CreateSocket()
3201 Const SEARCH_HOST As String = "search."
3202 Const SEARCH_PATH As String = "/search.atom"
3204 Dim tb As TabClass = TabInformations.GetInstance.Tabs(tabName)
3205 If tb Is Nothing Then Return ""
3206 Dim query As String = tb.SearchQuery
3207 If query = "" Then Return ""
3209 retMsg = DirectCast(sck.GetWebResponse(_protocol + SEARCH_HOST + _hubServer + SEARCH_PATH + "?" + query, resStatus, MySocket.REQ_TYPE.ReqGetAPINoAuth, userAgent:="Tween"), String)
3212 If resStatus.StartsWith("Err: BadRequest") Then
3213 Return "Invalid search query."
3214 ElseIf resStatus.StartsWith("Err: 420") Then '暫定:2010/1/18よりAPI制限で420返るらしい
3215 Return "Maybe, the requests reached API limit."
3221 Dim arIdx As Integer = -1
3222 Dim dlgt(_countApi) As GetIconImageDelegate 'countQueryに合わせる
3223 Dim ar(_countApi) As IAsyncResult 'countQueryに合わせる
3224 Dim xdoc As New XmlDocument
3226 xdoc.LoadXml(retMsg)
3227 Catch ex As Exception
3229 Return "Invalid ATOM!"
3231 Dim nsmgr As New XmlNamespaceManager(xdoc.NameTable)
3232 nsmgr.AddNamespace("search", "http://www.w3.org/2005/Atom")
3233 For Each xentryNode As XmlNode In xdoc.DocumentElement.SelectNodes("/search:feed/search:entry", nsmgr)
3234 Dim xentry As XmlElement = CType(xentryNode, XmlElement)
3235 Dim post As New PostClass
3237 post.Id = Long.Parse(xentry.Item("id").InnerText.Split(":"c)(2))
3238 post.PDate = DateTime.Parse(xentry.Item("published").InnerText)
3240 post.Data = xentry.Item("title").InnerText
3241 'Source取得(htmlの場合は、中身を取り出し)
3242 post.Source = xentry.Item("twitter:source").InnerText
3243 post.InReplyToId = 0
3244 post.InReplyToUser = ""
3248 Dim xUentry As XmlElement = CType(xentry.SelectSingleNode("./search:author", nsmgr), XmlElement)
3250 post.Name = xUentry.Item("name").InnerText.Split(" "c)(0).Trim
3251 post.Nickname = xUentry.Item("name").InnerText.Substring(post.Name.Length).Trim
3252 If post.Nickname.Length > 2 Then
3253 post.Nickname = post.Nickname.Substring(1, post.Nickname.Length - 2)
3255 post.Nickname = post.Name
3257 post.ImageUrl = CType(xentry.SelectSingleNode("./search:link[@type='image/png']", nsmgr), XmlElement).GetAttribute("href")
3258 post.IsProtect = False
3259 post.IsMe = post.Name.ToLower.Equals(_uid)
3262 post.OriginalData = CreateHtmlAnchor(post.Data, post.ReplyToList)
3263 post.Data = HttpUtility.HtmlDecode(post.Data)
3265 If post.Source.StartsWith("<") Then
3266 Dim rgS As New Regex(">(?<source>.+)<")
3267 Dim mS As Match = rgS.Match(post.Source)
3269 post.Source = mS.Result("${source}")
3274 post.IsReply = post.ReplyToList.Contains(_uid)
3277 If post.IsMe AndAlso Not read AndAlso _readOwnPost Then post.IsRead = True
3279 post.SearchTabName = tabName
3280 Catch ex As Exception
3285 '非同期アイコン取得&StatusDictionaryに追加
3287 dlgt(arIdx) = New GetIconImageDelegate(AddressOf GetIconImage)
3288 ar(arIdx) = dlgt(arIdx).BeginInvoke(post, Nothing, Nothing)
3292 For i As Integer = 0 To arIdx
3294 dlgt(i).EndInvoke(ar(i))
3295 Catch ex As Exception
3296 '最後までendinvoke回す(ゾンビ化回避)
3297 ex.Data("IsTerminatePermission") = False
3305 Public Function GetDirectMessageApi(ByVal read As Boolean, _
3306 ByVal gType As WORKERTYPE) As String
3307 If _endingFlag Then Return ""
3309 If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
3311 Dim retMsg As String = ""
3312 Dim resStatus As String = ""
3313 Dim sck As MySocket = CreateSocket()
3314 'スレッド取得は行わず、countで調整
3315 Const GET_COUNT As Integer = 20
3316 Const RECEIVE_PATH As String = "/direct_messages.xml"
3317 Const SENT_PATH As String = "/direct_messages/sent.xml"
3319 If gType = WORKERTYPE.DirectMessegeRcv Then
3320 retMsg = DirectCast(sck.GetWebResponse(_protocol + _hubServer + RECEIVE_PATH, resStatus, _ApiMethod), String)
3322 retMsg = DirectCast(sck.GetWebResponse(_protocol + _hubServer + SENT_PATH, resStatus, _ApiMethod), String)
3326 If resStatus.StartsWith("Err: BadRequest") Then
3327 Return "Maybe, the requests reached API limit."
3328 ElseIf resStatus.StartsWith("Err: Unauthorized") Then
3329 Twitter.AccountState = ACCOUNT_STATE.Invalid
3330 Return "Check your Username/Password."
3336 Dim arIdx As Integer = -1
3337 Dim dlgt(GET_COUNT) As GetIconImageDelegate 'countQueryに合わせる
3338 Dim ar(GET_COUNT) As IAsyncResult 'countQueryに合わせる
3339 Dim xdoc As New XmlDocument
3341 xdoc.LoadXml(retMsg)
3342 Catch ex As Exception
3344 'MessageBox.Show("不正なXMLです。(DM-LoadXml)")
3345 Return "Invalid XML!"
3348 For Each xentryNode As XmlNode In xdoc.DocumentElement.SelectNodes("./direct_message")
3349 Dim xentry As XmlElement = CType(xentryNode, XmlElement)
3350 Dim post As New PostClass
3352 post.Id = Long.Parse(xentry.Item("id").InnerText)
3355 If TabInformations.GetInstance.ContainsKey(post.Id) Then Continue For
3359 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)
3361 post.Data = xentry.Item("text").InnerText
3363 post.OriginalData = CreateHtmlAnchor(post.Data, post.ReplyToList)
3364 post.Data = HttpUtility.HtmlDecode(post.Data)
3365 post.Data = post.Data.Replace("<3", "♡")
3368 If gType = WORKERTYPE.DirectMessegeRcv Then
3375 Dim xUentry As XmlElement
3376 If gType = WORKERTYPE.DirectMessegeRcv Then
3377 xUentry = CType(xentry.SelectSingleNode("./sender"), XmlElement)
3380 xUentry = CType(xentry.SelectSingleNode("./recipient"), XmlElement)
3383 post.Uid = Long.Parse(xUentry.Item("id").InnerText)
3384 post.Name = xUentry.Item("screen_name").InnerText
3385 post.Nickname = xUentry.Item("name").InnerText
3386 post.ImageUrl = xUentry.Item("profile_image_url").InnerText
3387 post.IsProtect = Boolean.Parse(xUentry.Item("protected").InnerText)
3388 Catch ex As Exception
3390 'MessageBox.Show("不正なXMLです。(DM-Parse)")
3395 post.IsReply = False
3398 '非同期アイコン取得&StatusDictionaryに追加
3400 dlgt(arIdx) = New GetIconImageDelegate(AddressOf GetIconImage)
3401 ar(arIdx) = dlgt(arIdx).BeginInvoke(post, Nothing, Nothing)
3405 For i As Integer = 0 To arIdx
3407 dlgt(i).EndInvoke(ar(i))
3408 Catch ex As Exception
3409 '最後までendinvoke回す(ゾンビ化回避)
3410 ex.Data("IsTerminatePermission") = False
3415 If _ApiMethod = MySocket.REQ_TYPE.ReqGetAPI Then _remainCountApi = sck.RemainCountApi
3420 Public Function GetFavoritesApi(ByVal read As Boolean, _
3421 ByVal gType As WORKERTYPE) As String
3423 If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
3425 If _endingFlag Then Return ""
3427 Dim retMsg As String = ""
3428 Dim resStatus As String = ""
3429 Dim sck As MySocket = CreateSocket()
3430 'スレッド取得は行わず、countで調整
3431 Const COUNT_QUERY As String = "count="
3432 Const FAV_PATH As String = "/favorites.xml"
3434 retMsg = DirectCast(sck.GetWebResponse(_protocol + _hubServer + FAV_PATH + "?" + COUNT_QUERY + _countApi.ToString(), resStatus, _ApiMethod), String)
3437 If resStatus.StartsWith("Err: BadRequest") Then
3438 Return "Maybe, the requests reached API limit."
3439 ElseIf resStatus.StartsWith("Err: Unauthorized") Then
3440 Twitter.AccountState = ACCOUNT_STATE.Invalid
3441 Return "Check your Username/Password."
3447 Dim arIdx As Integer = -1
3448 Dim dlgt(_countApi) As GetIconImageDelegate 'countQueryに合わせる
3449 Dim ar(_countApi) As IAsyncResult 'countQueryに合わせる
3450 Dim xdoc As New XmlDocument
3452 xdoc.LoadXml(retMsg)
3453 Catch ex As Exception
3455 'MessageBox.Show("不正なXMLです。(TL-LoadXml)")
3456 Return "Invalid XML!"
3459 For Each xentryNode As XmlNode In xdoc.DocumentElement.SelectNodes("./status")
3460 Dim xentry As XmlElement = CType(xentryNode, XmlElement)
3461 Dim post As New PostClass
3463 post.Id = Long.Parse(xentry.Item("id").InnerText)
3466 If TabInformations.GetInstance.ContainsKey(post.Id) Then Continue For
3469 Dim xRnode As XmlNode = xentry.SelectSingleNode("./retweeted_status")
3470 If xRnode IsNot Nothing Then
3471 Dim xRentry As XmlElement = CType(xRnode, XmlElement)
3472 post.PDate = DateTime.ParseExact(xRentry.Item("created_at").InnerText, "ddd MMM dd HH:mm:ss zzzz yyyy", System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.None)
3474 post.RetweetedId = Long.Parse(xRentry.Item("id").InnerText)
3476 post.Data = xRentry.Item("text").InnerText
3477 'Source取得(htmlの場合は、中身を取り出し)
3478 post.Source = xRentry.Item("source").InnerText
3480 Long.TryParse(xRentry.Item("in_reply_to_status_id").InnerText, post.InReplyToId)
3481 post.InReplyToUser = xRentry.Item("in_reply_to_screen_name").InnerText
3482 'in_reply_to_user_idを使うか?
3483 post.IsFav = Boolean.Parse(xRentry.Item("favorited").InnerText)
3486 Dim xRUentry As XmlElement = CType(xRentry.SelectSingleNode("./user"), XmlElement)
3487 post.Uid = Long.Parse(xRUentry.Item("id").InnerText)
3488 post.Name = xRUentry.Item("screen_name").InnerText
3489 post.Nickname = xRUentry.Item("name").InnerText
3490 post.ImageUrl = xRUentry.Item("profile_image_url").InnerText
3491 post.IsProtect = Boolean.Parse(xRUentry.Item("protected").InnerText)
3492 post.IsMe = post.Name.ToLower.Equals(_uid)
3495 Dim xUentry As XmlElement = CType(xentry.SelectSingleNode("./user"), XmlElement)
3496 post.RetweetedBy = xUentry.Item("screen_name").InnerText
3498 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)
3500 post.Data = xentry.Item("text").InnerText
3501 'Source取得(htmlの場合は、中身を取り出し)
3502 post.Source = xentry.Item("source").InnerText
3503 Long.TryParse(xentry.Item("in_reply_to_status_id").InnerText, post.InReplyToId)
3504 post.InReplyToUser = xentry.Item("in_reply_to_screen_name").InnerText
3505 'in_reply_to_user_idを使うか?
3506 post.IsFav = Boolean.Parse(xentry.Item("favorited").InnerText)
3509 Dim xUentry As XmlElement = CType(xentry.SelectSingleNode("./user"), XmlElement)
3510 post.Uid = Long.Parse(xUentry.Item("id").InnerText)
3511 post.Name = xUentry.Item("screen_name").InnerText
3512 post.Nickname = xUentry.Item("name").InnerText
3513 post.ImageUrl = xUentry.Item("profile_image_url").InnerText
3514 post.IsProtect = Boolean.Parse(xUentry.Item("protected").InnerText)
3515 post.IsMe = post.Name.ToLower.Equals(_uid)
3518 post.OriginalData = CreateHtmlAnchor(post.Data, post.ReplyToList)
3519 post.Data = HttpUtility.HtmlDecode(post.Data)
3520 post.Data = post.Data.Replace("<3", "♡")
3522 If post.Source.StartsWith("<") Then
3523 Dim rgS As New Regex(">(?<source>.+)<")
3524 Dim mS As Match = rgS.Match(post.Source)
3526 post.Source = mS.Result("${source}")
3531 post.IsReply = post.ReplyToList.Contains(_uid)
3536 If followerId.Count > 0 Then post.IsOwl = Not followerId.Contains(post.Uid)
3540 Catch ex As Exception
3542 'MessageBox.Show("不正なXMLです。(TL-Parse)")
3546 '非同期アイコン取得&StatusDictionaryに追加
3548 dlgt(arIdx) = New GetIconImageDelegate(AddressOf GetIconImage)
3549 ar(arIdx) = dlgt(arIdx).BeginInvoke(post, Nothing, Nothing)
3553 For i As Integer = 0 To arIdx
3555 dlgt(i).EndInvoke(ar(i))
3556 Catch ex As Exception
3557 '最後までendinvoke回す(ゾンビ化回避)
3558 ex.Data("IsTerminatePermission") = False
3563 If _ApiMethod = MySocket.REQ_TYPE.ReqGetAPI Then _remainCountApi = sck.RemainCountApi
3568 Public Function GetFollowersApi() As String
3569 If _endingFlag Then Return ""
3570 Dim page As Long = -1
3575 Dim ret As String = FollowerApi(page)
3576 If ret <> "" Then Return ret
3581 Private Function FollowerApi(ByRef page As Long) As String
3582 If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
3584 Dim retMsg As String = ""
3585 Dim resStatus As String = ""
3586 Dim curCount As Integer = followerId.Count
3588 Const FOLLOWER_PATH As String = "/followers/ids.xml"
3590 retMsg = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + FOLLOWER_PATH + _cursorQry + page.ToString(), resStatus, _ApiMethod), String)
3593 If resStatus.StartsWith("Err: Unauthorized") Then
3594 Twitter.AccountState = ACCOUNT_STATE.Invalid
3595 Return "Check your Username/Password."
3601 Dim xdoc As New XmlDocument
3603 xdoc.LoadXml(retMsg)
3604 Catch ex As Exception
3606 MessageBox.Show("The data was broken. Please retry later.(FollowerApi-LoadXml)")
3607 Return "Invalid XML!"
3611 For Each xentryNode As XmlNode In xdoc.DocumentElement.SelectNodes("/id_list/ids/id")
3612 followerId.Add(Long.Parse(xentryNode.InnerText))
3614 page = Long.Parse(xdoc.DocumentElement.SelectSingleNode("/id_list/next_cursor").InnerText)
3615 Catch ex As Exception
3617 MessageBox.Show("The data was broken. Please retry later.(FollowerApi-Parse)")
3618 Return "Invalid XML!"
3625 Private Function CreateHtmlAnchor(ByVal Text As String, ByVal AtList As List(Of String)) As String
3626 Dim retStr As String = HttpUtility.HtmlEncode(Text) '要検証(デコードされて取得されるので再エンコード)
3629 Dim rgUrl As Regex = New Regex("(?<![0-9A-Za-z])(?:https?|shttp|ftps?)://(?:(?:[-_.!~*'()a-zA-Z0-9;:&=+$,]|%[0-9A-Fa-f" + _
3630 "][0-9A-Fa-f])*@)?(?:(?:[a-zA-Z0-9](?:[-a-zA-Z0-9]*[a-zA-Z0-9])?\.)" + _
3631 "*[a-zA-Z](?:[-a-zA-Z0-9]*[a-zA-Z0-9])?\.?|[0-9]+\.[0-9]+\.[0-9]+\." + _
3632 "[0-9]+)(?::[0-9]*)?(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f]" + _
3633 "[0-9A-Fa-f])*(?:;(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-" + _
3634 "Fa-f])*)*(?:/(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f" + _
3635 "])*(?:;(?:[-_.!~*'()a-zA-Z0-9:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*)*)" + _
3636 "*)?(?:\?(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])" + _
3637 "*)?(?:#(?:[-_.!~*'()a-zA-Z0-9;/?:@&=+$,]|%[0-9A-Fa-f][0-9A-Fa-f])*)?")
3639 retStr = rgUrl.Replace(Text, "<a href=""$&"">$&</a>")
3641 'Dim rg As New Regex("(^|[ -/:-@[-^`{-~])@([a-zA-Z0-9_]{1,20}/[a-zA-Z0-9_\-]{1,24}[a-zA-Z0-9_])")
3642 Dim rg As New Regex("(^|[^a-zA-Z0-9_])[@@]([a-zA-Z0-9_]{1,20}/[a-zA-Z0-9_\-]{1,24}[a-zA-Z0-9_])")
3643 Dim m As Match = rg.Match(retStr)
3645 retStr = rg.Replace(retStr, "$1@<a href=""/$2"">$2</a>")
3647 'rg = New Regex("(^|[ -/:-@[-^`{-~])@([a-zA-Z0-9_]{1,20})")
3648 rg = New Regex("(^|[^a-zA-Z0-9_])[@@]([a-zA-Z0-9_]{1,20})")
3649 m = rg.Match(retStr)
3651 AtList.Add(m.Result("$2").ToLower)
3655 retStr = rg.Replace(retStr, "$1@<a href=""/$2"">$2</a>")
3658 'Dim rgh As New Regex("(^|[ .!,\-:;<>?])#([^] !""#$%&'()*+,.:;<=>?@\-[\^`{|}~\r\n]+)")
3659 Dim rgh As New Regex("(^|[^a-zA-Z0-9_])[##]([a-zA-Z0-9_]+)")
3660 Dim mh As Match = rgh.Match(retStr)
3661 If mh.Success AndAlso Not IsNumeric(mh.Result("$2")) Then
3662 retStr = rgh.Replace(retStr, "$1<a href=""" + _protocol + "twitter.com/search?q=%23$2"">#$2</a>")
3666 retStr = AdjustHtml(ShortUrlResolve(PreProcessUrl(retStr))) 'IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
3670 Public ReadOnly Property RemainCountApi() As Integer
3672 Return _remainCountApi
3676 Public Function GetMaxCountApi() As Integer
3677 Dim _maxcnt As Integer = 0
3678 Dim resMsg As String = ""
3679 Dim resStatus As String = ""
3680 resMsg = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + _rateLimitStatus, resStatus, MySocket.REQ_TYPE.ReqGetAPI), String)
3681 Dim xdoc As New XmlDocument
3683 xdoc.LoadXml(resMsg)
3684 _maxcnt = Integer.Parse(xdoc.SelectSingleNode("/hash/hourly-limit").InnerText)
3685 Catch ex As Exception
3691 Public Function GetRemainCountApi() As Integer
3692 Dim _remain As Integer = 0
3693 Dim resMsg As String = ""
3694 Dim resStatus As String = ""
3695 resMsg = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + _rateLimitStatus, resStatus, MySocket.REQ_TYPE.ReqGetAPI), String)
3696 Dim xdoc As New XmlDocument
3698 xdoc.LoadXml(resMsg)
3699 _remain = Integer.Parse(xdoc.SelectSingleNode("/hash/remaining-hits").InnerText)
3700 Catch ex As Exception
3706 Public Function GetResetTimeApi() As DateTime
3708 Dim resMsg As String = ""
3709 Dim resStatus As String = ""
3710 resMsg = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + _rateLimitStatus, resStatus, MySocket.REQ_TYPE.ReqGetAPI), String)
3711 Dim xdoc As New XmlDocument
3713 xdoc.LoadXml(resMsg)
3714 _tm = DateTime.Parse(xdoc.SelectSingleNode("/hash/reset-time").InnerText)
3715 Catch ex As Exception
3721 Public Function GetInfoApi(ByRef info As ApiInfo) As Boolean
3723 Dim resMsg As String = ""
3724 Dim resStatus As String = ""
3725 resMsg = DirectCast(CreateSocket.GetWebResponse(_protocol + _hubServer + _rateLimitStatus, resStatus, MySocket.REQ_TYPE.ReqGetAPI), String)
3726 Dim xdoc As New XmlDocument
3728 xdoc.LoadXml(resMsg)
3729 info.MaxCount = Integer.Parse(xdoc.SelectSingleNode("/hash/hourly-limit").InnerText)
3730 info.RemainCount = Integer.Parse(xdoc.SelectSingleNode("/hash/remaining-hits").InnerText)
3731 info.ResetTime = DateTime.Parse(xdoc.SelectSingleNode("/hash/reset-time").InnerText)
3732 info.ResetTimeInSeconds = Integer.Parse(xdoc.SelectSingleNode("/hash/reset-time-in-seconds").InnerText)
3733 Catch ex As Exception
3739 #Region "デバッグモード解析キー自動生成"
3741 Public Sub GenerateAnalyzeKey()
3742 '解析キー情報部分のソースをwedataから作成する
3743 '生成したソースはプロジェクトのディレクトリにコピーする
3744 Dim sw As New System.IO.StreamWriter(".\AnalyzeKey.vb", _
3746 System.Text.Encoding.UTF8)
3748 sw.WriteLine("Public Module AnalyzeKey")
3749 sw.WriteLine("' このファイルはデバッグビルドのTweenにより自動作成されました 作成日時 " + DateAndTime.Now.ToString())
3752 sw.WriteLine(" Public _splitPost As String = " + Chr(34) + _splitPost.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3753 sw.WriteLine(" Public _splitPostRecent As String = " + Chr(34) + _splitPostRecent.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3754 sw.WriteLine(" Public _statusIdTo As String = " + Chr(34) + _statusIdTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3755 sw.WriteLine(" Public _splitDM As String = " + Chr(34) + _splitDM.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3756 sw.WriteLine(" Public _parseName As String = " + Chr(34) + _parseName.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3757 sw.WriteLine(" Public _parseNameTo As String = " + Chr(34) + _parseNameTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3758 sw.WriteLine(" Public _parseNick As String = " + Chr(34) + _parseNick.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3759 sw.WriteLine(" Public _parseNickTo As String = " + Chr(34) + _parseNickTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3760 sw.WriteLine(" Public _parseImg As String = " + Chr(34) + _parseImg.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3761 sw.WriteLine(" Public _parseImgTo As String = " + Chr(34) + _parseImgTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3762 sw.WriteLine(" Public _parseMsg1 As String = " + Chr(34) + _parseMsg1.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3763 sw.WriteLine(" Public _parseMsg2 As String = " + Chr(34) + _parseMsg2.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3764 sw.WriteLine(" Public _parseDM1 As String = " + Chr(34) + _parseDM1.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3765 sw.WriteLine(" Public _parseDM11 As String = " + Chr(34) + _parseDM11.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3766 sw.WriteLine(" Public _parseDM2 As String = " + Chr(34) + _parseDM2.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3767 sw.WriteLine(" Public _parseDate As String = " + Chr(34) + _parseDate.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3768 sw.WriteLine(" Public _parseDateTo As String = " + Chr(34) + _parseDateTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3769 sw.WriteLine(" Public _getAuthKey As String = " + Chr(34) + _getAuthKey.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3770 sw.WriteLine(" Public _getAuthKeyTo As String = " + Chr(34) + _getAuthKeyTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3771 'sw.WriteLine(" Public _parseStar As String = " + Chr(34) + _parseStar.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3772 'sw.WriteLine(" Public _parseStarTo As String = " + Chr(34) + _parseStarTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3773 'sw.WriteLine(" Public _parseStarEmpty As String = " + Chr(34) + _parseStarEmpty.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3774 sw.WriteLine(" Public _followerList As String = " + Chr(34) + _followerList.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3775 sw.WriteLine(" Public _followerMbr1 As String = " + Chr(34) + _followerMbr1.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3776 sw.WriteLine(" Public _followerMbr2 As String = " + Chr(34) + _followerMbr2.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3777 sw.WriteLine(" Public _followerMbr3 As String = " + Chr(34) + _followerMbr3.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3778 sw.WriteLine(" Public _getInfoTwitter As String = " + Chr(34) + _getInfoTwitter.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3779 sw.WriteLine(" Public _getInfoTwitterTo As String = " + Chr(34) + _getInfoTwitterTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3780 sw.WriteLine(" Public _isProtect As String = " + Chr(34) + _isProtect.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3781 sw.WriteLine(" Public _isReplyEng As String = " + Chr(34) + _isReplyEng.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3782 sw.WriteLine(" Public _isReplyJpn As String = " + Chr(34) + _isReplyJpn.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3783 sw.WriteLine(" Public _isReplyTo As String = " + Chr(34) + _isReplyTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3784 sw.WriteLine(" Public _parseProtectMsg1 As String = " + Chr(34) + _parseProtectMsg1.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3785 sw.WriteLine(" Public _parseProtectMsg2 As String = " + Chr(34) + _parseProtectMsg2.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3786 sw.WriteLine(" Public _parseDMcountFrom As String = " + Chr(34) + _parseDMcountFrom.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3787 sw.WriteLine(" Public _parseDMcountTo As String = " + Chr(34) + _parseDMcountTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3788 sw.WriteLine(" Public _parseSourceFrom As String = " + Chr(34) + _parseSourceFrom.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3789 sw.WriteLine(" Public _parseSource2 As String = " + Chr(34) + _parseSource2.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3790 sw.WriteLine(" Public _parseSourceTo As String = " + Chr(34) + _parseSourceTo.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3791 sw.WriteLine(" Public _removeClass As String = " + Chr(34) + _removeClass.Replace(Chr(34), Chr(34) + Chr(34)) + Chr(34))
3792 sw.WriteLine("End Module")
3795 'MessageBox.Show("解析キー情報定義ファイル AnalyzeKey.vbを生成しました")