Skip to content

Commit

Permalink
Follows and Followers (#211)
Browse files Browse the repository at this point in the history
* Implemented Twitter API v2 User Followers and Following.

* Updated assembly versions.
  • Loading branch information
JoeMayo authored Dec 21, 2020
1 parent 990f7d9 commit 1dc10ac
Show file tree
Hide file tree
Showing 7 changed files with 279 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ internal static async Task RunAsync(TwitterContext twitterCtx)
Console.WriteLine("\n\tReport spammer...\n");
await ReportSpammerAsync(twitterCtx);
break;
case '7':
Console.WriteLine("\n\tFinding followers...\n");
await FindFollowersAsync(twitterCtx);
break;
case '8':
Console.WriteLine("\n\tFinding following...\n");
await FindFollowingAsync(twitterCtx);
break;
case 'q':
case 'Q':
Console.WriteLine("\nReturning...\n");
Expand All @@ -72,6 +80,8 @@ static void ShowMenu()
Console.WriteLine("\t 4. Account Contributors");
Console.WriteLine("\t 5. Get Profile Banner Sizes");
Console.WriteLine("\t 6. Report Spammer");
Console.WriteLine("\t 7. Find Followers");
Console.WriteLine("\t 8. Find Following");
Console.WriteLine();
Console.Write("\t Q. Return to Main menu");
}
Expand Down Expand Up @@ -197,5 +207,39 @@ static async Task ReportSpammerAsync(TwitterContext twitterCtx)

Console.WriteLine("You just reported {0} as a spammer.", spammer?.ScreenNameResponse);
}

async static Task FindFollowersAsync(TwitterContext twitterCtx)
{
string userID = "15411837";

TwitterUserQuery? userResponse =
await
(from user in twitterCtx.TwitterUser
where user.Type == UserType.Followers &&
user.ID == userID
select user)
.SingleOrDefaultAsync();

if (userResponse != null)
userResponse.Users?.ForEach(user =>
Console.WriteLine("Name: " + user.Username));
}

