-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #65 from davidroberts63/VotingSprocket
Voting sprocket
- Loading branch information
Showing
8 changed files
with
491 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
The VotingSprocket code is licensed under the Apache 2.0 license. | ||
Located at: http://www.apache.org/licenses/LICENSE-2.0.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Timers; | ||
|
||
namespace VotingSprocket | ||
{ | ||
internal class Poll | ||
{ | ||
private List<string> _ballots; | ||
|
||
public Timer Timer { get; private set; } | ||
public Dictionary<string, int> Votes { get; private set; } | ||
|
||
public Poll() | ||
{ | ||
_ballots = new List<string>(); | ||
Votes = new Dictionary<string, int>(); | ||
#if DEBUG | ||
Timer = new Timer(250); | ||
#else | ||
Timer = new Timer(2 * 60 * 1000); | ||
#endif | ||
} | ||
|
||
public void CastBallot(string sender, string vote) | ||
{ | ||
if (HasAlreadyVoted(sender)) return; | ||
|
||
_ballots.Add(sender); | ||
if (!Votes.ContainsKey(vote)) Votes.Add(vote, 0); | ||
Votes[vote]++; | ||
} | ||
|
||
public bool HasAlreadyVoted(string sender) | ||
{ | ||
return _ballots.Contains(sender); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
using System.Reflection; | ||
using System.Runtime.CompilerServices; | ||
using System.Runtime.InteropServices; | ||
|
||
// General Information about an assembly is controlled through the following | ||
// set of attributes. Change these attribute values to modify the information | ||
// associated with an assembly. | ||
[assembly: AssemblyTitle("Voting Sprocket")] | ||
[assembly: AssemblyDescription("Jibbr sprocket for voting polls")] | ||
[assembly: AssemblyConfiguration("")] | ||
[assembly: AssemblyCompany("David Roberts")] | ||
[assembly: AssemblyProduct("VotingSprocket")] | ||
[assembly: AssemblyCopyright("Copyright © David Roberts 2012")] | ||
[assembly: AssemblyTrademark("")] | ||
[assembly: AssemblyCulture("")] | ||
|
||
// Setting ComVisible to false makes the types in this assembly not visible | ||
// to COM components. If you need to access a type in this assembly from | ||
// COM, set the ComVisible attribute to true on that type. | ||
[assembly: ComVisible(false)] | ||
|
||
// The following GUID is for the ID of the typelib if this project is exposed to COM | ||
[assembly: Guid("0aa67100-b2fc-4699-b5e0-03a5c3aceeb5")] | ||
|
||
// Version information for an assembly consists of the following four values: | ||
// | ||
// Major Version | ||
// Minor Version | ||
// Build Number | ||
// Revision | ||
// | ||
// You can specify all the values or you can default the Build and Revision Numbers | ||
// by using the '*' as shown below: | ||
// [assembly: AssemblyVersion("1.0.*")] | ||
[assembly: AssemblyVersion("0.0.1.0")] | ||
[assembly: AssemblyFileVersion("0.0.1.0")] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using Jabbot.Sprockets.Core; | ||
using Jabbot.Models; | ||
using Jabbot; | ||
using System.Text.RegularExpressions; | ||
using System.Timers; | ||
|
||
namespace VotingSprocket | ||
{ | ||
public class VoteSprocket : ISprocket | ||
{ | ||
private const string Poll_Command_Description = "To start a poll use: poll <roomname> <question>"; | ||
|
||
private Regex _pollCommand = new Regex("^poll[ ]*(?<room>[a-zA-Z0-9_-]*)[ ]*(?<question>.*)$", RegexOptions.IgnoreCase); | ||
private Regex _voteCommand = new Regex("^vote[ ]*(?<vote>[0-9]*)$", RegexOptions.IgnoreCase); | ||
|
||
private Dictionary<string, Poll> _roomPolls = new Dictionary<string, Poll>(); | ||
private IBot _bot; | ||
|
||
public bool Handle(ChatMessage message, IBot bot) | ||
{ | ||
_bot = bot; | ||
|
||
var pollMatch = _pollCommand.Match(message.Content); | ||
var voteMatch = _voteCommand.Match(message.Content); | ||
|
||
if ((!pollMatch.Success && !voteMatch.Success) | ||
|| (pollMatch.Success && message.Receiver != bot.Name) | ||
) return false; | ||
|
||
if (pollMatch.Success) | ||
{ | ||
string room = pollMatch.Groups["room"].Value; | ||
string question = pollMatch.Groups["question"].Value; | ||
|
||
if (string.IsNullOrWhiteSpace(room) || string.IsNullOrWhiteSpace(question)) | ||
{ | ||
bot.PrivateReply(message.Sender, Poll_Command_Description); | ||
} | ||
else if (_roomPolls.ContainsKey(room)) | ||
{ | ||
bot.PrivateReply(message.Sender, "A poll is already in effect for " + room + "."); | ||
} | ||
else if (!bot.Rooms.Contains(room)) | ||
{ | ||
bot.PrivateReply(message.Sender, "You are not in the room you specified for the poll."); | ||
bot.PrivateReply(message.Sender, Poll_Command_Description); | ||
} | ||
else | ||
{ | ||
_roomPolls.Add(room, new Poll()); | ||
|
||
string broadcast = "A poll has started: " + question; | ||
bot.Say(broadcast, room); | ||
bot.Say("Poll will close in 2 minutes. Public reply with vote 1 or vote 2 etc...", room); | ||
|
||
// I don't like breaking the Law of Demeter here. But adhering to it for the | ||
// timer would mean giving the poll the room (okay with) and the bot/ClosePoll.. (not so okay with). | ||
// Yes that means something is probably wrong here and I should fix it but it's late and | ||
// my mind needs a break. | ||
_roomPolls[room].Timer.Elapsed += (s, e) => ClosePollAndSendResults(room); | ||
_roomPolls[room].Timer.Start(); | ||
} | ||
} | ||
else if (voteMatch.Success) | ||
{ | ||
string room = message.Receiver; | ||
string vote = voteMatch.Groups["vote"].Value; | ||
|
||
if (room == bot.Name) | ||
{ | ||
bot.PrivateReply(message.Sender, "You must cast your vote publicy in the room the poll is in."); | ||
} | ||
else if (!_roomPolls.ContainsKey(room)) | ||
{ | ||
bot.PrivateReply(message.Sender, "There is no active poll for you to vote on."); | ||
bot.PrivateReply(message.Sender, Poll_Command_Description); | ||
} | ||
else | ||
{ | ||
if (_roomPolls[room].HasAlreadyVoted(message.Sender)) | ||
{ | ||
bot.PrivateReply(message.Sender, "You can only cast one vote for the current poll in this room."); | ||
} | ||
else | ||
{ | ||
_roomPolls[room].CastBallot(message.Sender, vote); | ||
} | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
private void ClosePollAndSendResults(string room) | ||
{ | ||
if (!_roomPolls.ContainsKey(room)) return; | ||
|
||
_roomPolls[room].Timer.Stop(); | ||
|
||
if (!_roomPolls[room].Votes.Any()) | ||
{ | ||
_bot.Say("No votes were cast for the poll.", room); | ||
} | ||
else | ||
{ | ||
string message = "poll results "; | ||
foreach (var kvp in _roomPolls[room].Votes) | ||
{ | ||
message += string.Format("{0} vote{2} for ({1}). ", kvp.Value, kvp.Key, kvp.Value == 1 ? "" : "s"); | ||
} | ||
_roomPolls.Remove(room); | ||
|
||
_bot.Say(message.Trim(), room); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
<PropertyGroup> | ||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> | ||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> | ||
<ProductVersion>8.0.30703</ProductVersion> | ||
<SchemaVersion>2.0</SchemaVersion> | ||
<ProjectGuid>{23F45EDA-A95D-4041-8B78-6F691A98B0CB}</ProjectGuid> | ||
<OutputType>Library</OutputType> | ||
<AppDesignerFolder>Properties</AppDesignerFolder> | ||
<RootNamespace>VotingSprocket</RootNamespace> | ||
<AssemblyName>VotingSprocket</AssemblyName> | ||
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion> | ||
<FileAlignment>512</FileAlignment> | ||
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\..\jibbr\</SolutionDir> | ||
<RestorePackages>true</RestorePackages> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> | ||
<DebugSymbols>true</DebugSymbols> | ||
<DebugType>full</DebugType> | ||
<Optimize>false</Optimize> | ||
<OutputPath>bin\Debug\</OutputPath> | ||
<DefineConstants>DEBUG;TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
</PropertyGroup> | ||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> | ||
<DebugType>pdbonly</DebugType> | ||
<Optimize>true</Optimize> | ||
<OutputPath>bin\Release\</OutputPath> | ||
<DefineConstants>TRACE</DefineConstants> | ||
<ErrorReport>prompt</ErrorReport> | ||
<WarningLevel>4</WarningLevel> | ||
</PropertyGroup> | ||
<ItemGroup> | ||
<Reference Include="System" /> | ||
<Reference Include="System.Core" /> | ||
<Reference Include="System.Xml.Linq" /> | ||
<Reference Include="System.Data.DataSetExtensions" /> | ||
<Reference Include="Microsoft.CSharp" /> | ||
<Reference Include="System.Data" /> | ||
<Reference Include="System.Xml" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<Compile Include="Poll.cs" /> | ||
<Compile Include="VoteSprocket.cs" /> | ||
<Compile Include="Properties\AssemblyInfo.cs" /> | ||
</ItemGroup> | ||
<ItemGroup> | ||
<ProjectReference Include="..\..\Jabbot\Jabbot.csproj"> | ||
<Project>{478BFCF7-9397-49A7-AFD4-060B6B749E77}</Project> | ||
<Name>Jabbot</Name> | ||
</ProjectReference> | ||
</ItemGroup> | ||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | ||
<Import Project="$(SolutionDir)\.nuget\nuget.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. | ||
<Target Name="BeforeBuild"> | ||
</Target> | ||
<Target Name="AfterBuild"> | ||
</Target> | ||
--> | ||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.