• R/O
  • HTTP
  • SSH
  • HTTPS

提交

標籤
無標籤

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

OpenTweenのfork


Commit MetaInfo

修訂4295ee3ce898978b8dc3f9e0e4f7694fcb876b09 (tree)
時間2012-02-22 19:49:30
作者Egtra <yusuke.ichinohe@gmai...>
CommiterKimura Youichi

Log Message

Port Tween/Twitter.vb to C#

Change Summary

差異

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