OSDN Git Service

APIレートリミット周りの実装を修正
authorKimura Youichi <kim.upsilon@bucyou.net>
Mon, 18 Feb 2013 12:32:02 +0000 (21:32 +0900)
committerKimura Youichi <kim.upsilon@bucyou.net>
Wed, 20 Feb 2013 13:37:54 +0000 (22:37 +0900)
15 files changed:
OpenTween.Tests/Api/ApiLimitTest.cs [new file with mode: 0644]
OpenTween.Tests/Api/TwitterApiStatusTest.cs [new file with mode: 0644]
OpenTween.Tests/ApiInformationTest.cs [deleted file]
OpenTween.Tests/OpenTween.Tests.csproj
OpenTween/Api/ApiLimit.cs [new file with mode: 0644]
OpenTween/Api/TwitterApiAccessLevel.cs [new file with mode: 0644]
OpenTween/Api/TwitterApiStatus.cs [new file with mode: 0644]
OpenTween/ApiInformation.cs [deleted file]
OpenTween/AppendSettingDialog.cs
OpenTween/Connection/HttpTwitter.cs
OpenTween/DataModel.cs
OpenTween/MyCommon.cs
OpenTween/OpenTween.csproj
OpenTween/Tween.cs
OpenTween/Twitter.cs