async static Task FindFollowingAsync(TwitterContext twitterCtx)
{
string userID = "15411837";

TwitterUserQuery? userResponse =
await
(from user in twitterCtx.TwitterUser
where user.Type == UserType.Following &&
user.ID == userID
select user)
.SingleOrDefaultAsync();

if (userResponse != null)
userResponse.Users?.ForEach(user =>
Console.WriteLine("ID: " + user.ID));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<Version>6.0.1</Version>
<Version>6.2.0</Version>
<Authors>Joe Mayo</Authors>
<Company>Joe Mayo</Company>
<Product>LINQ to Twitter for ASP.NET</Product>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,12 @@ public void GetParametersTest()
Expression<Func<TwitterUserQuery, bool>> expression =
tweet =>
tweet.Type == UserType.IdLookup &&
tweet.ID == "456" &&
tweet.Ids == "2,3" &&
tweet.Usernames == "joemayo,linq2twitr" &&
tweet.Expansions == "attachments.poll_ids,author_id" &&
tweet.MaxResults == 50 &&
tweet.PaginationToken == "123" &&
tweet.Expansions == "attachments.poll_ids,author_id" &&
tweet.TweetFields == "author_id,created_at" &&
tweet.UserFields == "created_at,verified";

Expand All @@ -42,7 +45,10 @@ public void GetParametersTest()
Assert.IsTrue(
queryParams.Contains(
new KeyValuePair<string, string>(nameof(TwitterUserQuery.Type), ((int)UserType.IdLookup).ToString(CultureInfo.InvariantCulture))));
Assert.IsTrue(
Assert.IsTrue(
queryParams.Contains(
new KeyValuePair<string, string>(nameof(TwitterUserQuery.ID), "456")));
Assert.IsTrue(
queryParams.Contains(
new KeyValuePair<string, string>(nameof(TwitterUserQuery.Ids), "2,3")));
Assert.IsTrue(
Expand All @@ -51,7 +57,13 @@ public void GetParametersTest()
Assert.IsTrue(
queryParams.Contains(
new KeyValuePair<string, string>(nameof(TwitterUserQuery.Expansions), "attachments.poll_ids,author_id")));
Assert.IsTrue(
Assert.IsTrue(
queryParams.Contains(
new KeyValuePair<string, string>(nameof(TwitterUserQuery.MaxResults), "50")));
Assert.IsTrue(
queryParams.Contains(
new KeyValuePair<string, string>(nameof(TwitterUserQuery.PaginationToken), "123")));
Assert.IsTrue(
queryParams.Contains(
new KeyValuePair<string, string>(nameof(TwitterUserQuery.TweetFields), "author_id,created_at")));
Assert.IsTrue(
Expand All @@ -60,7 +72,7 @@ public void GetParametersTest()
}

[TestMethod]
public void BuildUrl_WithIds_IncludesParameters()
public void BuildUrl_ForIdLookup_IncludesParameters()
{
const string ExpectedUrl =
BaseUrl2 + "users?" +
Expand All @@ -84,6 +96,62 @@ public void BuildUrl_WithIds_IncludesParameters()
Assert.AreEqual(ExpectedUrl, req.FullUrl);
}

[TestMethod]
public void BuildUrl_ForFollowing_IncludesParameters()
{
const string ExpectedUrl =
BaseUrl2 + "users/123/following?" +
"max_results=50&" +
"pagination_token=456&" +
"expansions=attachments.poll_ids%2Cauthor_id&" +
"tweet.fields=author_id%2Ccreated_at&" +
"user.fields=created_at%2Cverified";
var twitterUserReqProc = new TwitterUserRequestProcessor<TwitterUserQuery> { BaseUrl = BaseUrl2 };
var parameters =
new Dictionary<string, string>
{
{ nameof(TwitterUserQuery.Type), UserType.Following.ToString() },
{ nameof(TwitterUserQuery.ID), "123" },
{ nameof(TwitterUserQuery.Expansions), "attachments.poll_ids,author_id" },
{ nameof(TwitterUserQuery.MaxResults), "50" },
{ nameof(TwitterUserQuery.PaginationToken), "456" },
{ nameof(TwitterUserQuery.TweetFields), "author_id,created_at" },
{ nameof(TwitterUserQuery.UserFields), "created_at,verified" },
};

Request req = twitterUserReqProc.BuildUrl(parameters);

Assert.AreEqual(ExpectedUrl, req.FullUrl);
}

[TestMethod]
public void BuildUrl_ForFollowers_IncludesParameters()
{
const string ExpectedUrl =
BaseUrl2 + "users/123/followers?" +
"max_results=50&" +
"pagination_token=456&" +
"expansions=attachments.poll_ids%2Cauthor_id&" +
"tweet.fields=author_id%2Ccreated_at&" +
"user.fields=created_at%2Cverified";
var twitterUserReqProc = new TwitterUserRequestProcessor<TwitterUserQuery> { BaseUrl = BaseUrl2 };
var parameters =
new Dictionary<string, string>
{
{ nameof(TwitterUserQuery.Type), UserType.Followers.ToString() },
{ nameof(TwitterUserQuery.ID), "123" },
{ nameof(TwitterUserQuery.Expansions), "attachments.poll_ids,author_id" },
{ nameof(TwitterUserQuery.MaxResults), "50" },
{ nameof(TwitterUserQuery.PaginationToken), "456" },
{ nameof(TwitterUserQuery.TweetFields), "author_id,created_at" },
{ nameof(TwitterUserQuery.UserFields), "created_at,verified" },
};

Request req = twitterUserReqProc.BuildUrl(parameters);

Assert.AreEqual(ExpectedUrl, req.FullUrl);
}

[TestMethod]
public void BuildUrl_WithUsernames_IncludesParameters()
{
Expand Down Expand Up @@ -163,6 +231,42 @@ public void BuildUrl_WithoutIdsOnIdLookup_Throws()
Assert.AreEqual(nameof(TwitterUserQuery.Ids), ex.ParamName);
}

[TestMethod]
public void BuildUrl_WithoutIDOnFollowers_Throws()
{
var twitterUserReqProc = new TwitterUserRequestProcessor<TwitterUserQuery> { BaseUrl = BaseUrl2 };
var parameters =
new Dictionary<string, string>
{
{ nameof(TwitterUserQuery.Type), UserType.Followers.ToString() },
//{ nameof(TwitterUserQuery.ID), null }
};

ArgumentException ex =
L2TAssert.Throws<ArgumentException>(() =>
twitterUserReqProc.BuildUrl(parameters));

Assert.AreEqual(nameof(TwitterUserQuery.ID), ex.ParamName);
}

[TestMethod]
public void BuildUrl_WithoutIDOnFollowing_Throws()
{
var twitterUserReqProc = new TwitterUserRequestProcessor<TwitterUserQuery> { BaseUrl = BaseUrl2 };
var parameters =
new Dictionary<string, string>
{
{ nameof(TwitterUserQuery.Type), UserType.Following.ToString() },
//{ nameof(TwitterUserQuery.ID), null }
};

ArgumentException ex =
L2TAssert.Throws<ArgumentException>(() =>
twitterUserReqProc.BuildUrl(parameters));

Assert.AreEqual(nameof(TwitterUserQuery.ID), ex.ParamName);
}

[TestMethod]
public void BuildUrl_WithoutUsernamesOnUsernameLookup_Throws()
{
Expand Down Expand Up @@ -288,9 +392,12 @@ public void ProcessResults_Populates_Input_Parameters()
{
BaseUrl = BaseUrl2,
Type = UserType.IdLookup,
ID = "890",
Ids = "3,7",
Usernames = "9,0",
Expansions = "123",
MaxResults = 50,
PaginationToken = "567",
TweetFields = "678",
UserFields = "234"
};
Expand All @@ -302,9 +409,12 @@ public void ProcessResults_Populates_Input_Parameters()
var twitterUserQuery = results.Single();
Assert.IsNotNull(twitterUserQuery);
Assert.AreEqual(UserType.IdLookup, twitterUserQuery.Type);
Assert.AreEqual("890", twitterUserQuery.ID);
Assert.AreEqual("3,7", twitterUserQuery.Ids);
Assert.AreEqual("9,0", twitterUserQuery.Usernames);
Assert.AreEqual("123", twitterUserQuery.Expansions);
Assert.AreEqual(50, twitterUserQuery.MaxResults);
Assert.AreEqual("567", twitterUserQuery.PaginationToken);
Assert.AreEqual("678", twitterUserQuery.TweetFields);
Assert.AreEqual("234", twitterUserQuery.UserFields);
}
Expand Down
2 changes: 1 addition & 1 deletion src/LinqToTwitter6/LinqToTwitter/LinqToTwitter.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFramework>net5.0</TargetFramework>
<Nullable>enable</Nullable>
<Description>LINQ to Twitter is a 3rd party LINQ Provider that lets you tweet and query with the Twitter API.</Description>
<Version>6.1.0</Version>
<Version>6.2.0</Version>
<Authors>Joe Mayo</Authors>
<Company>Joe Mayo</Company>
<PackageId>linqtotwitter</PackageId>
Expand Down
15 changes: 15 additions & 0 deletions src/LinqToTwitter6/LinqToTwitter/User/TwitterUserQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,21 @@ public record TwitterUserQuery
/// </summary>
public string? Expansions { get; init; }

/// <summary>
/// User ID for following/follower queries
/// </summary>
public string? ID { get; init; }

/// <summary>
/// Max number of tweets to return per requrest - default 100 - possible 1000
/// </summary>
public int MaxResults { get; init; }

/// <summary>
/// If set, with token from previous response metadata, pages forward or backward
/// </summary>
public string? PaginationToken { get; init; }

/// <summary>
/// Comma-separated list of fields to return in the Tweet object
/// </summary>
Expand Down
Loading

0 comments on commit 1dc10ac

Please sign in to comment.