+++ /dev/null
-' Tween - Client of Twitter
-' Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
-' (c) 2008-2011 Moz (@syo68k)
-' (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
-' (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
-' (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
-' All rights reserved.
-'
-' This file is part of Tween.
-'
-' This program is free software; you can redistribute it and/or modify it
-' under the terms of the GNU General Public License as published by the Free
-' Software Foundation; either version 3 of the License, or (at your option)
-' any later version.
-'
-' This program is distributed in the hope that it will be useful, but
-' WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-' or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-' for more details.
-'
-' You should have received a copy of the GNU General Public License along
-' with this program. If not, see <http://www.gnu.org/licenses/>, or write to
-' the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
-' Boston, MA 02110-1301, USA.
-
-Imports System.Diagnostics
-Imports System.IO
-Imports System.Linq
-Imports System.Net
-Imports System.Reflection.MethodBase
-Imports System.Runtime.Serialization
-Imports System.Runtime.Serialization.Json
-Imports System.Text
-Imports System.Text.RegularExpressions
-Imports System.Threading
-Imports System.Web
-Imports System.Xml
-Imports System.Xml.Linq
-
-Public Class Twitter
- Implements IDisposable
-
- 'Hashtag用正規表現
- Private Const LATIN_ACCENTS As String = "\xc0-\xd6\xd8-\xf6\xf8-\xff"
- Private Const NON_LATIN_HASHTAG_CHARS As String = "\u0400-\u04ff\u0500-\u0527\u1100-\u11ff\u3130-\u3185\uA960-\uA97F\uAC00-\uD7AF\uD7B0-\uD7FF"
- '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"
- 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}"
- Private Const HASHTAG_BOUNDARY As String = "^|$|\s|「|」|。|\.|!"
- Private Const HASHTAG_ALPHA As String = "[a-z_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]"
- Private Const HASHTAG_ALPHANUMERIC As String = "[a-z0-9_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]"
- Private Const HASHTAG_TERMINATOR As String = "[^a-z0-9_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]"
- Public Const HASHTAG As String = "(" + HASHTAG_BOUNDARY + ")(#|#)(" + HASHTAG_ALPHANUMERIC + "*" + HASHTAG_ALPHA + HASHTAG_ALPHANUMERIC + "*)(?=" + HASHTAG_TERMINATOR + "|" + HASHTAG_BOUNDARY + ")"
- 'URL正規表現
- Private Const url_valid_domain As String = "(?<domain>(?:[^\p{P}\s][\.\-_](?=[^\p{P}\s])|[^\p{P}\s]){1,}\.[a-z]{2,}(?::[0-9]+)?)"
- Private Const url_valid_general_path_chars As String = "[a-z0-9!*';:=+$/%#\[\]\-_&,~]"
- Private Const url_balance_parens As String = "(?:\(" + url_valid_general_path_chars + "+\))"
- Private Const url_valid_url_path_ending_chars As String = "(?:[a-z0-9=_#/\-\+]+|" + url_balance_parens + ")"
- Private Const pth As String = "(?:" + url_balance_parens +
- "|@" + url_valid_general_path_chars + "+/" +
- "|[.,]?" + url_valid_general_path_chars + "+" +
- ")"
- Private Const pth2 As String = "(/(?:" +
- pth + "+" + url_valid_url_path_ending_chars + "|" +
- pth + "+" + url_valid_url_path_ending_chars + "?|" +
- url_valid_url_path_ending_chars +
- ")?)?"
- Private Const qry As String = "(?<query>\?[a-z0-9!*'();:&=+$/%#\[\]\-_.,~]*[a-z0-9_&=#])?"
- Public Const rgUrl As String = "(?<before>(?:[^\""':!=#]|^|\:/))" +
- "(?<url>(?<protocol>https?://)" +
- url_valid_domain +
- pth2 +
- qry +
- ")"
- Delegate Sub GetIconImageDelegate(ByVal post As PostClass)
- Private ReadOnly LockObj As New Object
- Private followerId As New List(Of Long)
- Private _GetFollowerResult As Boolean = False
- Private noRTId As New List(Of Long)
- Private _GetNoRetweetResult As Boolean = False
-
- Private _followersCount As Integer = 0
- Private _friendsCount As Integer = 0
- Private _statusesCount As Integer = 0
- Private _location As String = ""
- Private _bio As String = ""
- Private _protocol As String = "https://"
-
- 'プロパティからアクセスされる共通情報
- Private _uname As String
- Private _iconSz As Integer
- Private _getIcon As Boolean
- Private _dIcon As IDictionary(Of String, Image)
-
- Private _tinyUrlResolve As Boolean
- Private _restrictFavCheck As Boolean
-
- Private _hubServer As String
- Private _readOwnPost As Boolean
- Private _hashList As New List(Of String)
-
- '共通で使用する状態
- Private _remainCountApi As Integer = -1
-
- Private op As New Outputz
- 'max_idで古い発言を取得するために保持(lists分は個別タブで管理)
- Private minHomeTimeline As Long = Long.MaxValue
- Private minMentions As Long = Long.MaxValue
- Private minDirectmessage As Long = Long.MaxValue
- Private minDirectmessageSent As Long = Long.MaxValue
-
- 'Private favQueue As FavoriteQueue
-
- Private twCon As New HttpTwitter
-
- Public Event UserIdChanged()
-
- 'Private _deletemessages As New List(Of PostClass)
-
- Public Overloads Function Authenticate(ByVal username As String, ByVal password As String) As String
-
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- TwitterApiInfo.Initialize()
- Try
- res = twCon.AuthUserAndPass(username, password, content)
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- _uname = username.ToLower
- If AppendSettingDialog.Instance.UserstreamStartup Then Me.ReconnectUserStream()
- Return ""
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return My.Resources.Unauthorized + Environment.NewLine + content
- Else
- Return "Auth error:" + errMsg
- End If
- Case HttpStatusCode.Forbidden
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return "Err:Forbidden"
- Else
- Return "Err:" + errMsg
- End If
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- End Function
-
- Public Function StartAuthentication(ByRef pinPageUrl As String) As String
- 'OAuth PIN Flow
- Dim res As Boolean
- Dim content As String = ""
-
- TwitterApiInfo.Initialize()
- Try
- res = twCon.AuthGetRequestToken(pinPageUrl)
- Catch ex As Exception
- Return "Err:" + "Failed to access auth server."
- End Try
-
- Return ""
- End Function
-
- Public Overloads Function Authenticate(ByVal pinCode As String) As String
-
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- TwitterApiInfo.Initialize()
- Try
- res = twCon.AuthGetAccessToken(pinCode)
- Catch ex As Exception
- Return "Err:" + "Failed to access auth acc server."
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- _uname = Username.ToLower
- If AppendSettingDialog.Instance.UserstreamStartup Then Me.ReconnectUserStream()
- Return ""
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return "Check the PIN or retry." + Environment.NewLine + content
- Else
- Return "Auth error:" + errMsg
- End If
- Case HttpStatusCode.Forbidden
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return "Err:Forbidden"
- Else
- Return "Err:" + errMsg
- End If
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- End Function
-
- Public Sub ClearAuthInfo()
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- TwitterApiInfo.Initialize()
- twCon.ClearAuthInfo()
- End Sub
-
- Public Sub VerifyCredentials()
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- Try
- res = twCon.VerifyCredentials(content)
- Catch ex As Exception
- Exit Sub
- End Try
-
- If res = HttpStatusCode.OK Then
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Dim user As TwitterDataModel.User
- Try
- user = CreateDataFromJson(Of TwitterDataModel.User)(content)
- Catch ex As SerializationException
- Exit Sub
- End Try
- twCon.AuthenticatedUserId = user.Id
- End If
- End Sub
-
- Private Function GetErrorMessageJson(ByVal content As String) As String
- Try
- If Not String.IsNullOrEmpty(content) Then
- Using jsonReader As XmlDictionaryReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(content), XmlDictionaryReaderQuotas.Max)
- Dim xElm As XElement = XElement.Load(jsonReader)
- If xElm.Element("error") IsNot Nothing Then
- Return xElm.Element("error").Value
- Else
- Return ""
- End If
- End Using
- Else
- Return ""
- End If
- Catch ex As Exception
- Return ""
- End Try
- End Function
-
- Public Sub Initialize(ByVal token As String, ByVal tokenSecret As String, ByVal username As String, ByVal userId As Long)
- 'OAuth認証
- If String.IsNullOrEmpty(token) OrElse String.IsNullOrEmpty(tokenSecret) OrElse String.IsNullOrEmpty(username) Then
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- End If
- TwitterApiInfo.Initialize()
- twCon.Initialize(token, tokenSecret, username, userId)
- _uname = username.ToLower
- If AppendSettingDialog.Instance.UserstreamStartup Then Me.ReconnectUserStream()
- End Sub
-
- Public Function PreProcessUrl(ByVal orgData As String) As String
- Dim posl1 As Integer
- Dim posl2 As Integer = 0
- 'Dim IDNConveter As IdnMapping = New IdnMapping()
- Dim href As String = "<a href="""
-
- Do While True
- If orgData.IndexOf(href, posl2, StringComparison.Ordinal) > -1 Then
- Dim urlStr As String = ""
- ' IDN展開
- posl1 = orgData.IndexOf(href, posl2, StringComparison.Ordinal)
- posl1 += href.Length
- posl2 = orgData.IndexOf("""", posl1, StringComparison.Ordinal)
- urlStr = orgData.Substring(posl1, posl2 - posl1)
-
- If Not urlStr.StartsWith("http://") AndAlso Not urlStr.StartsWith("https://") AndAlso Not urlStr.StartsWith("ftp://") Then
- Continue Do
- End If
-
- Dim replacedUrl As String = IDNDecode(urlStr)
- If replacedUrl Is Nothing Then Continue Do
- If replacedUrl = urlStr Then Continue Do
-
- orgData = orgData.Replace("<a href=""" + urlStr, "<a href=""" + replacedUrl)
- posl2 = 0
- Else
- Exit Do
- End If
- Loop
- Return orgData
- End Function
-
- Private Function GetPlainText(ByVal orgData As String) As String
- Return HttpUtility.HtmlDecode(Regex.Replace(orgData, "(?<tagStart><a [^>]+>)(?<text>[^<]+)(?<tagEnd></a>)", "${text}"))
- End Function
-
- ' htmlの簡易サニタイズ(詳細表示に不要なタグの除去)
-
- Private Function SanitizeHtml(ByVal orgdata As String) As String
- Dim retdata As String = orgdata
-
- retdata = Regex.Replace(retdata, "<(script|object|applet|image|frameset|fieldset|legend|style).*" & _
- "</(script|object|applet|image|frameset|fieldset|legend|style)>", "", RegexOptions.IgnoreCase)
-
- retdata = Regex.Replace(retdata, "<(frame|link|iframe|img)>", "", RegexOptions.IgnoreCase)
-
- Return retdata
- End Function
-
- Private Function AdjustHtml(ByVal orgData As String) As String
- Dim retStr As String = orgData
- 'Dim m As Match = Regex.Match(retStr, "<a [^>]+>[#|#](?<1>[a-zA-Z0-9_]+)</a>")
- 'While m.Success
- ' SyncLock LockObj
- ' _hashList.Add("#" + m.Groups(1).Value)
- ' End SyncLock
- ' m = m.NextMatch
- 'End While
- retStr = Regex.Replace(retStr, "<a [^>]*href=""/", "<a href=""" + _protocol + "twitter.com/")
- retStr = retStr.Replace("<a href=", "<a target=""_self"" href=")
- retStr = retStr.Replace(vbLf, "<br>")
-
- '半角スペースを置換(Thanks @anis774)
- Dim ret As Boolean = False
- Do
- ret = EscapeSpace(retStr)
- Loop While Not ret
-
- Return SanitizeHtml(retStr)
- End Function
-
- Private Function EscapeSpace(ByRef html As String) As Boolean
- '半角スペースを置換(Thanks @anis774)
- Dim isTag As Boolean = False
- For i As Integer = 0 To html.Length - 1
- If html(i) = "<"c Then
- isTag = True
- End If
- If html(i) = ">"c Then
- isTag = False
- End If
-
- If (Not isTag) AndAlso (html(i) = " "c) Then
- html = html.Remove(i, 1)
- html = html.Insert(i, " ")
- Return False
- End If
- Next
- Return True
- End Function
-
- Private Structure PostInfo
- Public CreatedAt As String
- Public Id As String
- Public Text As String
- Public UserId As String
- Public Sub New(ByVal Created As String, ByVal IdStr As String, ByVal txt As String, ByVal uid As String)
- CreatedAt = Created
- Id = IdStr
- Text = txt
- UserId = uid
- End Sub
- Public Shadows Function Equals(ByVal dst As PostInfo) As Boolean
- If Me.CreatedAt = dst.CreatedAt AndAlso Me.Id = dst.Id AndAlso Me.Text = dst.Text AndAlso Me.UserId = dst.UserId Then
- Return True
- Else
- Return False
- End If
- End Function
- End Structure
-
- Private Function IsPostRestricted(ByVal status As TwitterDataModel.Status) As Boolean
- Static _prev As New PostInfo("", "", "", "")
- Dim _current As New PostInfo("", "", "", "")
-
- _current.CreatedAt = status.CreatedAt
- _current.Id = status.IdStr
- If status.Text Is Nothing Then
- _current.Text = ""
- Else
- _current.Text = status.Text
- End If
- _current.UserId = status.User.IdStr
-
- If _current.Equals(_prev) Then
- Return True
- End If
- _prev.CreatedAt = _current.CreatedAt
- _prev.Id = _current.Id
- _prev.Text = _current.Text
- _prev.UserId = _current.UserId
-
- Return False
- End Function
-
- Public Function PostStatus(ByVal postStr As String, ByVal reply_to As Long) As String
-
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- postStr = postStr.Trim()
-
- If Regex.Match(postStr, "^DM? +(?<id>[a-zA-Z0-9_]+) +(?<body>.+)", RegexOptions.IgnoreCase Or RegexOptions.Singleline).Success Then
- Return SendDirectMessage(postStr)
- End If
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Try
- res = twCon.UpdateStatus(postStr, reply_to, content)
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Dim status As TwitterDataModel.Status
- Try
- status = CreateDataFromJson(Of TwitterDataModel.Status)(content)
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
- _followersCount = status.User.FollowersCount
- _friendsCount = status.User.FriendsCount
- _statusesCount = status.User.StatusesCount
- _location = status.User.Location
- _bio = status.User.Description
-
- If IsPostRestricted(status) Then
- Return "OK:Delaying?"
- End If
- If op.Post(postStr.Length) Then
- Return ""
- Else
- Return "Outputz:Failed"
- End If
- Case HttpStatusCode.NotFound
- Return ""
- Case HttpStatusCode.Forbidden, HttpStatusCode.BadRequest
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return "Warn:" + res.ToString
- Else
- Return "Warn:" + errMsg
- End If
- Case HttpStatusCode.Conflict, _
- HttpStatusCode.ExpectationFailed, _
- HttpStatusCode.Gone, _
- HttpStatusCode.LengthRequired, _
- HttpStatusCode.MethodNotAllowed, _
- HttpStatusCode.NotAcceptable, _
- HttpStatusCode.NotFound, _
- HttpStatusCode.PaymentRequired, _
- HttpStatusCode.PreconditionFailed, _
- HttpStatusCode.RequestedRangeNotSatisfiable, _
- HttpStatusCode.RequestEntityTooLarge, _
- HttpStatusCode.RequestTimeout, _
- HttpStatusCode.RequestUriTooLong
- '仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
- Return "Warn:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return My.Resources.Unauthorized
- Else
- Return "Auth err:" + errMsg
- End If
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
- End Function
-
- Public Function PostStatusWithMedia(ByVal postStr As String, ByVal reply_to As Long, ByVal mediaFile As FileInfo) As String
-
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- postStr = postStr.Trim()
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Try
- res = twCon.UpdateStatusWithMedia(postStr, reply_to, mediaFile, content)
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Dim status As TwitterDataModel.Status
- Try
- status = CreateDataFromJson(Of TwitterDataModel.Status)(content)
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
- _followersCount = status.User.FollowersCount
- _friendsCount = status.User.FriendsCount
- _statusesCount = status.User.StatusesCount
- _location = status.User.Location
- _bio = status.User.Description
-
- If IsPostRestricted(status) Then
- Return "OK:Delaying?"
- End If
- If op.Post(postStr.Length) Then
- Return ""
- Else
- Return "Outputz:Failed"
- End If
- Case HttpStatusCode.NotFound
- Return ""
- Case HttpStatusCode.Forbidden, HttpStatusCode.BadRequest
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return "Warn:" + res.ToString
- Else
- Return "Warn:" + errMsg
- End If
- Case HttpStatusCode.Conflict, _
- HttpStatusCode.ExpectationFailed, _
- HttpStatusCode.Gone, _
- HttpStatusCode.LengthRequired, _
- HttpStatusCode.MethodNotAllowed, _
- HttpStatusCode.NotAcceptable, _
- HttpStatusCode.NotFound, _
- HttpStatusCode.PaymentRequired, _
- HttpStatusCode.PreconditionFailed, _
- HttpStatusCode.RequestedRangeNotSatisfiable, _
- HttpStatusCode.RequestEntityTooLarge, _
- HttpStatusCode.RequestTimeout, _
- HttpStatusCode.RequestUriTooLong
- '仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
- Return "Warn:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return My.Resources.Unauthorized
- Else
- Return "Auth err:" + errMsg
- End If
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
- End Function
-
- Public Function SendDirectMessage(ByVal postStr As String) As String
-
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
- If TwitterApiInfo.AccessLevel <> ApiAccessLevel.None Then
- If Not TwitterApiInfo.IsDirectMessagePermission Then Return "Auth Err:try to re-authorization."
- End If
-
- postStr = postStr.Trim()
-
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- Dim mc As Match = Regex.Match(postStr, "^DM? +(?<id>[a-zA-Z0-9_]+) +(?<body>.+)", RegexOptions.IgnoreCase Or RegexOptions.Singleline)
-
- Try
- res = twCon.SendDirectMessage(mc.Groups("body").Value, mc.Groups("id").Value, content)
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Dim status As TwitterDataModel.Directmessage
- Try
- status = CreateDataFromJson(Of TwitterDataModel.Directmessage)(content)
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
- _followersCount = status.Sender.FollowersCount
- _friendsCount = status.Sender.FriendsCount
- _statusesCount = status.Sender.StatusesCount
- _location = status.Sender.Location
- _bio = status.Sender.Description
-
- If op.Post(postStr.Length) Then
- Return ""
- Else
- Return "Outputz:Failed"
- End If
- Case HttpStatusCode.Forbidden, HttpStatusCode.BadRequest
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return "Warn:" + res.ToString
- Else
- Return "Warn:" + errMsg
- End If
- Case HttpStatusCode.Conflict, _
- HttpStatusCode.ExpectationFailed, _
- HttpStatusCode.Gone, _
- HttpStatusCode.LengthRequired, _
- HttpStatusCode.MethodNotAllowed, _
- HttpStatusCode.NotAcceptable, _
- HttpStatusCode.NotFound, _
- HttpStatusCode.PaymentRequired, _
- HttpStatusCode.PreconditionFailed, _
- HttpStatusCode.RequestedRangeNotSatisfiable, _
- HttpStatusCode.RequestEntityTooLarge, _
- HttpStatusCode.RequestTimeout, _
- HttpStatusCode.RequestUriTooLong
- '仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
- Return "Warn:" + res.ToString
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return My.Resources.Unauthorized
- Else
- Return "Auth err:" + errMsg
- End If
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
- End Function
-
- Public Function RemoveStatus(ByVal id As Long) As String
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
-
- Try
- res = twCon.DestroyStatus(id)
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Return ""
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.NotFound
- Return ""
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- End Function
-
- Public Function PostRetweet(ByVal id As Long, ByVal read As Boolean) As String
- If _endingFlag Then Return ""
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- 'データ部分の生成
- Dim target As Long = id
- Dim post As PostClass = TabInformations.GetInstance.Item(id)
- If post Is Nothing Then
- Return "Err:Target isn't found."
- End If
- If TabInformations.GetInstance.Item(id).RetweetedId > 0 Then
- target = TabInformations.GetInstance.Item(id).RetweetedId '再RTの場合は元発言をRT
- End If
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Try
- res = twCon.RetweetStatus(target, content)
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
-
- Select Case res
- Case HttpStatusCode.Unauthorized
- 'Blockユーザーの発言をRTすると認証エラー返る
- 'Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized + " or blocked user."
- Case Is <> HttpStatusCode.OK
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Twitter.AccountState = ACCOUNT_STATE.Valid
-
- Dim status As TwitterDataModel.Status
- Try
- status = CreateDataFromJson(Of TwitterDataModel.Status)(content)
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
-
- 'ReTweetしたものをTLに追加
- post = CreatePostsFromStatusData(status)
- If post Is Nothing Then Return "Invalid Json!"
-
- '二重取得回避
- SyncLock LockObj
- If TabInformations.GetInstance.ContainsKey(post.StatusId) Then Return ""
- End SyncLock
- 'Retweet判定
- If post.RetweetedId = 0 Then Return "Invalid Json!"
- 'ユーザー情報
- post.IsMe = True
-
- post.IsRead = read
- post.IsOwl = False
- If _readOwnPost Then post.IsRead = True
- post.IsDm = False
-
- TabInformations.GetInstance.AddPost(post)
-
- Return ""
- End Function
-
- Public Function RemoveDirectMessage(ByVal id As Long, ByVal post As PostClass) As String
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
- If TwitterApiInfo.AccessLevel <> ApiAccessLevel.None Then
- If Not TwitterApiInfo.IsDirectMessagePermission Then Return "Auth Err:try to re-authorization."
- End If
-
- Dim res As HttpStatusCode
-
- 'If post.IsMe Then
- ' _deletemessages.Add(post)
- 'End If
- Try
- res = twCon.DestroyDirectMessage(id)
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Return ""
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.NotFound
- Return ""
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
- End Function
-
- Public Function PostFollowCommand(ByVal screenName As String) As String
-
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- Try
- res = twCon.CreateFriendships(screenName, content)
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Return ""
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.Forbidden
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
- Else
- Return "Err:" + errMsg
- End If
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
- End Function
-
- Public Function PostRemoveCommand(ByVal screenName As String) As String
-
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- Try
- res = twCon.DestroyFriendships(screenName, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Return ""
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.Forbidden
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
- Else
- Return "Err:" + errMsg
- End If
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
- End Function
-
- Public Function PostCreateBlock(ByVal screenName As String) As String
-
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- Try
- res = twCon.CreateBlock(screenName, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Return ""
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.Forbidden
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
- Else
- Return "Err:" + errMsg
- End If
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
- End Function
-
- Public Function PostDestroyBlock(ByVal screenName As String) As String
-
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- Try
- res = twCon.DestroyBlock(screenName, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Return ""
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.Forbidden
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
- Else
- Return "Err:" + errMsg
- End If
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
- End Function
-
- Public Function PostReportSpam(ByVal screenName As String) As String
-
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- Try
- res = twCon.ReportSpam(screenName, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Return ""
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.Forbidden
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
- Else
- Return "Err:" + errMsg
- End If
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
- End Function
-
- Public Function GetFriendshipInfo(ByVal screenName As String, ByRef isFollowing As Boolean, ByRef isFollowed As Boolean) As String
-
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Try
- res = twCon.ShowFriendships(_uname, screenName, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Try
- Dim relation = CreateDataFromJson(Of TwitterDataModel.Relationship)(content)
- isFollowing = relation.relationship.Source.Following
- isFollowed = relation.relationship.Source.FollowedBy
- Return ""
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
- End Function
-
- Public Function GetUserInfo(ByVal screenName As String, ByRef user As TwitterDataModel.User) As String
-
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- user = Nothing
- Try
- res = twCon.ShowUserInfo(screenName, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Try
- user = CreateDataFromJson(Of TwitterDataModel.User)(content)
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
- Return ""
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return My.Resources.Unauthorized
- Else
- Return "Auth err:" + errMsg
- End If
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
- End Function
-
- Public Function GetStatus_Retweeted_Count(ByVal StatusId As Long, ByRef retweeted_count As Integer) As String
-
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Dim xmlBuf As String = ""
-
- retweeted_count = 0
-
- ' 注:dev.twitter.comに記述されているcountパラメータは間違い。100が正しい
- For i As Integer = 1 To 100
-
- Try
- res = twCon.Statusid_retweeted_by_ids(StatusId, 100, i, content)
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Try
- Dim ids As Int64() = CreateDataFromJson(Of Int64())(content)
- retweeted_count += ids.Length
- If ids.Length < 100 Then Exit For
- Catch ex As SerializationException
- retweeted_count = -1
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- retweeted_count = -1
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
- Case HttpStatusCode.BadRequest
- retweeted_count = -1
- Return "Err:API Limits?"
- Case HttpStatusCode.Unauthorized
- retweeted_count = -1
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case Else
- retweeted_count = -1
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
- Next
- Return ""
- End Function
-
- Public Function PostFavAdd(ByVal id As Long) As String
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- 'If Me.favQueue Is Nothing Then Me.favQueue = New FavoriteQueue(Me)
-
- 'If Me.favQueue.Contains(id) Then Me.favQueue.Remove(id)
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Try
- res = twCon.CreateFavorites(id, content)
- Catch ex As Exception
- 'Me.favQueue.Add(id)
- 'Return "Err:->FavoriteQueue:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- 'Me.favQueue.FavoriteCacheStart()
- If Not _restrictFavCheck Then Return ""
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.Forbidden
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
- Else
- 'If errMsg.Contains("It's great that you like so many updates") Then
- ' 'Me.favQueue.Add(id)
- ' Return "Err:->FavoriteQueue:" + errMsg
- 'End If
- Return "Err:" + errMsg
- End If
- 'Case HttpStatusCode.BadGateway, HttpStatusCode.ServiceUnavailable, HttpStatusCode.InternalServerError, HttpStatusCode.RequestTimeout
- ' 'Me.favQueue.Add(id)
- ' Return "Err:->FavoriteQueue:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- 'http://twitter.com/statuses/show/id.xml APIを発行して本文を取得
-
- 'Dim content As String = ""
- content = ""
- Try
- res = twCon.ShowStatuses(id, content)
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Dim status As TwitterDataModel.Status
- Try
- status = CreateDataFromJson(Of TwitterDataModel.Status)(content)
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
- If status.Favorited Then
- Return ""
- Else
- Return "NG(Restricted?)"
- End If
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- End Function
-
- Public Function PostFavRemove(ByVal id As Long) As String
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- 'If Me.favQueue Is Nothing Then Me.favQueue = New FavoriteQueue(Me)
-
- 'If Me.favQueue.Contains(id) Then
- ' Me.favQueue.Remove(id)
- ' Return ""
- 'End If
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Try
- res = twCon.DestroyFavorites(id, content)
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Return ""
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.Forbidden
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
- Else
- Return "Err:" + errMsg
- End If
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
- End Function
-
- Public Function PostUpdateProfile(ByVal name As String, ByVal url As String, ByVal location As String, ByVal description As String) As String
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Try
- res = twCon.UpdateProfile(name, url, location, description, content)
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Return ""
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.Forbidden
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
- Else
- Return "Err:" + errMsg
- End If
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
- End Function
-
- Public Function PostUpdateProfileImage(ByVal filename As String) As String
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Try
- res = twCon.UpdateProfileImage(New FileInfo(filename), content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Return ""
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.Forbidden
- Dim errMsg As String = GetErrorMessageJson(content)
- If String.IsNullOrEmpty(errMsg) Then
- Return "Err:Forbidden(" + GetCurrentMethod.Name + ")"
- Else
- Return "Err:" + errMsg
- End If
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
- End Function
-
- Public ReadOnly Property Username() As String
- Get
- Return twCon.AuthenticatedUsername
- End Get
- End Property
-
- Public ReadOnly Property UserId As Long
- Get
- Return twCon.AuthenticatedUserId
- End Get
- End Property
-
- Public ReadOnly Property Password() As String
- Get
- Return twCon.Password
- End Get
- End Property
-
- Private Shared _accountState As ACCOUNT_STATE = ACCOUNT_STATE.Valid
- Public Shared Property AccountState() As ACCOUNT_STATE
- Get
- Return _accountState
- End Get
- Set(ByVal value As ACCOUNT_STATE)
- _accountState = value
- End Set
- End Property
-
- Public WriteOnly Property GetIcon() As Boolean
- Set(ByVal value As Boolean)
- _getIcon = value
- End Set
- End Property
-
- Public WriteOnly Property TinyUrlResolve() As Boolean
- Set(ByVal value As Boolean)
- _tinyUrlResolve = value
- End Set
- End Property
-
- Public WriteOnly Property RestrictFavCheck() As Boolean
- Set(ByVal value As Boolean)
- _restrictFavCheck = value
- End Set
- End Property
-
- Public WriteOnly Property IconSize() As Integer
- Set(ByVal value As Integer)
- _iconSz = value
- End Set
- End Property
-
-#Region "バージョンアップ"
- Public Function GetVersionInfo() As String
- Dim content As String = ""
- If Not (New HttpVarious).GetData("http://tween.sourceforge.jp/version.txt?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), Nothing, content, GetUserAgentString()) Then
- Throw New Exception("GetVersionInfo Failed")
- End If
- Return content
- End Function
-
- Public Function GetTweenBinary(ByVal strVer As String) As String
- Try
- '本体
- If Not (New HttpVarious).GetDataToFile("http://tween.sourceforge.jp/Tween" + strVer + ".gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), _
- Path.Combine(MyCommon.settingPath, "TweenNew.exe")) Then
- Return "Err:Download failed"
- End If
- '英語リソース
- If Not Directory.Exists(Path.Combine(MyCommon.settingPath, "en")) Then
- Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, "en"))
- End If
- If Not (New HttpVarious).GetDataToFile("http://tween.sourceforge.jp/TweenResEn" + strVer + ".gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), _
- Path.Combine(Path.Combine(MyCommon.settingPath, "en"), "Tween.resourcesNew.dll")) Then
- Return "Err:Download failed"
- End If
- 'その他言語圏のリソース。取得失敗しても継続
- 'UIの言語圏のリソース
- Dim curCul As String = ""
- If Not Thread.CurrentThread.CurrentUICulture.IsNeutralCulture Then
- Dim idx As Integer = Thread.CurrentThread.CurrentUICulture.Name.LastIndexOf("-"c)
- If idx > -1 Then
- curCul = Thread.CurrentThread.CurrentUICulture.Name.Substring(0, idx)
- Else
- curCul = Thread.CurrentThread.CurrentUICulture.Name
- End If
- Else
- curCul = Thread.CurrentThread.CurrentUICulture.Name
- End If
- If Not String.IsNullOrEmpty(curCul) AndAlso curCul <> "en" AndAlso curCul <> "ja" Then
- If Not Directory.Exists(Path.Combine(MyCommon.settingPath, curCul)) Then
- Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, curCul))
- End If
- If Not (New HttpVarious).GetDataToFile("http://tween.sourceforge.jp/TweenRes" + curCul + strVer + ".gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), _
- Path.Combine(Path.Combine(MyCommon.settingPath, curCul), "Tween.resourcesNew.dll")) Then
- 'Return "Err:Download failed"
- End If
- End If
- 'スレッドの言語圏のリソース
- Dim curCul2 As String
- If Not Thread.CurrentThread.CurrentCulture.IsNeutralCulture Then
- Dim idx As Integer = Thread.CurrentThread.CurrentCulture.Name.LastIndexOf("-"c)
- If idx > -1 Then
- curCul2 = Thread.CurrentThread.CurrentCulture.Name.Substring(0, idx)
- Else
- curCul2 = Thread.CurrentThread.CurrentCulture.Name
- End If
- Else
- curCul2 = Thread.CurrentThread.CurrentCulture.Name
- End If
- If Not String.IsNullOrEmpty(curCul2) AndAlso curCul2 <> "en" AndAlso curCul2 <> curCul Then
- If Not Directory.Exists(Path.Combine(MyCommon.settingPath, curCul2)) Then
- Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, curCul2))
- End If
- If Not (New HttpVarious).GetDataToFile("http://tween.sourceforge.jp/TweenRes" + curCul2 + strVer + ".gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), _
- Path.Combine(Path.Combine(MyCommon.settingPath, curCul2), "Tween.resourcesNew.dll")) Then
- 'Return "Err:Download failed"
- End If
- End If
-
- 'アップデータ
- If Not (New HttpVarious).GetDataToFile("http://tween.sourceforge.jp/TweenUp3.gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), _
- Path.Combine(MyCommon.settingPath, "TweenUp3.exe")) Then
- Return "Err:Download failed"
- End If
- 'シリアライザDLL
- If Not (New HttpVarious).GetDataToFile("http://tween.sourceforge.jp/TweenDll" + strVer + ".gz?" + Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), _
- Path.Combine(MyCommon.settingPath, "TweenNew.XmlSerializers.dll")) Then
- Return "Err:Download failed"
- End If
- Return ""
- Catch ex As Exception
- Return "Err:Download failed"
- End Try
- End Function
-#End Region
-
- Public Property DetailIcon() As IDictionary(Of String, Image)
- Get
- Return _dIcon
- End Get
- Set(ByVal value As IDictionary(Of String, Image))
- _dIcon = value
- End Set
- End Property
-
- Public Property ReadOwnPost() As Boolean
- Get
- Return _readOwnPost
- End Get
- Set(ByVal value As Boolean)
- _readOwnPost = value
- End Set
- End Property
-
- Public ReadOnly Property FollowersCount() As Integer
- Get
- Return _followersCount
- End Get
- End Property
-
- Public ReadOnly Property FriendsCount() As Integer
- Get
- Return _friendsCount
- End Get
- End Property
-
- Public ReadOnly Property StatusesCount() As Integer
- Get
- Return _statusesCount
- End Get
- End Property
-
- Public ReadOnly Property Location() As String
- Get
- Return _location
- End Get
- End Property
-
- Public ReadOnly Property Bio() As String
- Get
- Return _bio
- End Get
- End Property
-
- Public WriteOnly Property UseSsl() As Boolean
- Set(ByVal value As Boolean)
- HttpTwitter.UseSsl = value
- If value Then
- _protocol = "https://"
- Else
- _protocol = "http://"
- End If
- End Set
- End Property
-
- Public Function GetTimelineApi(ByVal read As Boolean, _
- ByVal gType As WORKERTYPE, _
- ByVal more As Boolean, _
- ByVal startup As Boolean) As String
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- If _endingFlag Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Dim count As Integer = AppendSettingDialog.Instance.CountApi
- If gType = WORKERTYPE.Reply Then count = AppendSettingDialog.Instance.CountApiReply()
- If AppendSettingDialog.Instance.UseAdditionalCount Then
- If more AndAlso AppendSettingDialog.Instance.MoreCountApi <> 0 Then
- count = AppendSettingDialog.Instance.MoreCountApi
- ElseIf startup AndAlso AppendSettingDialog.Instance.FirstCountApi <> 0 AndAlso gType = WORKERTYPE.Timeline Then
- count = AppendSettingDialog.Instance.FirstCountApi
- End If
- End If
- Try
- If gType = WORKERTYPE.Timeline Then
- If more Then
- res = twCon.HomeTimeline(count, Me.minHomeTimeline, 0, content)
- Else
- res = twCon.HomeTimeline(count, 0, 0, content)
- End If
- Else
- If more Then
- res = twCon.Mentions(count, Me.minMentions, 0, content)
- Else
- res = twCon.Mentions(count, 0, 0, content)
- End If
- End If
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- If gType = WORKERTYPE.Timeline Then
- Return CreatePostsFromJson(content, gType, Nothing, read, count, Me.minHomeTimeline)
- Else
- Return CreatePostsFromJson(content, gType, Nothing, read, count, Me.minMentions)
- End If
- End Function
-
- Public Function GetUserTimelineApi(ByVal read As Boolean,
- ByVal count As Integer,
- ByVal userName As String,
- ByVal tab As TabClass,
- ByVal more As Boolean) As String
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- If _endingFlag Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- If count = 0 Then count = 20
- Try
- If String.IsNullOrEmpty(userName) Then
- Dim target As String = tab.User
- If String.IsNullOrEmpty(target) Then Return ""
- userName = target
- res = twCon.UserTimeline(0, target, count, 0, 0, content)
- Else
- If more Then
- res = twCon.UserTimeline(0, userName, count, tab.OldestId, 0, content)
- Else
- res = twCon.UserTimeline(0, userName, count, 0, 0, content)
- End If
- End If
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Return "Err:@" + userName + "'s Tweets are protected."
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Dim items As List(Of TwitterDataModel.Status)
- Try
- items = CreateDataFromJson(Of List(Of TwitterDataModel.Status))(content)
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Invalid Json!"
- End Try
-
- For Each status As TwitterDataModel.Status In items
- Dim item As PostClass = CreatePostsFromStatusData(status)
- If item Is Nothing Then Continue For
- If item.StatusId < tab.OldestId Then tab.OldestId = item.StatusId
- item.IsRead = read
- If item.IsMe AndAlso Not read AndAlso _readOwnPost Then item.IsRead = True
- If tab IsNot Nothing Then item.RelTabName = tab.TabName
- '非同期アイコン取得&StatusDictionaryに追加
- TabInformations.GetInstance.AddPost(item)
- Next
-
- Return ""
- End Function
-
- Public Function GetStatusApi(ByVal read As Boolean,
- ByVal id As Int64,
- ByRef post As PostClass) As String
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- If _endingFlag Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- Try
- res = twCon.ShowStatuses(id, content)
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case HttpStatusCode.Forbidden
- Return "Err:Protected user's tweet"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Dim status As TwitterDataModel.Status
- Try
- status = CreateDataFromJson(Of TwitterDataModel.Status)(content)
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Invalid Json!"
- End Try
-
- Dim item As PostClass = CreatePostsFromStatusData(status)
- If item Is Nothing Then Return "Err:Can't create post"
- item.IsRead = read
- If item.IsMe AndAlso Not read AndAlso _readOwnPost Then item.IsRead = True
-
- post = item
- Return ""
- End Function
-
- Public Function GetStatusApi(ByVal read As Boolean,
- ByVal id As Int64,
- ByVal tab As TabClass) As String
- Dim post As PostClass = Nothing
- Dim r As String = Me.GetStatusApi(read, id, post)
-
- If r = "" Then
- If tab IsNot Nothing Then post.RelTabName = tab.TabName
- '非同期アイコン取得&StatusDictionaryに追加
- TabInformations.GetInstance.AddPost(post)
- End If
-
- Return r
- End Function
-
- Private Function CreatePostsFromStatusData(ByVal status As TwitterDataModel.Status) As PostClass
- Dim post As New PostClass
- Dim entities As TwitterDataModel.Entities
-
- post.StatusId = status.Id
- If status.RetweetedStatus IsNot Nothing Then
- Dim retweeted As TwitterDataModel.RetweetedStatus = status.RetweetedStatus
-
- post.CreatedAt = DateTimeParse(retweeted.CreatedAt)
-
- 'Id
- post.RetweetedId = retweeted.Id
- '本文
- post.TextFromApi = retweeted.Text
- entities = retweeted.Entities
- 'Source取得(htmlの場合は、中身を取り出し)
- post.Source = retweeted.Source
- 'Reply先
- Long.TryParse(retweeted.InReplyToStatusId, post.InReplyToStatusId)
- post.InReplyToUser = retweeted.InReplyToScreenName
- Long.TryParse(status.InReplyToUserId, post.InReplyToUserId)
-
- '幻覚fav対策
- Dim tc As TabClass = TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites)
- post.IsFav = tc.Contains(post.RetweetedId)
-
- If retweeted.Geo IsNot Nothing Then post.PostGeo = New PostClass.StatusGeo() With {.Lat = retweeted.Geo.Coordinates(0), .Lng = retweeted.Geo.Coordinates(1)}
-
- '以下、ユーザー情報
- Dim user As TwitterDataModel.User = retweeted.User
-
- If user.ScreenName Is Nothing OrElse status.User.ScreenName Is Nothing Then Return Nothing
-
- post.UserId = user.Id
- post.ScreenName = user.ScreenName
- post.Nickname = user.Name.Trim()
- post.ImageUrl = user.ProfileImageUrl
- post.IsProtect = user.Protected
-
- 'Retweetした人
- post.RetweetedBy = status.User.ScreenName
- post.RetweetedByUserId = status.User.Id
- post.IsMe = post.RetweetedBy.ToLower.Equals(_uname)
- Else
- post.CreatedAt = DateTimeParse(status.CreatedAt)
- '本文
- post.TextFromApi = status.Text
- entities = status.Entities
- 'Source取得(htmlの場合は、中身を取り出し)
- post.Source = status.Source
- Long.TryParse(status.InReplyToStatusId, post.InReplyToStatusId)
- post.InReplyToUser = status.InReplyToScreenName
- Long.TryParse(status.InReplyToUserId, post.InReplyToUserId)
-
- If status.Geo IsNot Nothing Then post.PostGeo = New PostClass.StatusGeo() With {.Lat = status.Geo.Coordinates(0), .Lng = status.Geo.Coordinates(1)}
-
- '以下、ユーザー情報
- Dim user As TwitterDataModel.User = status.User
-
- If user.ScreenName Is Nothing Then Return Nothing
-
- post.UserId = user.Id
- post.ScreenName = user.ScreenName
- post.Nickname = user.Name.Trim()
- post.ImageUrl = user.ProfileImageUrl
- post.IsProtect = user.Protected
- post.IsMe = post.ScreenName.ToLower.Equals(_uname)
-
- '幻覚fav対策
- Dim tc As TabClass = TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites)
- post.IsFav = tc.Contains(post.StatusId) AndAlso TabInformations.GetInstance.Item(post.StatusId).IsFav
- End If
- 'HTMLに整形
- post.Text = CreateHtmlAnchor(post.TextFromApi, post.ReplyToList, entities, post.Media)
- post.TextFromApi = Me.ReplaceTextFromApi(post.TextFromApi, entities)
- post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi)
- post.TextFromApi = post.TextFromApi.Replace("<3", "♡")
-
- 'Source整形
- CreateSource(post)
-
- post.IsReply = post.ReplyToList.Contains(_uname)
- post.IsExcludeReply = False
-
- If post.IsMe Then
- post.IsOwl = False
- Else
- If followerId.Count > 0 Then post.IsOwl = Not followerId.Contains(post.UserId)
- End If
-
- post.IsDm = False
- Return post
- End Function
-
- 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
- Dim items As List(Of TwitterDataModel.Status)
- Try
- items = CreateDataFromJson(Of List(Of TwitterDataModel.Status))(content)
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Invalid Json!"
- End Try
-
- For Each status As TwitterDataModel.Status In items
- Dim post As PostClass = Nothing
- post = CreatePostsFromStatusData(status)
- If post Is Nothing Then Continue For
-
- If minimumId > post.StatusId Then minimumId = post.StatusId
- '二重取得回避
- SyncLock LockObj
- If tab Is Nothing Then
- If TabInformations.GetInstance.ContainsKey(post.StatusId) Then Continue For
- Else
- If TabInformations.GetInstance.ContainsKey(post.StatusId, tab.TabName) Then Continue For
- End If
- End SyncLock
-
- 'RT禁止ユーザーによるもの
- If post.RetweetedId > 0 AndAlso Me.noRTId.Contains(post.RetweetedByUserId) Then Continue For
-
- post.IsRead = read
- If post.IsMe AndAlso Not read AndAlso _readOwnPost Then post.IsRead = True
-
- If tab IsNot Nothing Then post.RelTabName = tab.TabName
- '非同期アイコン取得&StatusDictionaryに追加
- TabInformations.GetInstance.AddPost(post)
- Next
-
- Return ""
- End Function
-
- 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
- Dim items As TwitterDataModel.SearchResult
- Try
- items = CreateDataFromJson(Of TwitterDataModel.SearchResult)(content)
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Invalid Json!"
- End Try
-
- nextPageQuery = items.NextPage
-
- For Each status As TwitterDataModel.Status In items.Statuses
- Dim post As PostClass = Nothing
- post = CreatePostsFromStatusData(status)
- If post Is Nothing Then Continue For
-
- If minimumId > post.StatusId Then minimumId = post.StatusId
- '二重取得回避
- SyncLock LockObj
- If tab Is Nothing Then
- If TabInformations.GetInstance.ContainsKey(post.StatusId) Then Continue For
- Else
- If TabInformations.GetInstance.ContainsKey(post.StatusId, tab.TabName) Then Continue For
- End If
- End SyncLock
-
- post.IsRead = read
- If post.IsMe AndAlso Not read AndAlso _readOwnPost Then post.IsRead = True
-
- If tab IsNot Nothing Then post.RelTabName = tab.TabName
- '非同期アイコン取得&StatusDictionaryに追加
- TabInformations.GetInstance.AddPost(post)
- Next
-
- Return If(String.IsNullOrEmpty(items.ErrMsg), "", "Err:" + items.ErrMsg)
- End Function
-
- Public Overloads Function GetListStatus(ByVal read As Boolean, _
- ByVal tab As TabClass, _
- ByVal more As Boolean, _
- ByVal startup As Boolean) As String
-
- If _endingFlag Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Dim page As Integer = 0
- Dim count As Integer
- If AppendSettingDialog.Instance.UseAdditionalCount Then
- count = AppendSettingDialog.Instance.ListCountApi
- If count = 0 Then
- If more AndAlso AppendSettingDialog.Instance.MoreCountApi <> 0 Then
- count = AppendSettingDialog.Instance.MoreCountApi
- ElseIf startup AndAlso AppendSettingDialog.Instance.FirstCountApi <> 0 Then
- count = AppendSettingDialog.Instance.FirstCountApi
- Else
- count = AppendSettingDialog.Instance.CountApi
- End If
- End If
- Else
- count = AppendSettingDialog.Instance.CountApi
- End If
- Try
- If more Then
- res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, tab.OldestId, 0, AppendSettingDialog.Instance.IsListStatusesIncludeRts, content)
- Else
- res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, 0, 0, AppendSettingDialog.Instance.IsListStatusesIncludeRts, content)
- End If
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Return CreatePostsFromJson(content, WORKERTYPE.List, tab, read, count, tab.OldestId)
- End Function
-
-
- Private Function CheckReplyToPost(ByVal relPosts As List(Of PostClass)) As PostClass
- Dim tmpPost As PostClass = relPosts(0)
- Dim lastPost As PostClass = Nothing
- Do While tmpPost IsNot Nothing
- If tmpPost.InReplyToStatusId = 0 Then Return Nothing
- lastPost = tmpPost
- Dim replyToPost = From p In relPosts
- Where p.StatusId = tmpPost.InReplyToStatusId
- Select p
- tmpPost = replyToPost.FirstOrDefault()
- Loop
- Return lastPost
- End Function
-
- Public Function GetRelatedResult(ByVal read As Boolean, ByVal tab As TabClass) As String
- Dim rslt As String = ""
- Dim relPosts As New List(Of PostClass)
- If tab.RelationTargetPost.TextFromApi.Contains("@") AndAlso tab.RelationTargetPost.InReplyToStatusId = 0 Then
- '検索結果対応
- Dim p As PostClass = TabInformations.GetInstance.Item(tab.RelationTargetPost.StatusId)
- If p IsNot Nothing AndAlso p.InReplyToStatusId > 0 Then
- tab.RelationTargetPost = p
- Else
- rslt = Me.GetStatusApi(read, tab.RelationTargetPost.StatusId, p)
- If Not String.IsNullOrEmpty(rslt) Then Return rslt
- tab.RelationTargetPost = p
- End If
- End If
- relPosts.Add(tab.RelationTargetPost.Copy)
- Dim tmpPost As PostClass = relPosts(0)
- Do
- rslt = Me.GetRelatedResultsApi(read, tmpPost, tab, relPosts)
- If Not String.IsNullOrEmpty(rslt) Then Exit Do
- tmpPost = CheckReplyToPost(relPosts)
- Loop While tmpPost IsNot Nothing
-
- relPosts.ForEach(Sub(p) TabInformations.GetInstance.AddPost(p))
- Return rslt
- End Function
-
- Private Function GetRelatedResultsApi(ByVal read As Boolean,
- ByVal post As PostClass,
- ByVal tab As TabClass,
- ByVal relatedPosts As List(Of PostClass)) As String
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- If _endingFlag Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Try
- If post.RetweetedId > 0 Then
- res = twCon.GetRelatedResults(post.RetweetedId, content)
- Else
- res = twCon.GetRelatedResults(post.StatusId, content)
- End If
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Dim items As List(Of TwitterDataModel.RelatedResult)
- Try
- items = CreateDataFromJson(Of List(Of TwitterDataModel.RelatedResult))(content)
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Invalid Json!"
- End Try
-
- Dim targetItem As PostClass = post
- If targetItem Is Nothing Then
- Return ""
- Else
- targetItem = targetItem.Copy()
- End If
- targetItem.RelTabName = tab.TabName
- TabInformations.GetInstance.AddPost(targetItem)
-
- Dim replyToItem As PostClass = Nothing
- Dim replyToUserName As String = targetItem.InReplyToUser
- If targetItem.InReplyToStatusId > 0 AndAlso TabInformations.GetInstance.Item(targetItem.InReplyToStatusId) IsNot Nothing Then
- replyToItem = TabInformations.GetInstance.Item(targetItem.InReplyToStatusId).Copy
- replyToItem.IsRead = read
- If replyToItem.IsMe AndAlso Not read AndAlso _readOwnPost Then replyToItem.IsRead = True
- replyToItem.RelTabName = tab.TabName
- End If
-
- Dim replyAdded As Boolean = False
- For Each relatedData As TwitterDataModel.RelatedResult In items
- For Each result As TwitterDataModel.RelatedTweet In relatedData.Results
- Dim item As PostClass = CreatePostsFromStatusData(result.Status)
- If item Is Nothing Then Continue For
- If targetItem.InReplyToStatusId = item.StatusId Then
- replyToItem = Nothing
- replyAdded = True
- End If
- item.IsRead = read
- If item.IsMe AndAlso Not read AndAlso _readOwnPost Then item.IsRead = True
- If tab IsNot Nothing Then item.RelTabName = tab.TabName
- '非同期アイコン取得&StatusDictionaryに追加
- relatedPosts.Add(item)
- Next
- Next
- If replyToItem IsNot Nothing Then
- relatedPosts.Add(replyToItem)
- ElseIf targetItem.InReplyToStatusId > 0 AndAlso Not replyAdded Then
- Dim p As PostClass = Nothing
- Dim rslt As String = ""
- rslt = GetStatusApi(read, targetItem.InReplyToStatusId, p)
- If String.IsNullOrEmpty(rslt) Then
- p.IsRead = read
- p.RelTabName = tab.TabName
- relatedPosts.Add(p)
- End If
- Return rslt
- End If
-
- '発言者・返信先ユーザーの直近10発言取得
- 'Dim rslt As String = Me.GetUserTimelineApi(read, 10, "", tab)
- 'If Not String.IsNullOrEmpty(rslt) Then Return rslt
- 'If Not String.IsNullOrEmpty(replyToUserName) Then
- ' rslt = Me.GetUserTimelineApi(read, 10, replyToUserName, tab)
- 'End If
- 'Return rslt
-
-
- 'MRTとかに対応のためツイート内にあるツイートを指すURLを取り込む
- Dim ma As MatchCollection = Regex.Matches(tab.RelationTargetPost.Text, "title=""https?://twitter.com/(#!/)?(?<ScreenName>[a-zA-Z0-9_]+)(/status(es)?/(?<StatusId>[0-9]+))""")
- For Each _match As Match In ma
- Dim _statusId As Int64
- If Int64.TryParse(_match.Groups("StatusId").Value, _statusId) Then
- Dim p As PostClass = Nothing
- Dim _post As PostClass = TabInformations.GetInstance.Item(_statusId)
- If _post Is Nothing Then
- Dim rslt = Me.GetStatusApi(read, _statusId, p)
- Else
- p = _post.Copy
- End If
- If p IsNot Nothing Then
- p.IsRead = read
- p.RelTabName = tab.TabName
- relatedPosts.Add(p)
- End If
- End If
- Next
- Return ""
- End Function
-
- Public Function GetSearch(ByVal read As Boolean, _
- ByVal tab As TabClass, _
- ByVal more As Boolean) As String
-
- If _endingFlag Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Dim page As Integer = 0
- Dim sinceId As Long = 0
- Dim count As Integer = 100
- If AppendSettingDialog.Instance.UseAdditionalCount AndAlso
- AppendSettingDialog.Instance.SearchCountApi <> 0 Then
- count = AppendSettingDialog.Instance.SearchCountApi
- Else
- count = AppendSettingDialog.Instance.CountApi
- End If
- If more Then
- page = tab.GetSearchPage(count)
- Else
- sinceId = tab.SinceId
- End If
-
- Try
- ' TODO:一時的に40>100件に 件数変更UI作成の必要あり
- res = twCon.Search(tab.SearchWords, tab.SearchLang, count, page, sinceId, content)
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
- Select Case res
- Case HttpStatusCode.BadRequest
- Return "Invalid query"
- Case HttpStatusCode.NotFound
- Return "Invalid query"
- Case HttpStatusCode.PaymentRequired 'API Documentには420と書いてあるが、該当コードがないので402にしてある
- Return "Search API Limit?"
- Case HttpStatusCode.OK
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- If Not TabInformations.GetInstance.ContainsTab(tab) Then Return ""
- content = Regex.Replace(content, "[\x00-\x1f-[\x0a\x0d]]+", " ")
- Dim xdoc As New XmlDocument
- Try
- xdoc.LoadXml(content)
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Invalid ATOM!"
- End Try
- Dim nsmgr As New XmlNamespaceManager(xdoc.NameTable)
- nsmgr.AddNamespace("search", "http://www.w3.org/2005/Atom")
- nsmgr.AddNamespace("twitter", "http://api.twitter.com/")
- nsmgr.AddNamespace("georss", "http://www.georss.org/georss")
- For Each xentryNode As XmlNode In xdoc.DocumentElement.SelectNodes("/search:feed/search:entry", nsmgr)
- Dim xentry As XmlElement = CType(xentryNode, XmlElement)
- Dim post As New PostClass
- Try
- post.StatusId = Long.Parse(xentry.Item("id").InnerText.Split(":"c)(2))
- If TabInformations.GetInstance.ContainsKey(post.StatusId, tab.TabName) Then Continue For
- post.CreatedAt = DateTime.Parse(xentry.Item("published").InnerText)
- '本文
- post.TextFromApi = xentry.Item("title").InnerText
- 'Source取得(htmlの場合は、中身を取り出し)
- post.Source = xentry.Item("twitter:source").InnerText
- post.InReplyToStatusId = 0
- post.InReplyToUser = ""
- post.InReplyToUserId = 0
- post.IsFav = False
-
- ' Geoが勝手に付加されるバグがいっこうに修正されないので暫定的にGeo情報を無視する
- If xentry.Item("twitter:geo").HasChildNodes Then
- Dim pnt As String() = CType(xentry.SelectSingleNode("twitter:geo/georss:point", nsmgr), XmlElement).InnerText.Split(" "c)
- post.PostGeo = New PostClass.StatusGeo With {.Lat = Double.Parse(pnt(0)), .Lng = Double.Parse(pnt(1))}
- End If
-
- '以下、ユーザー情報
- Dim xUentry As XmlElement = CType(xentry.SelectSingleNode("./search:author", nsmgr), XmlElement)
- post.UserId = 0
- post.ScreenName = xUentry.Item("name").InnerText.Split(" "c)(0).Trim
- post.Nickname = xUentry.Item("name").InnerText.Substring(post.ScreenName.Length).Trim
- If post.Nickname.Length > 2 Then
- post.Nickname = post.Nickname.Substring(1, post.Nickname.Length - 2)
- Else
- post.Nickname = post.ScreenName
- End If
- post.ImageUrl = CType(xentry.SelectSingleNode("./search:link[@type='image/png']", nsmgr), XmlElement).GetAttribute("href")
- post.IsProtect = False
- post.IsMe = post.ScreenName.ToLower.Equals(_uname)
-
- 'HTMLに整形
- post.Text = CreateHtmlAnchor(HttpUtility.HtmlEncode(post.TextFromApi), post.ReplyToList, post.Media)
- post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi)
- 'Source整形
- CreateSource(post)
-
- post.IsRead = read
- post.IsReply = post.ReplyToList.Contains(_uname)
- post.IsExcludeReply = False
-
- post.IsOwl = False
- If post.IsMe AndAlso Not read AndAlso _readOwnPost Then post.IsRead = True
- post.IsDm = False
- post.RelTabName = tab.TabName
- If Not more AndAlso post.StatusId > tab.SinceId Then tab.SinceId = post.StatusId
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Continue For
- End Try
-
- 'Me._dIcon.Add(post.ImageUrl, Nothing)
- TabInformations.GetInstance.AddPost(post)
-
- Next
-
- '' TODO
- '' 遡るための情報max_idやnext_pageの情報を保持する
-
-#If 0 Then
- Dim xNode As XmlNode = xdoc.DocumentElement.SelectSingleNode("/search:feed/twitter:warning", nsmgr)
-
- If xNode IsNot Nothing Then
- Return "Warn:" + xNode.InnerText + "(" + GetCurrentMethod.Name + ")"
- End If
-#End If
-
- Return ""
- End Function
-
- Public Function GetPhoenixSearch(ByVal read As Boolean, _
- ByVal tab As TabClass, _
- ByVal more As Boolean) As String
-
- If _endingFlag Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Dim page As Integer = 0
- Dim sinceId As Long = 0
- Dim count As Integer = 100
- Dim querystr As String = ""
- If AppendSettingDialog.Instance.UseAdditionalCount AndAlso
- AppendSettingDialog.Instance.SearchCountApi <> 0 Then
- count = AppendSettingDialog.Instance.SearchCountApi
- End If
- If more Then
- page = tab.GetSearchPage(count)
- If Not String.IsNullOrEmpty(tab.NextPageQuery) Then
- querystr = tab.NextPageQuery
- End If
- Else
- sinceId = tab.SinceId
- End If
-
- Try
- If String.IsNullOrEmpty(querystr) Then
- res = twCon.PhoenixSearch(tab.SearchWords, tab.SearchLang, count, page, sinceId, content)
- Else
- res = twCon.PhoenixSearch(querystr, content)
- End If
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
- Select Case res
- Case HttpStatusCode.BadRequest
- Return "Invalid query"
- Case HttpStatusCode.NotFound
- Return "Invalid query"
- Case HttpStatusCode.PaymentRequired 'API Documentには420と書いてあるが、該当コードがないので402にしてある
- Return "Search API Limit?"
- Case HttpStatusCode.OK
- Case Else
- Return "Err:" + res.ToString + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- If Not TabInformations.GetInstance.ContainsTab(tab) Then Return ""
-
- '' TODO
- '' 遡るための情報max_idやnext_pageの情報を保持する
-
- Return CreatePostsFromPhoenixSearch(content, WORKERTYPE.PublicSearch, tab, read, count, tab.OldestId, tab.NextPageQuery)
- End Function
-
- Private Function CreateDirectMessagesFromJson(ByVal content As String, ByVal gType As WORKERTYPE, ByVal read As Boolean) As String
- Dim item As List(Of TwitterDataModel.Directmessage)
- Try
- If gType = WORKERTYPE.UserStream Then
- Dim itm As List(Of TwitterDataModel.DirectmessageEvent) = CreateDataFromJson(Of List(Of TwitterDataModel.DirectmessageEvent))(content)
- item = New List(Of TwitterDataModel.Directmessage)
- For Each dat As TwitterDataModel.DirectmessageEvent In itm
- item.Add(dat.Directmessage)
- Next
- Else
- item = CreateDataFromJson(Of List(Of TwitterDataModel.Directmessage))(content)
- End If
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Invalid Json!"
- End Try
-
- For Each message As TwitterDataModel.Directmessage In item
- Dim post As New PostClass
- Try
- post.StatusId = message.Id
- If gType <> WORKERTYPE.UserStream Then
- If gType = WORKERTYPE.DirectMessegeRcv Then
- If minDirectmessage > post.StatusId Then minDirectmessage = post.StatusId
- Else
- If minDirectmessageSent > post.StatusId Then minDirectmessageSent = post.StatusId
- End If
- End If
-
- '二重取得回避
- SyncLock LockObj
- If TabInformations.GetInstance.GetTabByType(TabUsageType.DirectMessage).Contains(post.StatusId) Then Continue For
- End SyncLock
- 'sender_id
- 'recipient_id
- post.CreatedAt = DateTimeParse(message.CreatedAt)
- '本文
- post.TextFromApi = message.Text
- 'HTMLに整形
- post.Text = CreateHtmlAnchor(post.TextFromApi, post.ReplyToList, post.Media)
- post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi)
- post.TextFromApi = post.TextFromApi.Replace("<3", "♡")
- post.IsFav = False
-
- '以下、ユーザー情報
- Dim user As TwitterDataModel.User
- If gType = WORKERTYPE.UserStream Then
- If twCon.AuthenticatedUsername.Equals(message.Recipient.ScreenName, StringComparison.CurrentCultureIgnoreCase) Then
- user = message.Sender
- post.IsMe = False
- post.IsOwl = True
- Else
- user = message.Recipient
- post.IsMe = True
- post.IsOwl = False
- End If
- Else
- If gType = WORKERTYPE.DirectMessegeRcv Then
- user = message.Sender
- post.IsMe = False
- post.IsOwl = True
- Else
- user = message.Recipient
- post.IsMe = True
- post.IsOwl = False
- End If
- End If
-
- post.UserId = user.Id
- post.ScreenName = user.ScreenName
- post.Nickname = user.Name.Trim()
- post.ImageUrl = user.ProfileImageUrl
- post.IsProtect = user.Protected
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- MessageBox.Show("Parse Error(CreateDirectMessagesFromJson)")
- Continue For
- End Try
-
- post.IsRead = read
- If post.IsMe AndAlso Not read AndAlso _readOwnPost Then post.IsRead = True
- post.IsReply = False
- post.IsExcludeReply = False
- post.IsDm = True
-
- TabInformations.GetInstance.AddPost(post)
- Next
-
- Return ""
-
- End Function
-
- Public Function GetDirectMessageApi(ByVal read As Boolean, _
- ByVal gType As WORKERTYPE, _
- ByVal more As Boolean) As String
- If _endingFlag Then Return ""
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
- If TwitterApiInfo.AccessLevel <> ApiAccessLevel.None Then
- If Not TwitterApiInfo.IsDirectMessagePermission Then Return "Auth Err:try to re-authorization."
- End If
-
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- Try
- If gType = WORKERTYPE.DirectMessegeRcv Then
- If more Then
- res = twCon.DirectMessages(20, minDirectmessage, 0, content)
- Else
- res = twCon.DirectMessages(20, 0, 0, content)
- End If
- Else
- If more Then
- res = twCon.DirectMessagesSent(20, minDirectmessageSent, 0, content)
- Else
- res = twCon.DirectMessagesSent(20, 0, 0, content)
- End If
- End If
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Return CreateDirectMessagesFromJson(content, gType, read)
- End Function
-
- Public Function GetFavoritesApi(ByVal read As Boolean,
- ByVal gType As WORKERTYPE,
- ByVal more As Boolean) As String
-
- Static page As Integer = 1
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- If _endingFlag Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Dim count As Integer = AppendSettingDialog.Instance.CountApi
- If AppendSettingDialog.Instance.UseAdditionalCount AndAlso
- AppendSettingDialog.Instance.FavoritesCountApi <> 0 Then
- count = AppendSettingDialog.Instance.FavoritesCountApi
- End If
-
- ' 前ページ取得の場合はページカウンタをインクリメント、それ以外の場合はページカウンタリセット
- If more Then
- page += 1
- Else
- page = 1
- End If
-
- Try
- res = twCon.Favorites(count, page, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Dim serializer As New DataContractJsonSerializer(GetType(List(Of TwitterDataModel.Status)))
- Dim item As List(Of TwitterDataModel.Status)
-
- Try
- item = CreateDataFromJson(Of List(Of TwitterDataModel.Status))(content)
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Invalid Json!"
- End Try
-
- For Each status As TwitterDataModel.Status In item
- Dim post As New PostClass
- Dim entities As TwitterDataModel.Entities
-
- Try
- post.StatusId = status.Id
- '二重取得回避
- SyncLock LockObj
- If TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites).Contains(post.StatusId) Then Continue For
- End SyncLock
- 'Retweet判定
- If status.RetweetedStatus IsNot Nothing Then
- Dim retweeted As TwitterDataModel.RetweetedStatus = status.RetweetedStatus
- post.CreatedAt = DateTimeParse(retweeted.CreatedAt)
-
- 'Id
- post.RetweetedId = post.StatusId
- '本文
- post.TextFromApi = retweeted.Text
- entities = retweeted.Entities
- 'Source取得(htmlの場合は、中身を取り出し)
- post.Source = retweeted.Source
- 'Reply先
- Long.TryParse(retweeted.InReplyToStatusId, post.InReplyToStatusId)
- post.InReplyToUser = retweeted.InReplyToScreenName
- Long.TryParse(retweeted.InReplyToUserId, post.InReplyToUserId)
- post.IsFav = True
-
- '以下、ユーザー情報
- Dim user As TwitterDataModel.User = retweeted.User
- post.UserId = user.Id
- post.ScreenName = user.ScreenName
- post.Nickname = user.Name.Trim()
- post.ImageUrl = user.ProfileImageUrl
- post.IsProtect = user.Protected
-
- 'Retweetした人
- post.RetweetedBy = status.User.ScreenName
- post.IsMe = post.RetweetedBy.ToLower.Equals(_uname)
- Else
- post.CreatedAt = DateTimeParse(status.CreatedAt)
-
- '本文
- post.TextFromApi = status.Text
- entities = status.Entities
- 'Source取得(htmlの場合は、中身を取り出し)
- post.Source = status.Source
- Long.TryParse(status.InReplyToStatusId, post.InReplyToStatusId)
- post.InReplyToUser = status.InReplyToScreenName
- Long.TryParse(status.InReplyToUserId, post.InReplyToUserId)
-
- post.IsFav = True
-
- '以下、ユーザー情報
- Dim user As TwitterDataModel.User = status.User
- post.UserId = user.Id
- post.ScreenName = user.ScreenName
- post.Nickname = user.Name.Trim()
- post.ImageUrl = user.ProfileImageUrl
- post.IsProtect = user.Protected
- post.IsMe = post.ScreenName.ToLower.Equals(_uname)
- End If
- 'HTMLに整形
- post.Text = CreateHtmlAnchor(post.TextFromApi, post.ReplyToList, entities, post.Media)
- post.TextFromApi = Me.ReplaceTextFromApi(post.TextFromApi, entities)
- post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi)
- post.TextFromApi = post.TextFromApi.Replace("<3", "♡")
- 'Source整形
- CreateSource(post)
-
- post.IsRead = read
- post.IsReply = post.ReplyToList.Contains(_uname)
- post.IsExcludeReply = False
-
- If post.IsMe Then
- post.IsOwl = False
- Else
- If followerId.Count > 0 Then post.IsOwl = Not followerId.Contains(post.UserId)
- End If
-
- post.IsDm = False
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Continue For
- End Try
-
- TabInformations.GetInstance.AddPost(post)
-
- Next
-
- Return ""
- End Function
-
- Private Function ReplaceTextFromApi(ByVal text As String, ByVal entities As TwitterDataModel.Entities) As String
- If entities IsNot Nothing Then
- If entities.Urls IsNot Nothing Then
- For Each m In entities.Urls
- If Not String.IsNullOrEmpty(m.DisplayUrl) Then text = text.Replace(m.Url, m.DisplayUrl)
- Next
- End If
- If entities.Media IsNot Nothing Then
- For Each m In entities.Media
- If Not String.IsNullOrEmpty(m.DisplayUrl) Then text = text.Replace(m.Url, m.DisplayUrl)
- Next
- End If
- End If
- Return text
- End Function
-
- Public Function GetFollowersApi() As String
- If _endingFlag Then Return ""
- Dim cursor As Long = -1
- Dim tmpFollower As New List(Of Long)(followerId)
-
- followerId.Clear()
- Do
- Dim ret As String = FollowerApi(cursor)
- If Not String.IsNullOrEmpty(ret) Then
- followerId.Clear()
- followerId.AddRange(tmpFollower)
- _GetFollowerResult = False
- Return ret
- End If
- Loop While cursor > 0
-
- TabInformations.GetInstance.RefreshOwl(followerId)
-
- _GetFollowerResult = True
- Return ""
- End Function
-
- Public ReadOnly Property GetFollowersSuccess() As Boolean
- Get
- Return _GetFollowerResult
- End Get
- End Property
-
- Private Function FollowerApi(ByRef cursor As Long) As String
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Try
- res = twCon.FollowerIds(cursor, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Try
- Dim followers = CreateDataFromJson(Of TwitterDataModel.Ids)(content)
- followerId.AddRange(followers.Id)
- cursor = followers.NextCursor
- Return ""
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
- End Function
-
- Public Function GetNoRetweetIdsApi() As String
- If _endingFlag Then Return ""
- Dim cursor As Long = -1
- Dim tmpIds As New List(Of Long)(noRTId)
-
- noRTId.Clear()
- Do
- Dim ret As String = NoRetweetApi(cursor)
- If Not String.IsNullOrEmpty(ret) Then
- noRTId.Clear()
- noRTId.AddRange(tmpIds)
- _GetNoRetweetResult = False
- Return ret
- End If
- Loop While cursor > 0
-
- _GetNoRetweetResult = True
- Return ""
- End Function
-
- Private Function NoRetweetApi(ByRef cursor As Long) As String
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Try
- res = twCon.NoRetweetIds(cursor, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Try
- Dim ids = CreateDataFromJson(Of Long())(content)
- noRTId.AddRange(ids)
- cursor = 0 '0より小さければ何でも良い。
- Return ""
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
- End Function
-
- Public ReadOnly Property GetNoRetweetSuccess() As Boolean
- Get
- Return _GetNoRetweetResult
- End Get
- End Property
-
- Public Function ConfigurationApi() As String
- Dim res As HttpStatusCode
- Dim content As String = ""
- Try
- res = twCon.GetConfiguration(content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Try
- AppendSettingDialog.Instance.TwitterConfiguration = CreateDataFromJson(Of TwitterDataModel.Configuration)(content)
- Return ""
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
- End Function
-
- Public Function GetListsApi() As String
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Dim cursor As Long = -1
-
- Dim lists As New List(Of ListElement)
- Do
- Try
- res = twCon.GetLists(Me.Username, cursor, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Try
- Dim lst = CreateDataFromJson(Of TwitterDataModel.Lists)(content)
- lists.AddRange(From le In lst.lists Select New ListElement(le, Me))
- cursor = lst.NextCursor
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
- Loop While cursor <> 0
-
- cursor = -1
- content = ""
- Do
- Try
- res = twCon.GetListsSubscriptions(Me.Username, cursor, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Try
- Dim lst = CreateDataFromJson(Of TwitterDataModel.Lists)(content)
- lists.AddRange(From le In lst.lists Select New ListElement(le, Me))
- cursor = lst.NextCursor
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
- Loop While cursor <> 0
-
- TabInformations.GetInstance.SubscribableLists = lists
- Return ""
- End Function
-
- Public Function DeleteList(ByVal list_id As String) As String
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- Try
- res = twCon.DeleteListID(Me.Username, list_id, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Return ""
- End Function
-
- 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
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- Try
- res = twCon.UpdateListID(Me.Username, list_id, new_name, isPrivate, description, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Try
- Dim le = CreateDataFromJson(Of TwitterDataModel.ListElementData)(content)
- Dim newList As New ListElement(le, Me)
- list.Description = newList.Description
- list.Id = newList.Id
- list.IsPublic = newList.IsPublic
- list.MemberCount = newList.MemberCount
- list.Name = newList.Name
- list.SubscriberCount = newList.SubscriberCount
- list.Slug = newList.Slug
- list.Nickname = newList.Nickname
- list.Username = newList.Username
- list.UserId = newList.UserId
- Return ""
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
-
- End Function
-
- Public Function GetListMembers(ByVal list_id As String, ByVal lists As List(Of UserInfo), ByRef cursor As Long) As String
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- 'Do
- Try
- res = twCon.GetListMembers(Me.Username, list_id, cursor, content)
- Catch ex As Exception
- Return "Err:" + ex.Message
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Try
- Dim users = CreateDataFromJson(Of TwitterDataModel.Users)(content)
- Array.ForEach(Of TwitterDataModel.User)(
- users.users,
- New Action(Of TwitterDataModel.User)(Sub(u)
- lists.Add(New UserInfo(u))
- End Sub))
- cursor = users.NextCursor
- Return ""
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
-
- Return ""
- End Function
-
- Public Function CreateListApi(ByVal listName As String, ByVal isPrivate As Boolean, ByVal description As String) As String
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- Try
- res = twCon.CreateLists(listName, isPrivate, description, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Try
- Dim le = CreateDataFromJson(Of TwitterDataModel.ListElementData)(content)
- TabInformations.GetInstance().SubscribableLists.Add(New ListElement(le, Me))
- Return ""
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
- End Function
-
- Public Function ContainsUserAtList(ByVal listId As String, ByVal user As String, ByRef value As Boolean) As String
- value = False
-
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- Try
- res = Me.twCon.ShowListMember(listId, user, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case HttpStatusCode.NotFound
- value = False
- Return ""
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Try
- Dim u = CreateDataFromJson(Of TwitterDataModel.User)(content)
- value = True
- Return ""
- Catch ex As Exception
- value = False
- Return ""
- End Try
- End Function
-
- Public Function AddUserToList(ByVal listId As String, ByVal user As String) As String
- Dim content As String = ""
- Dim res As HttpStatusCode
-
- Try
- res = twCon.CreateListMembers(listId, user, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:" + GetErrorMessageJson(content)
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Return ""
- End Function
-
- Public Function RemoveUserToList(ByVal listId As String, ByVal user As String) As String
-
- Dim content As String = ""
- Dim res As HttpStatusCode
-
- Try
- res = twCon.DeleteListMembers(listId, user, content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:" + GetErrorMessageJson(content)
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Return ""
- End Function
-
- Private Class range
- Public Property fromIndex As Integer
- Public Property toIndex As Integer
- Public Sub New(ByVal fromIndex As Integer, ByVal toIndex As Integer)
- Me.fromIndex = fromIndex
- Me.toIndex = toIndex
- End Sub
- End Class
- Public Function CreateHtmlAnchor(ByVal Text As String, ByVal AtList As List(Of String), ByVal media As Dictionary(Of String, String)) As String
- If Text Is Nothing Then Return Nothing
- Dim retStr As String = Text.Replace(">", "<<<<<tweenだいなり>>>>>").Replace("<", "<<<<<tweenしょうなり>>>>>")
- 'uriの正規表現
- 'Const url_valid_domain As String = "(?<domain>(?:[^\p{P}\s][\.\-_](?=[^\p{P}\s])|[^\p{P}\s]){1,}\.[a-z]{2,}(?::[0-9]+)?)"
- 'Const url_valid_general_path_chars As String = "[a-z0-9!*';:=+$/%#\[\]\-_,~]"
- 'Const url_balance_parens As String = "(?:\(" + url_valid_general_path_chars + "+\))"
- 'Const url_valid_url_path_ending_chars As String = "(?:[a-z0-9=_#/\-\+]+|" + url_balance_parens + ")"
- 'Const pth As String = "(?:" + url_balance_parens +
- ' "|@" + url_valid_general_path_chars + "+/" +
- ' "|[.,]?" + url_valid_general_path_chars + "+" +
- ' ")"
- 'Const pth2 As String = "(/(?:" +
- ' pth + "+" + url_valid_url_path_ending_chars + "|" +
- ' pth + "+" + url_valid_url_path_ending_chars + "?|" +
- ' url_valid_url_path_ending_chars +
- ' ")?)?"
- 'Const qry As String = "(?<query>\?[a-z0-9!*'();:&=+$/%#\[\]\-_.,~]*[a-z0-9_&=#])?"
- 'Const rgUrl As String = "(?<before>(?:[^\""':!=#]|^|\:/))" +
- ' "(?<url>(?<protocol>https?://)" +
- ' url_valid_domain +
- ' pth2 +
- ' qry +
- ' ")"
- 'Const rgUrl As String = "(?<before>(?:[^\""':!=#]|^|\:/))" +
- ' "(?<url>(?<protocol>https?://|www\.)" +
- ' url_valid_domain +
- ' pth2 +
- ' qry +
- ' ")"
- '絶対パス表現のUriをリンクに置換
- retStr = Regex.Replace(retStr,
- rgUrl,
- New MatchEvaluator(Function(mu As Match)
- Dim sb As New StringBuilder(mu.Result("${before}<a href="""))
- 'If mu.Result("${protocol}").StartsWith("w", StringComparison.OrdinalIgnoreCase) Then
- ' sb.Append("http://")
- 'End If
- Dim url As String = mu.Result("${url}")
- Dim title As String = ShortUrl.ResolveMedia(url, True)
- If url <> title Then
- title = ShortUrl.ResolveMedia(title, False)
- End If
- sb.Append(url + """ title=""" + title + """>").Append(url).Append("</a>")
- If media IsNot Nothing AndAlso Not media.ContainsKey(url) Then media.Add(url, title)
- Return sb.ToString
- End Function),
- RegexOptions.IgnoreCase)
-
- '@先をリンクに置換(リスト)
- retStr = Regex.Replace(retStr,
- "(^|[^a-zA-Z0-9_/])([@@]+)([a-zA-Z0-9_]{1,20}/[a-zA-Z][a-zA-Z0-9\p{IsLatin-1Supplement}\-]{0,79})",
- "$1$2<a href=""/$3"">$3</a>")
-
- Dim m As Match = Regex.Match(retStr, "(^|[^a-zA-Z0-9_])[@@]([a-zA-Z0-9_]{1,20})")
- While m.Success
- If Not AtList.Contains(m.Result("$2").ToLower) Then AtList.Add(m.Result("$2").ToLower)
- m = m.NextMatch
- End While
- '@先をリンクに置換
- retStr = Regex.Replace(retStr,
- "(^|[^a-zA-Z0-9_/])([@@])([a-zA-Z0-9_]{1,20})",
- "$1$2<a href=""/$3"">$3</a>")
-
- 'ハッシュタグを抽出し、リンクに置換
- Dim anchorRange As New List(Of range)
- For i As Integer = 0 To retStr.Length - 1
- Dim index As Integer = retStr.IndexOf("<a ", i)
- If index > -1 AndAlso index < retStr.Length Then
- i = index
- Dim toIndex As Integer = retStr.IndexOf("</a>", index)
- If toIndex > -1 Then
- anchorRange.Add(New range(index, toIndex + 3))
- i = toIndex
- End If
- End If
- Next
- 'retStr = Regex.Replace(retStr,
- ' "(^|[^a-zA-Z0-9/&])([##])([0-9a-zA-Z_]*[a-zA-Z_]+[a-zA-Z0-9_\xc0-\xd6\xd8-\xf6\xf8-\xff]*)",
- ' New MatchEvaluator(Function(mh As Match)
- ' For Each rng As range In anchorRange
- ' If mh.Index >= rng.fromIndex AndAlso
- ' mh.Index <= rng.toIndex Then Return mh.Result("$0")
- ' Next
- ' If IsNumeric(mh.Result("$3")) Then Return mh.Result("$0")
- ' SyncLock LockObj
- ' _hashList.Add("#" + mh.Result("$3"))
- ' End SyncLock
- ' Return mh.Result("$1") + "<a href=""" & _protocol & "twitter.com/search?q=%23" + mh.Result("$3") + """>" + mh.Result("$2$3") + "</a>"
- ' End Function),
- ' RegexOptions.IgnoreCase)
- retStr = Regex.Replace(retStr,
- HASHTAG,
- New MatchEvaluator(Function(mh As Match)
- For Each rng As range In anchorRange
- If mh.Index >= rng.fromIndex AndAlso
- mh.Index <= rng.toIndex Then Return mh.Result("$0")
- Next
- SyncLock LockObj
- _hashList.Add("#" + mh.Result("$3"))
- End SyncLock
- Return mh.Result("$1") + "<a href=""" & _protocol & "twitter.com/search?q=%23" + mh.Result("$3") + """>" + mh.Result("$2$3") + "</a>"
- End Function),
- RegexOptions.IgnoreCase)
-
-
- 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>")
-
- retStr = retStr.Replace("<<<<<tweenだいなり>>>>>", ">").Replace("<<<<<tweenしょうなり>>>>>", "<")
-
- 'retStr = AdjustHtml(ShortUrl.Resolve(PreProcessUrl(retStr), True)) 'IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
- retStr = AdjustHtml(PreProcessUrl(retStr)) 'IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
- Return retStr
- End Function
-
- Private Class EntityInfo
- Public Property StartIndex As Integer
- Public Property EndIndex As Integer
- Public Property Text As String
- Public Property Html As String
- Public Property Display As String
- End Class
- 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
- Dim ret As String = Text
-
- If entities IsNot Nothing Then
- Dim etInfo As New SortedList(Of Integer, EntityInfo)
- 'URL
- If entities.Urls IsNot Nothing Then
- For Each ent In entities.Urls
- If String.IsNullOrEmpty(ent.DisplayUrl) Then
- etInfo.Add(ent.Indices(0),
- New EntityInfo With {.StartIndex = ent.Indices(0),
- .EndIndex = ent.Indices(1),
- .Text = ent.Url,
- .Html = "<a href=""" + ent.Url + """>" + ent.Url + "</a>"})
- Else
- Dim expanded As String = ShortUrl.ResolveMedia(ent.ExpandedUrl, False)
- etInfo.Add(ent.Indices(0),
- New EntityInfo With {.StartIndex = ent.Indices(0),
- .EndIndex = ent.Indices(1),
- .Text = ent.Url,
- .Html = "<a href=""" + ent.Url + """ title=""" + expanded + """>" + ent.DisplayUrl + "</a>",
- .Display = ent.DisplayUrl})
- If media IsNot Nothing AndAlso Not media.ContainsKey(ent.Url) Then media.Add(ent.Url, expanded)
- End If
- Next
- End If
- If entities.Hashtags IsNot Nothing Then
- For Each ent In entities.Hashtags
- Dim hash As String = Text.Substring(ent.Indices(0), ent.Indices(1) - ent.Indices(0))
- etInfo.Add(ent.Indices(0),
- New EntityInfo With {.StartIndex = ent.Indices(0),
- .EndIndex = ent.Indices(1),
- .Text = hash,
- .Html = "<a href=""" & _protocol & "twitter.com/search?q=%23" + ent.Text + """>" + hash + "</a>"})
- SyncLock LockObj
- _hashList.Add("#" + ent.Text)
- End SyncLock
- Next
- End If
- If entities.UserMentions IsNot Nothing Then
- For Each ent In entities.UserMentions
- Dim screenName As String = Text.Substring(ent.Indices(0) + 1, ent.Indices(1) - ent.Indices(0) - 1)
- etInfo.Add(ent.Indices(0) + 1,
- New EntityInfo With {.StartIndex = ent.Indices(0) + 1,
- .EndIndex = ent.Indices(1),
- .Text = ent.ScreenName,
- .Html = "<a href=""/" + ent.ScreenName + """>" + screenName + "</a>"})
- If Not AtList.Contains(ent.ScreenName.ToLower) Then AtList.Add(ent.ScreenName.ToLower)
- Next
- End If
- If entities.Media IsNot Nothing Then
- For Each ent In entities.Media
- If ent.Type = "photo" Then
- etInfo.Add(ent.Indices(0),
- New EntityInfo With {.StartIndex = ent.Indices(0),
- .EndIndex = ent.Indices(1),
- .Text = ent.Url,
- .Html = "<a href=""" + ent.Url + """ title=""" + ent.ExpandedUrl + """>" + ent.DisplayUrl + "</a>",
- .Display = ent.DisplayUrl})
- If media IsNot Nothing AndAlso Not media.ContainsKey(ent.Url) Then media.Add(ent.Url, ent.MediaUrl)
- End If
- Next
- End If
- If etInfo.Count > 0 Then
- Try
- Dim idx As Integer = 0
- ret = ""
- For Each et In etInfo
- ret += Text.Substring(idx, et.Key - idx) + et.Value.Html
- idx = et.Value.EndIndex
- Next
- ret += Text.Substring(idx)
- Catch ex As ArgumentOutOfRangeException
- 'Twitterのバグで不正なエンティティ(Index指定範囲が重なっている)が返ってくる場合の対応
- ret = Text
- entities = Nothing
- If media IsNot Nothing Then media.Clear()
- End Try
- End If
- End If
-
- 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>")
- ret = AdjustHtml(ShortUrl.Resolve(PreProcessUrl(ret), False)) 'IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
-
- Return ret
- End Function
-
- 'Source整形
- Private Sub CreateSource(ByRef post As PostClass)
- If post.Source.StartsWith("<") Then
- If Not post.Source.Contains("</a>") Then
- post.Source += "</a>"
- End If
- Dim mS As Match = Regex.Match(post.Source, ">(?<source>.+)<")
- If mS.Success Then
- post.SourceHtml = String.Copy(ShortUrl.Resolve(PreProcessUrl(post.Source), False))
- post.Source = HttpUtility.HtmlDecode(mS.Result("${source}"))
- Else
- post.Source = ""
- post.SourceHtml = ""
- End If
- Else
- If post.Source = "web" Then
- post.SourceHtml = My.Resources.WebSourceString
- ElseIf post.Source = "Keitai Mail" Then
- post.SourceHtml = My.Resources.KeitaiMailSourceString
- Else
- post.SourceHtml = String.Copy(post.Source)
- End If
- End If
- End Sub
-
- Public Function GetInfoApi(ByVal info As ApiInfo) As Boolean
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return True
-
- If _endingFlag Then Return True
-
- Dim res As HttpStatusCode
- Dim content As String = ""
- Try
- res = twCon.RateLimitStatus(content)
- Catch ex As Exception
- TwitterApiInfo.Initialize()
- Return False
- End Try
-
- If res <> HttpStatusCode.OK Then Return False
-
- Try
- Dim limit = CreateDataFromJson(Of TwitterDataModel.RateLimitStatus)(content)
- Dim arg As New ApiInformationChangedEventArgs
- arg.ApiInfo.MaxCount = limit.HourlyLimit
- arg.ApiInfo.RemainCount = limit.RemainingHits
- arg.ApiInfo.ResetTime = DateTimeParse(limit.RestTime)
- arg.ApiInfo.ResetTimeInSeconds = limit.RestTimeInSeconds
- If info IsNot Nothing Then
- arg.ApiInfo.UsingCount = info.UsingCount
-
- info.MaxCount = arg.ApiInfo.MaxCount
- info.RemainCount = arg.ApiInfo.RemainCount
- info.ResetTime = arg.ApiInfo.ResetTime
- info.ResetTimeInSeconds = arg.ApiInfo.ResetTimeInSeconds
- End If
-
- RaiseEvent ApiInformationChanged(Me, arg)
- TwitterApiInfo.WriteBackEventArgs(arg)
- Return True
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- TwitterApiInfo.Initialize()
- Return False
- End Try
- End Function
- Public Function GetBlockUserIds() As String
- If Twitter.AccountState <> ACCOUNT_STATE.Valid Then Return ""
-
- Dim res As HttpStatusCode
- Dim content As String = ""
-
- Try
- res = twCon.GetBlockUserIds(content)
- Catch ex As Exception
- Return "Err:" + ex.Message + "(" + GetCurrentMethod.Name + ")"
- End Try
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- Return My.Resources.Unauthorized
- Case HttpStatusCode.BadRequest
- Return "Err:API Limits?"
- Case Else
- Return "Err:" + res.ToString() + "(" + GetCurrentMethod.Name + ")"
- End Select
-
- Try
- Dim Ids = CreateDataFromJson(Of List(Of Long))(content)
- If Ids.Contains(Me.UserId) Then Ids.Remove(Me.UserId)
- TabInformations.GetInstance.BlockIds.AddRange(Ids)
- Return ("")
- Catch ex As SerializationException
- TraceOut(ex.Message + Environment.NewLine + content)
- Return "Err:Json Parse Error(DataContractJsonSerializer)"
- Catch ex As Exception
- TraceOut(ex, GetCurrentMethod.Name & " " & content)
- Return "Err:Invalid Json!"
- End Try
-
- End Function
-
- Public Function GetHashList() As String()
- Dim hashArray As String()
- SyncLock LockObj
- hashArray = _hashList.ToArray
- _hashList.Clear()
- End SyncLock
- Return hashArray
- End Function
-
- Public ReadOnly Property AccessToken() As String
- Get
- Return twCon.AccessToken
- End Get
- End Property
-
- Public ReadOnly Property AccessTokenSecret() As String
- Get
- Return twCon.AccessTokenSecret
- End Get
- End Property
-
- Public Event ApiInformationChanged(ByVal sender As Object, ByVal e As ApiInformationChangedEventArgs)
-
- Private Sub Twitter_ApiInformationChanged(ByVal sender As Object, ByVal e As ApiInformationChangedEventArgs) Handles Me.ApiInformationChanged
- End Sub
-
-#Region "UserStream"
- Public Property TrackWord As String = ""
- Public Property AllAtReply As Boolean = False
-
- Public Event NewPostFromStream()
- Public Event UserStreamStarted()
- Public Event UserStreamStopped()
- Public Event UserStreamGetFriendsList()
- Public Event PostDeleted(ByVal id As Long)
- Public Event UserStreamEventReceived(ByVal eventType As FormattedEvent)
- Private _lastUserstreamDataReceived As DateTime
- Private WithEvents userStream As TwitterUserstream
-
- Public Class FormattedEvent
- Public Property Eventtype As EVENTTYPE
- Public Property CreatedAt As DateTime
- Public Property [Event] As String
- Public Property Username As String
- Public Property Target As String
- Public Property Id As Int64
- Public Property IsMe As Boolean
- End Class
-
- Public Property StoredEvent As New List(Of FormattedEvent)
-
- Private Class EventTypeTableElement
- Public Name As String
- Public Type As EVENTTYPE
-
- Public Sub New(ByVal name As String, ByVal type As EVENTTYPE)
- Me.Name = name
- Me.Type = type
- End Sub
- End Class
-
- Private EventTable As EventTypeTableElement() = {
- New EventTypeTableElement("favorite", EVENTTYPE.Favorite), _
- New EventTypeTableElement("unfavorite", EVENTTYPE.Unfavorite), _
- New EventTypeTableElement("follow", EVENTTYPE.Follow), _
- New EventTypeTableElement("list_member_added", EVENTTYPE.ListMemberAdded), _
- New EventTypeTableElement("list_member_removed", EVENTTYPE.ListMemberRemoved), _
- New EventTypeTableElement("block", EVENTTYPE.Block), _
- New EventTypeTableElement("unblock", EVENTTYPE.Unblock), _
- New EventTypeTableElement("user_update", EVENTTYPE.UserUpdate), _
- New EventTypeTableElement("deleted", EVENTTYPE.Deleted), _
- New EventTypeTableElement("list_created", EVENTTYPE.ListCreated), _
- New EventTypeTableElement("list_updated", EVENTTYPE.ListUpdated)
- }
-
- Public Function EventNameToEventType(ByVal EventName As String) As EVENTTYPE
- Return (From tbl In EventTable Where tbl.Name.Equals(EventName) Select tbl.Type).FirstOrDefault()
- End Function
-
- Public ReadOnly Property IsUserstreamDataReceived As Boolean
- Get
- Return Now.Subtract(Me._lastUserstreamDataReceived).TotalSeconds < 31
- End Get
- End Property
-
- Private Sub userStream_StatusArrived(ByVal line As String) Handles userStream.StatusArrived
- Me._lastUserstreamDataReceived = Now
- If String.IsNullOrEmpty(line) Then Exit Sub
-
- Dim isDm As Boolean = False
-
- Try
- Using jsonReader As XmlDictionaryReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(line), XmlDictionaryReaderQuotas.Max)
- Dim xElm As XElement = XElement.Load(jsonReader)
- If xElm.Element("friends") IsNot Nothing Then
- Debug.Print("friends")
- Exit Sub
- ElseIf xElm.Element("delete") IsNot Nothing Then
- Debug.Print("delete")
- Dim id As Int64
- If xElm.Element("delete").Element("direct_message") IsNot Nothing AndAlso
- xElm.Element("delete").Element("direct_message").Element("id") IsNot Nothing Then
- id = CLng(xElm.Element("delete").Element("direct_message").Element("id").Value)
- RaiseEvent PostDeleted(id)
- ElseIf xElm.Element("delete").Element("status") IsNot Nothing AndAlso
- xElm.Element("delete").Element("status").Element("id") IsNot Nothing Then
- id = CLng(xElm.Element("delete").Element("status").Element("id").Value)
- RaiseEvent PostDeleted(id)
- Else
- TraceOut("delete:" + line)
- Exit Sub
- End If
- For i As Integer = Me.StoredEvent.Count - 1 To 0 Step -1
- Dim sEvt As FormattedEvent = Me.StoredEvent(i)
- If sEvt.Id = id AndAlso (sEvt.Event = "favorite" OrElse sEvt.Event = "unfavorite") Then
- Me.StoredEvent.RemoveAt(i)
- End If
- Next
- Exit Sub
- ElseIf xElm.Element("limit") IsNot Nothing Then
- Debug.Print(line)
- Exit Sub
- ElseIf xElm.Element("event") IsNot Nothing Then
- Debug.Print("event: " + xElm.Element("event").Value)
- CreateEventFromJson(line)
- Exit Sub
- ElseIf xElm.Element("direct_message") IsNot Nothing Then
- Debug.Print("direct_message")
- isDm = True
- ElseIf xElm.Element("scrub_geo") IsNot Nothing Then
- Try
- TabInformations.GetInstance.ScrubGeoReserve(Long.Parse(xElm.Element("scrub_geo").Element("user_id").Value),
- Long.Parse(xElm.Element("scrub_geo").Element("up_to_status_id").Value))
- Catch ex As Exception
- TraceOut("scrub_geo:" + line)
- End Try
- Exit Sub
- End If
- End Using
-
- Dim res As New StringBuilder
- res.Length = 0
- res.Append("[")
- res.Append(line)
- res.Append("]")
-
- If isDm Then
- CreateDirectMessagesFromJson(res.ToString, WORKERTYPE.UserStream, False)
- Else
- CreatePostsFromJson(res.ToString, WORKERTYPE.Timeline, Nothing, False, Nothing, Nothing)
- End If
- Catch ex As NullReferenceException
- TraceOut("NullRef StatusArrived: " + line)
- End Try
-
- RaiseEvent NewPostFromStream()
- End Sub
-
- Private Sub CreateEventFromJson(ByVal content As String)
- Dim eventData As TwitterDataModel.EventData = Nothing
- Try
- eventData = CreateDataFromJson(Of TwitterDataModel.EventData)(content)
- Catch ex As SerializationException
- TraceOut(ex, "Event Serialize Exception!" + Environment.NewLine + content)
- Catch ex As Exception
- TraceOut(ex, "Event Exception!" + Environment.NewLine + content)
- End Try
-
- Dim evt As New FormattedEvent
- evt.CreatedAt = DateTimeParse(eventData.CreatedAt)
- evt.Event = eventData.Event
- evt.Username = eventData.Source.ScreenName
- evt.IsMe = evt.Username.ToLower().Equals(Me.Username.ToLower())
- evt.Eventtype = EventNameToEventType(evt.Event)
- Select Case eventData.Event
- Case "access_revoked"
- Exit Sub
- Case "follow"
- If eventData.Target.ScreenName.ToLower.Equals(_uname) Then
- If Not Me.followerId.Contains(eventData.Source.Id) Then Me.followerId.Add(eventData.Source.Id)
- Else
- Exit Sub 'Block後のUndoをすると、SourceとTargetが逆転したfollowイベントが帰ってくるため。
- End If
- evt.Target = ""
- Case "favorite", "unfavorite"
- evt.Target = "@" + eventData.TargetObject.User.ScreenName + ":" + HttpUtility.HtmlDecode(eventData.TargetObject.Text)
- evt.Id = eventData.TargetObject.Id
- If AppendSettingDialog.Instance.IsRemoveSameEvent Then
- If StoredEvent.Any(Function(ev As FormattedEvent)
- Return ev.Username = evt.Username AndAlso ev.Eventtype = evt.Eventtype AndAlso ev.Target = evt.Target
- End Function) Then Exit Sub
- End If
- If TabInformations.GetInstance.ContainsKey(eventData.TargetObject.Id) Then
- Dim post As PostClass = TabInformations.GetInstance.Item(eventData.TargetObject.Id)
- If eventData.Event = "favorite" Then
- If evt.Username.ToLower.Equals(_uname) Then
- post.IsFav = True
- TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites).Add(post.StatusId, post.IsRead, False)
- Else
- post.FavoritedCount += 1
- If Not TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites).Contains(post.StatusId) Then
- If AppendSettingDialog.Instance.FavEventUnread AndAlso post.IsRead Then
- post.IsRead = False
- End If
- TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites).Add(post.StatusId, post.IsRead, False)
- Else
- If AppendSettingDialog.Instance.FavEventUnread Then
- TabInformations.GetInstance.SetRead(False, TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites).TabName, TabInformations.GetInstance.GetTabByType(TabUsageType.Favorites).IndexOf(post.StatusId))
- End If
- End If
- End If
- Else
- If evt.Username.ToLower.Equals(_uname) Then
- post.IsFav = False
- Else
- post.FavoritedCount -= 1
- If post.FavoritedCount < 0 Then post.FavoritedCount = 0
- End If
- End If
- End If
- Case "list_member_added", "list_member_removed", "list_updated"
- evt.Target = eventData.TargetObject.FullName
- Case "block"
- If Not TabInformations.GetInstance.BlockIds.Contains(eventData.Target.Id) Then TabInformations.GetInstance.BlockIds.Add(eventData.Target.Id)
- evt.Target = ""
- Case "unblock"
- If TabInformations.GetInstance.BlockIds.Contains(eventData.Target.Id) Then TabInformations.GetInstance.BlockIds.Remove(eventData.Target.Id)
- evt.Target = ""
- Case "user_update"
- evt.Target = ""
- Case "list_created"
- evt.Target = ""
- Case Else
- TraceOut("Unknown Event:" + evt.Event + Environment.NewLine + content)
- End Select
- Me.StoredEvent.Insert(0, evt)
- RaiseEvent UserStreamEventReceived(evt)
- End Sub
-
- Private Sub userStream_Started() Handles userStream.Started
- RaiseEvent UserStreamStarted()
- End Sub
-
- Private Sub userStream_Stopped() Handles userStream.Stopped
- RaiseEvent UserStreamStopped()
- End Sub
-
- Public ReadOnly Property UserStreamEnabled As Boolean
- Get
- Return If(userStream Is Nothing, False, userStream.Enabled)
- End Get
- End Property
-
- Public Sub StartUserStream()
- If userStream IsNot Nothing Then
- StopUserStream()
- End If
- userStream = New TwitterUserstream(twCon)
- userStream.Start(Me.AllAtReply, Me.TrackWord)
- End Sub
-
- Public Sub StopUserStream()
- If userStream IsNot Nothing Then userStream.Dispose()
- userStream = Nothing
- If Not _endingFlag Then RaiseEvent UserStreamStopped()
- End Sub
-
- Public Sub ReconnectUserStream()
- If userStream IsNot Nothing Then
- Me.StartUserStream()
- End If
- End Sub
-
- Private Class TwitterUserstream
- Implements IDisposable
-
- Public Event StatusArrived(ByVal status As String)
- Public Event Stopped()
- Public Event Started()
- Private twCon As HttpTwitter
-
- Private _streamThread As Thread
- Private _streamActive As Boolean
-
- Private _allAtreplies As Boolean = False
- Private _trackwords As String = ""
-
- Public Sub New(ByVal twitterConnection As HttpTwitter)
- twCon = DirectCast(twitterConnection.Clone(), HttpTwitter)
- End Sub
-
- Public Sub Start(ByVal allAtReplies As Boolean, ByVal trackwords As String)
- Me.AllAtReplies = allAtReplies
- Me.TrackWords = trackwords
- _streamActive = True
- If _streamThread IsNot Nothing AndAlso _streamThread.IsAlive Then Exit Sub
- _streamThread = New Thread(AddressOf UserStreamLoop)
- _streamThread.Name = "UserStreamReceiver"
- _streamThread.IsBackground = True
- _streamThread.Start()
- End Sub
-
- Public ReadOnly Property Enabled() As Boolean
- Get
- Return _streamActive
- End Get
- End Property
-
- Public Property AllAtReplies As Boolean
- Get
- Return _allAtreplies
- End Get
- Set(ByVal value As Boolean)
- _allAtreplies = value
- End Set
- End Property
-
- Public Property TrackWords As String
- Get
- Return _trackwords
- End Get
- Set(ByVal value As String)
- _trackwords = value
- End Set
- End Property
-
- Private Sub UserStreamLoop()
- Dim st As Stream = Nothing
- Dim sr As StreamReader = Nothing
- Dim sleepSec As Integer = 0
- Do
- Try
- If Not MyCommon.IsNetworkAvailable() Then
- sleepSec = 30
- Continue Do
- End If
-
- RaiseEvent Started()
- Dim res As HttpStatusCode = twCon.UserStream(st, _allAtreplies, _trackwords, GetUserAgentString())
-
- Select Case res
- Case HttpStatusCode.OK
- Twitter.AccountState = ACCOUNT_STATE.Valid
- Case HttpStatusCode.Unauthorized
- Twitter.AccountState = ACCOUNT_STATE.Invalid
- sleepSec = 120
- Continue Do
- End Select
-
- If st Is Nothing Then
- sleepSec = 30
- 'TraceOut("Stop:stream is Nothing")
- Continue Do
- End If
-
- sr = New StreamReader(st)
-
- Do While _streamActive AndAlso Not sr.EndOfStream AndAlso Twitter.AccountState = ACCOUNT_STATE.Valid
- RaiseEvent StatusArrived(sr.ReadLine())
- 'Me.LastTime = Now
- Loop
-
- If sr.EndOfStream OrElse Twitter.AccountState = ACCOUNT_STATE.Invalid Then
- sleepSec = 30
- 'TraceOut("Stop:EndOfStream")
- Continue Do
- End If
- Exit Do
- Catch ex As WebException
- If ex.Status = WebExceptionStatus.Timeout Then
- sleepSec = 30 'TraceOut("Stop:Timeout")
- ElseIf ex.Response IsNot Nothing AndAlso CType(ex.Response, HttpWebResponse).StatusCode = 420 Then
- 'TraceOut("Stop:Connection Limit")
- Exit Do
- Else
- sleepSec = 30
- 'TraceOut("Stop:WebException " & ex.Status.ToString)
- End If
- Catch ex As ThreadAbortException
- Exit Do
- Catch ex As IOException
- sleepSec = 30
- 'TraceOut("Stop:IOException with Active." + Environment.NewLine + ex.Message)
- Catch ex As ArgumentException
- 'System.ArgumentException: ストリームを読み取れませんでした。
- 'サーバー側もしくは通信経路上で切断された場合?タイムアウト頻発後発生
- sleepSec = 30
- TraceOut(ex, "Stop:ArgumentException")
- Catch ex As Exception
- TraceOut("Stop:Exception." + Environment.NewLine + ex.Message)
- ExceptionOut(ex)
- sleepSec = 30
- Finally
- If _streamActive Then RaiseEvent Stopped()
- twCon.RequestAbort()
- If sr IsNot Nothing Then sr.Close()
- If st IsNot Nothing Then st.Close()
- If sleepSec > 0 Then
- Dim ms As Integer = 0
- Do While _streamActive AndAlso ms < sleepSec * 1000
- Thread.Sleep(500)
- ms += 500
- Loop
- End If
- sleepSec = 0
- End Try
- Loop While Me._streamActive
-
- If _streamActive Then RaiseEvent Stopped()
- TraceOut("Stop:EndLoop")
- End Sub
-
-#Region "IDisposable Support"
- Private disposedValue As Boolean ' 重複する呼び出しを検出するには
-
- ' IDisposable
- Protected Overridable Sub Dispose(ByVal disposing As Boolean)
- If Not Me.disposedValue Then
- If disposing Then
- ' TODO: マネージ状態を破棄します (マネージ オブジェクト)。
- _streamActive = False
- If _streamThread IsNot Nothing AndAlso _streamThread.IsAlive Then
- _streamThread.Abort()
- End If
- End If
-
- ' TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下の Finalize() をオーバーライドします。
- ' TODO: 大きなフィールドを null に設定します。
- End If
- Me.disposedValue = True
- End Sub
-
- ' TODO: 上の Dispose(ByVal disposing As Boolean) にアンマネージ リソースを解放するコードがある場合にのみ、Finalize() をオーバーライドします。
- 'Protected Overrides Sub Finalize()
- ' ' このコードを変更しないでください。クリーンアップ コードを上の Dispose(ByVal disposing As Boolean) に記述します。
- ' Dispose(False)
- ' MyBase.Finalize()
- 'End Sub
-
- ' このコードは、破棄可能なパターンを正しく実装できるように Visual Basic によって追加されました。
- Public Sub Dispose() Implements IDisposable.Dispose
- ' このコードを変更しないでください。クリーンアップ コードを上の Dispose(ByVal disposing As Boolean) に記述します。
- Dispose(True)
- GC.SuppressFinalize(Me)
- End Sub
-#End Region
-
- End Class
-#End Region
-
-#Region "IDisposable Support"
- Private disposedValue As Boolean ' 重複する呼び出しを検出するには
-
- ' IDisposable
- Protected Overridable Sub Dispose(ByVal disposing As Boolean)
- If Not Me.disposedValue Then
- If disposing Then
- ' TODO: マネージ状態を破棄します (マネージ オブジェクト)。
- Me.StopUserStream()
- End If
-
- ' TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下の Finalize() をオーバーライドします。
- ' TODO: 大きなフィールドを null に設定します。
- End If
- Me.disposedValue = True
- End Sub
-
- ' TODO: 上の Dispose(ByVal disposing As Boolean) にアンマネージ リソースを解放するコードがある場合にのみ、Finalize() をオーバーライドします。
- 'Protected Overrides Sub Finalize()
- ' ' このコードを変更しないでください。クリーンアップ コードを上の Dispose(ByVal disposing As Boolean) に記述します。
- ' Dispose(False)
- ' MyBase.Finalize()
- 'End Sub
-
- ' このコードは、破棄可能なパターンを正しく実装できるように Visual Basic によって追加されました。
- Public Sub Dispose() Implements IDisposable.Dispose
- ' このコードを変更しないでください。クリーンアップ コードを上の Dispose(ByVal disposing As Boolean) に記述します。
- Dispose(True)
- GC.SuppressFinalize(Me)
- End Sub
-#End Region
-
-End Class
--- /dev/null
+// Tween - Client of Twitter
+// Copyright (c) 2007-2011 kiri_feather (@kiri_feather) <kiri.feather@gmail.com>
+// (c) 2008-2011 Moz (@syo68k)
+// (c) 2008-2011 takeshik (@takeshik) <http://www.takeshik.org/>
+// (c) 2010-2011 anis774 (@anis774) <http://d.hatena.ne.jp/anis774/>
+// (c) 2010-2011 fantasticswallow (@f_swallow) <http://twitter.com/f_swallow>
+// (c) 2011 Egtra (@egtra) <http://dev.activebasic.com/egtra/>
+// All rights reserved.
+//
+// This file is part of Tween.
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by the Free
+// Software Foundation; either version 3 of the License, or (at your option)
+// any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+// for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program. If not, see <http://www.gnu.org/licenses/>, or write to
+// the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
+// Boston, MA 02110-1301, USA.
+
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Json;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Web;
+using System.Xml;
+using System.Xml.Linq;
+using System;
+using System.Reflection;
+using System.Collections.Generic;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Tween
+{
+ public class Twitter : IDisposable
+ {
+ //Hashtag用正規表現
+ private const string LATIN_ACCENTS = @"\xc0-\xd6\xd8-\xf6\xf8-\xff";
+ private const string NON_LATIN_HASHTAG_CHARS = @"\u0400-\u04ff\u0500-\u0527\u1100-\u11ff\u3130-\u3185\uA960-\uA97F\uAC00-\uD7AF\uD7B0-\uD7FF";
+ //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";
+ private const string CJ_HASHTAG_CHARACTERS = @"\u30A1-\u30FA\u30FC\u3005\uFF66-\uFF9F\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\u3041-\u309A\u3400-\u4DBF\p{IsCJKUnifiedIdeographs}";
+ private const string HASHTAG_BOUNDARY = @"^|$|\s|「|」|。|\.|!";
+ private const string HASHTAG_ALPHA = "[a-z_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]";
+ private const string HASHTAG_ALPHANUMERIC = "[a-z0-9_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]";
+ private const string HASHTAG_TERMINATOR = "[^a-z0-9_" + LATIN_ACCENTS + NON_LATIN_HASHTAG_CHARS + CJ_HASHTAG_CHARACTERS + "]";
+ public const string HASHTAG = "(" + HASHTAG_BOUNDARY + ")(#|#)(" + HASHTAG_ALPHANUMERIC + "*" + HASHTAG_ALPHA + HASHTAG_ALPHANUMERIC + "*)(?=" + HASHTAG_TERMINATOR + "|" + HASHTAG_BOUNDARY + ")";
+ //URL正規表現
+ private const string url_valid_domain = @"(?<domain>(?:[^\p{P}\s][\.\-_](?=[^\p{P}\s])|[^\p{P}\s]){1,}\.[a-z]{2,}(?::[0-9]+)?)";
+ private const string url_valid_general_path_chars = @"[a-z0-9!*';:=+$/%#\[\]\-_&,~]";
+ private const string url_balance_parens = @"(?:\(" + url_valid_general_path_chars + @"+\))";
+ private const string url_valid_url_path_ending_chars = @"(?:[a-z0-9=_#/\-\+]+|" + url_balance_parens + ")";
+ private const string pth = "(?:" + url_balance_parens +
+ "|@" + url_valid_general_path_chars + "+/" +
+ "|[.,]?" + url_valid_general_path_chars + "+" +
+ ")";
+ private const string pth2 = "(/(?:" +
+ pth + "+" + url_valid_url_path_ending_chars + "|" +
+ pth + "+" + url_valid_url_path_ending_chars + "?|" +
+ url_valid_url_path_ending_chars +
+ ")?)?";
+ private const string qry = @"(?<query>\?[a-z0-9!*'();:&=+$/%#\[\]\-_.,~]*[a-z0-9_&=#])?";
+ public const string rgUrl = @"(?<before>(?:[^\""':!=#]|^|\:/))" +
+ "(?<url>(?<protocol>https?://)" +
+ url_valid_domain +
+ pth2 +
+ qry +
+ ")";
+ delegate void GetIconImageDelegate(PostClass post);
+ private readonly object LockObj = new object();
+ private List<long> followerId = new List<long>();
+ private bool _GetFollowerResult = false;
+ private List<long> noRTId = new List<long>();
+ private bool _GetNoRetweetResult = false;
+
+ private int _followersCount = 0;
+ private int _friendsCount = 0;
+ private int _statusesCount = 0;
+ private string _location = "";
+ private string _bio = "";
+ private string _protocol = "https://";
+
+ //プロパティからアクセスされる共通情報
+ private string _uname;
+ private int _iconSz;
+ private bool _getIcon;
+ private IDictionary<string, Image> _dIcon;
+
+ private bool _tinyUrlResolve;
+ private bool _restrictFavCheck;
+
+ private string _hubServer;
+ private bool _readOwnPost;
+ private List<string> _hashList = new List<string>();
+
+ //共通で使用する状態
+ private int _remainCountApi = -1;
+
+ private Outputz op = new Outputz();
+ //max_idで古い発言を取得するために保持(lists分は個別タブで管理)
+ private long minHomeTimeline = long.MaxValue;
+ private long minMentions = long.MaxValue;
+ private long minDirectmessage = long.MaxValue;
+ private long minDirectmessageSent = long.MaxValue;
+
+ //private FavoriteQueue favQueue;
+
+ private HttpTwitter twCon = new HttpTwitter();
+
+ //public Event UserIdChanged();
+ public event Action UserIdChanged;
+
+ //private List<PostClass> _deletemessages = new List<PostClass>();
+
+ public string Authenticate(string username, string password)
+ {
+ HttpStatusCode res;
+ var content = "";
+
+ MyCommon.TwitterApiInfo.Initialize();
+ try
+ {
+ res = twCon.AuthUserAndPass(username, password, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ _uname = username.ToLower();
+ if (AppendSettingDialog.Instance.UserstreamStartup) this.ReconnectUserStream();
+ return "";
+ case HttpStatusCode.Unauthorized:
+ {
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return Properties.Resources.Unauthorized + Environment.NewLine + content;
+ }
+ else
+ {
+ return "Auth error:" + errMsg;
+ }
+ }
+ case HttpStatusCode.Forbidden:
+ {
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return "Err:Forbidden";
+ }
+ else
+ {
+ return "Err:" + errMsg;
+ }
+ }
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string StartAuthentication(ref string pinPageUrl)
+ {
+ //OAuth PIN Flow
+ bool res;
+ var content = "";
+
+ MyCommon.TwitterApiInfo.Initialize();
+ try
+ {
+ res = twCon.AuthGetRequestToken(ref pinPageUrl);
+ }
+ catch(Exception)
+ {
+ return "Err:" + "Failed to access auth server.";
+ }
+
+ return "";
+ }
+
+ public string Authenticate(string pinCode)
+ {
+ HttpStatusCode res;
+ var content = "";
+
+ MyCommon.TwitterApiInfo.Initialize();
+ try
+ {
+ res = twCon.AuthGetAccessToken(pinCode);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + "Failed to access auth acc server.";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ _uname = Username.ToLower();
+ if (AppendSettingDialog.Instance.UserstreamStartup) this.ReconnectUserStream();
+ return "";
+ case HttpStatusCode.Unauthorized:
+ {
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return "Check the PIN or retry." + Environment.NewLine + content;
+ }
+ else
+ {
+ return "Auth error:" + errMsg;
+ }
+ }
+ case HttpStatusCode.Forbidden:
+ {
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return "Err:Forbidden";
+ }
+ else
+ {
+ return "Err:" + errMsg;
+ }
+ }
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public void ClearAuthInfo()
+ {
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ MyCommon.TwitterApiInfo.Initialize();
+ twCon.ClearAuthInfo();
+ }
+
+ public void VerifyCredentials()
+ {
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ try
+ {
+ res = twCon.VerifyCredentials(ref content);
+ }
+ catch(Exception)
+ {
+ return;
+ }
+
+ if (res == HttpStatusCode.OK)
+ {
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ TwitterDataModel.User user;
+ try
+ {
+ user = MyCommon.CreateDataFromJson<TwitterDataModel.User>(content);
+ }
+ catch(SerializationException)
+ {
+ return;
+ }
+ twCon.AuthenticatedUserId = user.Id;
+ }
+ }
+
+ private string GetErrorMessageJson(string content)
+ {
+ try
+ {
+ if (!string.IsNullOrEmpty(content))
+ {
+ using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(content), XmlDictionaryReaderQuotas.Max))
+ {
+ var xElm = XElement.Load(jsonReader);
+ if (xElm.Element("error") != null)
+ {
+ return xElm.Element("error").Value;
+ }
+ else
+ {
+ return "";
+ }
+ }
+ }
+ else
+ {
+ return "";
+ }
+ }
+ catch(Exception)
+ {
+ return "";
+ }
+ }
+
+ public void Initialize(string token, string tokenSecret, string username, long userId)
+ {
+ //OAuth認証
+ if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(tokenSecret) || string.IsNullOrEmpty(username))
+ {
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ }
+ MyCommon.TwitterApiInfo.Initialize();
+ twCon.Initialize(token, tokenSecret, username, userId);
+ _uname = username.ToLower();
+ if (AppendSettingDialog.Instance.UserstreamStartup) this.ReconnectUserStream();
+ }
+
+ public string PreProcessUrl(string orgData)
+ {
+ int posl1;
+ var posl2 = 0;
+ //var IDNConveter = new IdnMapping();
+ var href = "<a href=\"";
+
+ while (true)
+ {
+ if (orgData.IndexOf(href, posl2, StringComparison.Ordinal) > -1)
+ {
+ var urlStr = "";
+ // IDN展開
+ posl1 = orgData.IndexOf(href, posl2, StringComparison.Ordinal);
+ posl1 += href.Length;
+ posl2 = orgData.IndexOf("\"", posl1, StringComparison.Ordinal);
+ urlStr = orgData.Substring(posl1, posl2 - posl1);
+
+ if (!urlStr.StartsWith("http://") && !urlStr.StartsWith("https://") && !urlStr.StartsWith("ftp://"))
+ {
+ continue;
+ }
+
+ var replacedUrl = MyCommon.IDNDecode(urlStr);
+ if (replacedUrl == null) continue;
+ if (replacedUrl == urlStr) continue;
+
+ orgData = orgData.Replace("<a href=\"" + urlStr, "<a href=\"" + replacedUrl);
+ posl2 = 0;
+ }
+ else
+ {
+ break;
+ }
+ }
+ return orgData;
+ }
+
+ private string GetPlainText(string orgData)
+ {
+ return HttpUtility.HtmlDecode(Regex.Replace(orgData, "(?<tagStart><a [^>]+>)(?<text>[^<]+)(?<tagEnd></a>)", "${text}"));
+ }
+
+ // htmlの簡易サニタイズ(詳細表示に不要なタグの除去)
+
+ private string SanitizeHtml(string orgdata)
+ {
+ var retdata = orgdata;
+
+ retdata = Regex.Replace(retdata, "<(script|object|applet|image|frameset|fieldset|legend|style).*" +
+ "</(script|object|applet|image|frameset|fieldset|legend|style)>", "", RegexOptions.IgnoreCase);
+
+ retdata = Regex.Replace(retdata, "<(frame|link|iframe|img)>", "", RegexOptions.IgnoreCase);
+
+ return retdata;
+ }
+
+ private string AdjustHtml(string orgData)
+ {
+ var retStr = orgData;
+ //var m = Regex.Match(retStr, "<a [^>]+>[#|#](?<1>[a-zA-Z0-9_]+)</a>");
+ //while (m.Success)
+ //{
+ // lock (LockObj)
+ // {
+ // _hashList.Add("#" + m.Groups(1).Value);
+ // }
+ // m = m.NextMatch;
+ //}
+ retStr = Regex.Replace(retStr, "<a [^>]*href=\"/", "<a href=\"" + _protocol + "twitter.com/");
+ retStr = retStr.Replace("<a href=", "<a target=\"_self\" href=");
+ retStr = retStr.Replace("\n", "<br>");
+
+ //半角スペースを置換(Thanks @anis774)
+ var ret = false;
+ do
+ {
+ ret = EscapeSpace(ref retStr);
+ } while (!ret);
+
+ return SanitizeHtml(retStr);
+ }
+
+ private bool EscapeSpace(ref string html)
+ {
+ //半角スペースを置換(Thanks @anis774)
+ var isTag = false;
+ for (int i = 0; i < html.Length; i++)
+ {
+ if (html[i] == '<')
+ {
+ isTag = true;
+ }
+ if (html[i] == '>')
+ {
+ isTag = false;
+ }
+
+ if ((!isTag) && (html[i] == ' '))
+ {
+ html = html.Remove(i, 1);
+ html = html.Insert(i, " ");
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private struct PostInfo
+ {
+ public string CreatedAt;
+ public string Id;
+ public string Text;
+ public string UserId;
+ public PostInfo(string Created, string IdStr, string txt, string uid)
+ {
+ CreatedAt = Created;
+ Id = IdStr;
+ Text = txt;
+ UserId = uid;
+ }
+ public bool Equals(PostInfo dst)
+ {
+ if (this.CreatedAt == dst.CreatedAt && this.Id == dst.Id && this.Text == dst.Text && this.UserId == dst.UserId)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ }
+
+ static private PostInfo _prev = new PostInfo("", "", "", "");
+ private bool IsPostRestricted(TwitterDataModel.Status status)
+ {
+ var _current = new PostInfo("", "", "", "");
+
+ _current.CreatedAt = status.CreatedAt;
+ _current.Id = status.IdStr;
+ if (status.Text == null)
+ {
+ _current.Text = "";
+ }
+ else
+ {
+ _current.Text = status.Text;
+ }
+ _current.UserId = status.User.IdStr;
+
+ if (_current.Equals(_prev))
+ {
+ return true;
+ }
+ _prev.CreatedAt = _current.CreatedAt;
+ _prev.Id = _current.Id;
+ _prev.Text = _current.Text;
+ _prev.UserId = _current.UserId;
+
+ return false;
+ }
+
+ public string PostStatus(string postStr, long reply_to)
+ {
+
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ postStr = postStr.Trim();
+
+ if (Regex.Match(postStr, "^DM? +(?<id>[a-zA-Z0-9_]+) +(?<body>.+)", RegexOptions.IgnoreCase | RegexOptions.Singleline).Success)
+ {
+ return SendDirectMessage(postStr);
+ }
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+ try
+ {
+ res = twCon.UpdateStatus(postStr, reply_to, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ TwitterDataModel.Status status;
+ try
+ {
+ status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+ _followersCount = status.User.FollowersCount;
+ _friendsCount = status.User.FriendsCount;
+ _statusesCount = status.User.StatusesCount;
+ _location = status.User.Location;
+ _bio = status.User.Description;
+
+ if (IsPostRestricted(status))
+ {
+ return "OK:Delaying?";
+ }
+ if (op.Post(postStr.Length))
+ {
+ return "";
+ }
+ else
+ {
+ return "Outputz:Failed";
+ }
+ case HttpStatusCode.NotFound:
+ return "";
+ case HttpStatusCode.Forbidden:
+ case HttpStatusCode.BadRequest:
+ {
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return "Warn:" + res.ToString();
+ }
+ else
+ {
+ return "Warn:" + errMsg;
+ }
+ }
+ case HttpStatusCode.Conflict:
+ case HttpStatusCode.ExpectationFailed:
+ case HttpStatusCode.Gone:
+ case HttpStatusCode.LengthRequired:
+ case HttpStatusCode.MethodNotAllowed:
+ case HttpStatusCode.NotAcceptable:
+ case HttpStatusCode.PaymentRequired:
+ case HttpStatusCode.PreconditionFailed:
+ case HttpStatusCode.RequestedRangeNotSatisfiable:
+ case HttpStatusCode.RequestEntityTooLarge:
+ case HttpStatusCode.RequestTimeout:
+ case HttpStatusCode.RequestUriTooLong:
+ //仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
+ return "Warn:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ case HttpStatusCode.Unauthorized:
+ {
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return Properties.Resources.Unauthorized;
+ }
+ else
+ {
+ return "Auth err:" + errMsg;
+ }
+ }
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string PostStatusWithMedia(string postStr, long reply_to, FileInfo mediaFile)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ postStr = postStr.Trim();
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+ try
+ {
+ res = twCon.UpdateStatusWithMedia(postStr, reply_to, mediaFile, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ TwitterDataModel.Status status;
+ try
+ {
+ status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+ _followersCount = status.User.FollowersCount;
+ _friendsCount = status.User.FriendsCount;
+ _statusesCount = status.User.StatusesCount;
+ _location = status.User.Location;
+ _bio = status.User.Description;
+
+ if (IsPostRestricted(status))
+ {
+ return "OK:Delaying?";
+ }
+ if (op.Post(postStr.Length))
+ {
+ return "";
+ }
+ else
+ {
+ return "Outputz:Failed";
+ }
+ case HttpStatusCode.NotFound:
+ return "";
+ case HttpStatusCode.Forbidden:
+ case HttpStatusCode.BadRequest:
+ {
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return "Warn:" + res.ToString();
+ }
+ else
+ {
+ return "Warn:" + errMsg;
+ }
+ }
+ case HttpStatusCode.Conflict:
+ case HttpStatusCode.ExpectationFailed:
+ case HttpStatusCode.Gone:
+ case HttpStatusCode.LengthRequired:
+ case HttpStatusCode.MethodNotAllowed:
+ case HttpStatusCode.NotAcceptable:
+ case HttpStatusCode.PaymentRequired:
+ case HttpStatusCode.PreconditionFailed:
+ case HttpStatusCode.RequestedRangeNotSatisfiable:
+ case HttpStatusCode.RequestEntityTooLarge:
+ case HttpStatusCode.RequestTimeout:
+ case HttpStatusCode.RequestUriTooLong:
+ //仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
+ return "Warn:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ case HttpStatusCode.Unauthorized:
+ {
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return Properties.Resources.Unauthorized;
+ }
+ else
+ {
+ return "Auth err:" + errMsg;
+ }
+ }
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string SendDirectMessage(string postStr)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+ if (MyCommon.TwitterApiInfo.AccessLevel != ApiAccessLevel.None)
+ {
+ if (!MyCommon.TwitterApiInfo.IsDirectMessagePermission) return "Auth Err:try to re-authorization.";
+ }
+
+ postStr = postStr.Trim();
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ var mc = Regex.Match(postStr, "^DM? +(?<id>[a-zA-Z0-9_]+) +(?<body>.+)", RegexOptions.IgnoreCase | RegexOptions.Singleline);
+
+ try
+ {
+ res = twCon.SendDirectMessage(mc.Groups["body"].Value, mc.Groups["id"].Value, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ TwitterDataModel.Directmessage status;
+ try
+ {
+ status = MyCommon.CreateDataFromJson<TwitterDataModel.Directmessage>(content);
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+ _followersCount = status.Sender.FollowersCount;
+ _friendsCount = status.Sender.FriendsCount;
+ _statusesCount = status.Sender.StatusesCount;
+ _location = status.Sender.Location;
+ _bio = status.Sender.Description;
+
+ if (op.Post(postStr.Length))
+ {
+ return "";
+ }
+ else
+ {
+ return "Outputz:Failed";
+ }
+ case HttpStatusCode.Forbidden:
+ case HttpStatusCode.BadRequest:
+ {
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return "Warn:" + res.ToString();
+ }
+ else
+ {
+ return "Warn:" + errMsg;
+ }
+ }
+ case HttpStatusCode.Conflict:
+ case HttpStatusCode.ExpectationFailed:
+ case HttpStatusCode.Gone:
+ case HttpStatusCode.LengthRequired:
+ case HttpStatusCode.MethodNotAllowed:
+ case HttpStatusCode.NotAcceptable:
+ case HttpStatusCode.NotFound:
+ case HttpStatusCode.PaymentRequired:
+ case HttpStatusCode.PreconditionFailed:
+ case HttpStatusCode.RequestedRangeNotSatisfiable:
+ case HttpStatusCode.RequestEntityTooLarge:
+ case HttpStatusCode.RequestTimeout:
+ case HttpStatusCode.RequestUriTooLong:
+ //仕様書にない400系エラー。サーバまでは到達しているのでリトライしない
+ return "Warn:" + res.ToString();
+ case HttpStatusCode.Unauthorized:
+ {
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return Properties.Resources.Unauthorized;
+ }
+ else
+ {
+ return "Auth err:" + errMsg;
+ }
+ }
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string RemoveStatus(long id)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+
+ try
+ {
+ res = twCon.DestroyStatus(id);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ return "";
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.NotFound:
+ return "";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string PostRetweet(long id, bool read)
+ {
+ if (MyCommon._endingFlag) return "";
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ //データ部分の生成
+ var target = id;
+ var post = TabInformations.GetInstance()[id];
+ if (post == null)
+ {
+ return "Err:Target isn't found.";
+ }
+ if (TabInformations.GetInstance()[id].RetweetedId > 0)
+ {
+ target = TabInformations.GetInstance()[id].RetweetedId; //再RTの場合は元発言をRT
+ }
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+ try
+ {
+ res = twCon.RetweetStatus(target, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+
+ if (res == HttpStatusCode.Unauthorized)
+ {
+ //Blockユーザーの発言をRTすると認証エラー返る
+ //Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid
+ return Properties.Resources.Unauthorized + " or blocked user.";
+ }
+ else if (res != HttpStatusCode.OK)
+ {
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+
+ TwitterDataModel.Status status;
+ try
+ {
+ status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+
+ //ReTweetしたものをTLに追加
+ post = CreatePostsFromStatusData(status);
+ if (post == null) return "Invalid Json!";
+
+ //二重取得回避
+ lock (LockObj)
+ {
+ if (TabInformations.GetInstance().ContainsKey(post.StatusId)) return "";
+ }
+ //Retweet判定
+ if (post.RetweetedId == 0) return "Invalid Json!";
+ //ユーザー情報
+ post.IsMe = true;
+
+ post.IsRead = read;
+ post.IsOwl = false;
+ if (_readOwnPost) post.IsRead = true;
+ post.IsDm = false;
+
+ TabInformations.GetInstance().AddPost(post);
+
+ return "";
+ }
+
+ public string RemoveDirectMessage(long id, PostClass post)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+ if (MyCommon.TwitterApiInfo.AccessLevel != ApiAccessLevel.None)
+ {
+ if (!MyCommon.TwitterApiInfo.IsDirectMessagePermission) return "Auth Err:try to re-authorization.";
+ }
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+
+ //if (post.IsMe)
+ // _deletemessages.Add(post)
+ //}
+ try
+ {
+ res = twCon.DestroyDirectMessage(id);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ return "";
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.NotFound:
+ return "";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string PostFollowCommand(string screenName)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ try
+ {
+ res = twCon.CreateFriendships(screenName, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ return "";
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.Forbidden:
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ else
+ {
+ return "Err:" + errMsg;
+ }
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string PostRemoveCommand(string screenName)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ try
+ {
+ res = twCon.DestroyFriendships(screenName, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ return "";
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.Forbidden:
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ else
+ {
+ return "Err:" + errMsg;
+ }
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string PostCreateBlock(string screenName)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ try
+ {
+ res = twCon.CreateBlock(screenName, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ return "";
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.Forbidden:
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ else
+ {
+ return "Err:" + errMsg;
+ }
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string PostDestroyBlock(string screenName)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ try
+ {
+ res = twCon.DestroyBlock(screenName, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ return "";
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.Forbidden:
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ else
+ {
+ return "Err:" + errMsg;
+ }
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string PostReportSpam(string screenName)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ try
+ {
+ res = twCon.ReportSpam(screenName, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ return "";
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.Forbidden:
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ else
+ {
+ return "Err:" + errMsg;
+ }
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string GetFriendshipInfo(string screenName, ref bool isFollowing, ref bool isFollowed)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+ try
+ {
+ res = twCon.ShowFriendships(_uname, screenName, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ try
+ {
+ var relation = MyCommon.CreateDataFromJson<TwitterDataModel.Relationship>(content);
+ isFollowing = relation.relationship.Source.Following;
+ isFollowed = relation.relationship.Source.FollowedBy;
+ return "";
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string GetUserInfo(string screenName, ref TwitterDataModel.User user)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+ user = null;
+ try
+ {
+ res = twCon.ShowUserInfo(screenName, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ try
+ {
+ user = MyCommon.CreateDataFromJson<TwitterDataModel.User>(content);
+ }
+ catch (SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch (Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+ return "";
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return Properties.Resources.Unauthorized;
+ }
+ else
+ {
+ return "Auth err:" + errMsg;
+ }
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string GetStatus_Retweeted_Count(long StatusId, ref int retweeted_count)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+ var xmlBuf = "";
+
+ retweeted_count = 0;
+
+ // 注:dev.twitter.comに記述されているcountパラメータは間違い。100が正しい
+ for (int i = 1; i <= 100; i++)
+ {
+ try
+ {
+ res = twCon.Statusid_retweeted_by_ids(StatusId, 100, i, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ try
+ {
+ var ids = MyCommon.CreateDataFromJson<Int64[]>(content);
+ retweeted_count += ids.Length;
+ if (ids.Length < 100) goto exit_for;
+ }
+ catch (SerializationException ex)
+ {
+ retweeted_count = -1;
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch (Exception ex)
+ {
+ retweeted_count = -1;
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+ break;
+ case HttpStatusCode.BadRequest:
+ retweeted_count = -1;
+ return "Err:API Limits?";
+ case HttpStatusCode.Unauthorized:
+ retweeted_count = -1;
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ default:
+ retweeted_count = -1;
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+ exit_for:
+ return "";
+ }
+
+ public string PostFavAdd(long id)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ //if (this.favQueue == null) this.favQueue = new FavoriteQueue(this)
+
+ //if (this.favQueue.Contains(id)) this.favQueue.Remove(id)
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+ try
+ {
+ res = twCon.CreateFavorites(id, ref content);
+ }
+ catch(Exception ex)
+ {
+ //this.favQueue.Add(id)
+ //return "Err:->FavoriteQueue:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ //this.favQueue.FavoriteCacheStart();
+ if (!_restrictFavCheck) return "";
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.Forbidden:
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ else
+ {
+ //if (errMsg.Contains("It's great that you like so many updates"))
+ // //this.favQueue.Add(id)
+ // return "Err:->FavoriteQueue:" + errMsg;
+ //}
+ return "Err:" + errMsg;
+ }
+ //Case HttpStatusCode.BadGateway, HttpStatusCode.ServiceUnavailable, HttpStatusCode.InternalServerError, HttpStatusCode.RequestTimeout
+ // //this.favQueue.Add(id)
+ // return "Err:->FavoriteQueue:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ //http://twitter.com/statuses/show/id.xml APIを発行して本文を取得
+
+ //var content = "";
+ content = "";
+ try
+ {
+ res = twCon.ShowStatuses(id, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ TwitterDataModel.Status status;
+ try
+ {
+ status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
+ }
+ catch (SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch (Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+ if (status.Favorited)
+ {
+ return "";
+ }
+ else
+ {
+ return "NG(Restricted?)";
+ }
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string PostFavRemove(long id)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ //if (this.favQueue == null) this.favQueue = new FavoriteQueue(this)
+
+ //if (this.favQueue.Contains(id))
+ // this.favQueue.Remove(id)
+ // return "";
+ //}
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+ try
+ {
+ res = twCon.DestroyFavorites(id, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ return "";
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.Forbidden:
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ else
+ {
+ return "Err:" + errMsg;
+ }
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string PostUpdateProfile(string name, string url, string location, string description)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+ try
+ {
+ res = twCon.UpdateProfile(name, url, location, description, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ return "";
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.Forbidden:
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ else
+ {
+ return "Err:" + errMsg;
+ }
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string PostUpdateProfileImage(string filename)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+ try
+ {
+ res = twCon.UpdateProfileImage(new FileInfo(filename), ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ return "";
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.Forbidden:
+ var errMsg = GetErrorMessageJson(content);
+ if (string.IsNullOrEmpty(errMsg))
+ {
+ return "Err:Forbidden(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ else
+ {
+ return "Err:" + errMsg;
+ }
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+ }
+
+ public string Username
+ {
+ get
+ {
+ return twCon.AuthenticatedUsername;
+ }
+ }
+
+ public long UserId
+ {
+ get
+ {
+ return twCon.AuthenticatedUserId;
+ }
+ }
+
+ public string Password
+ {
+ get
+ {
+ return twCon.Password;
+ }
+ }
+
+ private static MyCommon.ACCOUNT_STATE _accountState = MyCommon.ACCOUNT_STATE.Valid;
+ public static MyCommon.ACCOUNT_STATE AccountState
+ {
+ get
+ {
+ return _accountState;
+ }
+ set
+ {
+ _accountState = value;
+ }
+ }
+
+ public bool GetIcon
+ {
+ set
+ {
+ _getIcon = value;
+ }
+ }
+
+ public bool TinyUrlResolve
+ {
+ set
+ {
+ _tinyUrlResolve = value;
+ }
+ }
+
+ public bool RestrictFavCheck
+ {
+ set
+ {
+ _restrictFavCheck = value;
+ }
+ }
+
+ public int IconSize
+ {
+ set
+ {
+ _iconSz = value;
+ }
+ }
+
+#region "バージョンアップ"
+ public string GetVersionInfo()
+ {
+ var content = "";
+ if (!(new HttpVarious()).GetData("http://tween.sourceforge.jp/version.txt?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(), null, out content, MyCommon.GetUserAgentString()))
+ {
+ throw new Exception("GetVersionInfo Failed");
+ }
+ return content;
+ }
+
+ public string GetTweenBinary(string strVer)
+ {
+ try
+ {
+ //本体
+ if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/Tween" + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
+ Path.Combine(MyCommon.settingPath, "TweenNew.exe")))
+ {
+ return "Err:Download failed";
+ }
+ //英語リソース
+ if (!Directory.Exists(Path.Combine(MyCommon.settingPath, "en")))
+ {
+ Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, "en"));
+ }
+ if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenResEn" + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
+ Path.Combine(Path.Combine(MyCommon.settingPath, "en"), "Tween.resourcesNew.dll")))
+ {
+ return "Err:Download failed";
+ }
+ //その他言語圏のリソース。取得失敗しても継続
+ //UIの言語圏のリソース
+ var curCul = "";
+ if (!Thread.CurrentThread.CurrentUICulture.IsNeutralCulture)
+ {
+ var idx = Thread.CurrentThread.CurrentUICulture.Name.LastIndexOf('-');
+ if (idx > -1)
+ {
+ curCul = Thread.CurrentThread.CurrentUICulture.Name.Substring(0, idx);
+ }
+ else
+ {
+ curCul = Thread.CurrentThread.CurrentUICulture.Name;
+ }
+ }
+ else
+ {
+ curCul = Thread.CurrentThread.CurrentUICulture.Name;
+ }
+ if (!string.IsNullOrEmpty(curCul) && curCul != "en" && curCul != "ja")
+ {
+ if (!Directory.Exists(Path.Combine(MyCommon.settingPath, curCul)))
+ {
+ Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, curCul));
+ }
+ if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenRes" + curCul + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
+ Path.Combine(Path.Combine(MyCommon.settingPath, curCul), "Tween.resourcesNew.dll")))
+ {
+ //return "Err:Download failed";
+ }
+ }
+ //スレッドの言語圏のリソース
+ string curCul2;
+ if (!Thread.CurrentThread.CurrentCulture.IsNeutralCulture)
+ {
+ var idx = Thread.CurrentThread.CurrentCulture.Name.LastIndexOf('-');
+ if (idx > -1)
+ {
+ curCul2 = Thread.CurrentThread.CurrentCulture.Name.Substring(0, idx);
+ }
+ else
+ {
+ curCul2 = Thread.CurrentThread.CurrentCulture.Name;
+ }
+ }
+ else
+ {
+ curCul2 = Thread.CurrentThread.CurrentCulture.Name;
+ }
+ if (!string.IsNullOrEmpty(curCul2) && curCul2 != "en" && curCul2 != curCul)
+ {
+ if (!Directory.Exists(Path.Combine(MyCommon.settingPath, curCul2)))
+ {
+ Directory.CreateDirectory(Path.Combine(MyCommon.settingPath, curCul2));
+ }
+ if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenRes" + curCul2 + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
+ Path.Combine(Path.Combine(MyCommon.settingPath, curCul2), "Tween.resourcesNew.dll")))
+ {
+ //return "Err:Download failed";
+ }
+ }
+
+ //アップデータ
+ if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenUp3.gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
+ Path.Combine(MyCommon.settingPath, "TweenUp3.exe")))
+ {
+ return "Err:Download failed";
+ }
+ //シリアライザDLL
+ if (!(new HttpVarious()).GetDataToFile("http://tween.sourceforge.jp/TweenDll" + strVer + ".gz?" + DateTime.Now.ToString("yyMMddHHmmss") + Environment.TickCount.ToString(),
+ Path.Combine(MyCommon.settingPath, "TweenNew.XmlSerializers.dll")))
+ {
+ return "Err:Download failed";
+ }
+ return "";
+ }
+ catch(Exception)
+ {
+ return "Err:Download failed";
+ }
+ }
+#endregion
+
+ public IDictionary<string, Image> DetailIcon
+ {
+ get
+ {
+ return _dIcon;
+ }
+ set
+ {
+ _dIcon = value;
+ }
+ }
+
+ public bool ReadOwnPost
+ {
+ get
+ {
+ return _readOwnPost;
+ }
+ set
+ {
+ _readOwnPost = value;
+ }
+ }
+
+ public int FollowersCount
+ {
+ get
+ {
+ return _followersCount;
+ }
+ }
+
+ public int FriendsCount
+ {
+ get
+ {
+ return _friendsCount;
+ }
+ }
+
+ public int StatusesCount
+ {
+ get
+ {
+ return _statusesCount;
+ }
+ }
+
+ public string Location
+ {
+ get
+ {
+ return _location;
+ }
+ }
+
+ public string Bio
+ {
+ get
+ {
+ return _bio;
+ }
+ }
+
+ public bool UseSsl
+ {
+ set
+ {
+ HttpTwitter.UseSsl = value;
+ if (value)
+ {
+ _protocol = "https://";
+ }
+ else
+ {
+ _protocol = "http://";
+ }
+ }
+ }
+
+ public string GetTimelineApi(bool read,
+ MyCommon.WORKERTYPE gType,
+ bool more,
+ bool startup)
+ {
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ if (MyCommon._endingFlag) return "";
+
+ HttpStatusCode res;
+ var content = "";
+ var count = AppendSettingDialog.Instance.CountApi;
+ if (gType == MyCommon.WORKERTYPE.Reply) count = AppendSettingDialog.Instance.CountApiReply;
+ if (AppendSettingDialog.Instance.UseAdditionalCount)
+ {
+ if (more && AppendSettingDialog.Instance.MoreCountApi != 0)
+ {
+ count = AppendSettingDialog.Instance.MoreCountApi;
+ }
+ else if (startup && AppendSettingDialog.Instance.FirstCountApi != 0 && gType == MyCommon.WORKERTYPE.Timeline)
+ {
+ count = AppendSettingDialog.Instance.FirstCountApi;
+ }
+ }
+ try
+ {
+ if (gType == MyCommon.WORKERTYPE.Timeline)
+ {
+ if (more)
+ {
+ res = twCon.HomeTimeline(count, this.minHomeTimeline, 0, ref content);
+ }
+ else
+ {
+ res = twCon.HomeTimeline(count, 0, 0, ref content);
+ }
+ }
+ else
+ {
+ if (more)
+ {
+ res = twCon.Mentions(count, this.minMentions, 0, ref content);
+ }
+ else
+ {
+ res = twCon.Mentions(count, 0, 0, ref content);
+ }
+ }
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ if (gType == MyCommon.WORKERTYPE.Timeline)
+ {
+ return CreatePostsFromJson(content, gType, null, read, count, ref this.minHomeTimeline);
+ }
+ else
+ {
+ return CreatePostsFromJson(content, gType, null, read, count, ref this.minMentions);
+ }
+ }
+
+ public string GetUserTimelineApi(bool read,
+ int count,
+ string userName,
+ TabClass tab,
+ bool more)
+ {
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ if (MyCommon._endingFlag) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ if (count == 0) count = 20;
+ try
+ {
+ if (string.IsNullOrEmpty(userName))
+ {
+ var target = tab.User;
+ if (string.IsNullOrEmpty(target)) return "";
+ userName = target;
+ res = twCon.UserTimeline(0, target, count, 0, 0, ref content);
+ }
+ else
+ {
+ if (more)
+ {
+ res = twCon.UserTimeline(0, userName, count, tab.OldestId, 0, ref content);
+ }
+ else
+ {
+ res = twCon.UserTimeline(0, userName, count, 0, 0, ref content);
+ }
+ }
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ return "Err:@" + userName + "'s Tweets are protected.";
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ List<TwitterDataModel.Status> items;
+ try
+ {
+ items = MyCommon.CreateDataFromJson<List<TwitterDataModel.Status>>(content);
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Invalid Json!";
+ }
+
+ foreach (var status in items)
+ {
+ var item = CreatePostsFromStatusData(status);
+ if (item == null) continue;
+ if (item.StatusId < tab.OldestId) tab.OldestId = item.StatusId;
+ item.IsRead = read;
+ if (item.IsMe && !read && _readOwnPost) item.IsRead = true;
+ if (tab != null) item.RelTabName = tab.TabName;
+ //非同期アイコン取得&StatusDictionaryに追加
+ TabInformations.GetInstance().AddPost(item);
+ }
+
+ return "";
+ }
+
+ public string GetStatusApi(bool read,
+ Int64 id,
+ ref PostClass post)
+ {
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ if (MyCommon._endingFlag) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ try
+ {
+ res = twCon.ShowStatuses(id, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ case HttpStatusCode.Forbidden:
+ return "Err:protected user's tweet";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ TwitterDataModel.Status status;
+ try
+ {
+ status = MyCommon.CreateDataFromJson<TwitterDataModel.Status>(content);
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Invalid Json!";
+ }
+
+ var item = CreatePostsFromStatusData(status);
+ if (item == null) return "Err:Can't create post";
+ item.IsRead = read;
+ if (item.IsMe && !read && _readOwnPost) item.IsRead = true;
+
+ post = item;
+ return "";
+ }
+
+ public string GetStatusApi(bool read,
+ Int64 id,
+ TabClass tab)
+ {
+ PostClass post = null;
+ var r = this.GetStatusApi(read, id, ref post);
+
+ if (r == "")
+ {
+ if (tab != null) post.RelTabName = tab.TabName;
+ //非同期アイコン取得&StatusDictionaryに追加
+ TabInformations.GetInstance().AddPost(post);
+ }
+
+ return r;
+ }
+
+ private PostClass CreatePostsFromStatusData(TwitterDataModel.Status status)
+ {
+ var post = new PostClass();
+ TwitterDataModel.Entities entities;
+
+ post.StatusId = status.Id;
+ if (status.RetweetedStatus != null)
+ {
+ var retweeted = status.RetweetedStatus;
+
+ post.CreatedAt = MyCommon.DateTimeParse(retweeted.CreatedAt);
+
+ //Id
+ post.RetweetedId = retweeted.Id;
+ //本文
+ post.TextFromApi = retweeted.Text;
+ entities = retweeted.Entities;
+ //Source取得(htmlの場合は、中身を取り出し)
+ post.Source = retweeted.Source;
+ //Reply先
+ long inReplyToStatusId;
+ long.TryParse(retweeted.InReplyToStatusId, out inReplyToStatusId);
+ post.InReplyToStatusId = inReplyToStatusId;
+ post.InReplyToUser = retweeted.InReplyToScreenName;
+ long inReplyToUserId;
+ long.TryParse(status.InReplyToUserId, out inReplyToUserId);
+ post.InReplyToUserId = inReplyToUserId;
+
+ //幻覚fav対策
+ var tc = TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites);
+ post.IsFav = tc.Contains(post.RetweetedId);
+
+ if (retweeted.Geo != null) post.PostGeo = new PostClass.StatusGeo {Lat = retweeted.Geo.Coordinates[0], Lng = retweeted.Geo.Coordinates[1]};
+
+ //以下、ユーザー情報
+ var user = retweeted.User;
+
+ if (user.ScreenName == null || status.User.ScreenName == null) return null;
+
+ post.UserId = user.Id;
+ post.ScreenName = user.ScreenName;
+ post.Nickname = user.Name.Trim();
+ post.ImageUrl = user.ProfileImageUrl;
+ post.IsProtect = user.Protected;
+
+ //Retweetした人
+ post.RetweetedBy = status.User.ScreenName;
+ post.RetweetedByUserId = status.User.Id;
+ post.IsMe = post.RetweetedBy.ToLower().Equals(_uname);
+ }
+ else
+ {
+ post.CreatedAt = MyCommon.DateTimeParse(status.CreatedAt);
+ //本文
+ post.TextFromApi = status.Text;
+ entities = status.Entities;
+ //Source取得(htmlの場合は、中身を取り出し)
+ post.Source = status.Source;
+ long inReplyToStatusId;
+ long.TryParse(status.InReplyToStatusId, out inReplyToStatusId);
+ post.InReplyToStatusId = inReplyToStatusId;
+ post.InReplyToUser = status.InReplyToScreenName;
+ long inReplyToUserId;
+ long.TryParse(status.InReplyToUserId, out inReplyToUserId);
+ post.InReplyToUserId = inReplyToUserId;
+
+ if (status.Geo != null) post.PostGeo = new PostClass.StatusGeo {Lat = status.Geo.Coordinates[0], Lng = status.Geo.Coordinates[1]};
+
+ //以下、ユーザー情報
+ var user = status.User;
+
+ if (user.ScreenName == null) return null;
+
+ post.UserId = user.Id;
+ post.ScreenName = user.ScreenName;
+ post.Nickname = user.Name.Trim();
+ post.ImageUrl = user.ProfileImageUrl;
+ post.IsProtect = user.Protected;
+ post.IsMe = post.ScreenName.ToLower().Equals(_uname);
+
+ //幻覚fav対策
+ var tc = TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites);
+ post.IsFav = tc.Contains(post.StatusId) && TabInformations.GetInstance()[post.StatusId].IsFav;
+ }
+ //HTMLに整形
+ string textFromApi = post.TextFromApi;
+ post.Text = CreateHtmlAnchor(ref textFromApi, post.ReplyToList, entities, post.Media);
+ post.TextFromApi = textFromApi;
+ post.TextFromApi = this.ReplaceTextFromApi(post.TextFromApi, entities);
+ post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi);
+ post.TextFromApi = post.TextFromApi.Replace("<3", "?");
+
+ //Source整形
+ CreateSource(ref post);
+
+ post.IsReply = post.ReplyToList.Contains(_uname);
+ post.IsExcludeReply = false;
+
+ if (post.IsMe)
+ {
+ post.IsOwl = false;
+ }
+ else
+ {
+ if (followerId.Count > 0) post.IsOwl = !followerId.Contains(post.UserId);
+ }
+
+ post.IsDm = false;
+ return post;
+ }
+
+ private string CreatePostsFromJson(string content, MyCommon.WORKERTYPE gType, TabClass tab, bool read, int count, ref long minimumId)
+ {
+ List<TwitterDataModel.Status> items;
+ try
+ {
+ items = MyCommon.CreateDataFromJson<List<TwitterDataModel.Status>>(content);
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Json Parse Error(DataContractJsonSerializer)";;
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Invalid Json!";
+ }
+
+ foreach (var status in items)
+ {
+ PostClass post = null;
+ post = CreatePostsFromStatusData(status);
+ if (post == null) continue;
+
+ if (minimumId > post.StatusId) minimumId = post.StatusId;
+ //二重取得回避
+ lock (LockObj)
+ {
+ if (tab == null)
+ {
+ if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue;
+ }
+ else
+ {
+ if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue;
+ }
+ }
+
+ //RT禁止ユーザーによるもの
+ if (post.RetweetedId > 0 && this.noRTId.Contains(post.RetweetedByUserId)) continue;
+
+ post.IsRead = read;
+ if (post.IsMe && !read && _readOwnPost) post.IsRead = true;
+
+ if (tab != null) post.RelTabName = tab.TabName;
+ //非同期アイコン取得&StatusDictionaryに追加
+ TabInformations.GetInstance().AddPost(post);
+ }
+
+ return "";
+ }
+
+ private string CreatePostsFromPhoenixSearch(string content, MyCommon.WORKERTYPE gType, TabClass tab, bool read, int count, ref long minimumId, ref string nextPageQuery)
+ {
+ TwitterDataModel.SearchResult items;
+ try
+ {
+ items = MyCommon.CreateDataFromJson<TwitterDataModel.SearchResult>(content);
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Invalid Json!";
+ }
+
+ nextPageQuery = items.NextPage;
+
+ foreach (var status in items.Statuses)
+ {
+ PostClass post = null;
+ post = CreatePostsFromStatusData(status);
+ if (post == null) continue;
+
+ if (minimumId > post.StatusId) minimumId = post.StatusId;
+ //二重取得回避
+ lock (LockObj)
+ {
+ if (tab == null)
+ {
+ if (TabInformations.GetInstance().ContainsKey(post.StatusId)) continue;
+ }
+ else
+ {
+ if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue;
+ }
+ }
+
+ post.IsRead = read;
+ if (post.IsMe && !read && _readOwnPost) post.IsRead = true;
+
+ if (tab != null) post.RelTabName = tab.TabName;
+ //非同期アイコン取得&StatusDictionaryに追加
+ TabInformations.GetInstance().AddPost(post);
+ }
+
+ return string.IsNullOrEmpty(items.ErrMsg) ? "" : "Err:" + items.ErrMsg;
+ }
+
+ public string GetListStatus(bool read,
+ TabClass tab,
+ bool more,
+ bool startup)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ HttpStatusCode res;
+ var content = "";
+ var page = 0;
+ int count;
+ if (AppendSettingDialog.Instance.UseAdditionalCount)
+ {
+ count = AppendSettingDialog.Instance.ListCountApi;
+ if (count == 0)
+ {
+ if (more && AppendSettingDialog.Instance.MoreCountApi != 0)
+ {
+ count = AppendSettingDialog.Instance.MoreCountApi;
+ }
+ else if (startup && AppendSettingDialog.Instance.FirstCountApi != 0)
+ {
+ count = AppendSettingDialog.Instance.FirstCountApi;
+ }
+ else
+ {
+ count = AppendSettingDialog.Instance.CountApi;
+ }
+ }
+ }
+ else
+ {
+ count = AppendSettingDialog.Instance.CountApi;
+ }
+ try
+ {
+ if (more)
+ {
+ res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, tab.OldestId, 0, AppendSettingDialog.Instance.IsListStatusesIncludeRts, ref content);
+ }
+ else
+ {
+ res = twCon.GetListsStatuses(tab.ListInfo.UserId, tab.ListInfo.Id, count, 0, 0, AppendSettingDialog.Instance.IsListStatusesIncludeRts, ref content);
+ }
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ return CreatePostsFromJson(content, MyCommon.WORKERTYPE.List, tab, read, count, ref tab.OldestId);
+ }
+
+
+ private PostClass CheckReplyToPost(List<PostClass> relPosts)
+ {
+ var tmpPost = relPosts[0];
+ PostClass lastPost = null;
+ while (tmpPost != null)
+ {
+ if (tmpPost.InReplyToStatusId == 0) return null;
+ lastPost = tmpPost;
+ var replyToPost = from p in relPosts
+ where p.StatusId == tmpPost.InReplyToStatusId
+ select p;
+ tmpPost = replyToPost.FirstOrDefault();
+ }
+ return lastPost;
+ }
+
+ public string GetRelatedResult(bool read, TabClass tab)
+ {
+ var rslt = "";
+ var relPosts = new List<PostClass>();
+ if (tab.RelationTargetPost.TextFromApi.Contains("@") && tab.RelationTargetPost.InReplyToStatusId == 0)
+ {
+ //検索結果対応
+ var p = TabInformations.GetInstance()[tab.RelationTargetPost.StatusId];
+ if (p != null && p.InReplyToStatusId > 0)
+ {
+ tab.RelationTargetPost = p;
+ }
+ else
+ {
+ rslt = this.GetStatusApi(read, tab.RelationTargetPost.StatusId, ref p);
+ if (!string.IsNullOrEmpty(rslt)) return rslt;
+ tab.RelationTargetPost = p;
+ }
+ }
+ relPosts.Add(tab.RelationTargetPost.Copy());
+ var tmpPost = relPosts[0];
+ do
+ {
+ rslt = this.GetRelatedResultsApi(read, tmpPost, tab, relPosts);
+ if (!string.IsNullOrEmpty(rslt)) break;
+ tmpPost = CheckReplyToPost(relPosts);
+ } while (tmpPost != null);
+
+ relPosts.ForEach(p => TabInformations.GetInstance().AddPost(p));
+ return rslt;
+ }
+
+ private string GetRelatedResultsApi(bool read,
+ PostClass post,
+ TabClass tab,
+ List<PostClass> relatedPosts)
+ {
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ if (MyCommon._endingFlag) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+ try
+ {
+ if (post.RetweetedId > 0)
+ {
+ res = twCon.GetRelatedResults(post.RetweetedId, ref content);
+ }
+ else
+ {
+ res = twCon.GetRelatedResults(post.StatusId, ref content);
+ }
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ List<TwitterDataModel.RelatedResult> items;
+ try
+ {
+ items = MyCommon.CreateDataFromJson<List<TwitterDataModel.RelatedResult>>(content);
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Invalid Json!";
+ }
+
+ var targetItem = post;
+ if (targetItem == null)
+ {
+ return "";
+ }
+ else
+ {
+ targetItem = targetItem.Copy();
+ }
+ targetItem.RelTabName = tab.TabName;
+ TabInformations.GetInstance().AddPost(targetItem);
+
+ PostClass replyToItem = null;
+ var replyToUserName = targetItem.InReplyToUser;
+ if (targetItem.InReplyToStatusId > 0 && TabInformations.GetInstance()[targetItem.InReplyToStatusId] != null)
+ {
+ replyToItem = TabInformations.GetInstance()[targetItem.InReplyToStatusId].Copy();
+ replyToItem.IsRead = read;
+ if (replyToItem.IsMe && !read && _readOwnPost) replyToItem.IsRead = true;
+ replyToItem.RelTabName = tab.TabName;
+ }
+
+ var replyAdded = false;
+ foreach (var relatedData in items)
+ {
+ foreach (var result in relatedData.Results)
+ {
+ var item = CreatePostsFromStatusData(result.Status);
+ if (item == null) continue;
+ if (targetItem.InReplyToStatusId == item.StatusId)
+ {
+ replyToItem = null;
+ replyAdded = true;
+ }
+ item.IsRead = read;
+ if (item.IsMe && !read && _readOwnPost) item.IsRead = true;
+ if (tab != null) item.RelTabName = tab.TabName;
+ //非同期アイコン取得&StatusDictionaryに追加
+ relatedPosts.Add(item);
+ }
+ }
+ if (replyToItem != null)
+ {
+ relatedPosts.Add(replyToItem);
+ }
+ else if (targetItem.InReplyToStatusId > 0 && !replyAdded)
+ {
+ PostClass p = null;
+ var rslt = "";
+ rslt = GetStatusApi(read, targetItem.InReplyToStatusId, ref p);
+ if (string.IsNullOrEmpty(rslt))
+ {
+ p.IsRead = read;
+ p.RelTabName = tab.TabName;
+ relatedPosts.Add(p);
+ }
+ return rslt;
+ }
+
+ //発言者・返信先ユーザーの直近10発言取得
+ //var rslt = this.GetUserTimelineApi(read, 10, "", tab);
+ //if (!string.IsNullOrEmpty(rslt)) return rslt;
+ //if (!string.IsNullOrEmpty(replyToUserName))
+ // rslt = this.GetUserTimelineApi(read, 10, replyToUserName, tab);
+ //}
+ //return rslt;
+
+
+ //MRTとかに対応のためツイート内にあるツイートを指すURLを取り込む
+ var ma = Regex.Matches(tab.RelationTargetPost.Text, "title=\"https?://twitter.com/(#!/)?(?<ScreenName>[a-zA-Z0-9_]+)(/status(es)?/(?<StatusId>[0-9]+))\"");
+ foreach (Match _match in ma)
+ {
+ Int64 _statusId;
+ if (Int64.TryParse(_match.Groups["StatusId"].Value, out _statusId))
+ {
+ PostClass p = null;
+ var _post = TabInformations.GetInstance()[_statusId];
+ if (_post == null)
+ {
+ var rslt = this.GetStatusApi(read, _statusId, ref p);
+ }
+ else
+ {
+ p = _post.Copy();
+ }
+ if (p != null)
+ {
+ p.IsRead = read;
+ p.RelTabName = tab.TabName;
+ relatedPosts.Add(p);
+ }
+ }
+ }
+ return "";
+ }
+
+ public string GetSearch(bool read,
+ TabClass tab,
+ bool more)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ HttpStatusCode res;
+ var content = "";
+ var page = 0;
+ var sinceId = 0;
+ var count = 100;
+ if (AppendSettingDialog.Instance.UseAdditionalCount &&
+ AppendSettingDialog.Instance.SearchCountApi != 0)
+ {
+ count = AppendSettingDialog.Instance.SearchCountApi;
+ }
+ else
+ {
+ count = AppendSettingDialog.Instance.CountApi;
+ }
+ if (more)
+ {
+ page = tab.GetSearchPage(count);
+ }
+ else
+ {
+ sinceId = (int)tab.SinceId;
+ }
+
+ try
+ {
+ // TODO:一時的に40>100件に 件数変更UI作成の必要あり
+ res = twCon.Search(tab.SearchWords, tab.SearchLang, count, page, sinceId, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+ switch (res)
+ {
+ case HttpStatusCode.BadRequest:
+ return "Invalid query";
+ case HttpStatusCode.NotFound:
+ return "Invalid query";
+ case HttpStatusCode.PaymentRequired: //API Documentには420と書いてあるが、該当コードがないので402にしてある
+ return "Search API Limit?";
+ case HttpStatusCode.OK:
+ break;
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ if (!TabInformations.GetInstance().ContainsTab(tab)) return "";
+ content = Regex.Replace(content, @"[\x00-\x1f-[\x0a\x0d]]+", " ");
+ var xdoc = new XmlDocument();
+ try
+ {
+ xdoc.LoadXml(content);
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Invalid ATOM!";
+ }
+ var nsmgr = new XmlNamespaceManager(xdoc.NameTable);
+ nsmgr.AddNamespace("search", "http://www.w3.org/2005/Atom");
+ nsmgr.AddNamespace("twitter", "http://api.twitter.com/");
+ nsmgr.AddNamespace("georss", "http://www.georss.org/georss");
+ foreach (var xentryNode in xdoc.DocumentElement.SelectNodes("/search:feed/search:entry", nsmgr))
+ {
+ var xentry = (XmlElement)xentryNode;
+ var post = new PostClass();
+ try
+ {
+ post.StatusId = long.Parse(xentry["id"].InnerText.Split(':')[2]);
+ if (TabInformations.GetInstance().ContainsKey(post.StatusId, tab.TabName)) continue;
+ post.CreatedAt = DateTime.Parse(xentry["published"].InnerText);
+ //本文
+ post.TextFromApi = xentry["title"].InnerText;
+ //Source取得(htmlの場合は、中身を取り出し)
+ post.Source = xentry["twitter:source"].InnerText;
+ post.InReplyToStatusId = 0;
+ post.InReplyToUser = "";
+ post.InReplyToUserId = 0;
+ post.IsFav = false;
+
+ // Geoが勝手に付加されるバグがいっこうに修正されないので暫定的にGeo情報を無視する
+ if (xentry["twitter:geo"].HasChildNodes)
+ {
+ var pnt = ((XmlElement)xentry.SelectSingleNode("twitter:geo/georss:point", nsmgr)).InnerText.Split(' ');
+ post.PostGeo = new PostClass.StatusGeo {Lat = Double.Parse(pnt[0]), Lng = Double.Parse(pnt[1])};
+ }
+
+ //以下、ユーザー情報
+ var xUentry = (XmlElement)xentry.SelectSingleNode("./search:author", nsmgr);
+ post.UserId = 0;
+ post.ScreenName = xUentry["name"].InnerText.Split(' ')[0].Trim();
+ post.Nickname = xUentry["name"].InnerText.Substring(post.ScreenName.Length).Trim();
+ if (post.Nickname.Length > 2)
+ {
+ post.Nickname = post.Nickname.Substring(1, post.Nickname.Length - 2);
+ }
+ else
+ {
+ post.Nickname = post.ScreenName;
+ }
+ post.ImageUrl = ((XmlElement)xentry.SelectSingleNode("./search:link[@type='image/png']", nsmgr)).GetAttribute("href");
+ post.IsProtect = false;
+ post.IsMe = post.ScreenName.ToLower().Equals(_uname);
+
+ //HTMLに整形
+ post.Text = CreateHtmlAnchor(HttpUtility.HtmlEncode(post.TextFromApi), post.ReplyToList, post.Media);
+ post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi);
+ //Source整形
+ CreateSource(ref post);
+
+ post.IsRead = read;
+ post.IsReply = post.ReplyToList.Contains(_uname);
+ post.IsExcludeReply = false;
+
+ post.IsOwl = false;
+ if (post.IsMe && !read && _readOwnPost) post.IsRead = true;
+ post.IsDm = false;
+ post.RelTabName = tab.TabName;
+ if (!more && post.StatusId > tab.SinceId) tab.SinceId = post.StatusId;
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ continue;
+ }
+
+ //this._dIcon.Add(post.ImageUrl, null);
+ TabInformations.GetInstance().AddPost(post);
+
+ }
+
+ // TODO
+ // 遡るための情報max_idやnext_pageの情報を保持する
+
+#if UNDEFINED__
+ var xNode = xdoc.DocumentElement.SelectSingleNode("/search:feed/twitter:warning", nsmgr);
+
+ if (xNode != null)
+ {
+ return "Warn:" + xNode.InnerText + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+#endif
+
+ return "";
+ }
+
+ public string GetPhoenixSearch(bool read,
+ TabClass tab,
+ bool more)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ HttpStatusCode res;
+ var content = "";
+ var page = 0;
+ var sinceId = 0;
+ var count = 100;
+ var querystr = "";
+ if (AppendSettingDialog.Instance.UseAdditionalCount &&
+ AppendSettingDialog.Instance.SearchCountApi != 0)
+ {
+ count = AppendSettingDialog.Instance.SearchCountApi;
+ }
+ if (more)
+ {
+ page = tab.GetSearchPage(count);
+ if (!string.IsNullOrEmpty(tab.NextPageQuery))
+ {
+ querystr = tab.NextPageQuery;
+ }
+ }
+ else
+ {
+ sinceId = (int)tab.SinceId;
+ }
+
+ try
+ {
+ if (string.IsNullOrEmpty(querystr))
+ {
+ res = twCon.PhoenixSearch(tab.SearchWords, tab.SearchLang, count, page, sinceId, ref content);
+ }
+ else
+ {
+ res = twCon.PhoenixSearch(querystr, ref content);
+ }
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+ switch (res)
+ {
+ case HttpStatusCode.BadRequest:
+ return "Invalid query";
+ case HttpStatusCode.NotFound:
+ return "Invalid query";
+ case HttpStatusCode.PaymentRequired: //API Documentには420と書いてあるが、該当コードがないので402にしてある
+ return "Search API Limit?";
+ case HttpStatusCode.OK:
+ break;
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ if (!TabInformations.GetInstance().ContainsTab(tab)) return "";
+
+ //// TODO
+ //// 遡るための情報max_idやnext_pageの情報を保持する
+
+ string nextPageQuery = tab.NextPageQuery;
+ var ret = CreatePostsFromPhoenixSearch(content, MyCommon.WORKERTYPE.PublicSearch, tab, read, count, ref tab.OldestId, ref nextPageQuery);
+ tab.NextPageQuery = nextPageQuery;
+ return ret;
+ }
+
+ private string CreateDirectMessagesFromJson(string content, MyCommon.WORKERTYPE gType, bool read)
+ {
+ List<TwitterDataModel.Directmessage> item;
+ try
+ {
+ if (gType == MyCommon.WORKERTYPE.UserStream)
+ {
+ var itm = MyCommon.CreateDataFromJson<List<TwitterDataModel.DirectmessageEvent>>(content);
+ item = new List<TwitterDataModel.Directmessage>();
+ foreach (var dat in itm)
+ {
+ item.Add(dat.Directmessage);
+ }
+ }
+ else
+ {
+ item = MyCommon.CreateDataFromJson<List<TwitterDataModel.Directmessage>>(content);
+ }
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Invalid Json!";
+ }
+
+ foreach (var message in item)
+ {
+ var post = new PostClass();
+ try
+ {
+ post.StatusId = message.Id;
+ if (gType != MyCommon.WORKERTYPE.UserStream)
+ {
+ if (gType == MyCommon.WORKERTYPE.DirectMessegeRcv)
+ {
+ if (minDirectmessage > post.StatusId) minDirectmessage = post.StatusId;
+ }
+ else
+ {
+ if (minDirectmessageSent > post.StatusId) minDirectmessageSent = post.StatusId;
+ }
+ }
+
+ //二重取得回避
+ lock (LockObj)
+ {
+ if (TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.DirectMessage).Contains(post.StatusId)) continue;
+ }
+ //sender_id
+ //recipient_id
+ post.CreatedAt = MyCommon.DateTimeParse(message.CreatedAt);
+ //本文
+ post.TextFromApi = message.Text;
+ //HTMLに整形
+ post.Text = CreateHtmlAnchor(post.TextFromApi, post.ReplyToList, post.Media);
+ post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi);
+ post.TextFromApi = post.TextFromApi.Replace("<3", "?");
+ post.IsFav = false;
+
+ //以下、ユーザー情報
+ TwitterDataModel.User user;
+ if (gType == MyCommon.WORKERTYPE.UserStream)
+ {
+ if (twCon.AuthenticatedUsername.Equals(message.Recipient.ScreenName, StringComparison.CurrentCultureIgnoreCase))
+ {
+ user = message.Sender;
+ post.IsMe = false;
+ post.IsOwl = true;
+ }
+ else
+ {
+ user = message.Recipient;
+ post.IsMe = true;
+ post.IsOwl = false;
+ }
+ }
+ else
+ {
+ if (gType == MyCommon.WORKERTYPE.DirectMessegeRcv)
+ {
+ user = message.Sender;
+ post.IsMe = false;
+ post.IsOwl = true;
+ }
+ else
+ {
+ user = message.Recipient;
+ post.IsMe = true;
+ post.IsOwl = false;
+ }
+ }
+
+ post.UserId = user.Id;
+ post.ScreenName = user.ScreenName;
+ post.Nickname = user.Name.Trim();
+ post.ImageUrl = user.ProfileImageUrl;
+ post.IsProtect = user.Protected;
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ MessageBox.Show("Parse Error(CreateDirectMessagesFromJson)");
+ continue;
+ }
+
+ post.IsRead = read;
+ if (post.IsMe && !read && _readOwnPost) post.IsRead = true;
+ post.IsReply = false;
+ post.IsExcludeReply = false;
+ post.IsDm = true;
+
+ TabInformations.GetInstance().AddPost(post);
+ }
+
+ return "";
+
+ }
+
+ public string GetDirectMessageApi(bool read,
+ MyCommon.WORKERTYPE gType,
+ bool more)
+ {
+ if (MyCommon._endingFlag) return "";
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+ if (MyCommon.TwitterApiInfo.AccessLevel != ApiAccessLevel.None)
+ {
+ if (!MyCommon.TwitterApiInfo.IsDirectMessagePermission) return "Auth Err:try to re-authorization.";
+ }
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ try
+ {
+ if (gType == MyCommon.WORKERTYPE.DirectMessegeRcv)
+ {
+ if (more)
+ {
+ res = twCon.DirectMessages(20, minDirectmessage, 0, ref content);
+ }
+ else
+ {
+ res = twCon.DirectMessages(20, 0, 0, ref content);
+ }
+ }
+ else
+ {
+ if (more)
+ {
+ res = twCon.DirectMessagesSent(20, minDirectmessageSent, 0, ref content);
+ }
+ else
+ {
+ res = twCon.DirectMessagesSent(20, 0, 0, ref content);
+ }
+ }
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ return CreateDirectMessagesFromJson(content, gType, read);
+ }
+
+ static int page_ = 1;
+ public string GetFavoritesApi(bool read,
+ MyCommon.WORKERTYPE gType,
+ bool more)
+ {
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ if (MyCommon._endingFlag) return "";
+
+ HttpStatusCode res;
+ var content = "";
+ var count = AppendSettingDialog.Instance.CountApi;
+ if (AppendSettingDialog.Instance.UseAdditionalCount &&
+ AppendSettingDialog.Instance.FavoritesCountApi != 0)
+ {
+ count = AppendSettingDialog.Instance.FavoritesCountApi;
+ }
+
+ // 前ページ取得の場合はページカウンタをインクリメント、それ以外の場合はページカウンタリセット
+ if (more)
+ {
+ page_++;
+ }
+ else
+ {
+ page_ = 1;
+ }
+
+ try
+ {
+ res = twCon.Favorites(count, page_, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ var serializer = new DataContractJsonSerializer(typeof(List<TwitterDataModel.Status>));
+ List<TwitterDataModel.Status> item;
+
+ try
+ {
+ item = MyCommon.CreateDataFromJson<List<TwitterDataModel.Status>>(content);
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Invalid Json!";
+ }
+
+ foreach (var status in item)
+ {
+ var post = new PostClass();
+ TwitterDataModel.Entities entities;
+
+ try
+ {
+ post.StatusId = status.Id;
+ //二重取得回避
+ lock (LockObj)
+ {
+ if (TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Contains(post.StatusId)) continue;
+ }
+ //Retweet判定
+ if (status.RetweetedStatus != null)
+ {
+ var retweeted = status.RetweetedStatus;
+ post.CreatedAt = MyCommon.DateTimeParse(retweeted.CreatedAt);
+
+ //Id
+ post.RetweetedId = post.StatusId;
+ //本文
+ post.TextFromApi = retweeted.Text;
+ entities = retweeted.Entities;
+ //Source取得(htmlの場合は、中身を取り出し)
+ post.Source = retweeted.Source;
+ //Reply先
+ long inReplyToStatusId;
+ long.TryParse(retweeted.InReplyToStatusId, out inReplyToStatusId);
+ post.InReplyToStatusId = inReplyToStatusId;
+ post.InReplyToUser = retweeted.InReplyToScreenName;
+ long inReplyToUserId;
+ long.TryParse(retweeted.InReplyToUserId, out inReplyToUserId);
+ post.InReplyToUserId = inReplyToUserId;
+ post.IsFav = true;
+
+ //以下、ユーザー情報
+ var user = retweeted.User;
+ post.UserId = user.Id;
+ post.ScreenName = user.ScreenName;
+ post.Nickname = user.Name.Trim();
+ post.ImageUrl = user.ProfileImageUrl;
+ post.IsProtect = user.Protected;
+
+ //Retweetした人
+ post.RetweetedBy = status.User.ScreenName;
+ post.IsMe = post.RetweetedBy.ToLower().Equals(_uname);
+ }
+ else
+ {
+ post.CreatedAt = MyCommon.DateTimeParse(status.CreatedAt);
+
+ //本文
+ post.TextFromApi = status.Text;
+ entities = status.Entities;
+ //Source取得(htmlの場合は、中身を取り出し)
+ post.Source = status.Source;
+ long inReplyToStatusId;
+ long.TryParse(status.InReplyToStatusId, out inReplyToStatusId);
+ post.InReplyToStatusId = inReplyToStatusId;
+ post.InReplyToUser = status.InReplyToScreenName;
+ long inReplyToUserId;
+ long.TryParse(status.InReplyToUserId, out inReplyToUserId);
+ post.InReplyToUserId = inReplyToUserId;
+
+ post.IsFav = true;
+
+ //以下、ユーザー情報
+ var user = status.User;
+ post.UserId = user.Id;
+ post.ScreenName = user.ScreenName;
+ post.Nickname = user.Name.Trim();
+ post.ImageUrl = user.ProfileImageUrl;
+ post.IsProtect = user.Protected;
+ post.IsMe = post.ScreenName.ToLower().Equals(_uname);
+ }
+ //HTMLに整形
+ string textFromApi = post.TextFromApi;
+ post.Text = CreateHtmlAnchor(ref textFromApi, post.ReplyToList, entities, post.Media);
+ post.TextFromApi = textFromApi;
+ post.TextFromApi = this.ReplaceTextFromApi(post.TextFromApi, entities);
+ post.TextFromApi = HttpUtility.HtmlDecode(post.TextFromApi);
+ post.TextFromApi = post.TextFromApi.Replace("<3", "?");
+ //Source整形
+ CreateSource(ref post);
+
+ post.IsRead = read;
+ post.IsReply = post.ReplyToList.Contains(_uname);
+ post.IsExcludeReply = false;
+
+ if (post.IsMe)
+ {
+ post.IsOwl = false;
+ }
+ else
+ {
+ if (followerId.Count > 0) post.IsOwl = !followerId.Contains(post.UserId);
+ }
+
+ post.IsDm = false;
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ continue;
+ }
+
+ TabInformations.GetInstance().AddPost(post);
+
+ }
+
+ return "";
+ }
+
+ private string ReplaceTextFromApi(string text, TwitterDataModel.Entities entities)
+ {
+ if (entities != null)
+ {
+ if (entities.Urls != null)
+ {
+ foreach (var m in entities.Urls)
+ {
+ if (!string.IsNullOrEmpty(m.DisplayUrl)) text = text.Replace(m.Url, m.DisplayUrl);
+ }
+ }
+ if (entities.Media != null)
+ {
+ foreach (var m in entities.Media)
+ {
+ if (!string.IsNullOrEmpty(m.DisplayUrl)) text = text.Replace(m.Url, m.DisplayUrl);
+ }
+ }
+ }
+ return text;
+ }
+
+ public string GetFollowersApi()
+ {
+ if (MyCommon._endingFlag) return "";
+ long cursor = -1;
+ var tmpFollower = new List<long>(followerId);
+
+ followerId.Clear();
+ do
+ {
+ var ret = FollowerApi(ref cursor);
+ if (!string.IsNullOrEmpty(ret))
+ {
+ followerId.Clear();
+ followerId.AddRange(tmpFollower);
+ _GetFollowerResult = false;
+ return ret;
+ }
+ } while (cursor > 0);
+
+ TabInformations.GetInstance().RefreshOwl(followerId);
+
+ _GetFollowerResult = true;
+ return "";
+ }
+
+ public bool GetFollowersSuccess
+ {
+ get
+ {
+ return _GetFollowerResult;
+ }
+ }
+
+ private string FollowerApi(ref long cursor)
+ {
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+ try
+ {
+ res = twCon.FollowerIds(cursor, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ try
+ {
+ var followers = MyCommon.CreateDataFromJson<TwitterDataModel.Ids>(content);
+ followerId.AddRange(followers.Id);
+ cursor = followers.NextCursor;
+ return "";
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+ }
+
+ public string GetNoRetweetIdsApi()
+ {
+ if (MyCommon._endingFlag) return "";
+ long cursor = -1;
+ var tmpIds = new List<long>(noRTId);
+
+ noRTId.Clear();
+ do
+ {
+ var ret = NoRetweetApi(ref cursor);
+ if (!string.IsNullOrEmpty(ret))
+ {
+ noRTId.Clear();
+ noRTId.AddRange(tmpIds);
+ _GetNoRetweetResult = false;
+ return ret;
+ }
+ } while (cursor > 0);
+
+ _GetNoRetweetResult = true;
+ return "";
+ }
+
+ private string NoRetweetApi(ref long cursor)
+ {
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+ try
+ {
+ res = twCon.NoRetweetIds(cursor, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ try
+ {
+ var ids = MyCommon.CreateDataFromJson<long[]>(content);
+ noRTId.AddRange(ids);
+ cursor = 0; //0より小さければ何でも良い。
+ return "";
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+ }
+
+ public bool GetNoRetweetSuccess
+ {
+ get
+ {
+ return _GetNoRetweetResult;
+ }
+ }
+
+ public string ConfigurationApi()
+ {
+ HttpStatusCode res;
+ var content = "";
+ try
+ {
+ res = twCon.GetConfiguration(ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ try
+ {
+ AppendSettingDialog.Instance.TwitterConfiguration = MyCommon.CreateDataFromJson<TwitterDataModel.Configuration>(content);
+ return "";
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+ }
+
+ public string GetListsApi()
+ {
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+ long cursor = -1;
+
+ var lists = new List<ListElement>();
+ do
+ {
+ try
+ {
+ res = twCon.GetLists(this.Username, cursor, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ try
+ {
+ var lst = MyCommon.CreateDataFromJson<TwitterDataModel.Lists>(content);
+ lists.AddRange(from le in lst.lists select new ListElement(le, this));
+ cursor = lst.NextCursor;
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+ } while (cursor != 0);
+
+ cursor = -1;
+ content = "";
+ do
+ {
+ try
+ {
+ res = twCon.GetListsSubscriptions(this.Username, cursor, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ try
+ {
+ var lst = MyCommon.CreateDataFromJson<TwitterDataModel.Lists>(content);
+ lists.AddRange(from le in lst.lists select new ListElement(le, this));
+ cursor = lst.NextCursor;
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+ } while (cursor != 0);
+
+ TabInformations.GetInstance().SubscribableLists = lists;
+ return "";
+ }
+
+ public string DeleteList(string list_id)
+ {
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ try
+ {
+ res = twCon.DeleteListID(this.Username, list_id, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ return "";
+ }
+
+ public string EditList(string list_id, string new_name, bool isPrivate, string description, ref ListElement list)
+ {
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ try
+ {
+ res = twCon.UpdateListID(this.Username, list_id, new_name, isPrivate, description, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ try
+ {
+ var le = MyCommon.CreateDataFromJson<TwitterDataModel.ListElementData>(content);
+ var newList = new ListElement(le, this);
+ list.Description = newList.Description;
+ list.Id = newList.Id;
+ list.IsPublic = newList.IsPublic;
+ list.MemberCount = newList.MemberCount;
+ list.Name = newList.Name;
+ list.SubscriberCount = newList.SubscriberCount;
+ list.Slug = newList.Slug;
+ list.Nickname = newList.Nickname;
+ list.Username = newList.Username;
+ list.UserId = newList.UserId;
+ return "";
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+
+ }
+
+ public string GetListMembers(string list_id, List<UserInfo> lists, ref long cursor)
+ {
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ //Do
+ try
+ {
+ res = twCon.GetListMembers(this.Username, list_id, cursor, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message;
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ try
+ {
+ var users = MyCommon.CreateDataFromJson<TwitterDataModel.Users>(content);
+ Array.ForEach<TwitterDataModel.User>(
+ users.users,
+ new Action<TwitterDataModel.User>(u => lists.Add(new UserInfo(u))));
+ cursor = users.NextCursor;
+ return "";
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+
+ return "";
+ }
+
+ public string CreateListApi(string listName, bool isPrivate, string description)
+ {
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ try
+ {
+ res = twCon.CreateLists(listName, isPrivate, description, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ try
+ {
+ var le = MyCommon.CreateDataFromJson<TwitterDataModel.ListElementData>(content);
+ TabInformations.GetInstance().SubscribableLists.Add(new ListElement(le, this));
+ return "";
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+ }
+
+ public string ContainsUserAtList(string listId, string user, ref bool value)
+ {
+ value = false;
+
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ try
+ {
+ res = this.twCon.ShowListMember(listId, user, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ case HttpStatusCode.NotFound:
+ value = false;
+ return "";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ try
+ {
+ var u = MyCommon.CreateDataFromJson<TwitterDataModel.User>(content);
+ value = true;
+ return "";
+ }
+ catch(Exception)
+ {
+ value = false;
+ return "";
+ }
+ }
+
+ public string AddUserToList(string listId, string user)
+ {
+ var content = "";
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+
+ try
+ {
+ res = twCon.CreateListMembers(listId, user, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:" + GetErrorMessageJson(content);
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ return "";
+ }
+
+ public string RemoveUserToList(string listId, string user)
+ {
+
+ var content = "";
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+
+ try
+ {
+ res = twCon.DeleteListMembers(listId, user, ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:" + GetErrorMessageJson(content);
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ return "";
+ }
+
+ private class range
+ {
+ public int fromIndex { get; set; }
+ public int toIndex { get; set; }
+ public range(int fromIndex, int toIndex)
+ {
+ this.fromIndex = fromIndex;
+ this.toIndex = toIndex;
+ }
+ }
+ public string CreateHtmlAnchor(string Text, List<string> AtList, Dictionary<string, string> media)
+ {
+ if (Text == null) return null;
+ var retStr = Text.Replace(">", "<<<<<tweenだいなり>>>>>").Replace("<", "<<<<<tweenしょうなり>>>>>");
+ //uriの正規表現
+ //const string url_valid_domain = "(?<domain>(?:[^\p{P}\s][\.\-_](?=[^\p{P}\s])|[^\p{P}\s]){1,}\.[a-z]{2,}(?::[0-9]+)?)"
+ //const string url_valid_general_path_chars = "[a-z0-9!*';:=+$/%#\[\]\-_,~]"
+ //const string url_balance_parens = "(?:\(" + url_valid_general_path_chars + "+\))"
+ //const string url_valid_url_path_ending_chars = "(?:[a-z0-9=_#/\-\+]+|" + url_balance_parens + ")"
+ //const string pth = "(?:" + url_balance_parens +
+ // "|@" + url_valid_general_path_chars + "+/" +
+ // "|[.,]?" + url_valid_general_path_chars + "+" +
+ // ")"
+ //const string pth2 = "(/(?:" +
+ // pth + "+" + url_valid_url_path_ending_chars + "|" +
+ // pth + "+" + url_valid_url_path_ending_chars + "?|" +
+ // url_valid_url_path_ending_chars +
+ // ")?)?"
+ //const string qry = "(?<query>\?[a-z0-9!*'();:&=+$/%#\[\]\-_.,~]*[a-z0-9_&=#])?"
+ //const string rgUrl = "(?<before>(?:[^\""':!=#]|^|\:/))" +
+ // "(?<url>(?<protocol>https?://)" +
+ // url_valid_domain +
+ // pth2 +
+ // qry +
+ // ")"
+ //const string rgUrl = "(?<before>(?:[^\""':!=#]|^|\:/))" +
+ // "(?<url>(?<protocol>https?://|www\.)" +
+ // url_valid_domain +
+ // pth2 +
+ // qry +
+ // ")"
+ //絶対パス表現のUriをリンクに置換
+ retStr = Regex.Replace(retStr,
+ rgUrl,
+ new MatchEvaluator((Match mu) =>
+ {
+ var sb = new StringBuilder(mu.Result("${before}<a href=\""));
+ //if (mu.Result("${protocol}").StartsWith("w", StringComparison.OrdinalIgnoreCase))
+ // sb.Append("http://");
+ //}
+ var url = mu.Result("${url}");
+ var title = ShortUrl.ResolveMedia(url, true);
+ if (url != title)
+ {
+ title = ShortUrl.ResolveMedia(title, false);
+ }
+ sb.Append(url + "\" title=\"" + title + "\">").Append(url).Append("</a>");
+ if (media != null && !media.ContainsKey(url)) media.Add(url, title);
+ return sb.ToString();
+ }),
+ RegexOptions.IgnoreCase);
+
+ //@先をリンクに置換(リスト)
+ retStr = Regex.Replace(retStr,
+ @"(^|[^a-zA-Z0-9_/])([@@]+)([a-zA-Z0-9_]{1,20}/[a-zA-Z][a-zA-Z0-9\p{IsLatin-1Supplement}\-]{0,79})",
+ "$1$2<a href=\"/$3\">$3</a>");
+
+ var m = Regex.Match(retStr, "(^|[^a-zA-Z0-9_])[@@]([a-zA-Z0-9_]{1,20})");
+ while (m.Success)
+ {
+ if (!AtList.Contains(m.Result("$2").ToLower())) AtList.Add(m.Result("$2").ToLower());
+ m = m.NextMatch();
+ }
+ //@先をリンクに置換
+ retStr = Regex.Replace(retStr,
+ "(^|[^a-zA-Z0-9_/])([@@])([a-zA-Z0-9_]{1,20})",
+ "$1$2<a href=\"/$3\">$3</a>");
+
+ //ハッシュタグを抽出し、リンクに置換
+ var anchorRange = new List<range>();
+ for (int i = 0; i < retStr.Length; i++)
+ {
+ var index = retStr.IndexOf("<a ", i);
+ if (index > -1 && index < retStr.Length)
+ {
+ i = index;
+ var toIndex = retStr.IndexOf("</a>", index);
+ if (toIndex > -1)
+ {
+ anchorRange.Add(new range(index, toIndex + 3));
+ i = toIndex;
+ }
+ }
+ }
+ //retStr = Regex.Replace(retStr,
+ // "(^|[^a-zA-Z0-9/&])([##])([0-9a-zA-Z_]*[a-zA-Z_]+[a-zA-Z0-9_\xc0-\xd6\xd8-\xf6\xf8-\xff]*)",
+ // new MatchEvaluator(Function(mh As Match)
+ // foreach (var rng in anchorRange)
+ // {
+ // if (mh.Index >= rng.fromIndex &&
+ // mh.Index <= rng.toIndex) return mh.Result("$0");
+ // }
+ // if (IsNumeric(mh.Result("$3"))) return mh.Result("$0");
+ // lock (LockObj)
+ // {
+ // _hashList.Add("#" + mh.Result("$3"))
+ // }
+ // return mh.Result("$1") + "<a href=\"" + _protocol + "twitter.com/search?q=%23" + mh.Result("$3") + "\">" + mh.Result("$2$3") + "</a>";
+ // }),
+ // RegexOptions.IgnoreCase)
+ retStr = Regex.Replace(retStr,
+ HASHTAG,
+ new MatchEvaluator(mh =>
+ {
+ foreach (var rng in anchorRange)
+ {
+ if (mh.Index >= rng.fromIndex &&
+ mh.Index <= rng.toIndex) return mh.Result("$0");
+ }
+ lock (LockObj)
+ {
+ _hashList.Add("#" + mh.Result("$3"));
+ }
+ return mh.Result("$1") + "<a href=\"" + _protocol + "twitter.com/search?q=%23" + mh.Result("$3") + "\">" + mh.Result("$2$3") + "</a>";
+ }),
+ RegexOptions.IgnoreCase);
+
+
+ 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>");
+
+ retStr = retStr.Replace("<<<<<tweenだいなり>>>>>", ">").Replace("<<<<<tweenしょうなり>>>>>", "<");
+
+ //retStr = AdjustHtml(ShortUrl.Resolve(PreProcessUrl(retStr), true)) //IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
+ retStr = AdjustHtml(PreProcessUrl(retStr)); //IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
+ return retStr;
+ }
+
+ private class EntityInfo
+ {
+ public int StartIndex { get; set; }
+ public int EndIndex { get; set; }
+ public string Text { get; set; }
+ public string Html { get; set; }
+ public string Display { get; set; }
+ }
+ public string CreateHtmlAnchor(ref string Text, List<string> AtList, TwitterDataModel.Entities entities, Dictionary<string, string> media)
+ {
+ var ret = Text;
+
+ if (entities != null)
+ {
+ var etInfo = new SortedList<int, EntityInfo>();
+ //URL
+ if (entities.Urls != null)
+ {
+ foreach (var ent in entities.Urls)
+ {
+ if (string.IsNullOrEmpty(ent.DisplayUrl))
+ {
+ etInfo.Add(ent.Indices[0],
+ new EntityInfo {StartIndex = ent.Indices[0],
+ EndIndex = ent.Indices[1],
+ Text = ent.Url,
+ Html = "<a href=\"" + ent.Url + "\">" + ent.Url + "</a>"});
+ }
+ else
+ {
+ var expanded = ShortUrl.ResolveMedia(ent.ExpandedUrl, false);
+ etInfo.Add(ent.Indices[0],
+ new EntityInfo {StartIndex = ent.Indices[0],
+ EndIndex = ent.Indices[1],
+ Text = ent.Url,
+ Html = "<a href=\"" + ent.Url + "\" title=\"" + expanded + "\">" + ent.DisplayUrl + "</a>",
+ Display = ent.DisplayUrl});
+ if (media != null && !media.ContainsKey(ent.Url)) media.Add(ent.Url, expanded);
+ }
+ }
+ }
+ if (entities.Hashtags != null)
+ {
+ foreach (var ent in entities.Hashtags)
+ {
+ var hash = Text.Substring(ent.Indices[0], ent.Indices[1] - ent.Indices[0]);
+ etInfo.Add(ent.Indices[0],
+ new EntityInfo {StartIndex = ent.Indices[0],
+ EndIndex = ent.Indices[1],
+ Text = hash,
+ Html = "<a href=\"" + _protocol + "twitter.com/search?q=%23" + ent.Text + "\">" + hash + "</a>"});
+ lock (LockObj)
+ {
+ _hashList.Add("#" + ent.Text);
+ }
+ }
+ }
+ if (entities.UserMentions != null)
+ {
+ foreach (var ent in entities.UserMentions)
+ {
+ var screenName = Text.Substring(ent.Indices[0] + 1, ent.Indices[1] - ent.Indices[0] - 1);
+ etInfo.Add(ent.Indices[0] + 1,
+ new EntityInfo {StartIndex = ent.Indices[0] + 1,
+ EndIndex = ent.Indices[1],
+ Text = ent.ScreenName,
+ Html = "<a href=\"/" + ent.ScreenName + "\">" + screenName + "</a>"});
+ if (!AtList.Contains(ent.ScreenName.ToLower())) AtList.Add(ent.ScreenName.ToLower());
+ }
+ }
+ if (entities.Media != null)
+ {
+ foreach (var ent in entities.Media)
+ {
+ if (ent.Type == "photo")
+ {
+ etInfo.Add(ent.Indices[0],
+ new EntityInfo {StartIndex = ent.Indices[0],
+ EndIndex = ent.Indices[1],
+ Text = ent.Url,
+ Html = "<a href=\"" + ent.Url + "\" title=\"" + ent.ExpandedUrl + "\">" + ent.DisplayUrl + "</a>",
+ Display = ent.DisplayUrl});
+ if (media != null && !media.ContainsKey(ent.Url)) media.Add(ent.Url, ent.MediaUrl);
+ }
+ }
+ }
+ if (etInfo.Count > 0)
+ {
+ try
+ {
+ var idx = 0;
+ ret = "";
+ foreach (var et in etInfo)
+ {
+ ret += Text.Substring(idx, et.Key - idx) + et.Value.Html;
+ idx = et.Value.EndIndex;
+ }
+ ret += Text.Substring(idx);
+ }
+ catch(ArgumentOutOfRangeException)
+ {
+ //Twitterのバグで不正なエンティティ(Index指定範囲が重なっている)が返ってくる場合の対応
+ ret = Text;
+ entities = null;
+ if (media != null) media.Clear();
+ }
+ }
+ }
+
+ 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>");
+ ret = AdjustHtml(ShortUrl.Resolve(PreProcessUrl(ret), false)); //IDN置換、短縮Uri解決、@リンクを相対→絶対にしてtarget属性付与
+
+ return ret;
+ }
+
+ //Source整形
+ private void CreateSource(ref PostClass post)
+ {
+ if (post.Source.StartsWith("<"))
+ {
+ if (!post.Source.Contains("</a>"))
+ {
+ post.Source += "</a>";
+ }
+ var mS = Regex.Match(post.Source, ">(?<source>.+)<");
+ if (mS.Success)
+ {
+ post.SourceHtml = string.Copy(ShortUrl.Resolve(PreProcessUrl(post.Source), false));
+ post.Source = HttpUtility.HtmlDecode(mS.Result("${source}"));
+ }
+ else
+ {
+ post.Source = "";
+ post.SourceHtml = "";
+ }
+ }
+ else
+ {
+ if (post.Source == "web")
+ {
+ post.SourceHtml = Properties.Resources.WebSourceString;
+ }
+ else if (post.Source == "Keitai Mail")
+ {
+ post.SourceHtml = Properties.Resources.KeitaiMailSourceString;
+ }
+ else
+ {
+ post.SourceHtml = string.Copy(post.Source);
+ }
+ }
+ }
+
+ public bool GetInfoApi(ApiInfo info)
+ {
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return true;
+
+ if (MyCommon._endingFlag) return true;
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+ try
+ {
+ res = twCon.RateLimitStatus(ref content);
+ }
+ catch(Exception)
+ {
+ MyCommon.TwitterApiInfo.Initialize();
+ return false;
+ }
+
+ if (res != HttpStatusCode.OK) return false;
+
+ try
+ {
+ var limit = MyCommon.CreateDataFromJson<TwitterDataModel.RateLimitStatus>(content);
+ var arg = new ApiInformationChangedEventArgs();
+ arg.ApiInfo.MaxCount = limit.HourlyLimit;
+ arg.ApiInfo.RemainCount = limit.RemainingHits;
+ arg.ApiInfo.ResetTime = MyCommon.DateTimeParse(limit.RestTime);
+ arg.ApiInfo.ResetTimeInSeconds = limit.RestTimeInSeconds;
+ if (info != null)
+ {
+ arg.ApiInfo.UsingCount = info.UsingCount;
+
+ info.MaxCount = arg.ApiInfo.MaxCount;
+ info.RemainCount = arg.ApiInfo.RemainCount;
+ info.ResetTime = arg.ApiInfo.ResetTime;
+ info.ResetTimeInSeconds = arg.ApiInfo.ResetTimeInSeconds;
+ }
+
+ if (ApiInformationChanged != null)
+ {
+ ApiInformationChanged(this, arg);
+ }
+ MyCommon.TwitterApiInfo.WriteBackEventArgs(arg);
+ return true;
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ MyCommon.TwitterApiInfo.Initialize();
+ return false;
+ }
+ }
+ public string GetBlockUserIds()
+ {
+ if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
+
+ HttpStatusCode res = HttpStatusCode.BadRequest;
+ var content = "";
+
+ try
+ {
+ res = twCon.GetBlockUserIds(ref content);
+ }
+ catch(Exception ex)
+ {
+ return "Err:" + ex.Message + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ return Properties.Resources.Unauthorized;
+ case HttpStatusCode.BadRequest:
+ return "Err:API Limits?";
+ default:
+ return "Err:" + res.ToString() + "(" + MethodBase.GetCurrentMethod().Name + ")";
+ }
+
+ try
+ {
+ var Ids = MyCommon.CreateDataFromJson<List<long>>(content);
+ if (Ids.Contains(this.UserId)) Ids.Remove(this.UserId);
+ TabInformations.GetInstance().BlockIds.AddRange(Ids);
+ return ("");
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex.Message + Environment.NewLine + content);
+ return "Err:Json Parse Error(DataContractJsonSerializer)";
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
+ return "Err:Invalid Json!";
+ }
+
+ }
+
+ public string[] GetHashList()
+ {
+ string[] hashArray;
+ lock (LockObj)
+ {
+ hashArray = _hashList.ToArray();
+ _hashList.Clear();
+ }
+ return hashArray;
+ }
+
+ public string AccessToken
+ {
+ get
+ {
+ return twCon.AccessToken;
+ }
+ }
+
+ public string AccessTokenSecret
+ {
+ get
+ {
+ return twCon.AccessTokenSecret;
+ }
+ }
+
+ public event EventHandler<ApiInformationChangedEventArgs> ApiInformationChanged;
+
+ private void Twitter_ApiInformationChanged(object sender, ApiInformationChangedEventArgs e)
+ {
+ }
+
+ public Twitter()
+ {
+ ApiInformationChanged += Twitter_ApiInformationChanged;
+ }
+
+#region "UserStream"
+ private string trackWord_ = "";
+ public string TrackWord
+ {
+ get
+ {
+ return trackWord_;
+ }
+ set
+ {
+ trackWord_ = value;
+ }
+ }
+ private bool allAtReply_ = false;
+ public bool AllAtReply
+ {
+ get
+ {
+ return allAtReply_;
+ }
+ set
+ {
+ allAtReply_ = value;
+ }
+ }
+
+ public event Action NewPostFromStream;
+ public event Action UserStreamStarted;
+ public event Action UserStreamStopped;
+ public event Action UserStreamGetFriendsList;
+ public event Action<long> PostDeleted;
+ public event Action<FormattedEvent> UserStreamEventReceived;
+ private DateTime _lastUserstreamDataReceived;
+ private TwitterUserstream userStream;
+
+ public class FormattedEvent
+ {
+ public MyCommon.EVENTTYPE Eventtype { get; set; }
+ public DateTime CreatedAt { get; set; }
+ public string Event { get; set; }
+ public string Username { get; set; }
+ public string Target { get; set; }
+ public Int64 Id { get; set; }
+ public bool IsMe { get; set; }
+ }
+
+ public List<FormattedEvent> storedEvent_ = new List<FormattedEvent>();
+ public List<FormattedEvent> StoredEvent
+ {
+ get
+ {
+ return storedEvent_;
+ }
+ set
+ {
+ storedEvent_ = value;
+ }
+ }
+
+ private class EventTypeTableElement
+ {
+ public string Name;
+ public MyCommon.EVENTTYPE Type;
+
+ public EventTypeTableElement(string name, MyCommon.EVENTTYPE type)
+ {
+ this.Name = name;
+ this.Type = type;
+ }
+ }
+
+ private EventTypeTableElement[] EventTable = {
+ new EventTypeTableElement("favorite", MyCommon.EVENTTYPE.Favorite),
+ new EventTypeTableElement("unfavorite", MyCommon.EVENTTYPE.Unfavorite),
+ new EventTypeTableElement("follow", MyCommon.EVENTTYPE.Follow),
+ new EventTypeTableElement("list_member_added", MyCommon.EVENTTYPE.ListMemberAdded),
+ new EventTypeTableElement("list_member_removed", MyCommon.EVENTTYPE.ListMemberRemoved),
+ new EventTypeTableElement("block", MyCommon.EVENTTYPE.Block),
+ new EventTypeTableElement("unblock", MyCommon.EVENTTYPE.Unblock),
+ new EventTypeTableElement("user_update", MyCommon.EVENTTYPE.UserUpdate),
+ new EventTypeTableElement("deleted", MyCommon.EVENTTYPE.Deleted),
+ new EventTypeTableElement("list_created", MyCommon.EVENTTYPE.ListCreated),
+ new EventTypeTableElement("list_updated", MyCommon.EVENTTYPE.ListUpdated),
+ };
+
+ public MyCommon.EVENTTYPE EventNameToEventType(string EventName)
+ {
+ return (from tbl in EventTable where tbl.Name.Equals(EventName) select tbl.Type).FirstOrDefault();
+ }
+
+ public bool IsUserstreamDataReceived
+ {
+ get
+ {
+ return DateTime.Now.Subtract(this._lastUserstreamDataReceived).TotalSeconds < 31;
+ }
+ }
+
+ private void userStream_StatusArrived(string line)
+ {
+ this._lastUserstreamDataReceived = DateTime.Now;
+ if (string.IsNullOrEmpty(line)) return;
+
+ var isDm = false;
+
+ try
+ {
+ using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(line), XmlDictionaryReaderQuotas.Max))
+ {
+ var xElm = XElement.Load(jsonReader);
+ if (xElm.Element("friends") != null)
+ {
+ Debug.WriteLine("friends");
+ return;
+ }
+ else if (xElm.Element("delete") != null)
+ {
+ Debug.WriteLine("delete");
+ Int64 id;
+ if (xElm.Element("delete").Element("direct_message") != null &&
+ xElm.Element("delete").Element("direct_message").Element("id") != null)
+ {
+ id = 0;
+ long.TryParse(xElm.Element("delete").Element("direct_message").Element("id").Value, out id);
+ if (PostDeleted != null)
+ {
+ PostDeleted(id);
+ }
+ }
+ else if (xElm.Element("delete").Element("status") != null &&
+ xElm.Element("delete").Element("status").Element("id") != null)
+ {
+ id = 0;
+ long.TryParse(xElm.Element("delete").Element("status").Element("id").Value, out id);
+ if (PostDeleted != null)
+ {
+ PostDeleted(id);
+ }
+ }
+ else
+ {
+ MyCommon.TraceOut("delete:" + line);
+ return;
+ }
+ for (int i = this.StoredEvent.Count - 1; i >= 0; i--)
+ {
+ var sEvt = this.StoredEvent[i];
+ if (sEvt.Id == id && (sEvt.Event == "favorite" || sEvt.Event == "unfavorite"))
+ {
+ this.StoredEvent.RemoveAt(i);
+ }
+ }
+ return;
+ }
+ else if (xElm.Element("limit") != null)
+ {
+ Debug.WriteLine(line);
+ return;
+ }
+ else if (xElm.Element("event") != null)
+ {
+ Debug.WriteLine("event: " + xElm.Element("event").Value);
+ CreateEventFromJson(line);
+ return;
+ }
+ else if (xElm.Element("direct_message") != null)
+ {
+ Debug.WriteLine("direct_message");
+ isDm = true;
+ }
+ else if (xElm.Element("scrub_geo") != null)
+ {
+ try
+ {
+ TabInformations.GetInstance().ScrubGeoReserve(long.Parse(xElm.Element("scrub_geo").Element("user_id").Value),
+ long.Parse(xElm.Element("scrub_geo").Element("up_to_status_id").Value));
+ }
+ catch(Exception)
+ {
+ MyCommon.TraceOut("scrub_geo:" + line);
+ }
+ return;
+ }
+ }
+
+ var res = new StringBuilder();
+ res.Length = 0;
+ res.Append("[");
+ res.Append(line);
+ res.Append("]");
+
+ if (isDm)
+ {
+ CreateDirectMessagesFromJson(res.ToString(), MyCommon.WORKERTYPE.UserStream, false);
+ }
+ else
+ {
+ long dummy = 0;
+ CreatePostsFromJson(res.ToString(), MyCommon.WORKERTYPE.Timeline, null, false, 0, ref dummy);
+ }
+ }
+ catch(NullReferenceException)
+ {
+ MyCommon.TraceOut("NullRef StatusArrived: " + line);
+ }
+
+ if (NewPostFromStream != null)
+ {
+ NewPostFromStream();
+ }
+ }
+
+ private void CreateEventFromJson(string content)
+ {
+ TwitterDataModel.EventData eventData = null;
+ try
+ {
+ eventData = MyCommon.CreateDataFromJson<TwitterDataModel.EventData>(content);
+ }
+ catch(SerializationException ex)
+ {
+ MyCommon.TraceOut(ex, "Event Serialize Exception!" + Environment.NewLine + content);
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut(ex, "Event Exception!" + Environment.NewLine + content);
+ }
+
+ var evt = new FormattedEvent();
+ evt.CreatedAt = MyCommon.DateTimeParse(eventData.CreatedAt);
+ evt.Event = eventData.Event;
+ evt.Username = eventData.Source.ScreenName;
+ evt.IsMe = evt.Username.ToLower().Equals(this.Username.ToLower());
+ evt.Eventtype = EventNameToEventType(evt.Event);
+ switch (eventData.Event)
+ {
+ case "access_revoked":
+ return;
+ case "follow":
+ if (eventData.Target.ScreenName.ToLower().Equals(_uname))
+ {
+ if (!this.followerId.Contains(eventData.Source.Id)) this.followerId.Add(eventData.Source.Id);
+ }
+ else
+ {
+ return; //Block後のUndoをすると、SourceとTargetが逆転したfollowイベントが帰ってくるため。
+ }
+ evt.Target = "";
+ break;
+ case "favorite":
+ case "unfavorite":
+ evt.Target = "@" + eventData.TargetObject.User.ScreenName + ":" + HttpUtility.HtmlDecode(eventData.TargetObject.Text);
+ evt.Id = eventData.TargetObject.Id;
+ if (AppendSettingDialog.Instance.IsRemoveSameEvent)
+ {
+ if (StoredEvent.Any(ev =>
+ {
+ return ev.Username == evt.Username && ev.Eventtype == evt.Eventtype && ev.Target == evt.Target;
+ })) return;
+ }
+ if (TabInformations.GetInstance().ContainsKey(eventData.TargetObject.Id))
+ {
+ var post = TabInformations.GetInstance()[eventData.TargetObject.Id];
+ if (eventData.Event == "favorite")
+ {
+ if (evt.Username.ToLower().Equals(_uname))
+ {
+ post.IsFav = true;
+ TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Add(post.StatusId, post.IsRead, false);
+ }
+ else
+ {
+ post.FavoritedCount++;
+ if (!TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Contains(post.StatusId))
+ {
+ if (AppendSettingDialog.Instance.FavEventUnread && post.IsRead)
+ {
+ post.IsRead = false;
+ }
+ TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).Add(post.StatusId, post.IsRead, false);
+ }
+ else
+ {
+ if (AppendSettingDialog.Instance.FavEventUnread)
+ {
+ TabInformations.GetInstance().SetRead(false, TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).TabName, TabInformations.GetInstance().GetTabByType(MyCommon.TabUsageType.Favorites).IndexOf(post.StatusId));
+ }
+ }
+ }
+ }
+ else
+ {
+ if (evt.Username.ToLower().Equals(_uname))
+ {
+ post.IsFav = false;
+ }
+ else
+ {
+ post.FavoritedCount--;
+ if (post.FavoritedCount < 0) post.FavoritedCount = 0;
+ }
+ }
+ }
+ break;
+ case "list_member_added":
+ case "list_member_removed":
+ case "list_updated":
+ evt.Target = eventData.TargetObject.FullName;
+ break;
+ case "block":
+ if (!TabInformations.GetInstance().BlockIds.Contains(eventData.Target.Id)) TabInformations.GetInstance().BlockIds.Add(eventData.Target.Id);
+ evt.Target = "";
+ break;
+ case "unblock":
+ if (TabInformations.GetInstance().BlockIds.Contains(eventData.Target.Id)) TabInformations.GetInstance().BlockIds.Remove(eventData.Target.Id);
+ evt.Target = "";
+ break;
+ case "user_update":
+ evt.Target = "";
+ break;
+ case "list_created":
+ evt.Target = "";
+ break;
+ default:
+ MyCommon.TraceOut("Unknown Event:" + evt.Event + Environment.NewLine + content);
+ break;
+ }
+ this.StoredEvent.Insert(0, evt);
+ if (UserStreamEventReceived != null)
+ {
+ UserStreamEventReceived(evt);
+ }
+ }
+
+ private void userStream_Started()
+ {
+ if (UserStreamStarted != null)
+ {
+ UserStreamStarted();
+ }
+ }
+
+ private void userStream_Stopped()
+ {
+ if (UserStreamStopped != null)
+ {
+ UserStreamStopped();
+ }
+ }
+
+ public bool UserStreamEnabled
+ {
+ get
+ {
+ return userStream == null ? false : userStream.Enabled;
+ }
+ }
+
+ public void StartUserStream()
+ {
+ if (userStream != null)
+ {
+ StopUserStream();
+ }
+ userStream = new TwitterUserstream(twCon);
+ userStream.StatusArrived += userStream_StatusArrived;
+ userStream.Started += userStream_Started;
+ userStream.Stopped += userStream_Stopped;
+ userStream.Start(this.AllAtReply, this.TrackWord);
+ }
+
+ public void StopUserStream()
+ {
+ if (userStream != null) userStream.Dispose();
+ userStream = null;
+ if (!MyCommon._endingFlag)
+ {
+ if (UserStreamStopped != null)
+ {
+ UserStreamStopped();
+ }
+ }
+ }
+
+ public void ReconnectUserStream()
+ {
+ if (userStream != null)
+ {
+ this.StartUserStream();
+ }
+ }
+
+ private class TwitterUserstream : IDisposable
+ {
+ public event Action<string> StatusArrived;
+ public event Action Stopped;
+ public event Action Started;
+ private HttpTwitter twCon;
+
+ private Thread _streamThread;
+ private bool _streamActive;
+
+ private bool _allAtreplies = false;
+ private string _trackwords = "";
+
+ public TwitterUserstream(HttpTwitter twitterConnection)
+ {
+ twCon = (HttpTwitter)twitterConnection.Clone();
+ }
+
+ public void Start(bool allAtReplies, string trackwords)
+ {
+ this.AllAtReplies = allAtReplies;
+ this.TrackWords = trackwords;
+ _streamActive = true;
+ if (_streamThread != null && _streamThread.IsAlive) return;
+ _streamThread = new Thread(UserStreamLoop);
+ _streamThread.Name = "UserStreamReceiver";
+ _streamThread.IsBackground = true;
+ _streamThread.Start();
+ }
+
+ public bool Enabled
+ {
+ get
+ {
+ return _streamActive;
+ }
+ }
+
+ public bool AllAtReplies
+ {
+ get
+ {
+ return _allAtreplies;
+ }
+ set
+ {
+ _allAtreplies = value;
+ }
+ }
+
+ public string TrackWords
+ {
+ get
+ {
+ return _trackwords;
+ }
+ set
+ {
+ _trackwords = value;
+ }
+ }
+
+ private void UserStreamLoop()
+ {
+ Stream st = null;
+ StreamReader sr = null;
+ var sleepSec = 0;
+ do
+ {
+ try
+ {
+ if (!MyCommon.IsNetworkAvailable())
+ {
+ sleepSec = 30;
+ continue;
+ }
+
+ if (Started != null)
+ {
+ Started();
+ }
+ var res = twCon.UserStream(ref st, _allAtreplies, _trackwords, MyCommon.GetUserAgentString());
+
+ switch (res)
+ {
+ case HttpStatusCode.OK:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Valid;
+ break;
+ case HttpStatusCode.Unauthorized:
+ Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
+ sleepSec = 120;
+ continue;
+ }
+
+ if (st == null)
+ {
+ sleepSec = 30;
+ //MyCommon.TraceOut("Stop:stream is null")
+ continue;
+ }
+
+ sr = new StreamReader(st);
+
+ while (_streamActive && !sr.EndOfStream && Twitter.AccountState == MyCommon.ACCOUNT_STATE.Valid)
+ {
+ if (StatusArrived != null)
+ {
+ StatusArrived(sr.ReadLine());
+ }
+ //this.LastTime = Now;
+ }
+
+ if (sr.EndOfStream || Twitter.AccountState == MyCommon.ACCOUNT_STATE.Invalid)
+ {
+ sleepSec = 30;
+ //MyCommon.TraceOut("Stop:EndOfStream")
+ continue;
+ }
+ break;
+ }
+ catch(WebException ex)
+ {
+ if (ex.Status == WebExceptionStatus.Timeout)
+ {
+ sleepSec = 30; //MyCommon.TraceOut("Stop:Timeout")
+ }
+ else if (ex.Response != null && (int)((HttpWebResponse)ex.Response).StatusCode == 420)
+ {
+ //MyCommon.TraceOut("Stop:Connection Limit")
+ break;
+ }
+ else
+ {
+ sleepSec = 30;
+ //MyCommon.TraceOut("Stop:WebException " + ex.Status.ToString())
+ }
+ }
+ catch(ThreadAbortException)
+ {
+ break;
+ }
+ catch(IOException)
+ {
+ sleepSec = 30;
+ //MyCommon.TraceOut("Stop:IOException with Active." + Environment.NewLine + ex.Message)
+ }
+ catch(ArgumentException ex)
+ {
+ //System.ArgumentException: ストリームを読み取れませんでした。
+ //サーバー側もしくは通信経路上で切断された場合?タイムアウト頻発後発生
+ sleepSec = 30;
+ MyCommon.TraceOut(ex, "Stop:ArgumentException");
+ }
+ catch(Exception ex)
+ {
+ MyCommon.TraceOut("Stop:Exception." + Environment.NewLine + ex.Message);
+ MyCommon.ExceptionOut(ex);
+ sleepSec = 30;
+ }
+ finally
+ {
+ if (_streamActive)
+ {
+ if (Stopped != null)
+ {
+ Stopped();
+ }
+ }
+ twCon.RequestAbort();
+ if (sr != null) sr.Close();
+ if (st != null) st.Close();
+ if (sleepSec > 0)
+ {
+ var ms = 0;
+ while (_streamActive && ms < sleepSec * 1000)
+ {
+ Thread.Sleep(500);
+ ms += 500;
+ }
+ }
+ sleepSec = 0;
+ }
+ } while (this._streamActive);
+
+ if (_streamActive)
+ {
+ if (Stopped != null)
+ {
+ Stopped();
+ }
+ }
+ MyCommon.TraceOut("Stop:EndLoop");
+ }
+
+#region "IDisposable Support"
+ private bool disposedValue; // 重複する呼び出しを検出するには
+
+ // IDisposable
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!this.disposedValue)
+ {
+ if (disposing)
+ {
+ // TODO: マネージ状態を破棄します (マネージ オブジェクト)。
+ _streamActive = false;
+ if (_streamThread != null && _streamThread.IsAlive)
+ {
+ _streamThread.Abort();
+ }
+ }
+
+ // TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下の Finalize() をオーバーライドします。
+ // TODO: 大きなフィールドを null に設定します。
+ }
+ this.disposedValue = true;
+ }
+
+ // TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードがある場合にのみ、Finalize() をオーバーライドします。
+ //protected Overrides void Finalize()
+ //{
+ // // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
+ // Dispose(false)
+ // MyBase.Finalize()
+ //}
+
+ // このコードは、破棄可能なパターンを正しく実装できるように Visual Basic によって追加されました。
+ public void Dispose()
+ {
+ // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+#endregion
+
+ }
+#endregion
+
+#region "IDisposable Support"
+ private bool disposedValue; // 重複する呼び出しを検出するには
+
+ // IDisposable
+ protected virtual void Dispose(bool disposing)
+ {
+ if (!this.disposedValue)
+ {
+ if (disposing)
+ {
+ // TODO: マネージ状態を破棄します (マネージ オブジェクト)。
+ this.StopUserStream();
+ }
+
+ // TODO: アンマネージ リソース (アンマネージ オブジェクト) を解放し、下の Finalize() をオーバーライドします。
+ // TODO: 大きなフィールドを null に設定します。
+ }
+ this.disposedValue = true;
+ }
+
+ // TODO: 上の Dispose(bool disposing) にアンマネージ リソースを解放するコードがある場合にのみ、Finalize() をオーバーライドします。
+ //protected Overrides void Finalize()
+ //{
+ // // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
+ // Dispose(false)
+ // MyBase.Finalize()
+ //}
+
+ // このコードは、破棄可能なパターンを正しく実装できるように Visual Basic によって追加されました。
+ public void Dispose()
+ {
+ // このコードを変更しないでください。クリーンアップ コードを上の Dispose(bool disposing) に記述します。
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+#endregion
+ }
+}