diff --git a/OpenTween.Tests/Api/ApiLimitTest.cs b/OpenTween.Tests/Api/ApiLimitTest.cs
new file mode 100644 (file)
index 0000000..1f720ac
--- /dev/null
@@ -0,0 +1,50 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2013 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// 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;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+
+namespace OpenTween.Api
+{
+    [TestFixture]
+    class ApiLimitTest
+    {
+        public static object[] Equals_TestCase =
+        {
+            new object[] { new ApiLimit(150, 100, new DateTime(2013, 1, 1, 0, 0, 0)), true },
+            new object[] { new ApiLimit(350, 100, new DateTime(2013, 1, 1, 0, 0, 0)), false },
+            new object[] { new ApiLimit(150, 150, new DateTime(2013, 1, 1, 0, 0, 0)), false },
+            new object[] { new ApiLimit(150, 100, new DateTime(2012, 12, 31, 0, 0, 0)), false },
+            new object[] { null, false },
+            new object[] { new object(), false },
+        };
+        [TestCaseSource("Equals_TestCase")]
+        public void EqualsTest(object obj2, bool expect)
+        {
+            var obj1 = new ApiLimit(150, 100, new DateTime(2013, 1, 1, 0, 0, 0));
+
+            Assert.That(obj1.Equals(obj2), Is.EqualTo(expect));
+        }
+    }
+}
diff --git a/OpenTween.Tests/Api/TwitterApiStatusTest.cs b/OpenTween.Tests/Api/TwitterApiStatusTest.cs
new file mode 100644 (file)
index 0000000..a1a36dd
--- /dev/null
@@ -0,0 +1,281 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2013 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// 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;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+
+namespace OpenTween.Api
+{
+    [TestFixture]
+    class TwitterApiStatusTest
+    {
+        [Test]
+        public void ResetTest()
+        {
+            var apiStatus = new TwitterApiStatus();
+
+            apiStatus.AccessLimit = new ApiLimit(150, 100, new DateTime(2013, 1, 1, 0, 0, 0));
+            apiStatus.MediaUploadLimit = new ApiLimit(150, 100, new DateTime(2013, 1, 1, 0, 0, 0));
+            apiStatus.AccessLevel = TwitterApiAccessLevel.ReadWriteAndDirectMessage;
+
+            apiStatus.Reset();
+
+            Assert.That(apiStatus.AccessLimit, Is.Null);
+            Assert.That(apiStatus.MediaUploadLimit, Is.Null);
+            Assert.That(apiStatus.AccessLevel, Is.EqualTo(TwitterApiAccessLevel.Anonymous));
+        }
+
+        public static object[] ParseRateLimit_TestCase =
+        {
+            new object[] {
+                new Dictionary<string, string> {
+                    {"X-RateLimit-Limit", "150"},
+                    {"X-RateLimit-Remaining", "100"},
+                    {"X-RateLimit-Reset", "1356998400"},
+                },
+                new ApiLimit(150, 100, new DateTime(2013, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime()),
+            },
+            new object[] {
+                new Dictionary<string, string> {
+                    {"X-RateLimit-Limit", "150"},
+                    {"X-RateLimit-Remaining", "100"},
+                    {"X-RateLimit-Reset", "hogehoge"},
+                },
+                null,
+            },
+            new object[] {
+                new Dictionary<string, string> {
+                    {"X-RateLimit-Limit", "150"},
+                    {"X-RateLimit-Remaining", "100"},
+                },
+                null,
+            },
+        };
+        [TestCaseSource("ParseRateLimit_TestCase")]
+        public void ParseRateLimitTest(IDictionary<string, string> header, ApiLimit expect)
+        {
+            var limit = TwitterApiStatus.ParseRateLimit(header);
+            Assert.That(limit, Is.EqualTo(expect));
+        }
+
+        public static object[] ParseMediaRateLimit_TestCase =
+        {
+            new object[] {
+                new Dictionary<string, string> {
+                    {"X-MediaRateLimit-Limit", "30"},
+                    {"X-MediaRateLimit-Remaining", "20"},
+                    {"X-MediaRateLimit-Reset", "1234567890"},
+                },
+                new ApiLimit(30, 20, new DateTime(2009, 2, 13, 23, 31, 30, DateTimeKind.Utc).ToLocalTime()),
+            },
+            new object[] {
+                new Dictionary<string, string> {
+                    {"X-MediaRateLimit-Limit", "30"},
+                    {"X-MediaRateLimit-Remaining", "20"},
+                    {"X-MediaRateLimit-Reset", "hogehoge"},
+                },
+                null,
+            },
+            new object[] {
+                new Dictionary<string, string> {
+                    {"X-MediaRateLimit-Limit", "30"},
+                    {"X-MediaRateLimit-Remaining", "20"},
+                },
+                null,
+            },
+        };
+        [TestCaseSource("ParseMediaRateLimit_TestCase")]
+        public void ParseMediaRateLimitTest(IDictionary<string, string> header, ApiLimit expect)
+        {
+            var limit = TwitterApiStatus.ParseMediaRateLimit(header);
+            Assert.That(limit, Is.EqualTo(expect));
+        }
+
+        public static object[] ParseAccessLevel_TestCase =
+        {
+            new object[] {
+                new Dictionary<string, string> { {"X-Access-Level", "read"} },
+                TwitterApiAccessLevel.Read,
+            },
+            new object[] {
+                new Dictionary<string, string> { {"X-Access-Level", "read-write"} },
+                TwitterApiAccessLevel.ReadWrite,
+            },
+            new object[] {
+                new Dictionary<string, string> { {"X-Access-Level", "read-write-directmessages"} },
+                TwitterApiAccessLevel.ReadWriteAndDirectMessage,
+            },
+            new object[] {
+                new Dictionary<string, string> { {"X-Access-Level", ""} }, // 何故かたまに出てくるやつ
+                null,
+            },
+            new object[] {
+                new Dictionary<string, string> { },
+                null,
+            },
+        };
+        [TestCaseSource("ParseAccessLevel_TestCase")]
+        public void ParseAccessLevelTest(IDictionary<string, string> header, TwitterApiAccessLevel? expect)
+        {
+            var accessLevel = TwitterApiStatus.ParseAccessLevel(header);
+            Assert.That(accessLevel, Is.EqualTo(expect));
+        }
+
+        [Test]
+        public void UpdateFromHeaderTest()
+        {
+            var status = new TwitterApiStatus();
+
+            var eventCalled = false;
+            status.AccessLimitUpdated += (s, e) => eventCalled = true;
+
+            var header = new Dictionary<string, string>
+            {
+                {"X-RateLimit-Limit", "150"},
+                {"X-RateLimit-Remaining", "100"},
+                {"X-RateLimit-Reset", "1356998400"},
+                {"X-MediaRateLimit-Limit", "30"},
+                {"X-MediaRateLimit-Remaining", "20"},
+                {"X-MediaRateLimit-Reset", "1357084800"},
+                {"X-Access-Level", "read-write-directmessages"},
+            };
+
+            status.UpdateFromHeader(header);
+
+            var rateLimit = status.AccessLimit;
+            Assert.That(rateLimit.AccessLimitCount, Is.EqualTo(150));
+            Assert.That(rateLimit.AccessLimitRemain, Is.EqualTo(100));
+            Assert.That(rateLimit.AccessLimitResetDate, Is.EqualTo(new DateTime(2013, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime()));
+
+            var mediaLimit = status.MediaUploadLimit;
+            Assert.That(mediaLimit.AccessLimitCount, Is.EqualTo(30));
+            Assert.That(mediaLimit.AccessLimitRemain, Is.EqualTo(20));
+            Assert.That(mediaLimit.AccessLimitResetDate, Is.EqualTo(new DateTime(2013, 1, 2, 0, 0, 0, DateTimeKind.Utc).ToLocalTime()));
+
+            Assert.That(status.AccessLevel, Is.EqualTo(TwitterApiAccessLevel.ReadWriteAndDirectMessage));
+
+            Assert.That(eventCalled, Is.True);
+        }
+
+        [Test]
+        public void UpdateFromApiTest()
+        {
+            var status = new TwitterApiStatus();
+
+            var eventCalled = false;
+            status.AccessLimitUpdated += (s, e) => eventCalled = true;
+
+            var apiResponse = new TwitterDataModel.RateLimitStatus
+            {
+                HourlyLimit = 150,
+                RemainingHits = 100,
+                ResetTime = "Tue Jan 01 00:00:00 +0000 2013",
+                ResetTimeInSeconds = 1356998400,
+                Photos = new TwitterDataModel.MediaRateLimitStatus
+                {
+                    DailyLimit = 30,
+                    RemainingHits = 20,
+                    ResetTime = "Wed Jan 02 00:00:00 +0000 2013",
+                    RestTimeInSeconds = 1357084800,
+                },
+            };
+
+            status.UpdateFromApi(apiResponse);
+
+            var rateLimit = status.AccessLimit;
+            Assert.That(rateLimit.AccessLimitCount, Is.EqualTo(150));
+            Assert.That(rateLimit.AccessLimitRemain, Is.EqualTo(100));
+            Assert.That(rateLimit.AccessLimitResetDate, Is.EqualTo(new DateTime(2013, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime()));
+
+            var mediaLimit = status.MediaUploadLimit;
+            Assert.That(mediaLimit.AccessLimitCount, Is.EqualTo(30));
+            Assert.That(mediaLimit.AccessLimitRemain, Is.EqualTo(20));
+            Assert.That(mediaLimit.AccessLimitResetDate, Is.EqualTo(new DateTime(2013, 1, 2, 0, 0, 0, DateTimeKind.Utc).ToLocalTime()));
+
+            Assert.That(eventCalled, Is.True);
+        }
+
+        [Test]
+        public void UpdateFromApiTest2()
+        {
+            var status = new TwitterApiStatus();
+
+            var eventCalled = false;
+            status.AccessLimitUpdated += (s, e) => eventCalled = true;
+
+            var apiResponse = new TwitterDataModel.RateLimitStatus
+            {
+                HourlyLimit = 150,
+                RemainingHits = 100,
+                ResetTime = "Tue Jan 01 00:00:00 +0000 2013",
+                ResetTimeInSeconds = 1356998400,
+                Photos = null,
+            };
+
+            status.UpdateFromApi(apiResponse);
+
+            var rateLimit = status.AccessLimit;
+            Assert.That(rateLimit.AccessLimitCount, Is.EqualTo(150));
+            Assert.That(rateLimit.AccessLimitRemain, Is.EqualTo(100));
+            Assert.That(rateLimit.AccessLimitResetDate, Is.EqualTo(new DateTime(2013, 1, 1, 0, 0, 0, DateTimeKind.Utc).ToLocalTime()));
+
+            Assert.That(status.MediaUploadLimit, Is.Null);
+
+            Assert.That(eventCalled, Is.True);
+        }
+
+        [Test]
+        public void UpdateFromApiTest3()
+        {
+            var status = new TwitterApiStatus();
+
+            var eventCalled = false;
+            status.AccessLimitUpdated += (s, e) => eventCalled = true;
+
+            Assert.That(() => status.UpdateFromApi(null), Throws.TypeOf<ArgumentNullException>());
+
+            Assert.That(status.AccessLimit, Is.Null);
+            Assert.That(status.MediaUploadLimit, Is.Null);
+
+            Assert.That(eventCalled, Is.False);
+        }
+
+        [Test]
+        public void AccessLimitUpdatedTest()
+        {
+            var apiStatus = new TwitterApiStatus();
+
+            var eventCount = 0;
+            apiStatus.AccessLimitUpdated += (s, e) => eventCount++;
+
+            Assert.That(eventCount, Is.EqualTo(0));
+
+            apiStatus.AccessLimit = new ApiLimit(150, 100, new DateTime(2013, 1, 1, 0, 0, 0));
+            Assert.That(eventCount, Is.EqualTo(1));
+
+            apiStatus.Reset();
+            Assert.That(eventCount, Is.EqualTo(2));
+        }
+    }
+}
diff --git a/OpenTween.Tests/ApiInformationTest.cs b/OpenTween.Tests/ApiInformationTest.cs
deleted file mode 100644 (file)
index f3df09e..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-// OpenTween - Client of Twitter
-// Copyright (c) 2012 the40san <http://sourceforge.jp/users/the40san/>
-// All rights reserved.
-//
-// This file is part of OpenTween.
-//
-// 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;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using NUnit.Framework;
-
-
-namespace OpenTween
-{
-    [TestFixture]
-    public class ApiInformationTest
-    {
-        [Test]
-        public void Initialize()
-        {
-            ApiInformation apiinfo = new ApiInformation();
-            apiinfo.Initialize();
-
-            Assert.That(apiinfo.HttpHeaders["X-RateLimit-Remaining"], Is.EqualTo("-1"));
-            Assert.That(apiinfo.HttpHeaders["X-RateLimit-Limit"], Is.EqualTo("-1"));
-            Assert.That(apiinfo.HttpHeaders["X-RateLimit-Reset"], Is.EqualTo("-1"));
-
-            Assert.That(apiinfo.HttpHeaders["X-Access-Level"], Is.EqualTo("read-write-directmessages"));
-
-            Assert.That(apiinfo.HttpHeaders["X-MediaRateLimit-Remaining"], Is.EqualTo("-1"));
-            Assert.That(apiinfo.HttpHeaders["X-MediaRateLimit-Limit"], Is.EqualTo("-1"));
-            Assert.That(apiinfo.HttpHeaders["X-MediaRateLimit-Reset"], Is.EqualTo("-1"));
-            
-
-        }
-
-        [Test]
-        [Combinatorial] 
-        public void Test_MaxCount([Values(100, 0, -100)]int value)
-        {
-            ApiInformation apiinfo = new ApiInformation();
-            apiinfo.MaxCount = value;
-            Assert.That(apiinfo.MaxCount, Is.EqualTo(value));
-        }
-
-
-        [TestCase(-100, Result = -100)]
-        [TestCase(0, Result = 0)]
-        [TestCase(100, Result = 100)]
-        [TestCase(int.MaxValue, Result = int.MaxValue)]
-        [TestCase(int.MinValue, Result = int.MinValue)]
-        public int Test_RemainCount(int value)
-        {
-            ApiInformation apiinfo = new ApiInformation();
-            apiinfo.RemainCount = value;
-            return apiinfo.RemainCount;
-        }
-
-
-
-
-
-        [TestCase(-100, Result = -100)]
-        [TestCase(0, Result = 0)]
-        [TestCase(100, Result = 100)]
-        [TestCase(int.MaxValue, Result = int.MaxValue)]
-        [TestCase(int.MinValue, Result = int.MinValue)]
-        public int Test_MediaMaxCount(int value)
-        {
-            ApiInformation apiinfo = new ApiInformation();
-            apiinfo.MediaMaxCount = value;
-            return apiinfo.MediaMaxCount;
-        }
-
-        [TestCase(-100, Result = -100)]
-        [TestCase(0, Result = 0)]
-        [TestCase(100, Result = 100)]
-        [TestCase(int.MaxValue, Result = int.MaxValue)]
-        [TestCase(int.MinValue, Result = int.MinValue)]
-        public int Test_ResetTimeInSeconds(int value)
-        {
-            ApiInformation apiinfo = new ApiInformation();
-            apiinfo.ResetTimeInSeconds = value;
-            return apiinfo.ResetTimeInSeconds;
-        }
-
-        [TestCase(-100, Result = -100)]
-        [TestCase(0, Result = 0)]
-        [TestCase(100, Result = 100)]
-        [TestCase(int.MaxValue, Result = int.MaxValue)]
-        [TestCase(int.MinValue, Result = int.MinValue)]
-        public int Test_UsingCount(int value)
-        {
-            ApiInformation apiinfo = new ApiInformation();
-            apiinfo.UsingCount = value;
-            return apiinfo.UsingCount;
-        }
-
-        //↓以下DateTime系
-
-        [Test]
-        public void Test_ConvertResetTimeInSecondsToResetTime()
-        {
-            ApiInformation apiinfo = new ApiInformation();
-            DateTime d = apiinfo.ConvertResetTimeInSecondsToResetTime(-1);
-            Assert.That(d, Is.EqualTo(new DateTime()));
-        }
-
-        [Test]
-        public void Test_MediaResetTime()
-        {
-            ApiInformation apiinfo = new ApiInformation();
-            DateTime d = new DateTime(1970, 1, 1, 0, 0, 0);
-            apiinfo.MediaResetTime = d;
-            Assert.That(apiinfo.MediaResetTime, Is.EqualTo(d));
-        }
-
-
-    }
-}
index 0c280ad..b7d0416 100644 (file)
@@ -50,7 +50,8 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="ApiInformationTest.cs" />
+    <Compile Include="Api\ApiLimitTest.cs" />
+    <Compile Include="Api\TwitterApiStatusTest.cs" />
     <Compile Include="BingTest.cs" />
     <Compile Include="FoursquareTest.cs" />
     <Compile Include="LRUCacheDictionaryTest.cs" />
@@ -84,7 +85,6 @@
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
   </ItemGroup>
-  <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
        Other similar extension points exist, see Microsoft.Common.targets.
diff --git a/OpenTween/Api/ApiLimit.cs b/OpenTween/Api/ApiLimit.cs
new file mode 100644 (file)
index 0000000..42b48de
--- /dev/null
@@ -0,0 +1,71 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2013 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// 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;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OpenTween.Api
+{
+    public class ApiLimit
+    {
+        /// <summary>
+        /// API 実行回数制限の値
+        /// </summary>
+        public readonly int AccessLimitCount;
+
+        /// <summary>
+        /// API 実行回数制限までの残回数
+        /// </summary>
+        public readonly int AccessLimitRemain;
+
+        /// <summary>
+        /// API 実行回数制限がリセットされる日時
+        /// </summary>
+        public readonly DateTime AccessLimitResetDate;
+
+        public ApiLimit(int limitCount, int limitRemain, DateTime resetDate)
+        {
+            this.AccessLimitCount = limitCount;
+            this.AccessLimitRemain = limitRemain;
+            this.AccessLimitResetDate = resetDate;
+        }
+
+        public override bool Equals(object obj)
+        {
+            return this.Equals(obj as ApiLimit);
+        }
+
+        public bool Equals(ApiLimit obj)
+        {
+            return obj != null &&
+                this.AccessLimitCount == obj.AccessLimitCount &&
+                this.AccessLimitRemain == obj.AccessLimitRemain &&
+                this.AccessLimitResetDate == obj.AccessLimitResetDate;
+        }
+
+        public override int GetHashCode()
+        {
+            return this.AccessLimitCount ^ this.AccessLimitRemain ^ this.AccessLimitResetDate.GetHashCode();
+        }
+    }
+}
diff --git a/OpenTween/Api/TwitterApiAccessLevel.cs b/OpenTween/Api/TwitterApiAccessLevel.cs
new file mode 100644 (file)
index 0000000..7b1c597
--- /dev/null
@@ -0,0 +1,36 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2013 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// 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;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OpenTween.Api
+{
+    public enum TwitterApiAccessLevel
+    {
+        Anonymous,
+        Read,
+        ReadWrite,
+        ReadWriteAndDirectMessage,
+    }
+}
diff --git a/OpenTween/Api/TwitterApiStatus.cs b/OpenTween/Api/TwitterApiStatus.cs
new file mode 100644 (file)
index 0000000..7c3dfe1
--- /dev/null
@@ -0,0 +1,159 @@
+// OpenTween - Client of Twitter
+// Copyright (c) 2013 kim_upsilon (@kim_upsilon) <https://upsilo.net/~upsilon/>
+// All rights reserved.
+//
+// This file is part of OpenTween.
+//
+// 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;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace OpenTween.Api
+{
+    public class TwitterApiStatus
+    {
+        public ApiLimit AccessLimit
+        {
+            get { return this._AccessLimit; }
+            set
+            {
+                this._AccessLimit = value;
+                this.OnAccessLimitUpdated(EventArgs.Empty);
+            }
+        }
+        public ApiLimit _AccessLimit;
+
+        public ApiLimit MediaUploadLimit { get; set; }
+        public TwitterApiAccessLevel AccessLevel { get; set; }
+
+        public event EventHandler AccessLimitUpdated;
+
+        private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+        protected internal TwitterApiStatus()
+        {
+            this.Reset();
+        }
+
+        public void Reset()
+        {
+            this.AccessLimit = null;
+            this.MediaUploadLimit = null;
+            this.AccessLevel = TwitterApiAccessLevel.Anonymous;
+        }
+
+        public void UpdateFromHeader(IDictionary<string, string> header)
+        {
+            var rateLimit = TwitterApiStatus.ParseRateLimit(header);
+            if (rateLimit != null)
+                this.AccessLimit = rateLimit;
+
+            var mediaLimit = TwitterApiStatus.ParseMediaRateLimit(header);
+            if (mediaLimit != null)
+                this.MediaUploadLimit = mediaLimit;
+
+            var accessLevel = TwitterApiStatus.ParseAccessLevel(header);
+            if (accessLevel.HasValue)
+                this.AccessLevel = accessLevel.Value;
+        }
+
+        internal static ApiLimit ParseRateLimit(IDictionary<string, string> header)
+        {
+            var limitCount = ParseHeaderValue(header, "X-RateLimit-Limit") ?? -1;
+            var limitRemain = ParseHeaderValue(header, "X-RateLimit-Remaining") ?? -1;
+            var limitReset = ParseHeaderValue(header, "X-RateLimit-Reset") ?? -1;
+
+            if (limitCount == -1 || limitRemain == -1 || limitReset == -1)
+                return null;
+
+            var limitResetDate = UnixEpoch.AddSeconds(limitReset).ToLocalTime();
+            return new ApiLimit(limitCount, limitRemain, limitResetDate);
+        }
+
+        internal static ApiLimit ParseMediaRateLimit(IDictionary<string, string> header)
+        {
+            var limitCount = ParseHeaderValue(header, "X-MediaRateLimit-Limit") ?? -1;
+            var limitRemain = ParseHeaderValue(header, "X-MediaRateLimit-Remaining") ?? -1;
+            var limitReset = ParseHeaderValue(header, "X-MediaRateLimit-Reset") ?? -1;
+
+            if (limitCount == -1 || limitRemain == -1 || limitReset == -1)
+                return null;
+
+            var limitResetDate = UnixEpoch.AddSeconds(limitReset).ToLocalTime();
+            return new ApiLimit(limitCount, limitRemain, limitResetDate);
+        }
+
+        internal static TwitterApiAccessLevel? ParseAccessLevel(IDictionary<string, string> header)
+        {
+            if (!header.ContainsKey("X-Access-Level"))
+                return null;
+
+            switch (header["X-Access-Level"])
+            {
+                case "read-write-directmessages":
+                case "read-write-privatemessages":
+                    return TwitterApiAccessLevel.ReadWriteAndDirectMessage;
+                case "read-write":
+                    return TwitterApiAccessLevel.ReadWrite;
+                case "read":
+                    return TwitterApiAccessLevel.Read;
+                case "":
+                    // たまに出てくる空文字列は無視する
+                    return null;
+                default:
+                    MyCommon.TraceOut("Unknown ApiAccessLevel:" + header["X-Access-Level"]);
+                    return TwitterApiAccessLevel.ReadWriteAndDirectMessage;
+            }
+        }
+
+        internal static int? ParseHeaderValue(IDictionary<string, string> dict, params string[] keys)
+        {
+            foreach (var key in keys)
+            {
+                if (!dict.ContainsKey(key)) continue;
+
+                int result;
+                if (int.TryParse(dict[key], out result))
+                    return result;
+            }
+
+            return null;
+        }
+
+        public void UpdateFromApi(TwitterDataModel.RateLimitStatus limit)
+        {
+            if (limit == null)
+                throw new ArgumentNullException();
+
+            this.AccessLimit = new ApiLimit(limit.HourlyLimit, limit.RemainingHits, MyCommon.DateTimeParse(limit.ResetTime));
+
+            var mediaLimit = limit.Photos;
+            if (mediaLimit != null)
+            {
+                this.MediaUploadLimit = new ApiLimit(mediaLimit.DailyLimit, mediaLimit.RemainingHits, MyCommon.DateTimeParse(mediaLimit.ResetTime));
+            }
+        }
+
+        protected virtual void OnAccessLimitUpdated(EventArgs e)
+        {
+            if (this.AccessLimitUpdated != null)
+                this.AccessLimitUpdated(this, e);
+        }
+    }
+}
diff --git a/OpenTween/ApiInformation.cs b/OpenTween/ApiInformation.cs
deleted file mode 100644 (file)
index 3780609..0000000
+++ /dev/null
@@ -1,491 +0,0 @@
-// OpenTween - 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 OpenTween.
-// 
-// 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;
-using System.Collections.Generic;
-
-namespace OpenTween
-{
-    public class ApiInformationChangedEventArgs : EventArgs
-    {
-        public ApiInfo ApiInfo = new ApiInfo();
-    }
-
-    public abstract class ApiInfoBase
-    {
-        protected static int _MaxCount = -1;
-        protected static int _RemainCount = -1;
-        protected static DateTime _ResetTime = new DateTime();
-        protected static int _ResetTimeInSeconds = -1;
-        protected static int _UsingCount = -1;
-        protected static ApiAccessLevel _AccessLevel = ApiAccessLevel.None;
-        protected static int _MediaMaxCount = -1;
-        protected static DateTime _MediaResetTime = new DateTime();
-        protected static int _MediaRemainCount = -1;
-    }
-
-    public enum ApiAccessLevel
-    {
-        None,
-        Unknown,
-        Read,
-        ReadWrite,
-        ReadWriteAndDirectMessage,
-    }
-
-    public class ApiInfo : ApiInfoBase
-    {
-        public int MaxCount;
-        public int RemainCount;
-        public DateTime ResetTime;
-        public int ResetTimeInSeconds;
-        public int UsingCount;
-        public ApiAccessLevel AccessLevel;
-        public int MediaMaxCount;
-        public int MediaRemainCount;
-        public DateTime MediaResetTime;
-
-        public ApiInfo()
-        {
-            this.MaxCount = _MaxCount;
-            this.RemainCount = _RemainCount;
-            this.ResetTime = _ResetTime;
-            this.ResetTimeInSeconds = _ResetTimeInSeconds;
-            this.UsingCount = _UsingCount;
-            this.AccessLevel = _AccessLevel;
-            this.MediaMaxCount = _MediaMaxCount;
-            this.MediaRemainCount = _MediaRemainCount;
-            this.MediaResetTime = _MediaResetTime;
-        }
-    }
-
-    public class ApiInformation : ApiInfoBase
-    {
-        //private ReadOnly _lockobj As new Object 更新時にロックが必要かどうかは様子見
-
-        public Dictionary<string, string> HttpHeaders = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase);
-
-        public void Initialize()
-        {
-            HttpHeaders["X-RateLimit-Remaining"] = "-1";
-            HttpHeaders["X-RateLimit-Limit"] = "-1";
-            HttpHeaders["X-RateLimit-Reset"] = "-1";
-            HttpHeaders["X-Access-Level"] = "read-write-directmessages";
-            HttpHeaders["X-MediaRateLimit-Remaining"] = "-1";
-            HttpHeaders["X-MediaRateLimit-Limit"] = "-1";
-            HttpHeaders["X-MediaRateLimit-Reset"] = "-1";
-
-            _MaxCount = -1;
-            _RemainCount = -1;
-            _ResetTime = new DateTime();
-            _ResetTimeInSeconds = -1;
-            AccessLevel = ApiAccessLevel.None;
-            _MediaMaxCount = -1;
-            _MediaRemainCount = -1;
-            _MediaResetTime = new DateTime();
-
-            //_UsingCount = -1
-            var arg = new ApiInformationChangedEventArgs();
-            var changed = Changed;
-            if (changed != null)
-            {
-                changed(this, arg);
-            }
-        }
-
-        public DateTime ConvertResetTimeInSecondsToResetTime(int seconds)
-        {
-            if (seconds >= 0)
-            {
-                return System.TimeZone.CurrentTimeZone.ToLocalTime((new DateTime(1970, 1, 1, 0, 0, 0)).AddSeconds(seconds));
-            }
-            else
-            {
-                return new DateTime();
-            }
-        }
-
-        public event EventHandler<ApiInformationChangedEventArgs> Changed;
-
-        private void Raise_Changed()
-        {
-            var arg = new ApiInformationChangedEventArgs();
-            var changed = Changed;
-            if (changed != null)
-            {
-                changed(this, arg);
-            }
-            _MaxCount = arg.ApiInfo.MaxCount;
-            _RemainCount = arg.ApiInfo.RemainCount;
-            _ResetTime = arg.ApiInfo.ResetTime;
-            _ResetTimeInSeconds = arg.ApiInfo.ResetTimeInSeconds;
-            //_UsingCount = arg.ApiInfo.UsingCount
-        }
-
-        public int MaxCount
-        {
-            get
-            {
-                return _MaxCount;
-            }
-            set
-            {
-                if (_MaxCount != value)
-                {
-                    _MaxCount = value;
-                    Raise_Changed();
-                }
-            }
-        }
-
-        public int RemainCount
-        {
-            get
-            {
-                return _RemainCount;
-            }
-            set
-            {
-                if (_RemainCount != value)
-                {
-                    _RemainCount = value;
-                    Raise_Changed();
-                }
-            }
-        }
-
-        public DateTime ResetTime
-        {
-            get
-            {
-                return _ResetTime;
-            }
-            set
-            {
-                if (_ResetTime != value)
-                {
-                    _ResetTime = value;
-                    Raise_Changed();
-                }
-            }
-        }
-
-        public int MediaMaxCount
-        {
-            get
-            {
-                return _MediaMaxCount;
-            }
-            set
-            {
-                if (_MediaMaxCount != value)
-                {
-                    _MediaMaxCount = value;
-                    Raise_Changed();
-                }
-            }
-        }
-
-        public int MediaRemainCount
-        {
-            get
-            {
-                return _MediaRemainCount;
-            }
-            set
-            {
-                if (_MediaRemainCount != value)
-                {
-                    _MediaRemainCount = value;
-                    Raise_Changed();
-                }
-            }
-        }
-
-        public DateTime MediaResetTime
-        {
-            get
-            {
-                return _MediaResetTime;
-            }
-            set
-            {
-                if (_MediaResetTime != value)
-                {
-                    _MediaResetTime = value;
-                    Raise_Changed();
-                }
-            }
-        }
-
-        public int ResetTimeInSeconds
-        {
-            get
-            {
-                return _ResetTimeInSeconds;
-            }
-            set
-            {
-                if (_ResetTimeInSeconds != value)
-                {
-                    _ResetTimeInSeconds = value;
-                    Raise_Changed();
-                }
-            }
-        }
-
-        public int UsingCount
-        {
-            get
-            {
-                return _UsingCount;
-            }
-            set
-            {
-                if (_UsingCount != value)
-                {
-                    _UsingCount = value;
-                    Raise_Changed();
-                }
-            }
-        }
-
-        public ApiAccessLevel AccessLevel
-        {
-            get
-            {
-                return _AccessLevel;
-            }
-            private set
-            {
-                if (_AccessLevel != value)
-                {
-                    _AccessLevel = value;
-                    Raise_Changed();
-                }
-            }
-        }
-
-        public bool IsReadPermission
-        {
-            get
-            {
-                return AccessLevel == ApiAccessLevel.Read ||
-                    AccessLevel == ApiAccessLevel.ReadWrite ||
-                    AccessLevel == ApiAccessLevel.ReadWriteAndDirectMessage;
-            }
-        }
-
-        public bool IsWritePermission
-        {
-            get
-            {
-                return AccessLevel == ApiAccessLevel.ReadWrite ||
-                    AccessLevel == ApiAccessLevel.ReadWriteAndDirectMessage;
-            }
-        }
-
-        public bool IsDirectMessagePermission
-        {
-            get
-            {
-                return AccessLevel == ApiAccessLevel.ReadWriteAndDirectMessage;
-            }
-        }
-
-        private int RemainCountFromHttpHeader
-        {
-            get
-            {
-                int result = 0;
-                if (string.IsNullOrEmpty(HttpHeaders["X-RateLimit-Remaining"])) return -1;
-                if (int.TryParse(HttpHeaders["X-RateLimit-Remaining"], out result))
-                {
-                    return result;
-                }
-                return -1;
-            }
-        }
-
-        private int MaxCountFromHttpHeader
-        {
-            get
-            {
-                int result;
-                if (string.IsNullOrEmpty(HttpHeaders["X-RateLimit-Limit"])) return -1;
-                if (int.TryParse(HttpHeaders["X-RateLimit-Limit"], out result))
-                {
-                    return result;
-                }
-                return -1;
-            }
-        }
-
-        private DateTime ResetTimeFromHttpHeader
-        {
-            get
-            {
-                int i;
-                if (int.TryParse(HttpHeaders["X-RateLimit-Reset"], out i))
-                {
-                    if (i >= 0)
-                    {
-                        return TimeZone.CurrentTimeZone.ToLocalTime((new DateTime(1970, 1, 1, 0, 0, 0)).AddSeconds(i));
-                    }
-                    else
-                    {
-                        return new DateTime();
-                    }
-                }
-                else
-                {
-                    return new DateTime();
-                }
-            }
-        }
-
-        private int MediaRemainCountFromHttpHeader
-        {
-            get
-            {
-                int result = 0;
-                if (string.IsNullOrEmpty(HttpHeaders["X-MediaRateLimit-Remaining"])) return -1;
-                if (int.TryParse(HttpHeaders["X-MediaRateLimit-Remaining"], out result))
-                {
-                    return result;
-                }
-                return -1;
-            }
-        }
-
-        private int MediaMaxCountFromHttpHeader
-        {
-            get
-            {
-                int result = 0;
-                if (string.IsNullOrEmpty(HttpHeaders["X-MediaRateLimit-Limit"])) return -1;
-                if (int.TryParse(HttpHeaders["X-MediaRateLimit-Limit"], out result))
-                {
-                    return result;
-                }
-                return -1;
-            }
-        }
-
-        private DateTime MediaResetTimeFromHttpHeader
-        {
-            get
-            {
-                int i;
-                if (int.TryParse(HttpHeaders["X-MediaRateLimit-Reset"], out i))
-                {
-                    if (i >= 0)
-                    {
-                        return TimeZone.CurrentTimeZone.ToLocalTime((new DateTime(1970, 1, 1, 0, 0, 0)).AddSeconds(i));
-                    }
-                    else
-                    {
-                        return new DateTime();
-                    }
-                }
-                else
-                {
-                    return new DateTime();
-                }
-            }
-        }
-
-        private ApiAccessLevel ApiAccessLevelFromHttpHeader
-        {
-            get
-            {
-                switch (HttpHeaders["X-Access-Level"])
-                {
-                case "read":
-                    return ApiAccessLevel.Read;
-                case "read-write":
-                    return ApiAccessLevel.ReadWrite;
-                case "read-write-directmessages":
-                case "read-write-privatemessages":
-                    return ApiAccessLevel.ReadWriteAndDirectMessage;
-                default:
-                    MyCommon.TraceOut("Unknown ApiAccessLevel:" + HttpHeaders["X-Access-Level"]);
-                    return ApiAccessLevel.ReadWriteAndDirectMessage;     //未知のアクセスレベルの場合Read/Write/Dmと仮定して処理継続;
-                }
-            }
-        }
-
-        public void ParseHttpHeaders(Dictionary<string, string> headers)
-        {
-            int tmp;
-            DateTime tmpd;
-            tmp = MaxCountFromHttpHeader;
-            if (tmp != -1)
-            {
-                _MaxCount = tmp;
-            }
-            tmp = RemainCountFromHttpHeader;
-            if (tmp != -1)
-            {
-                _RemainCount = tmp;
-            }
-            tmpd = ResetTimeFromHttpHeader;
-            if (tmpd != new DateTime())
-            {
-                _ResetTime = tmpd;
-            }
-
-            tmp = MediaMaxCountFromHttpHeader;
-            if (tmp != -1)
-            {
-                _MediaMaxCount = tmp;
-            }
-            tmp = MediaRemainCountFromHttpHeader;
-            if (tmp != -1)
-            {
-                _MediaRemainCount = tmp;
-            }
-            tmpd = MediaResetTimeFromHttpHeader;
-            if (tmpd != new DateTime())
-            {
-                _MediaResetTime = tmpd;
-            }
-
-            AccessLevel = ApiAccessLevelFromHttpHeader;
-            Raise_Changed();
-        }
-
-        public void WriteBackEventArgs(ApiInformationChangedEventArgs arg)
-        {
-            _MaxCount = arg.ApiInfo.MaxCount;
-            _RemainCount = arg.ApiInfo.RemainCount;
-            _ResetTime = arg.ApiInfo.ResetTime;
-            _ResetTimeInSeconds = arg.ApiInfo.ResetTimeInSeconds;
-            _UsingCount = arg.ApiInfo.UsingCount;
-            Raise_Changed();
-        }
-    }
-}
index 437b2f1..5461c48 100644 (file)
@@ -36,6 +36,7 @@ using System.Threading;
 using System.IO;
 using System.Resources;
 using OpenTween.Thumbnail;
+using System.Threading.Tasks;
 
 namespace OpenTween
 {
@@ -1818,13 +1819,14 @@ namespace OpenTween
 
         private void DisplayApiMaxCount()
         {
-            if (MyCommon.TwitterApiInfo.MaxCount > -1)
+            var limit = MyCommon.TwitterApiInfo.AccessLimit;
+            if (limit != null)
             {
-                LabelApiUsing.Text = string.Format(Properties.Resources.SettingAPIUse1, MyCommon.TwitterApiInfo.UsingCount, MyCommon.TwitterApiInfo.MaxCount);
+                LabelApiUsing.Text = string.Format(Properties.Resources.SettingAPIUse1, limit.AccessLimitCount - limit.AccessLimitRemain, limit.AccessLimitCount);
             }
             else
             {
-                LabelApiUsing.Text = string.Format(Properties.Resources.SettingAPIUse1, MyCommon.TwitterApiInfo.UsingCount, "???");
+                LabelApiUsing.Text = string.Format(Properties.Resources.SettingAPIUse1, "???", "???");
             }
         }
 
@@ -1906,16 +1908,19 @@ namespace OpenTween
 
             if (tw != null)
             {
-                if (MyCommon.TwitterApiInfo.MaxCount == -1)
+                var limit = MyCommon.TwitterApiInfo.AccessLimit;
+                if (limit == null)
                 {
                     if (Twitter.AccountState == MyCommon.ACCOUNT_STATE.Valid)
                     {
-                        MyCommon.TwitterApiInfo.UsingCount = UsingApi;
-                        Thread proc = new Thread(new System.Threading.ThreadStart(() => {
-                            tw.GetInfoApi(null); //取得エラー時はinfoCountは初期状態(値:-1)
-                            if (this.IsHandleCreated && !this.IsDisposed) Invoke(new MethodInvoker(DisplayApiMaxCount));
-                        }));
-                        proc.Start();
+                        Task.Factory.StartNew(() => tw.GetInfoApi()) //取得エラー時はinfoCountは初期状態(値:-1)
+                            .ContinueWith(t =>
+                            {
+                                if (this.IsHandleCreated && !this.IsDisposed)
+                                {
+                                    this.DisplayApiMaxCount();
+                                }
+                            }, TaskScheduler.FromCurrentSynchronizationContext());
                     }
                     else
                     {
@@ -1924,7 +1929,7 @@ namespace OpenTween
                 }
                 else
                 {
-                    LabelApiUsing.Text = string.Format(Properties.Resources.SettingAPIUse1, UsingApi, MyCommon.TwitterApiInfo.MaxCount);
+                    LabelApiUsing.Text = string.Format(Properties.Resources.SettingAPIUse1, UsingApi, limit.AccessLimitCount);
                 }
             }
 
index 83bcc2a..94285c2 100644 (file)
@@ -65,6 +65,17 @@ namespace OpenTween
         private static string tks = "";
         private static string un = "";
 
+        private Dictionary<string, string> apiStatusHeaders = new Dictionary<string, string>
+        {
+            {"X-Access-Level", ""},
+            {"X-RateLimit-Limit", ""},
+            {"X-RateLimit-Remaining", ""},
+            {"X-RateLimit-Reset", ""},
+            {"X-MediaRateLimit-Limit", ""},
+            {"X-MediaRateLimit-Remaining", ""},
+            {"X-MediaRateLimit-Reset", ""},
+        };
+
         public void Initialize(string accessToken,
                                         string accessTokenSecret,
                                         string username,
@@ -210,7 +221,7 @@ namespace OpenTween
                                       param,
                                       binary,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -275,7 +286,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/users/show.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -354,7 +365,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/friendships/show.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -366,7 +377,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/statuses/show/" + id.ToString() + ".json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -406,7 +417,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/statuses/home_timeline.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -435,7 +446,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/statuses/user_timeline.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -455,7 +466,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/statuses/public_timeline.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -475,7 +486,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/statuses/mentions.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -494,7 +505,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/direct_messages.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -513,7 +524,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/direct_messages/sent.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -533,7 +544,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/favorites.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -620,7 +631,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/followers/ids.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -633,7 +644,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/friendships/no_retweet_ids.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -643,7 +654,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/account/rate_limit_status.json"),
                                       null,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -657,7 +668,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/lists.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -705,7 +716,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/lists/subscriptions.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -729,7 +740,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/lists/statuses.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -763,7 +774,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/lists/members.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -850,7 +861,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/lists/members/show.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
             //return httpCon.GetContent(GetMethod,
             //                          CreateTwitterUri("/1/" + user + "/" + list_id + "/members/" + id + ".json"),
@@ -873,7 +884,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/statuses/" + statusid.ToString()+ "/retweeted_by/ids.json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -920,7 +931,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/related_results/show/" + id.ToString()+ ".json"),
                                       param,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -930,7 +941,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/blocks/blocking/ids.json"),
                                       null,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -940,7 +951,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/help/configuration.json"),
                                       null,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -950,7 +961,7 @@ namespace OpenTween
                                       CreateTwitterUri("/1/account/verify_credentials.json"),
                                       null,
                                       ref content,
-                                      MyCommon.TwitterApiInfo.HttpHeaders,
+                                      this.apiStatusHeaders,
                                       GetApiCallback);
         }
 
@@ -1001,7 +1012,7 @@ namespace OpenTween
         private void GetApiCallback(Object sender, ref HttpStatusCode code, ref string content)
         {
             if (code < HttpStatusCode.InternalServerError)
-                MyCommon.TwitterApiInfo.ParseHttpHeaders(MyCommon.TwitterApiInfo.HttpHeaders);
+                MyCommon.TwitterApiInfo.UpdateFromHeader(this.apiStatusHeaders);
         }
 
         public HttpStatusCode UserStream(ref Stream content,
index 9ee61a4..2810e37 100644 (file)
@@ -377,10 +377,20 @@ namespace OpenTween
         [DataContract]
         public class RateLimitStatus
         {
-            [DataMember(Name = "reset_time_in_seconds")] public int RestTimeInSeconds;
+            [DataMember(Name = "reset_time_in_seconds")] public int ResetTimeInSeconds;
             [DataMember(Name = "remaining_hits")] public int RemainingHits;
-            [DataMember(Name = "reset_time")] public string RestTime;
+            [DataMember(Name = "reset_time")] public string ResetTime;
             [DataMember(Name = "hourly_limit")] public int HourlyLimit;
+            [DataMember(Name = "photos")] public MediaRateLimitStatus Photos;
+        }
+
+        [DataContract]
+        public class MediaRateLimitStatus
+        {
+            [DataMember(Name = "reset_time_in_seconds")] public int RestTimeInSeconds;
+            [DataMember(Name = "remaining_hits")] public int RemainingHits;
+            [DataMember(Name = "reset_time")] public string ResetTime;
+            [DataMember(Name = "daily_limit")] public int DailyLimit;
         }
 
         [DataContract]
index 063d2c1..16a08ab 100644 (file)
@@ -44,6 +44,7 @@ using System.Diagnostics;
 using System.Text.RegularExpressions;
 using System.Net.NetworkInformation;
 using System.Runtime.InteropServices;
+using OpenTween.Api;
 
 namespace OpenTween
 {
@@ -677,7 +678,7 @@ namespace OpenTween
             return GetAssemblyName() + "/" + fileVersion;
         }
 
-        public static ApiInformation TwitterApiInfo = new ApiInformation();
+        public static TwitterApiStatus TwitterApiInfo = new TwitterApiStatus();
 
         public static bool IsAnimatedGif(string filename)
         {
index 90e8b64..163d87e 100644 (file)
@@ -53,7 +53,9 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
-    <Compile Include="ApiInformation.cs" />
+    <Compile Include="Api\ApiLimit.cs" />
+    <Compile Include="Api\TwitterApiAccessLevel.cs" />
+    <Compile Include="Api\TwitterApiStatus.cs" />
     <Compile Include="ApplicationEvents.cs">
       <SubType>Code</SubType>
     </Compile>
index 635f90a..34dc8e3 100644 (file)
@@ -47,6 +47,7 @@ using System.Diagnostics;
 using OpenTween.Thumbnail;
 using System.Threading.Tasks;
 using System.Net;
+using OpenTween.Api;
 
 namespace OpenTween
 {
@@ -550,7 +551,7 @@ namespace OpenTween
 
             SecurityManager = new InternetSecurityManager(PostBrowser);
 
-            MyCommon.TwitterApiInfo.Changed += SetStatusLabelApiHandler;
+            MyCommon.TwitterApiInfo.AccessLimitUpdated += SetStatusLabelApiHandler;
             Microsoft.Win32.SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
 
             string[] cmdArgs = Environment.GetCommandLineArgs();
@@ -9416,7 +9417,7 @@ namespace OpenTween
 
         delegate void SetStatusLabelApiDelegate();
 
-        private void SetStatusLabelApiHandler(object sender, ApiInformationChangedEventArgs e)
+        private void SetStatusLabelApiHandler(object sender, EventArgs e)
         {
             try
             {
@@ -9441,9 +9442,13 @@ namespace OpenTween
 
         private void SetStatusLabelApi()
         {
-            this._apiGauge.RemainCount = MyCommon.TwitterApiInfo.RemainCount;
-            this._apiGauge.MaxCount = MyCommon.TwitterApiInfo.MaxCount;
-            this._apiGauge.ResetTime = MyCommon.TwitterApiInfo.ResetTime;
+            var apiLimit = MyCommon.TwitterApiInfo.AccessLimit;
+            if (apiLimit == null)
+                return;
+
+            this._apiGauge.RemainCount = apiLimit.AccessLimitRemain;
+            this._apiGauge.MaxCount = apiLimit.AccessLimitCount;
+            this._apiGauge.ResetTime = apiLimit.AccessLimitResetDate;
         }
 
         private void SetStatusLabelUrl()
@@ -10747,7 +10752,7 @@ namespace OpenTween
                     GetTimeline(MyCommon.WORKERTYPE.Configuration, 0, 0, "");
 
                 // 権限チェック read/write権限(xAuthで取得したトークン)の場合は再認証を促す
-                if (MyCommon.TwitterApiInfo.AccessLevel == ApiAccessLevel.ReadWrite)
+                if (MyCommon.TwitterApiInfo.AccessLevel == TwitterApiAccessLevel.ReadWrite)
                 {
                     MessageBox.Show(Properties.Resources.ReAuthorizeText);
                     SettingStripMenuItem_Click(null, null);
@@ -10973,42 +10978,37 @@ namespace OpenTween
         }
 
 
-        private class GetApiInfoArgs
-        {
-            public Twitter tw;
-            public ApiInfo info;
-        }
-
         private void GetApiInfo_Dowork(object sender, DoWorkEventArgs e)
         {
-            GetApiInfoArgs args = (GetApiInfoArgs)e.Argument;
-            e.Result = tw.GetInfoApi(args.info);
+            if (tw.GetInfoApi())
+                e.Result = MyCommon.TwitterApiInfo;
+            else
+                e.Result = null;
         }
 
         private void ApiUsageInfoMenuItem_Click(object sender, EventArgs e)
         {
-            ApiInfo info = new ApiInfo();
             StringBuilder tmp = new StringBuilder();
-            GetApiInfoArgs args = new GetApiInfoArgs() {tw = tw, info = info};
 
-            using (FormInfo dlg = new FormInfo(this, Properties.Resources.ApiInfo6, GetApiInfo_Dowork, null, args))
+            using (FormInfo dlg = new FormInfo(this, Properties.Resources.ApiInfo6, GetApiInfo_Dowork))
             {
                 dlg.ShowDialog();
-                if ((bool)dlg.Result)
+                TwitterApiStatus result = dlg.Result as TwitterApiStatus;
+                if (result != null)
                 {
-                    tmp.AppendLine(Properties.Resources.ApiInfo1 + args.info.MaxCount.ToString());
-                    tmp.AppendLine(Properties.Resources.ApiInfo2 + args.info.RemainCount.ToString());
-                    tmp.AppendLine(Properties.Resources.ApiInfo3 + args.info.ResetTime.ToString());
-                    tmp.AppendLine(Properties.Resources.ApiInfo7 + (tw.UserStreamEnabled ? Properties.Resources.Enable : Properties.Resources.Disable).ToString());
+                    tmp.AppendLine(Properties.Resources.ApiInfo1 + result.AccessLimit.AccessLimitCount);
+                    tmp.AppendLine(Properties.Resources.ApiInfo2 + result.AccessLimit.AccessLimitRemain);
+                    tmp.AppendLine(Properties.Resources.ApiInfo3 + result.AccessLimit.AccessLimitResetDate);
+                    tmp.AppendLine(Properties.Resources.ApiInfo7 + (tw.UserStreamEnabled ? Properties.Resources.Enable : Properties.Resources.Disable));
 
                     tmp.AppendLine();
-                    tmp.AppendLine(Properties.Resources.ApiInfo8 + args.info.AccessLevel.ToString());
+                    tmp.AppendLine(Properties.Resources.ApiInfo8 + result.AccessLevel);
                     SetStatusLabelUrl();
 
                     tmp.AppendLine();
-                    tmp.AppendLine(Properties.Resources.ApiInfo9 + (args.info.MediaMaxCount < 0 ? Properties.Resources.ApiInfo91 : args.info.MediaMaxCount.ToString()));
-                    tmp.AppendLine(Properties.Resources.ApiInfo10 + (args.info.MediaRemainCount < 0 ? Properties.Resources.ApiInfo91 : args.info.MediaRemainCount.ToString()));
-                    tmp.AppendLine(Properties.Resources.ApiInfo11 + (args.info.MediaResetTime == new DateTime() ? Properties.Resources.ApiInfo91 : args.info.MediaResetTime.ToString()));
+                    tmp.AppendLine(Properties.Resources.ApiInfo9 + (result.MediaUploadLimit == null ? Properties.Resources.ApiInfo91 : result.MediaUploadLimit.AccessLimitCount.ToString()));
+                    tmp.AppendLine(Properties.Resources.ApiInfo10 + (result.MediaUploadLimit == null ? Properties.Resources.ApiInfo91 : result.MediaUploadLimit.AccessLimitRemain.ToString()));
+                    tmp.AppendLine(Properties.Resources.ApiInfo11 + (result.MediaUploadLimit == null ? Properties.Resources.ApiInfo91 : result.MediaUploadLimit.AccessLimitResetDate.ToString()));
                 }
                 else
                 {
index 03f4f59..627ebce 100644 (file)
@@ -41,6 +41,7 @@ using System.Reflection;
 using System.Collections.Generic;
 using System.Drawing;
 using System.Windows.Forms;
+using OpenTween.Api;
 
 namespace OpenTween
 {
@@ -159,7 +160,7 @@ namespace OpenTween
             HttpStatusCode res;
             var content = "";
 
-            MyCommon.TwitterApiInfo.Initialize();
+            MyCommon.TwitterApiInfo.Reset();
             try
             {
                 res = twCon.AuthUserAndPass(username, password, ref content);
@@ -211,7 +212,7 @@ namespace OpenTween
             //OAuth PIN Flow
             bool res;
 
-            MyCommon.TwitterApiInfo.Initialize();
+            MyCommon.TwitterApiInfo.Reset();
             try
             {
                 res = twCon.AuthGetRequestToken(ref pinPageUrl);
@@ -229,7 +230,7 @@ namespace OpenTween
             HttpStatusCode res;
             var content = "";
 
-            MyCommon.TwitterApiInfo.Initialize();
+            MyCommon.TwitterApiInfo.Reset();
             try
             {
                 res = twCon.AuthGetAccessToken(pinCode);
@@ -279,7 +280,7 @@ namespace OpenTween
         public void ClearAuthInfo()
         {
             Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
-            MyCommon.TwitterApiInfo.Initialize();
+            MyCommon.TwitterApiInfo.Reset();
             twCon.ClearAuthInfo();
         }
 
@@ -350,7 +351,7 @@ namespace OpenTween
             {
                 Twitter.AccountState = MyCommon.ACCOUNT_STATE.Invalid;
             }
-            MyCommon.TwitterApiInfo.Initialize();
+            MyCommon.TwitterApiInfo.Reset();
             twCon.Initialize(token, tokenSecret, username, userId);
             _uname = username.ToLower();
             if (AppendSettingDialog.Instance.UserstreamStartup) this.ReconnectUserStream();
@@ -736,9 +737,9 @@ namespace OpenTween
             if (MyCommon._endingFlag) return "";
 
             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
-            if (MyCommon.TwitterApiInfo.AccessLevel != ApiAccessLevel.None)
+            if (MyCommon.TwitterApiInfo.AccessLevel == TwitterApiAccessLevel.Read || MyCommon.TwitterApiInfo.AccessLevel == TwitterApiAccessLevel.ReadWrite)
             {
-                if (!MyCommon.TwitterApiInfo.IsDirectMessagePermission) return "Auth Err:try to re-authorization.";
+                return "Auth Err:try to re-authorization.";
             }
 
             postStr = postStr.Trim();
@@ -954,9 +955,9 @@ namespace OpenTween
             if (MyCommon._endingFlag) return "";
 
             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
-            if (MyCommon.TwitterApiInfo.AccessLevel != ApiAccessLevel.None)
+            if (MyCommon.TwitterApiInfo.AccessLevel == TwitterApiAccessLevel.Read || MyCommon.TwitterApiInfo.AccessLevel == TwitterApiAccessLevel.ReadWrite)
             {
-                if (!MyCommon.TwitterApiInfo.IsDirectMessagePermission) return "Auth Err:try to re-authorization.";
+                return "Auth Err:try to re-authorization.";
             }
 
             HttpStatusCode res = HttpStatusCode.BadRequest;
@@ -2901,9 +2902,9 @@ namespace OpenTween
             if (MyCommon._endingFlag) return "";
 
             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return "";
-            if (MyCommon.TwitterApiInfo.AccessLevel != ApiAccessLevel.None)
+            if (MyCommon.TwitterApiInfo.AccessLevel == TwitterApiAccessLevel.Read || MyCommon.TwitterApiInfo.AccessLevel == TwitterApiAccessLevel.ReadWrite)
             {
-                if (!MyCommon.TwitterApiInfo.IsDirectMessagePermission) return "Auth Err:try to re-authorization.";
+                return "Auth Err:try to re-authorization.";
             }
 
             HttpStatusCode res = HttpStatusCode.BadRequest;
@@ -4057,7 +4058,7 @@ namespace OpenTween
             }
         }
 
-        public bool GetInfoApi(ApiInfo info)
+        public bool GetInfoApi()
         {
             if (Twitter.AccountState != MyCommon.ACCOUNT_STATE.Valid) return true;
 
@@ -4071,7 +4072,7 @@ namespace OpenTween
             }
             catch(Exception)
             {
-                MyCommon.TwitterApiInfo.Initialize();
+                MyCommon.TwitterApiInfo.Reset();
                 return false;
             }
 
@@ -4080,32 +4081,13 @@ namespace OpenTween
             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);
+                MyCommon.TwitterApiInfo.UpdateFromApi(limit);
                 return true;
             }
             catch(Exception ex)
             {
                 MyCommon.TraceOut(ex, MethodBase.GetCurrentMethod().Name + " " + content);
-                MyCommon.TwitterApiInfo.Initialize();
+                MyCommon.TwitterApiInfo.Reset();
                 return false;
             }
         }
@@ -4186,17 +4168,6 @@ namespace OpenTween
             }
         }
 
-        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