Skip to content

Commit

Permalink
Adds TwitterBot (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
mwdavis84 authored Jul 30, 2021
1 parent 4a24696 commit a4349eb
Show file tree
Hide file tree
Showing 62 changed files with 3,147 additions and 6 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ For more information on GroupMe's bot framework, see https://dev.groupme.com/tut

# Examples Included
- [DinoBot](dotnet/DinoBot/README.md), a C# based bot that responds to messages with dinosaur emoji
- [TwitterBot](dotnet/TwitterBot/README.md), a C# based bot that periodically searches Twitter for specific terms and posts them to your groups
4 changes: 2 additions & 2 deletions dotnet/DinoBot/DinoBot.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ VisualStudioVersion = 16.0.30503.244
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DinoBot", "DinoBot\DinoBot.csproj", "{78C1A8E2-9C41-4DD9-A272-6D75A7968704}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GroupMeShared", "GroupMeShared\GroupMeShared.csproj", "{6AEE518E-62E1-4AA2-A7A4-9308966115F9}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GroupMeShared", "..\GroupMeShared\GroupMeShared.csproj", "{6AEE518E-62E1-4AA2-A7A4-9308966115F9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GroupMeBots", "GroupMeBots\GroupMeBots.csproj", "{1914515D-F3A7-4A5A-B1D3-891470021979}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GroupMeBots", "..\GroupMeBots\GroupMeBots.csproj", "{1914515D-F3A7-4A5A-B1D3-891470021979}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{97CBD3DC-875D-4F24-882C-5BC268A670DA}"
ProjectSection(SolutionItems) = preProject
Expand Down
4 changes: 2 additions & 2 deletions dotnet/DinoBot/DinoBot/DinoBot.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GroupMeBots\GroupMeBots.csproj" />
<ProjectReference Include="..\GroupMeShared\GroupMeShared.csproj" />
<ProjectReference Include="..\..\GroupMeBots\GroupMeBots.csproj" />
<ProjectReference Include="..\..\GroupMeShared\GroupMeShared.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
Expand Down
4 changes: 2 additions & 2 deletions dotnet/DinoBot/DinoTests/DinoTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@
<Project>{78C1A8E2-9C41-4DD9-A272-6D75A7968704}</Project>
<Name>DinoBot</Name>
</ProjectReference>
<ProjectReference Include="..\GroupMeBots\GroupMeBots.csproj">
<ProjectReference Include="..\..\GroupMeBots\GroupMeBots.csproj">
<Project>{1914515D-F3A7-4A5A-B1D3-891470021979}</Project>
<Name>GroupMeBots</Name>
</ProjectReference>
<ProjectReference Include="..\GroupMeShared\GroupMeShared.csproj">
<ProjectReference Include="..\..\GroupMeShared\GroupMeShared.csproj">
<Project>{6AEE518E-62E1-4AA2-A7A4-9308966115F9}</Project>
<Name>GroupMeShared</Name>
</ProjectReference>
Expand Down
File renamed without changes.
51 changes: 51 additions & 0 deletions dotnet/GroupMeBots/Model/CreateBotRequest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Runtime.Serialization;

namespace GroupMeBot.Model
{
/// <summary>
/// Request object for creating a new GroupMe bot
/// </summary>
[DataContract]
public class CreateBotRequest
{
/// <summary>
/// Gets or sets the bot data
/// </summary>
[DataMember(Name = "bot")]
public BotData Bot { get; set; }

/// <summary>
/// Data about the bot to create
/// </summary>
[DataContract]
public class BotData
{
/// <summary>
/// Gets or sets the name of the bot. Displayed on all messages the bot posts.
/// </summary>
[DataMember(Name = "name")]
public string Name { get; set; }

/// <summary>
/// Gets or sets the ID of the group in which to create the bot
/// </summary>
[DataMember(Name = "group_id")]
public string GroupId { get; set; }

/// <summary>
/// Gets or sets the URL that should be called every time a message is posted to the group
/// </summary>
[DataMember(Name = "callback_url", EmitDefaultValue = false)]
public string CallbackUrl { get; set; }

/// <summary>
/// Gets or sets the URL of the bot's avatar. Dispalyed on every message posted by the bot.
/// </summary>
[DataMember(Name = "avatar_url", EmitDefaultValue = false)]
public string AvatarUrl { get; set; }
}
}
}
60 changes: 60 additions & 0 deletions dotnet/GroupMeBots/Model/CreateBotResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using GroupMeShared.Model;
using System.Runtime.Serialization;

namespace GroupMeBot.Model
{
/// <summary>
/// Represents the response from a request to create a new bot
/// </summary>
[DataContract]
public class CreateBotResponse
{
/// <summary>
/// Gets or sets the call response metadata
/// </summary>
[DataMember(Name = "meta")]
public Meta ResponseMeta { get; set; }

/// <summary>
/// Gets or sets the response details
/// </summary>
[DataMember(Name = "response")]
public BotResponse Response { get; set; }

/// <summary>
/// Represents information about a newly created bot
/// </summary>
[DataContract]
public class BotResponse
{
/// <summary>
/// Gets or sets the new bot
/// </summary>
[DataMember(Name = "bot")]
public BotData Bot { get; set; }
}

/// <summary>
/// Represents details about a newly created bot
/// </summary>
[DataContract]
public class BotData
{
/// <summary>
/// Gets or sets the name of the bot
/// </summary>
[DataMember(Name = "name")]
public string Name { get; set; }

/// <summary>
/// Gets or sets the newly created bot's ID
/// </summary>
[DataMember(Name = "bot_id")]
public string BotId { get; set; }
}

}
}
78 changes: 78 additions & 0 deletions dotnet/GroupMeBots/Service/BotCreator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

using GroupMeBot.Model;
using static GroupMeBot.Model.CreateBotResponse;
using GroupMeShared.Service;
using GroupMeShared.Utilities;

namespace GroupMeBots.Service
{
public class BotCreator : IBotCreator
{
private const string TwitterAvatarUrl = "https://gmbots.azurewebsites.net/twitter.png";
private const string BotCreationUrl = "https://api.groupme.com/v3/bots?token={0}";
private const int BotCreatedCode = 201;
private const string WelcomeText = "Hello! I'm TwitterBot. I'll be searching Twitter every five minutes for references of '{0}'. If I find anything, I'll post it here!";

private IBotPoster _botPoster;

public BotCreator(IBotPoster botPoster)
{
_botPoster = botPoster;
}

/// <summary>
/// Creates a Twitter bot in GroupMe
/// </summary>
/// <param name="accessToken">GroupMe access token with which to create the bot</param>
/// <param name="groupId">ID of the group in which to create the bot</param>
/// <param name="botname">Name of the bot you are creating</param>
/// <param name="searchTerm">Term to use to search Twitter with the bot</param>
/// <returns>New bot details, null if failed</returns>
public async Task<BotData> CreateTwitterBot(string accessToken, string groupId, string botname, string searchTerm)
{
if (string.IsNullOrEmpty(accessToken) || string.IsNullOrEmpty(groupId) || string.IsNullOrEmpty(searchTerm) || string.IsNullOrEmpty(botname))
{
throw new ArgumentException();
}

var request = new CreateBotRequest
{
Bot = new CreateBotRequest.BotData
{
Name = $"{botname} {searchTerm}",
GroupId = groupId,
AvatarUrl = TwitterAvatarUrl
}
};

using (StringContent content = JsonSerializer.SerializeToJson(request))
{
var client = new HttpClient();
string requestUrl = string.Format(BotCreationUrl, accessToken);
HttpResponseMessage response = await client.PostAsync(requestUrl, content);
if (response.IsSuccessStatusCode)
{
var newBot = await JsonSerializer.DeserializeJsonAsync<CreateBotResponse>(response);
if (newBot?.ResponseMeta?.Code == BotCreatedCode)
{
await _botPoster.PostAsync(string.Format(WelcomeText, searchTerm), newBot.Response.Bot.BotId);
return newBot?.Response?.Bot;
}
}
else if (response.StatusCode == HttpStatusCode.Unauthorized)
{
throw new AuthorizationException();
}
}

return null;
}
}
}
File renamed without changes.
21 changes: 21 additions & 0 deletions dotnet/GroupMeBots/Service/IBotCreator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//
using System.Threading.Tasks;
using static GroupMeBot.Model.CreateBotResponse;

namespace GroupMeBots.Service
{
public interface IBotCreator
{
/// <summary>
/// Creates a Twitter bot in GroupMe
/// </summary>
/// <param name="accessToken">GroupMe access token with which to create the bot</param>
/// <param name="groupId">ID of the group in which to create the bot</param>
/// <param name="botname">Name of the bot you are creating</param>
/// <param name="searchTerm">Term to use to search Twitter with the bot</param>
/// <returns>New bot details, null if failed</returns>
Task<BotData> CreateTwitterBot(string accessToken, string groupId, string botName, string searchTerm);
}
}
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,8 @@
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>

<ItemGroup>
<Reference Include="System.Web" />
</ItemGroup>

</Project>
File renamed without changes.
108 changes: 108 additions & 0 deletions dotnet/GroupMeShared/Model/Group.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Runtime.Serialization;

namespace GroupMeShared.Model
{
[DataContract]
public class Group
{
/// <summary>
/// Gets or sets the ID of this chat (should match GroupId)
/// </summary>
[DataMember(Name = "id")]
public string Id { get; set; }

/// <summary>
/// Gets or sets the unique ID for this group
/// </summary>
[DataMember(Name = "group_id")]
public string GroupId { get; set; }

/// <summary>
/// Gets or sets the display name of this group
/// </summary>
[DataMember(Name = "name")]
public string GroupName { get; set; }

/// <summary>
/// Gets or sets the type of group
/// </summary>
[DataMember(Name = "type")]
public string Type { get; set; }

/// <summary>
/// Gets or sets the telephone number used to communicate with this group via SMS
/// </summary>
[DataMember(Name = "phone_number")]
public string TelephoneNumber { get; set; }

/// <summary>
/// Gets or sets the time (since epoch) since the group was updated
/// </summary>
[DataMember(Name = "updated_at")]
public long UpdatedFromEpoch { get; set; }

/// <summary>
/// Gets or sets the time (since epoch) since the group was created
/// </summary>
[DataMember(Name = "created_at")]
public long CreatedAtEpoch { get; set; }

/// <summary>
/// Gets or sets a value indicating whether or not group is in office mode
/// </summary>
[DataMember(Name = "office_mode")]
public bool OfficeMode { get; set; }

/// <summary>
/// Gets or sets the share URL for this group
/// </summary>
[DataMember(Name = "share_url")]
public string ShareUrl { get; set; }

/// <summary>
/// Gets or sets the maximum numberof members in a group
/// </summary>
[DataMember(Name = "max_members")]
public int MaxMembers { get; set; }

/// <summary>
/// Gets or sets the URL to the avatar for this group
/// </summary>
[DataMember(Name = "image_url")]
public string AvatarUrl { get; set; }

/// <summary>
/// Gets or sets the description.
/// </summary>
/// <value>
/// The description.
/// </value>
[DataMember(Name = "description")]
public string Description { get; set; }

/// <summary>
/// Gets or sets the creator user identifier.
/// </summary>
/// <value>
/// The creator user identifier.
/// </value>
[DataMember(Name = "creator_user_id")]
public string CreatorUserId { get; set; }
}

/// <summary>
/// Represents a response from the web service with a list of group chats
/// </summary>
[DataContract]
public class GroupResponse
{
/// <summary>
/// Gets or sets the list of group chat conversations
/// </summary>
[DataMember(Name = "response")]
public Group[] Groups { get; set; }
}
}
File renamed without changes.
20 changes: 20 additions & 0 deletions dotnet/GroupMeShared/Model/Meta.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System.Runtime.Serialization;

namespace GroupMeShared.Model
{
/// <summary>
/// Represents response metadata
/// </summary>
[DataContract]
public class Meta
{
/// <summary>
/// Gets or sets response metadata code
/// </summary>
[DataMember(Name = "code")]
public int Code { get; set; }
}
}
Loading

0 comments on commit a4349eb

Please sign in to comment.