using System.Threading.Tasks;
using Moq;
using OpenTween.Connection;
+using OpenTween.Models;
using Xunit;
namespace OpenTween.Api.GraphQL
{
TweetText = "tetete",
InReplyToTweetId = new("12345"),
- ExcludeReplyUserIds = new[] { "11111", "22222" },
+ ExcludeReplyUserIds = new TwitterUserId[] { new("11111"), new("22222") },
};
await request.Send(mock.Object);
mock.VerifyAll();
var request = new LikesRequest
{
- UserId = "12345",
+ UserId = new("12345"),
Count = 20,
};
var request = new LikesRequest
{
- UserId = "12345",
+ UserId = new("12345"),
Count = 20,
Cursor = new("aaa"),
};
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
+ var post = postFactory.CreateFromStatus(status, selfUserId: new("1"), new HashSet<PersonId>(), firstLoad: false);
Assert.Equal("1613784711020826626", post.StatusId.Id);
- Assert.Equal(40480664L, post.UserId);
+ Assert.Equal(new TwitterUserId("40480664"), post.UserId);
Assert.False(post.IsPromoted);
}
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
+ var post = postFactory.CreateFromStatus(status, selfUserId: new("1"), new HashSet<PersonId>(), firstLoad: false);
Assert.Equal("1614587968567783424", post.StatusId.Id);
- Assert.Equal(40480664L, post.UserId);
+ Assert.Equal(new TwitterUserId("40480664"), post.UserId);
Assert.Equal(2, post.Media.Count);
Assert.Equal("https://pbs.twimg.com/media/FmgrJiEaAAEU42G.png", post.Media[0].Url);
Assert.Equal("OpenTweenで @opentween のツイート一覧を表示しているスクショ", post.Media[0].AltText);
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
+ var post = postFactory.CreateFromStatus(status, selfUserId: new("1"), new HashSet<PersonId>(), firstLoad: false);
Assert.Equal("1617128268548964354", post.StatusId.Id);
- Assert.Equal(40480664L, post.RetweetedByUserId);
+ Assert.Equal(new TwitterUserId("40480664"), post.RetweetedByUserId);
Assert.Equal("1617126084138659840", post.RetweetedId!.Id);
- Assert.Equal(514241801L, post.UserId);
+ Assert.Equal(new TwitterUserId("514241801"), post.UserId);
}
[Fact]
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
+ var post = postFactory.CreateFromStatus(status, selfUserId: new("1"), new HashSet<PersonId>(), firstLoad: false);
Assert.Equal("1602775353088524288", post.StatusId.Id);
- Assert.Equal(357750891L, post.UserId);
+ Assert.Equal(new TwitterUserId("357750891"), post.UserId);
}
[Fact]
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
+ var post = postFactory.CreateFromStatus(status, selfUserId: new("1"), new HashSet<PersonId>(), firstLoad: false);
Assert.Equal("1511751702684499968", post.StatusId.Id);
- Assert.Equal(40480664L, post.UserId);
+ Assert.Equal(new TwitterUserId("40480664"), post.UserId);
}
[Fact]
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
+ var post = postFactory.CreateFromStatus(status, selfUserId: new("1"), new HashSet<PersonId>(), firstLoad: false);
Assert.Equal("1588614645866147840", post.StatusId.Id);
var quotedPostId = Assert.Single(post.QuoteStatusIds);
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
+ var post = postFactory.CreateFromStatus(status, selfUserId: new("1"), new HashSet<PersonId>(), firstLoad: false);
Assert.Equal("1614653321310253057", post.StatusId.Id);
var quotedPostId = Assert.Single(post.QuoteStatusIds);
var timelineTweet = new TimelineTweet(rootElm);
var status = timelineTweet.ToTwitterStatus();
var postFactory = new TwitterPostFactory(this.CreateTabInfo(), new());
- var post = postFactory.CreateFromStatus(status, selfUserId: 1L, new HashSet<long>(), firstLoad: false);
+ var post = postFactory.CreateFromStatus(status, selfUserId: new("1"), new HashSet<PersonId>(), firstLoad: false);
Assert.Equal("1674737917363888129", post.StatusId.Id);
- Assert.Equal(2941313791L, post.UserId);
+ Assert.Equal(new TwitterUserId("2941313791"), post.UserId);
Assert.True(post.IsPromoted);
Assert.Matches(new Regex(@"^\[Promoted\]\n"), post.TextFromApi);
}
using System.Threading.Tasks;
using Moq;
using OpenTween.Connection;
+using OpenTween.Models;
using Xunit;
namespace OpenTween.Api.GraphQL
})
.ReturnsAsync(apiResponse);
- var request = new UserTweetsAndRepliesRequest(userId: "40480664")
+ var request = new UserTweetsAndRepliesRequest(userId: new("40480664"))
{
Count = 20,
};
})
.ReturnsAsync(apiResponse);
- var request = new UserTweetsAndRepliesRequest(userId: "40480664")
+ var request = new UserTweetsAndRepliesRequest(userId: new("40480664"))
{
Count = 20,
Cursor = new("aaa"),
using Moq;
using OpenTween.Api.DataModel;
using OpenTween.Connection;
+using OpenTween.Models;
using Xunit;
namespace OpenTween.Api
replyToId: new("100"),
mediaIds: new[] { 10L, 20L },
autoPopulateReplyMetadata: true,
- excludeReplyUserIds: new[] { 100L, 200L },
+ excludeReplyUserIds: new TwitterUserId[] { new("100"), new("200") },
attachmentUrl: "https://twitter.com/twitterapi/status/22634515958"
)
.IgnoreResponse();
using var twitterApi = new TwitterApi();
twitterApi.ApiConnection = mock.Object;
- await twitterApi.StatusesUpdate("hogehoge", replyToId: null, mediaIds: null, excludeReplyUserIds: Array.Empty<long>())
+ await twitterApi.StatusesUpdate("hogehoge", replyToId: null, mediaIds: null, excludeReplyUserIds: Array.Empty<TwitterUserId>())
.IgnoreResponse();
mock.VerifyAll();
using var twitterApi = new TwitterApi();
twitterApi.ApiConnection = mock.Object;
- await twitterApi.DirectMessagesEventsNew(recipientId: 12345L, text: "hogehoge", mediaId: 67890L);
+ await twitterApi.DirectMessagesEventsNew(recipientId: new("12345"), text: "hogehoge", mediaId: 67890L);
mock.VerifyAll();
}
using var twitterApi = new TwitterApi();
twitterApi.ApiConnection = mock.Object;
- await twitterApi.UsersLookup(userIds: new[] { "11111", "22222" });
+ await twitterApi.UsersLookup(userIds: new TwitterUserId[] { new("11111"), new("22222") });
mock.VerifyAll();
}
--- /dev/null
+// OpenTween - Client of Twitter
+// Copyright (c) 2024 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 Moq;
+using Xunit;
+
+namespace OpenTween.Models
+{
+ public class PersonIdTest
+ {
+ private PersonId CreatePersonId(string type, string id)
+ {
+ var mock = new Mock<PersonId>() { CallBase = true };
+ mock.Setup(x => x.IdType).Returns(type);
+ mock.Setup(x => x.Id).Returns(id);
+ return mock.Object;
+ }
+
+ [Fact]
+ public void CompareTo_Test()
+ {
+ var a = this.CreatePersonId("mastodon", "200");
+ var b = this.CreatePersonId("twitter", "100");
+ Assert.True(a.CompareTo(b) < 0);
+ Assert.True(b.CompareTo(a) > 0);
+ Assert.Equal(0, a.CompareTo(a));
+ }
+
+ [Fact]
+ public void CompareTo_SameIdTypeTest()
+ {
+ var a = this.CreatePersonId("twitter", "100");
+ var b = this.CreatePersonId("twitter", "200");
+ Assert.True(a.CompareTo(b) < 0);
+ Assert.True(b.CompareTo(a) > 0);
+ Assert.Equal(0, a.CompareTo(a));
+ }
+
+ [Fact]
+ public void CompareTo_IdLengthTest()
+ {
+ var a = this.CreatePersonId("twitter", "200");
+ var b = this.CreatePersonId("twitter", "1000");
+ Assert.True(a.CompareTo(b) < 0);
+ Assert.True(b.CompareTo(a) > 0);
+ }
+
+ [Fact]
+ public void OperatorGreaterThan_Test()
+ {
+ var a = this.CreatePersonId("twitter", "100");
+ var b = this.CreatePersonId("twitter", "200");
+#pragma warning disable CS1718
+ Assert.False(a < a);
+ Assert.True(a < b);
+ Assert.False(b < a);
+ Assert.False(b < b);
+ Assert.True(a <= a);
+ Assert.True(a <= b);
+ Assert.False(b <= a);
+ Assert.True(b <= b);
+#pragma warning restore CS1718
+ }
+
+ [Fact]
+ public void OperatorLessThan_Test()
+ {
+ var a = this.CreatePersonId("twitter", "100");
+ var b = this.CreatePersonId("twitter", "200");
+#pragma warning disable CS1718
+ Assert.False(a > a);
+ Assert.False(a > b);
+ Assert.True(b > a);
+ Assert.False(b > b);
+ Assert.True(a >= a);
+ Assert.False(a >= b);
+ Assert.True(b >= a);
+ Assert.True(b >= b);
+#pragma warning restore CS1718
+ }
+
+ [Fact]
+ public void Equals_Test()
+ {
+ var a = this.CreatePersonId("twitter", "100");
+ var b = this.CreatePersonId("twitter", "100");
+ Assert.True(a.Equals(b));
+ Assert.True(b.Equals(a));
+ Assert.True(a == b);
+ Assert.True(b == a);
+ }
+
+ [Fact]
+ public void Equals_NotSameIdTypeTest()
+ {
+ var a = this.CreatePersonId("mastodon", "100");
+ var b = this.CreatePersonId("twitter", "100");
+ Assert.False(a.Equals(b));
+ Assert.False(b.Equals(a));
+ Assert.True(a != b);
+ Assert.True(b != a);
+ }
+
+ [Fact]
+ public void Equals_NotSameIdTest()
+ {
+ var a = this.CreatePersonId("twitter", "100");
+ var b = this.CreatePersonId("twitter", "200");
+ Assert.False(a.Equals(b));
+ Assert.False(b.Equals(a));
+ Assert.True(a != b);
+ Assert.True(b != a);
+ }
+
+ [Fact]
+ public void GetHashCode_SameIdTest()
+ {
+ var a = this.CreatePersonId("twitter", "100");
+ var b = this.CreatePersonId("twitter", "100");
+ Assert.Equal(a.GetHashCode(), b.GetHashCode());
+ }
+
+ [Fact]
+ public void GetHashCode_NotSameIdTypeTest()
+ {
+ var a = this.CreatePersonId("mastodon", "100");
+ var b = this.CreatePersonId("twitter", "100");
+ Assert.NotEqual(a.GetHashCode(), b.GetHashCode());
+ }
+
+ [Fact]
+ public void GetHashCode_NotSameIdTest()
+ {
+ var a = this.CreatePersonId("twitter", "100");
+ var b = this.CreatePersonId("twitter", "200");
+ Assert.NotEqual(a.GetHashCode(), b.GetHashCode());
+ }
+ }
+}
{
IsDm = true,
IsMe = true, // 自分が送信した DM
- UserId = 222L, // 送信先ユーザーID
+ UserId = new TwitterUserId("222"), // 送信先ユーザーID
};
- Assert.True(post.CanDeleteBy(selfUserId: 111L));
+ Assert.True(post.CanDeleteBy(selfUserId: new TwitterUserId("111")));
}
[Fact]
{
IsDm = true,
IsMe = false, // 自分が受け取った DM
- UserId = 222L, // 送信元ユーザーID
+ UserId = new TwitterUserId("222"), // 送信元ユーザーID
};
- Assert.True(post.CanDeleteBy(selfUserId: 111L));
+ Assert.True(post.CanDeleteBy(selfUserId: new TwitterUserId("111")));
}
[Fact]
{
var post = new PostClass
{
- UserId = 111L, // 自分のツイート
+ UserId = new TwitterUserId("111"), // 自分のツイート
};
- Assert.True(post.CanDeleteBy(selfUserId: 111L));
+ Assert.True(post.CanDeleteBy(selfUserId: new TwitterUserId("111")));
}
[Fact]
{
var post = new PostClass
{
- UserId = 222L, // 他人のツイート
+ UserId = new TwitterUserId("222"), // 他人のツイート
};
- Assert.False(post.CanDeleteBy(selfUserId: 111L));
+ Assert.False(post.CanDeleteBy(selfUserId: new TwitterUserId("111")));
}
[Fact]
{
var post = new PostClass
{
- RetweetedByUserId = 111L, // 自分がリツイートした
- UserId = 222L, // 他人のツイート
+ RetweetedByUserId = new TwitterUserId("111"), // 自分がリツイートした
+ UserId = new TwitterUserId("222"), // 他人のツイート
};
- Assert.True(post.CanDeleteBy(selfUserId: 111L));
+ Assert.True(post.CanDeleteBy(selfUserId: new TwitterUserId("111")));
}
[Fact]
{
var post = new PostClass
{
- RetweetedByUserId = 333L, // 他人がリツイートした
- UserId = 222L, // 他人のツイート
+ RetweetedByUserId = new TwitterUserId("333"), // 他人がリツイートした
+ UserId = new TwitterUserId("222"), // 他人のツイート
};
- Assert.False(post.CanDeleteBy(selfUserId: 111L));
+ Assert.False(post.CanDeleteBy(selfUserId: new TwitterUserId("111")));
}
[Fact]
{
var post = new PostClass
{
- RetweetedByUserId = 222L, // 他人がリツイートした
- UserId = 111L, // 自分のツイート
+ RetweetedByUserId = new TwitterUserId("222"), // 他人がリツイートした
+ UserId = new TwitterUserId("111"), // 自分のツイート
};
- Assert.True(post.CanDeleteBy(selfUserId: 111L));
+ Assert.True(post.CanDeleteBy(selfUserId: new TwitterUserId("111")));
}
[Fact]
{
IsDm = true,
IsMe = false, // 自分が受け取った DM
- UserId = 222L, // 送信元ユーザーID
+ UserId = new TwitterUserId("222"), // 送信元ユーザーID
};
- Assert.False(post.CanRetweetBy(selfUserId: 111L));
+ Assert.False(post.CanRetweetBy(selfUserId: new TwitterUserId("111")));
}
[Fact]
{
var post = new PostClass
{
- UserId = 111L, // 自分のツイート
+ UserId = new TwitterUserId("111"), // 自分のツイート
};
- Assert.True(post.CanRetweetBy(selfUserId: 111L));
+ Assert.True(post.CanRetweetBy(selfUserId: new TwitterUserId("111")));
}
[Fact]
{
var post = new PostClass
{
- UserId = 111L, // 自分のツイート
+ UserId = new TwitterUserId("111"), // 自分のツイート
IsProtect = true,
};
- Assert.True(post.CanRetweetBy(selfUserId: 111L));
+ Assert.True(post.CanRetweetBy(selfUserId: new TwitterUserId("111")));
}
[Fact]
{
var post = new PostClass
{
- UserId = 222L, // 他人のツイート
+ UserId = new TwitterUserId("222"), // 他人のツイート
IsProtect = false,
};
- Assert.True(post.CanRetweetBy(selfUserId: 111L));
+ Assert.True(post.CanRetweetBy(selfUserId: new TwitterUserId("111")));
}
[Fact]
{
var post = new PostClass
{
- UserId = 222L, // 他人のツイート
+ UserId = new TwitterUserId("222"), // 他人のツイート
IsProtect = true,
};
- Assert.False(post.CanRetweetBy(selfUserId: 111L));
+ Assert.False(post.CanRetweetBy(selfUserId: new TwitterUserId("111")));
}
[Fact]
CreatedAtForSorting = new(2023, 1, 2, 0, 0, 0),
CreatedAt = new(2023, 1, 1, 0, 0, 0),
ScreenName = "@aaa",
- UserId = 1L,
+ UserId = new TwitterUserId("1"),
RetweetedId = new TwitterStatusId("50"),
RetweetedBy = "@bbb",
- RetweetedByUserId = 2L,
+ RetweetedByUserId = new TwitterUserId("2"),
RetweetedCount = 0,
};
Assert.Equal(new(2023, 1, 1, 0, 0, 0), originalPost.CreatedAt);
Assert.Equal(new(2023, 1, 1, 0, 0, 0), originalPost.CreatedAtForSorting);
Assert.Equal("@aaa", originalPost.ScreenName);
- Assert.Equal(1L, originalPost.UserId);
+ Assert.Equal(new TwitterUserId("1"), originalPost.UserId);
Assert.Null(originalPost.RetweetedId);
Assert.Equal("", originalPost.RetweetedBy);
[Fact]
public void IsMuted_Test()
{
- this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
+ this.tabinfo.MuteUserIds = new HashSet<PersonId> { new TwitterUserId("12345") };
var post = new PostClass
{
- UserId = 12345L,
+ UserId = new TwitterUserId("12345"),
Text = "hogehoge",
};
Assert.True(this.tabinfo.IsMuted(post, isHomeTimeline: true));
[Fact]
public void IsMuted_NotMutingTest()
{
- this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
+ this.tabinfo.MuteUserIds = new HashSet<PersonId> { new TwitterUserId("12345") };
var post = new PostClass
{
- UserId = 11111L,
+ UserId = new TwitterUserId("11111"),
Text = "hogehoge",
};
Assert.False(this.tabinfo.IsMuted(post, isHomeTimeline: true));
[Fact]
public void IsMuted_RetweetTest()
{
- this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
+ this.tabinfo.MuteUserIds = new HashSet<PersonId> { new TwitterUserId("12345") };
var post = new PostClass
{
- UserId = 11111L,
- RetweetedByUserId = 12345L,
+ UserId = new TwitterUserId("11111"),
+ RetweetedByUserId = new TwitterUserId("12345"),
Text = "hogehoge",
};
Assert.True(this.tabinfo.IsMuted(post, isHomeTimeline: true));
[Fact]
public void IsMuted_RetweetNotMutingTest()
{
- this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
+ this.tabinfo.MuteUserIds = new HashSet<PersonId> { new TwitterUserId("12345") };
var post = new PostClass
{
- UserId = 11111L,
- RetweetedByUserId = 22222L,
+ UserId = new TwitterUserId("11111"),
+ RetweetedByUserId = new TwitterUserId("22222"),
Text = "hogehoge",
};
Assert.False(this.tabinfo.IsMuted(post, isHomeTimeline: true));
[Fact]
public void IsMuted_ReplyTest()
{
- this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
+ this.tabinfo.MuteUserIds = new HashSet<PersonId> { new TwitterUserId("12345") };
// ミュート対象のユーザーであってもリプライの場合は対象外とする
var post = new PostClass
{
- UserId = 12345L,
+ UserId = new TwitterUserId("12345"),
Text = "@foo hogehoge",
IsReply = true,
};
[Fact]
public void IsMuted_NotInHomeTimelineTest()
{
- this.tabinfo.MuteUserIds = new HashSet<long> { 12345L };
+ this.tabinfo.MuteUserIds = new HashSet<PersonId> { new TwitterUserId("12345") };
// Recent以外のタブ(検索など)の場合は対象外とする
var post = new PostClass
{
- UserId = 12345L,
+ UserId = new TwitterUserId("12345"),
Text = "hogehoge",
};
Assert.False(this.tabinfo.IsMuted(post, isHomeTimeline: false));
[Fact]
public void IsMuted_MuteTabRulesTest()
{
- this.tabinfo.MuteUserIds = new HashSet<long> { };
+ this.tabinfo.MuteUserIds = new HashSet<PersonId> { };
var muteTab = new MuteTabModel();
muteTab.AddFilter(new PostFilterRule
var post = new PostClass
{
- UserId = 12345L,
+ UserId = new TwitterUserId("12345"),
ScreenName = "foo",
Text = "hogehoge",
};
[Fact]
public void IsMuted_MuteTabRules_NotInHomeTimelineTest()
{
- this.tabinfo.MuteUserIds = new HashSet<long> { };
+ this.tabinfo.MuteUserIds = new HashSet<PersonId> { };
var muteTab = new MuteTabModel();
muteTab.AddFilter(new PostFilterRule
// ミュートタブによるミュートはリプライも対象とする
var post = new PostClass
{
- UserId = 12345L,
+ UserId = new TwitterUserId("12345"),
ScreenName = "foo",
Text = "@hoge hogehoge",
IsReply = true,
{
StatusId = new TwitterStatusId("100"),
ScreenName = "aaa",
- UserId = 123L,
+ UserId = new TwitterUserId("123"),
IsOwl = true,
};
this.tabinfo.AddPost(post);
this.tabinfo.DistributePosts();
this.tabinfo.SubmitUpdate();
- var followerIds = new HashSet<long> { 123L };
+ var followerIds = new HashSet<PersonId> { new TwitterUserId("123") };
this.tabinfo.RefreshOwl(followerIds);
Assert.False(post.IsOwl);
{
StatusId = new TwitterStatusId("100"),
ScreenName = "aaa",
- UserId = 123L,
+ UserId = new TwitterUserId("123"),
IsOwl = true,
};
tab.AddPostQueue(post);
this.tabinfo.DistributePosts();
this.tabinfo.SubmitUpdate();
- var followerIds = new HashSet<long> { 123L };
+ var followerIds = new HashSet<PersonId> { new TwitterUserId("123") };
this.tabinfo.RefreshOwl(followerIds);
Assert.False(post.IsOwl);
{
StatusId = new TwitterStatusId("100"),
ScreenName = "aaa",
- UserId = 123L,
+ UserId = new TwitterUserId("123"),
IsOwl = false,
};
this.tabinfo.AddPost(post);
this.tabinfo.DistributePosts();
this.tabinfo.SubmitUpdate();
- var followerIds = new HashSet<long> { 456L };
+ var followerIds = new HashSet<PersonId> { new TwitterUserId("456") };
this.tabinfo.RefreshOwl(followerIds);
Assert.True(post.IsOwl);
{
public class TwitterPostFactoryTest
{
- private static readonly ISet<long> EmptyIdSet = new HashSet<long>();
+ private static readonly ISet<PersonId> EmptyIdSet = new HashSet<PersonId>();
private readonly Random random = new();
{
var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var status = this.CreateStatus();
- var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds: EmptyIdSet, firstLoad: false);
+ var post = factory.CreateFromStatus(status, selfUserId: new("20000"), followerIds: EmptyIdSet, firstLoad: false);
Assert.Equal(new TwitterStatusId(status.IdStr), post.StatusId);
Assert.Equal(new DateTimeUtc(2022, 1, 1, 0, 0, 0), post.CreatedAt);
Assert.Null(post.RetweetedBy);
Assert.Null(post.RetweetedByUserId);
- Assert.Equal(status.User.Id, post.UserId);
+ Assert.Equal(new TwitterUserId(status.User.IdStr), post.UserId);
Assert.Equal("tetete", post.ScreenName);
Assert.Equal("ててて", post.Nickname);
Assert.Equal("https://example.com/profile.png", post.ImageUrl);
{
var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var status = this.CreateStatus();
- var selfUserId = status.User.Id;
+ var selfUserId = new TwitterUserId(status.User.IdStr);
var post = factory.CreateFromStatus(status, selfUserId, followerIds: EmptyIdSet, firstLoad: false);
Assert.True(post.IsMe);
{
var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var status = this.CreateStatus();
- var followerIds = new HashSet<long> { status.User.Id };
- var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds, firstLoad: false);
+ var followerIds = new HashSet<PersonId> { new TwitterUserId(status.User.IdStr) };
+ var post = factory.CreateFromStatus(status, selfUserId: new("20000"), followerIds, firstLoad: false);
Assert.False(post.IsOwl);
}
{
var factory = new TwitterPostFactory(this.CreateTabinfo(), new());
var status = this.CreateStatus();
- var followerIds = new HashSet<long> { 30000L };
- var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds, firstLoad: false);
+ var followerIds = new HashSet<PersonId> { new TwitterUserId("30000") };
+ var post = factory.CreateFromStatus(status, selfUserId: new("20000"), followerIds, firstLoad: false);
Assert.True(post.IsOwl);
}
retweetStatus.RetweetedStatus = originalStatus;
retweetStatus.Source = """<a href="https://mobile.twitter.com" rel="nofollow">Twitter Web App</a>""";
- var post = factory.CreateFromStatus(retweetStatus, selfUserId: 20000L, followerIds: EmptyIdSet, firstLoad: false);
+ var post = factory.CreateFromStatus(retweetStatus, selfUserId: new("20000"), followerIds: EmptyIdSet, firstLoad: false);
Assert.Equal(new TwitterStatusId(retweetStatus.IdStr), post.StatusId);
- Assert.Equal(retweetStatus.User.Id, post.RetweetedByUserId);
+ Assert.Equal(new TwitterUserId(retweetStatus.User.IdStr), post.RetweetedByUserId);
Assert.Equal(new TwitterStatusId(originalStatus.IdStr), post.RetweetedId);
- Assert.Equal(originalStatus.User.Id, post.UserId);
+ Assert.Equal(new TwitterUserId(originalStatus.User.IdStr), post.UserId);
Assert.Equal("OpenTween", post.Source);
Assert.Equal("https://www.opentween.org/", post.SourceUri?.OriginalString);
var factory = new TwitterPostFactory(this.CreateTabinfo(), settingCommon);
var status = this.CreateStatus();
- var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds: EmptyIdSet, firstLoad: true);
+ var post = factory.CreateFromStatus(status, selfUserId: new("20000"), followerIds: EmptyIdSet, firstLoad: true);
Assert.True(post.IsRead); // 既読
}
var factory = new TwitterPostFactory(this.CreateTabinfo(), settingCommon);
var status = this.CreateStatus();
- var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds: EmptyIdSet, firstLoad: true);
+ var post = factory.CreateFromStatus(status, selfUserId: new("20000"), followerIds: EmptyIdSet, firstLoad: true);
Assert.False(post.IsRead); // 未読
}
var factory = new TwitterPostFactory(this.CreateTabinfo(), settingCommon);
var status = this.CreateStatus();
status.User.Id = 20000L;
- var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds: EmptyIdSet, firstLoad: false);
+ var post = factory.CreateFromStatus(status, selfUserId: new("20000"), followerIds: EmptyIdSet, firstLoad: false);
Assert.False(post.IsRead); // 未読
}
var factory = new TwitterPostFactory(this.CreateTabinfo(), settingCommon);
var status = this.CreateStatus();
- status.User.Id = 20000L;
- var post = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds: EmptyIdSet, firstLoad: false);
+ status.User.IdStr = "20000";
+ var post = factory.CreateFromStatus(status, selfUserId: new("20000"), followerIds: EmptyIdSet, firstLoad: false);
Assert.True(post.IsRead); // 既読
}
var selfUser = this.CreateUser();
var otherUser = this.CreateUser();
var eventItem = this.CreateDirectMessage(senderId: otherUser.IdStr, recipientId: selfUser.IdStr);
- var users = new Dictionary<string, TwitterUser>()
+ var users = new Dictionary<TwitterUserId, TwitterUser>()
{
- [selfUser.IdStr] = selfUser,
- [otherUser.IdStr] = otherUser,
+ [new(selfUser.IdStr)] = selfUser,
+ [new(otherUser.IdStr)] = otherUser,
};
var apps = this.CreateApps();
- var post = factory.CreateFromDirectMessageEvent(eventItem, users, apps, selfUserId: selfUser.Id, firstLoad: false);
+ var post = factory.CreateFromDirectMessageEvent(eventItem, users, apps, selfUserId: new(selfUser.IdStr), firstLoad: false);
Assert.Equal(new TwitterDirectMessageId(eventItem.Id), post.StatusId);
Assert.Equal(new DateTimeUtc(2022, 1, 1, 0, 0, 0), post.CreatedAt);
Assert.Null(post.RetweetedBy);
Assert.Null(post.RetweetedByUserId);
- Assert.Equal(otherUser.Id, post.UserId);
+ Assert.Equal(new TwitterUserId(otherUser.IdStr), post.UserId);
Assert.Equal("tetete", post.ScreenName);
Assert.Equal("ててて", post.Nickname);
Assert.Equal("https://example.com/profile.png", post.ImageUrl);
var selfUser = this.CreateUser();
var otherUser = this.CreateUser();
var eventItem = this.CreateDirectMessage(senderId: selfUser.IdStr, recipientId: otherUser.IdStr);
- var users = new Dictionary<string, TwitterUser>()
+ var users = new Dictionary<TwitterUserId, TwitterUser>()
{
- [selfUser.IdStr] = selfUser,
- [otherUser.IdStr] = otherUser,
+ [new(selfUser.IdStr)] = selfUser,
+ [new(otherUser.IdStr)] = otherUser,
};
var apps = this.CreateApps();
- var post = factory.CreateFromDirectMessageEvent(eventItem, users, apps, selfUserId: selfUser.Id, firstLoad: false);
+ var post = factory.CreateFromDirectMessageEvent(eventItem, users, apps, selfUserId: new(selfUser.IdStr), firstLoad: false);
- Assert.Equal(otherUser.Id, post.UserId);
+ Assert.Equal(new TwitterUserId(otherUser.IdStr), post.UserId);
Assert.False(post.IsOwl);
Assert.True(post.IsMe);
}
},
};
- _ = factory.CreateFromStatus(status, selfUserId: 20000L, followerIds: EmptyIdSet, firstLoad: false);
+ _ = factory.CreateFromStatus(status, selfUserId: new("20000"), followerIds: EmptyIdSet, firstLoad: false);
Assert.Equal(new[] { "#OpenTween" }, factory.GetReceivedHashtags());
Assert.Empty(factory.GetReceivedHashtags());
},
};
- var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet, firstLoad: false);
+ var post = factory.CreateFromStatus(status, selfUserId: new("100"), followerIds: EmptyIdSet, firstLoad: false);
var accessibleText = string.Format(Properties.Resources.ImageAltText, "代替テキスト");
Assert.Equal(accessibleText, post.AccessibleText);
},
};
- var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet, firstLoad: false);
+ var post = factory.CreateFromStatus(status, selfUserId: new("100"), followerIds: EmptyIdSet, firstLoad: false);
Assert.Equal("pic.twitter.com/hoge", post.AccessibleText);
Assert.Equal("""<a href="https://t.co/hoge" title="https://twitter.com/hoge/status/1234567890/photo/1">pic.twitter.com/hoge</a>""", post.Text);
FullText = "test",
};
- var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet, firstLoad: false);
+ var post = factory.CreateFromStatus(status, selfUserId: new("100"), followerIds: EmptyIdSet, firstLoad: false);
var accessibleText = string.Format(Properties.Resources.QuoteStatus_AccessibleText, "foo", "test");
Assert.Equal(accessibleText, post.AccessibleText);
Expanded = "https://twitter.com/hoge/status/1234567890",
};
- var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet, firstLoad: false);
+ var post = factory.CreateFromStatus(status, selfUserId: new("100"), followerIds: EmptyIdSet, firstLoad: false);
var accessibleText = "hoge " + string.Format(Properties.Resources.QuoteStatus_AccessibleText, "foo", "test");
Assert.Equal(accessibleText, post.AccessibleText);
};
status.QuotedStatus = null;
- var post = factory.CreateFromStatus(status, selfUserId: 100L, followerIds: EmptyIdSet, firstLoad: false);
+ var post = factory.CreateFromStatus(status, selfUserId: new("100"), followerIds: EmptyIdSet, firstLoad: false);
var accessibleText = "twitter.com/hoge/status/1…";
Assert.Equal(accessibleText, post.AccessibleText);
TwitterAuthType = APIAuthType.OAuth1,
Token = "aaaaa",
TokenSecret = "bbbbb",
- UserId = this.random.Next(),
+ UserId = this.random.Next().ToString(),
Username = "tetete",
};
}
accounts.LoadFromSettings(settingCommon);
Assert.Single(accounts.Items);
- Assert.Equal(settingCommon.UserAccounts[0].UserId, accounts.Primary.UserId);
+ Assert.Equal(new TwitterUserId(settingCommon.UserAccounts[0].UserId), accounts.Primary.UserId);
}
[Fact]
accounts.LoadFromSettings(settingCommon1);
var accountItem1 = Assert.Single(accounts.Items);
- Assert.Equal(settingCommon1.UserAccounts[0].UserId, accounts.Primary.UserId);
+ Assert.Equal(new TwitterUserId(settingCommon1.UserAccounts[0].UserId), accounts.Primary.UserId);
var settingCommon2 = new SettingCommon
{
accounts.LoadFromSettings(settingCommon1);
var accountItem1 = Assert.Single(accounts.Items);
- Assert.Equal(settingCommon1.UserAccounts[0].UserId, accounts.Primary.UserId);
+ Assert.Equal(new TwitterUserId(settingCommon1.UserAccounts[0].UserId), accounts.Primary.UserId);
var settingCommon2 = new SettingCommon
{
accounts.LoadFromSettings(settingCommon2);
var accountItem2 = Assert.Single(accounts.Items);
- Assert.Equal(settingCommon2.UserAccounts[0].UserId, accounts.Primary.UserId);
+ Assert.Equal(new TwitterUserId(settingCommon2.UserAccounts[0].UserId), accounts.Primary.UserId);
// 同一の ID は同じインスタンスを使用
Assert.Same(accountItem1, accountItem2);
Assert.NotEqual(
- settingCommon1.UserAccounts[0].UserId,
+ new TwitterUserId(settingCommon1.UserAccounts[0].UserId),
accountItem2.UserId
);
Assert.Equal(
- settingCommon2.UserAccounts[0].UserId,
+ new TwitterUserId(settingCommon2.UserAccounts[0].UserId),
accountItem2.UserId
);
Assert.False(accountItem2.IsDisposed);
// Boston, MA 02110-1301, USA.
using OpenTween.Api.DataModel;
+using OpenTween.Models;
using Xunit;
namespace OpenTween.SocialProtocol.Twitter
};
accountState.UpdateFromUser(twitterUser);
- Assert.Equal(514241801L, accountState.UserId);
+ Assert.Equal(new TwitterUserId("514241801"), accountState.UserId);
Assert.Equal("OpenTween", accountState.UserName);
Assert.Equal(31, accountState.StatusesCount);
Assert.Equal(1, accountState.FriendsCount);
// Boston, MA 02110-1301, USA.
using System;
+using OpenTween.Models;
using Xunit;
namespace OpenTween.SocialProtocol.Twitter
TwitterAuthType = APIAuthType.OAuth1,
Token = "aaaaa",
TokenSecret = "aaaaa",
- UserId = 11111L,
+ UserId = "11111",
Username = "tetete",
};
var settingCommon = new SettingCommon();
account.Initialize(accountSettings, settingCommon);
- Assert.Equal(11111L, account.UserId);
+ Assert.Equal(new TwitterUserId("11111"), account.UserId);
Assert.Equal("tetete", account.UserName);
Assert.Equal(APIAuthType.OAuth1, account.AuthType);
Assert.Same(account.Legacy.Api.Connection, account.Connection);
TwitterAuthType = APIAuthType.OAuth1,
Token = "aaaaa",
TokenSecret = "aaaaa",
- UserId = 11111L,
+ UserId = "11111",
Username = "tetete",
};
var settingCommon1 = new SettingCommon();
account.Initialize(accountSettings1, settingCommon1);
- Assert.Equal(11111L, account.UserId);
+ Assert.Equal(new TwitterUserId("11111"), account.UserId);
var accountSettings2 = new UserAccount
{
TwitterAuthType = APIAuthType.OAuth1,
Token = "bbbbb",
TokenSecret = "bbbbb",
- UserId = 22222L,
+ UserId = "22222",
Username = "hoge",
};
var settingCommon2 = new SettingCommon();
account.Initialize(accountSettings2, settingCommon2);
- Assert.Equal(22222L, account.UserId);
+ Assert.Equal(new TwitterUserId("22222"), account.UserId);
}
[Fact]
TwitterAuthType = APIAuthType.OAuth1,
Token = "aaaaa",
TokenSecret = "aaaaa",
- UserId = 11111L,
+ UserId = "11111",
Username = "tetete",
};
var settingCommon = new SettingCommon();
UniqueKey = accountKey,
TwitterAuthType = APIAuthType.TwitterComCookie,
TwitterComCookie = "auth_token=foo; ct0=bar",
- UserId = 11111L,
+ UserId = "11111",
Username = "tetete",
};
var settingCommon = new SettingCommon();
return new()
{
StatusId = new TwitterStatusId(this.random.Next(10000)),
- UserId = this.random.Next(10000),
+ UserId = new TwitterUserId(this.random.Next(10000).ToString()),
ScreenName = "test",
Nickname = "てすと",
AccessibleText = "foo",
{
StatusId = new TwitterStatusId("100"),
Text = "hoge",
- UserId = 111L,
+ UserId = new TwitterUserId("111"),
ScreenName = "opentween",
CreatedAt = new(2024, 1, 1, 0, 0, 0),
};
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
+using OpenTween.Models;
namespace OpenTween.Api.DataModel
{
}
[DataContract]
- public class TwitterIds : TwitterPageable<long>
+ public class TwitterIds : TwitterPageable<TwitterUserId>
{
[DataMember(Name = "ids")]
- public long[] Ids { get; set; }
+ public string[] Ids { get; set; }
[IgnoreDataMember]
- public override long[] Items
- => this.Ids;
+ public override TwitterUserId[] Items
+ => this.Ids.Select(x => new TwitterUserId(x)).ToArray();
/// <exception cref="SerializationException"/>
public static TwitterIds ParseJson(string json)
public TwitterStatusId? InReplyToTweetId { get; set; }
- public string[] ExcludeReplyUserIds { get; set; } = Array.Empty<string>();
+ public TwitterUserId[] ExcludeReplyUserIds { get; set; } = Array.Empty<TwitterUserId>();
public string[] MediaIds { get; set; } = Array.Empty<string>();
Reply: this.InReplyToTweetId != null
? new(
InReplyToTweetId: this.InReplyToTweetId.Id,
- ExcludeReplyUserIds: this.ExcludeReplyUserIds
+ ExcludeReplyUserIds: this.ExcludeReplyUserIds.Select(x => x.Id).ToArray()
)
: null,
Media: this.MediaIds.Length > 0
using System.Collections.Generic;
using System.Threading.Tasks;
using OpenTween.Connection;
+using OpenTween.Models;
namespace OpenTween.Api.GraphQL
{
private static readonly Uri EndpointUri = new("https://twitter.com/i/api/graphql/G_zHbTiwSqLm0TAK_3sNWQ/Likes");
- public required string UserId { get; set; }
+ public required TwitterUserId UserId { get; set; }
public int Count { get; set; } = 20;
return new()
{
["variables"] = "{" +
- $@"""userId"":""{JsonUtils.EscapeJsonString(this.UserId)}""," +
+ $@"""userId"":""{JsonUtils.EscapeJsonString(this.UserId.Id)}""," +
$@"""count"":{this.Count}," +
$@"""includePromotedContent"":false," +
$@"""withClientEventToken"":false," +
using System.Collections.Generic;
using System.Threading.Tasks;
using OpenTween.Connection;
+using OpenTween.Models;
namespace OpenTween.Api.GraphQL
{
private static readonly Uri EndpointUri = new("https://twitter.com/i/api/graphql/YlkSUg0mRBx7-EkxCvc-bw/UserTweetsAndReplies");
- public string UserId { get; set; }
+ public TwitterUserId UserId { get; set; }
public int Count { get; set; } = 20;
public TwitterGraphqlCursor? Cursor { get; set; }
- public UserTweetsAndRepliesRequest(string userId)
+ public UserTweetsAndRepliesRequest(TwitterUserId userId)
=> this.UserId = userId;
public Dictionary<string, string> CreateParameters()
return new()
{
["variables"] = "{" +
- $@"""userId"":""{JsonUtils.EscapeJsonString(this.UserId)}""," +
+ $@"""userId"":""{JsonUtils.EscapeJsonString(this.UserId.Id)}""," +
$@"""count"":{this.Count}," +
$@"""includePromotedContent"":true," +
$@"""withCommunity"":true," +
TwitterStatusId? replyToId,
IReadOnlyList<long>? mediaIds,
bool? autoPopulateReplyMetadata = null,
- IReadOnlyList<long>? excludeReplyUserIds = null,
+ IReadOnlyList<TwitterUserId>? excludeReplyUserIds = null,
string? attachmentUrl = null)
{
var param = new Dictionary<string, string>
.ConfigureAwait(false);
}
- public async Task<LazyJson<TwitterMessageEventSingle>> DirectMessagesEventsNew(long recipientId, string text, long? mediaId = null)
+ public async Task<LazyJson<TwitterMessageEventSingle>> DirectMessagesEventsNew(TwitterUserId recipientId, string text, long? mediaId = null)
{
var attachment = "";
if (mediaId != null)
"type": "message_create",
"message_create": {
"target": {
- "recipient_id": "{{JsonUtils.EscapeJsonString(recipientId.ToString())}}"
+ "recipient_id": "{{JsonUtils.EscapeJsonString(recipientId.Id)}}"
},
"message_data": {
"text": "{{JsonUtils.EscapeJsonString(text)}}"{{attachment}}
.ConfigureAwait(false);
}
- public async Task<TwitterUser[]> UsersLookup(IReadOnlyList<string> userIds)
+ public async Task<TwitterUser[]> UsersLookup(IReadOnlyList<TwitterUserId> userIds)
{
var request = new GetRequest
{
RequestUri = new("users/lookup.json", UriKind.Relative),
Query = new Dictionary<string, string>
{
- ["user_id"] = string.Join(",", userIds),
+ ["user_id"] = string.Join(",", userIds.Select(x => x.Id)),
["include_entities"] = "true",
["include_ext_alt_text"] = "true",
["tweet_mode"] = "extended",
return response.ReadAsLazyJson<TwitterFriendship>();
}
- public async Task<long[]> NoRetweetIds()
+ public async Task<TwitterUserId[]> NoRetweetIds()
{
var request = new GetRequest
{
using var response = await this.Connection.SendAsync(request)
.ConfigureAwait(false);
- return await response.ReadAsJson<long[]>()
+ var idsStr = await response.ReadAsJson<string[]>()
.ConfigureAwait(false);
+
+ return idsStr.Select(x => new TwitterUserId(x)).ToArray();
}
public async Task<TwitterIds> FollowersIds(long? cursor = null)
using var apiConnection = new TwitterApiConnection(new TwitterCredentialCookie(appToken), new());
twitterApi.Initialize(apiConnection);
var twitterUser = await twitterApi.AccountVerifyCredentials();
- newAccount.UserId = twitterUser.Id;
+ newAccount.UserId = twitterUser.IdStr;
newAccount.Username = twitterUser.ScreenName;
}
else
TwitterOAuth1ConsumerKey = appToken.OAuth1CustomConsumerKey?.Value ?? "",
TwitterOAuth1ConsumerSecret = appToken.OAuth1CustomConsumerSecret?.Value ?? "",
Username = accessTokenResponse["screen_name"],
- UserId = long.Parse(accessTokenResponse["user_id"]),
+ UserId = accessTokenResponse["user_id"],
Token = accessTokenResponse["oauth_token"],
TokenSecret = accessTokenResponse["oauth_token_secret"],
};
using System.Threading.Tasks;
using System.Xml.Serialization;
using OpenTween.Api.DataModel;
+using OpenTween.Models;
namespace OpenTween
{
public bool IsPublic = true;
public int SubscriberCount = 0; // 購読者数
public int MemberCount = 0; // リストメンバ数
- public long UserId = 0;
+ public PersonId UserId = null!;
public string Username = "";
public string Nickname = "";
this.Slug = listElementData.Slug;
this.Nickname = listElementData.User.Name.Trim();
this.Username = listElementData.User.ScreenName;
- this.UserId = listElementData.User.Id;
+ this.UserId = new TwitterUserId(listElementData.User.IdStr);
this.tw = tw;
}
--- /dev/null
+// OpenTween - Client of Twitter
+// Copyright (c) 2024 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.
+
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace OpenTween.Models
+{
+ [DebuggerDisplay("{IdType}:{Id}")]
+ public abstract class PersonId
+ : IEquatable<PersonId>, IComparable<PersonId>
+ {
+ public abstract string IdType { get; }
+
+ public abstract string Id { get; }
+
+ public virtual int CompareTo(PersonId other)
+ {
+ var compareByIdType = this.IdType.CompareTo(other.IdType);
+ if (compareByIdType != 0)
+ return compareByIdType;
+
+ // 辞書順による比較のみだと "20" > "100" となってしまうため文字数による比較も加える
+ var compareByIdLength = this.Id.Length.CompareTo(other.Id.Length);
+ if (compareByIdLength != 0)
+ return compareByIdLength;
+
+ return this.Id.CompareTo(other.Id);
+ }
+
+ public virtual bool Equals(PersonId other)
+ => this.IdType == other.IdType && this.Id == other.Id;
+
+ public override bool Equals(object obj)
+ => obj is PersonId otherId && this.Equals(otherId);
+
+ public override int GetHashCode()
+ => this.IdType.GetHashCode() ^ this.Id.GetHashCode();
+
+ public override string ToString()
+ => this.Id;
+
+ public static bool operator ==(PersonId? left, PersonId? right)
+ => EqualityComparer<PersonId?>.Default.Equals(left, right);
+
+ public static bool operator !=(PersonId? left, PersonId? right)
+ => !EqualityComparer<PersonId?>.Default.Equals(left, right);
+
+ public static bool operator <(PersonId left, PersonId right)
+ => Comparer<PersonId>.Default.Compare(left, right) < 0;
+
+ public static bool operator <=(PersonId left, PersonId right)
+ => Comparer<PersonId>.Default.Compare(left, right) <= 0;
+
+ public static bool operator >=(PersonId left, PersonId right)
+ => Comparer<PersonId>.Default.Compare(left, right) >= 0;
+
+ public static bool operator >(PersonId left, PersonId right)
+ => Comparer<PersonId>.Default.Compare(left, right) > 0;
+ }
+}
public Uri? SourceUri { get; init; }
- public List<(long UserId, string ScreenName)> ReplyToList { get; init; } = new();
+ public List<(PersonId UserId, string ScreenName)> ReplyToList { get; init; } = new();
public bool IsMe { get; init; }
public bool IsDm { get; init; }
- public long UserId { get; init; }
+ public PersonId UserId { get; init; } = null!;
public string? RetweetedBy { get; init; }
public PostId? RetweetedId { get; init; }
- public long? RetweetedByUserId { get; init; }
+ public PersonId? RetweetedByUserId { get; init; }
- public long? InReplyToUserId { get; init; }
+ public PersonId? InReplyToUserId { get; init; }
public List<MediaInfo> Media { get; init; } = new();
/// </summary>
/// <param name="selfUserId">ツイートを削除しようとするユーザーのID</param>
/// <returns>削除可能であれば true、そうでなければ false</returns>
- public bool CanDeleteBy(long selfUserId)
+ public bool CanDeleteBy(PersonId selfUserId)
{
// 自分が送った DM と自分に届いた DM のどちらも削除可能
if (this.IsDm)
/// </summary>
/// <param name="selfUserId">リツイートしようとするユーザーのID</param>
/// <returns>リツイート可能であれば true、そうでなければ false</returns>
- public bool CanRetweetBy(long selfUserId)
+ public bool CanRetweetBy(PersonId selfUserId)
{
// DM は常にリツイート不可
if (this.IsDm)
public Stack<TabModel> RemovedTab { get; } = new();
- public ISet<long> BlockIds { get; set; } = new HashSet<long>();
+ public ISet<PersonId> BlockIds { get; set; } = new HashSet<PersonId>();
- public ISet<long> MuteUserIds { get; set; } = new HashSet<long>();
+ public ISet<PersonId> MuteUserIds { get; set; } = new HashSet<PersonId>();
// 発言の追加
// AddPost(複数回) -> DistributePosts -> SubmitUpdate
MyCommon.TabUsageType.UserTimeline
=> new UserTimelineTabModel(tabName, tabSetting.User!)
{
- UserId = tabSetting.UserId,
+ UserId = tabSetting.UserId is { } userId ? new TwitterUserId(userId) : null,
},
MyCommon.TabUsageType.PublicSearch
=> new PublicSearchTabModel(tabName)
if (this.MuteUserIds.Contains(post.UserId))
return true;
- if (post.RetweetedByUserId != null && this.MuteUserIds.Contains(post.RetweetedByUserId.Value))
+ if (post.RetweetedByUserId != null && this.MuteUserIds.Contains(post.RetweetedByUserId))
return true;
return false;
}
}
- public void RefreshOwl(ISet<long> follower)
+ public void RefreshOwl(ISet<PersonId> follower)
{
lock (this.lockObj)
{
public PostClass CreateFromStatus(
TwitterStatus status,
- long selfUserId,
- ISet<long> followerIds,
+ TwitterUserId selfUserId,
+ ISet<PersonId> followerIds,
bool firstLoad,
bool favTweet = false
)
{
var statusUser = status.User ?? TwitterUser.CreateUnknownUser();
+ var statusUserId = new TwitterUserId(statusUser.IdStr);
// リツイートでない場合は null
var retweetedStatus = (TwitterStatus?)null;
// リツイートであるか否かに関わらず常にオリジナルのツイート及びユーザーを指す
var originalStatus = retweetedStatus ?? status;
var originalStatusUser = originalStatus.User ?? TwitterUser.CreateUnknownUser();
+ var originalStatusUserId = new TwitterUserId(originalStatusUser.IdStr);
- var isMe = statusUser.Id == selfUserId;
+ var isMe = statusUserId == selfUserId;
bool isFav = favTweet;
if (isFav == false)
var isOwl = false;
if (!isMe && followerIds.Count > 0)
- isOwl = !followerIds.Contains(originalStatusUser.Id);
+ isOwl = !followerIds.Contains(originalStatusUserId);
var createdAtForSorting = ParseDateTimeFromSnowflakeId(status.Id, status.CreatedAt);
var createdAt = retweetedStatus != null
IsReply = retweetedStatus == null && replyToList.Any(x => x.UserId == selfUserId),
InReplyToStatusId = originalStatus.InReplyToStatusIdStr != null ? new TwitterStatusId(originalStatus.InReplyToStatusIdStr) : null,
InReplyToUser = originalStatus.InReplyToScreenName,
- InReplyToUserId = originalStatus.InReplyToUserId,
+ InReplyToUserId = originalStatus.InReplyToUserIdStr is { } inReplyToUserId ? new TwitterUserId(inReplyToUserId) : null,
// originalStatusUser から生成
- UserId = originalStatusUser.Id,
+ UserId = originalStatusUserId,
ScreenName = screenName,
Nickname = nickname,
ImageUrl = imageUrl,
// retweeterUser から生成
RetweetedBy = retweeterUser != null ? string.Intern(retweeterUser.ScreenName) : null,
- RetweetedByUserId = retweeterUser?.Id,
+ RetweetedByUserId = retweeterUser?.IdStr is { } retweetedByUserId ? new TwitterUserId(retweetedByUserId) : null,
IsRead = this.DetermineUnreadState(isMe, firstLoad),
};
public PostClass CreateFromDirectMessageEvent(
TwitterMessageEvent eventItem,
- IReadOnlyDictionary<string, TwitterUser> users,
+ IReadOnlyDictionary<TwitterUserId, TwitterUser> users,
IReadOnlyDictionary<string, TwitterMessageEventList.App> apps,
- long selfUserId,
+ TwitterUserId selfUserId,
bool firstLoad
)
{
.ToArray();
// 以下、ユーザー情報
- var senderIsMe = eventItem.MessageCreate.SenderId == selfUserId.ToString(CultureInfo.InvariantCulture);
+ var senderId = new TwitterUserId(eventItem.MessageCreate.SenderId);
+ var senderIsMe = senderId == selfUserId;
var displayUserId = senderIsMe
- ? eventItem.MessageCreate.Target.RecipientId
- : eventItem.MessageCreate.SenderId;
+ ? new TwitterUserId(eventItem.MessageCreate.Target.RecipientId)
+ : senderId;
if (!users.TryGetValue(displayUserId, out var displayUser))
displayUser = TwitterUser.CreateUnknownUser();
SourceUri = sourceUri,
// displayUser から生成
- UserId = displayUser.Id,
+ UserId = new TwitterUserId(displayUser.IdStr),
ScreenName = screenName,
Nickname = nickname,
ImageUrl = imageUrl,
return text;
}
- private (List<(long UserId, string ScreenName)> ReplyToList, List<MediaInfo> Media) ExtractEntities(TwitterEntities? entities)
+ private (List<(PersonId UserId, string ScreenName)> ReplyToList, List<MediaInfo> Media) ExtractEntities(TwitterEntities? entities)
{
- var atList = new List<(long UserId, string ScreenName)>();
+ var atList = new List<(PersonId UserId, string ScreenName)>();
var media = new List<MediaInfo>();
if (entities == null)
if (entities.UserMentions != null)
{
foreach (var ent in entities.UserMentions)
- atList.Add((ent.Id, ent.ScreenName));
+ atList.Add((new TwitterUserId(ent.IdStr), ent.ScreenName));
}
if (entities.Media != null)
--- /dev/null
+// OpenTween - Client of Twitter
+// Copyright (c) 2024 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.
+
+#nullable enable
+
+namespace OpenTween.Models
+{
+ public class TwitterUserId : PersonId
+ {
+ public override string IdType => "twitter_user";
+
+ public override string Id { get; }
+
+ public TwitterUserId(string id)
+ => this.Id = id;
+
+ public TwitterUserId(long id)
+ => this.Id = id.ToString();
+ }
+}
public string ScreenName { get; }
- public string? UserId { get; set; }
+ public PersonId? UserId { get; set; }
public UserTimelineTabModel(string tabName, string screenName)
: base(tabName)
public bool AutoPopulateReplyMetadata { get; set; }
- public IReadOnlyList<long> ExcludeReplyUserIds { get; set; } = Array.Empty<long>();
+ public IReadOnlyList<PersonId> ExcludeReplyUserIds { get; set; } = Array.Empty<PersonId>();
public string? AttachmentUrl { get; set; }
}
}
else
{
- settingCommon.UserId = 0;
+ settingCommon.UserId = "";
settingCommon.UserName = "";
settingCommon.Token = "";
settingCommon.TokenSecret = "";
return password;
}
- public long UserId = 0;
+ public string UserId = "";
public List<string> TabList = new();
public int TimelinePeriod = 180;
public int ReplyPeriod = 180;
this.SelectedAccountKey = selectedAccount?.UniqueKey;
- if (selectedAccount?.UserId == 0)
+ if (selectedAccount != null && MyCommon.IsNullOrEmpty(selectedAccount.UserId))
selectedAccount.UserId = this.UserId;
if (MyCommon.IsNullOrEmpty(this.Token))
public Guid UniqueKey { get; set; } = Guid.NewGuid();
public string Username = "";
- public long UserId = 0;
+ public string UserId = "";
public APIAuthType TwitterAuthType { get; set; } = APIAuthType.OAuth1;
using System;
using OpenTween.Connection;
+using OpenTween.Models;
namespace OpenTween.SocialProtocol
{
public Guid UniqueKey { get; }
- public long UserId { get; }
+ public PersonId UserId { get; }
public string UserName { get; }
using System;
using System.Diagnostics;
using OpenTween.Connection;
+using OpenTween.Models;
namespace OpenTween.SocialProtocol.Twitter
{
public OpenTween.Twitter Legacy
=> this.twLegacy;
- public long UserId
+ public PersonId UserId
=> this.AccountState.UserId;
public string UserName
Debug.Assert(accountSettings.UniqueKey == this.UniqueKey, "UniqueKey must be same as current value.");
var credential = accountSettings.GetTwitterCredential();
+ var userId = new TwitterUserId(accountSettings.UserId);
- this.AccountState = new TwitterAccountState(accountSettings.UserId, accountSettings.Username)
+ this.AccountState = new TwitterAccountState(userId, accountSettings.Username)
{
HasUnrecoverableError = credential is TwitterCredentialNone,
};
using System.Collections.Generic;
using OpenTween.Api.DataModel;
+using OpenTween.Models;
namespace OpenTween.SocialProtocol.Twitter
{
public class TwitterAccountState
{
- public long UserId { get; private set; }
+ public TwitterUserId UserId { get; private set; }
public string UserName { get; private set; }
public int? StatusesCount { get; private set; }
- public ISet<long> FollowerIds { get; set; } = new HashSet<long>();
+ public ISet<PersonId> FollowerIds { get; set; } = new HashSet<PersonId>();
- public ISet<long> NoRetweetUserIds { get; set; } = new HashSet<long>();
+ public ISet<TwitterUserId> NoRetweetUserIds { get; set; } = new HashSet<TwitterUserId>();
public bool HasUnrecoverableError { get; set; } = true;
public TwitterAccountState()
- : this(0L, "")
+ : this(new("0"), "")
{
}
- public TwitterAccountState(long userId, string userName)
+ public TwitterAccountState(TwitterUserId userId, string userName)
{
this.UserId = userId;
this.UserName = userName;
/// <summary>ユーザー情報を更新します</summary>
public void UpdateFromUser(TwitterUser self)
{
- this.UserId = self.Id;
+ this.UserId = new(self.IdStr);
this.UserName = self.ScreenName;
this.FollowersCount = self.FollowersCount;
this.FriendsCount = self.FriendsCount;
private async void SettingStripMenuItem_Click(object sender, EventArgs e)
{
// 設定画面表示前のユーザー情報
- var previousUserId = this.settings.Common.UserId;
+ var previousAccountId = this.settings.Common.SelectedAccountKey;
var oldIconCol = this.Use2ColumnsMode;
if (this.ShowSettingDialog() == DialogResult.OK)
this.TopMost = this.settings.Common.AlwaysTop;
this.SaveConfigsAll(false);
- if (this.PrimaryAccount.UserId != previousUserId)
+ if (this.PrimaryAccount.UniqueKey != previousAccountId)
await this.DoGetFollowersMenu();
}
/// <summary>
/// 投稿時に auto_populate_reply_metadata オプションによって自動で追加されるメンションを除去します
/// </summary>
- private string RemoveAutoPopuratedMentions(string statusText, out long[] autoPopulatedUserIds)
+ private string RemoveAutoPopuratedMentions(string statusText, out PersonId[] autoPopulatedUserIds)
{
- var autoPopulatedUserIdList = new List<long>();
+ var autoPopulatedUserIdList = new List<PersonId>();
var replyToPost = this.inReplyTo != null ? this.statuses[this.inReplyTo.Value.StatusId] : null;
if (replyToPost != null)
/// <summary>
/// <see cref="FormatStatusText"/> に加えて、拡張モードで140字にカウントされない文字列の除去を行います
/// </summary>
- private string FormatStatusTextExtended(string statusText, out long[] autoPopulatedUserIds, out string? attachmentUrl)
+ private string FormatStatusTextExtended(string statusText, out PersonId[] autoPopulatedUserIds, out string? attachmentUrl)
{
statusText = this.RemoveAutoPopuratedMentions(statusText, out autoPopulatedUserIds);
break;
case UserTimelineTabModel userTab:
tabSetting.User = userTab.ScreenName;
- tabSetting.UserId = userTab.UserId;
+ tabSetting.UserId = userTab.UserId?.Id;
break;
case PublicSearchTabModel searchTab:
tabSetting.SearchWords = searchTab.SearchWords;
{
TweetText = param.Text,
InReplyToTweetId = param.InReplyToStatusId?.ToTwitterStatusId(),
- ExcludeReplyUserIds = param.ExcludeReplyUserIds.Select(x => x.ToString()).ToArray(),
+ ExcludeReplyUserIds = param.ExcludeReplyUserIds.OfType<TwitterUserId>().ToArray(),
MediaIds = param.MediaIds.Select(x => x.ToString()).ToArray(),
AttachmentUrl = param.AttachmentUrl,
};
param.InReplyToStatusId?.ToTwitterStatusId(),
param.MediaIds,
param.AutoPopulateReplyMetadata,
- param.ExcludeReplyUserIds,
+ param.ExcludeReplyUserIds.OfType<TwitterUserId>().ToArray(),
param.AttachmentUrl
)
.ConfigureAwait(false);
var recipient = await this.GetUserInfo(recipientName)
.ConfigureAwait(false);
- using var response = await this.Api.DirectMessagesEventsNew(recipient.Id, body, mediaId)
+ var recipientUserId = new TwitterUserId(recipient.IdStr);
+ using var response = await this.Api.DirectMessagesEventsNew(recipientUserId, body, mediaId)
.ConfigureAwait(false);
var messageEventSingle = await response.LoadJsonAsync()
public string Username
=> this.AccountState.UserName;
- public long UserId
+ public TwitterUserId UserId
=> this.AccountState.UserId;
public bool RestrictFavCheck { get; set; }
TwitterStatus[] statuses;
if (this.Api.AuthType == APIAuthType.TwitterComCookie)
{
- var userId = tab.UserId;
- if (MyCommon.IsNullOrEmpty(userId))
+ var userId = tab.UserId as TwitterUserId;
+ if (userId == null)
{
var user = await this.GetUserInfo(tab.ScreenName)
.ConfigureAwait(false);
- userId = user.IdStr;
- tab.UserId = user.IdStr;
+ userId = new TwitterUserId(user.IdStr);
+ tab.UserId = userId;
}
var cursor = more ? tab.CursorBottom : tab.CursorTop;
.ConfigureAwait(false);
statuses = response.ToTwitterStatuses()
- .Where(x => x.User.IdStr == userId) // リプライツリーに含まれる他ユーザーのツイートを除外
+ .Where(x => x.User.IdStr == userId.Id) // リプライツリーに含まれる他ユーザーのツイートを除外
.ToArray();
tab.CursorBottom = response.CursorBottom;
}
internal PostClass[] FilterNoRetweetUserPosts(PostClass[] posts)
- => posts.Where(x => x.RetweetedByUserId == null || !this.AccountState.NoRetweetUserIds.Contains(x.RetweetedByUserId.Value)).ToArray();
+ => posts.Where(x => x.RetweetedByUserId == null || !this.AccountState.NoRetweetUserIds.Contains(x.RetweetedByUserId)).ToArray();
public async Task GetListStatus(ListTimelineTabModel tab, bool more, bool firstLoad)
{
return Array.Empty<PostClass>();
var userIds = Enumerable.Concat(
- events.Select(x => x.MessageCreate.SenderId),
- events.Select(x => x.MessageCreate.Target.RecipientId)
+ events.Select(x => new TwitterUserId(x.MessageCreate.SenderId)),
+ events.Select(x => new TwitterUserId(x.MessageCreate.Target.RecipientId))
).Distinct().ToArray();
var users = (await this.Api.UsersLookup(userIds).ConfigureAwait(false))
- .ToDictionary(x => x.IdStr);
+ .ToDictionary(x => new TwitterUserId(x.IdStr));
var apps = eventList.Apps ?? new Dictionary<string, TwitterMessageEventList.App>();
private PostClass[] CreateDirectMessagesEventFromJson(
IReadOnlyCollection<TwitterMessageEvent> events,
- IReadOnlyDictionary<string, TwitterUser> users,
+ IReadOnlyDictionary<TwitterUserId, TwitterUser> users,
IReadOnlyDictionary<string, TwitterMessageEventList.App> apps,
bool firstLoad)
{
var cursor = backward ? tab.CursorBottom : tab.CursorTop;
var request = new LikesRequest
{
- UserId = this.UserId.ToString(CultureInfo.InvariantCulture),
+ UserId = this.UserId,
Count = count,
Cursor = cursor?.As<TwitterGraphqlCursor>(),
};
if (MyCommon.EndingFlag) return;
var cursor = -1L;
- var newFollowerIds = Enumerable.Empty<long>();
+ var newFollowerIds = Enumerable.Empty<PersonId>();
do
{
var ret = await this.Api.FollowersIds(cursor)
if (ret.Ids == null)
throw new WebApiException("ret.ids == null");
- newFollowerIds = newFollowerIds.Concat(ret.Ids);
+ newFollowerIds = newFollowerIds.Concat(ret.Ids.Select(x => new TwitterUserId(x)));
cursor = ret.NextCursor;
}
while (cursor != 0);
var noRetweetUserIds = await this.Api.NoRetweetIds()
.ConfigureAwait(false);
- this.AccountState.NoRetweetUserIds = new HashSet<long>(noRetweetUserIds);
+ this.AccountState.NoRetweetUserIds = new HashSet<TwitterUserId>(noRetweetUserIds);
this.GetNoRetweetSuccess = true;
}
if (MyCommon.EndingFlag) return;
var cursor = -1L;
- var newBlockIds = Enumerable.Empty<long>();
+ var newBlockIds = Enumerable.Empty<PersonId>();
do
{
var ret = await this.Api.BlocksIds(cursor)
.ConfigureAwait(false);
- newBlockIds = newBlockIds.Concat(ret.Ids);
+ newBlockIds = newBlockIds.Concat(ret.Ids.Select(x => new TwitterUserId(x)));
cursor = ret.NextCursor;
}
while (cursor != 0);
var ids = await TwitterIds.GetAllItemsAsync(x => this.Api.MutesUsersIds(x))
.ConfigureAwait(false);
- TabInformations.GetInstance().MuteUserIds = ids.ToHashSet();
+ TabInformations.GetInstance().MuteUserIds = ids.ToHashSet<PersonId>();
}
public string[] GetHashList()
this.LinkLabelTweet.Tag = profileUrl;
this.ToolTip1.SetToolTip(this.LinkLabelTweet, profileUrl);
- if (this.twitter.UserId == user.Id)
+ var userId = new TwitterUserId(user.IdStr);
+ if (this.twitter.UserId == userId)
{
this.ButtonEdit.Enabled = true;
this.ChangeIconToolStripMenuItem.Enabled = true;
private async void ButtonEdit_Click(object sender, EventArgs e)
{
+ var displayUserId = new TwitterUserId(this.displayUser.IdStr);
// 自分以外のプロフィールは変更できない
- if (this.twitter.UserId != this.displayUser.Id)
+ if (this.twitter.UserId != displayUserId)
return;
using (ControlTransaction.Disabled(this.ButtonEdit))