Merge remote-tracking branch 'refs/remotes/Kwoth/1.0' into 1.0-fearnlj01

This commit is contained in:
Jordan Fearnley 2016-10-24 22:13:35 +01:00
commit 56471cb48b
6 changed files with 138 additions and 50 deletions

View File

@ -1,19 +1,24 @@
Copyright (c) 2015 Master Kwoth This is free and unencumbered software released into the public domain.
Permission is hereby granted, free of charge, to any person obtaining a copy Anyone is free to copy, modify, publish, use, compile, sell, or
of this software and associated documentation files (the "Software"), to deal distribute this software, either in source code form or as a compiled
in the Software without restriction, including without limitation the rights binary, for any purpose, commercial or non-commercial, and by any
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell means.
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all In jurisdictions that recognize copyright laws, the author or authors
copies or substantial portions of the Software. of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
SOFTWARE. OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>

View File

@ -1,5 +1,6 @@
using Discord; using Discord;
using Discord.Commands; using Discord.Commands;
using Discord.WebSocket;
using NadekoBot.Attributes; using NadekoBot.Attributes;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
@ -15,8 +16,18 @@ namespace NadekoBot.Modules.Games
public static ConcurrentDictionary<IGuild, Poll> ActivePolls = new ConcurrentDictionary<IGuild, Poll>(); public static ConcurrentDictionary<IGuild, Poll> ActivePolls = new ConcurrentDictionary<IGuild, Poll>();
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequirePermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Poll(IUserMessage umsg, [Remainder] string arg = null) public Task Poll(IUserMessage umsg, [Remainder] string arg = null)
=> InternalStartPoll(umsg, arg, isPublic: false);
[NadekoCommand, Usage, Description, Aliases]
[RequirePermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)]
public Task PublicPoll(IUserMessage umsg, [Remainder] string arg = null)
=> InternalStartPoll(umsg, arg, isPublic: true);
private async Task InternalStartPoll(IUserMessage umsg, string arg, bool isPublic = false)
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
@ -28,57 +39,63 @@ namespace NadekoBot.Modules.Games
if (data.Length < 3) if (data.Length < 3)
return; return;
var poll = new Poll(umsg, data[0], data.Skip(1)); var poll = new Poll(umsg, data[0], data.Skip(1), isPublic: isPublic);
if (ActivePolls.TryAdd(channel.Guild, poll)) if (ActivePolls.TryAdd(channel.Guild, poll))
{ {
await poll.StartPoll().ConfigureAwait(false); await poll.StartPoll().ConfigureAwait(false);
} }
else
await channel.SendMessageAsync("`Poll is already running on this server.`").ConfigureAwait(false);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]
[RequirePermission(GuildPermission.ManageMessages)]
[RequireContext(ContextType.Guild)] [RequireContext(ContextType.Guild)]
public async Task Pollend(IUserMessage umsg) public async Task Pollend(IUserMessage umsg)
{ {
var channel = (ITextChannel)umsg.Channel; var channel = (ITextChannel)umsg.Channel;
if (!(umsg.Author as IGuildUser).GuildPermissions.ManageChannels)
return;
Poll poll; Poll poll;
ActivePolls.TryGetValue(channel.Guild, out poll); ActivePolls.TryRemove(channel.Guild, out poll);
await poll.StopPoll(channel).ConfigureAwait(false); await poll.StopPoll().ConfigureAwait(false);
} }
} }
public class Poll public class Poll
{ {
private readonly IUserMessage umsg; private readonly IUserMessage originalMessage;
private readonly IGuild guild;
private readonly string[] answers; private readonly string[] answers;
private ConcurrentDictionary<IUser, int> participants = new ConcurrentDictionary<IUser, int>(); private ConcurrentDictionary<ulong, int> participants = new ConcurrentDictionary<ulong, int>();
private readonly string question; private readonly string question;
private DateTime started; private DateTime started;
private CancellationTokenSource pollCancellationSource = new CancellationTokenSource(); private CancellationTokenSource pollCancellationSource = new CancellationTokenSource();
private readonly bool isPublic;
public Poll(IUserMessage umsg, string question, IEnumerable<string> enumerable) public Poll(IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false)
{ {
this.umsg = umsg; this.originalMessage = umsg;
this.guild = ((ITextChannel)umsg.Channel).Guild;
this.question = question; this.question = question;
this.answers = enumerable as string[] ?? enumerable.ToArray(); this.answers = enumerable as string[] ?? enumerable.ToArray();
this.isPublic = isPublic;
} }
public async Task StartPoll() public async Task StartPoll()
{ {
started = DateTime.Now; started = DateTime.Now;
NadekoBot.Client.MessageReceived += Vote; NadekoBot.Client.MessageReceived += Vote;
var msgToSend = $@"📃**{umsg.Author.Username}** has created a poll which requires your attention: var msgToSend = $"📃**{originalMessage.Author.Username}** has created a poll which requires your attention:\n\n**{question}**\n";
**{question}**\n";
var num = 1; var num = 1;
msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n"); msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n");
if (!isPublic)
msgToSend += "\n**Private Message me with the corresponding number of the answer.**"; msgToSend += "\n**Private Message me with the corresponding number of the answer.**";
await umsg.Channel.SendMessageAsync(msgToSend).ConfigureAwait(false); else
msgToSend += "\n**Send a Message here with the corresponding number of the answer.**";
await originalMessage.Channel.SendMessageAsync(msgToSend).ConfigureAwait(false);
} }
public async Task StopPoll(IGuildChannel ch) public async Task StopPoll()
{ {
NadekoBot.Client.MessageReceived -= Vote; NadekoBot.Client.MessageReceived -= Vote;
try try
@ -90,7 +107,7 @@ namespace NadekoBot.Modules.Games
var totalVotesCast = results.Sum(kvp => kvp.Value); var totalVotesCast = results.Sum(kvp => kvp.Value);
if (totalVotesCast == 0) if (totalVotesCast == 0)
{ {
await umsg.Channel.SendMessageAsync("📄 **No votes have been cast.**").ConfigureAwait(false); await originalMessage.Channel.SendMessageAsync("📄 **No votes have been cast.**").ConfigureAwait(false);
return; return;
} }
var closeMessage = $"--------------**POLL CLOSED**--------------\n" + var closeMessage = $"--------------**POLL CLOSED**--------------\n" +
@ -99,7 +116,7 @@ namespace NadekoBot.Modules.Games
$" has {kvp.Value} votes." + $" has {kvp.Value} votes." +
$"({kvp.Value * 1.0f / totalVotesCast * 100}%)\n"); $"({kvp.Value * 1.0f / totalVotesCast * 100}%)\n");
await umsg.Channel.SendMessageAsync($"📄 **Total votes cast**: {totalVotesCast}\n{closeMessage}").ConfigureAwait(false); await originalMessage.Channel.SendMessageAsync($"📄 **Total votes cast**: {totalVotesCast}\n{closeMessage}").ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@ -113,23 +130,54 @@ namespace NadekoBot.Modules.Games
var msg = imsg as IUserMessage; var msg = imsg as IUserMessage;
if (msg == null || msg.Author.IsBot) if (msg == null || msg.Author.IsBot)
return Task.CompletedTask; return Task.CompletedTask;
// channel must be private
IPrivateChannel ch;
if ((ch = msg.Channel as IPrivateChannel) == null)
return Task.CompletedTask;
// has to be an integer // has to be an integer
int vote; int vote;
if (!int.TryParse(msg.Content, out vote)) return Task.CompletedTask; if (!int.TryParse(imsg.Content, out vote))
return Task.CompletedTask;
if (vote < 1 || vote > answers.Length)
return Task.CompletedTask;
var t = Task.Run(async () => var t = Task.Run(async () =>
{ {
if (vote < 1 || vote > answers.Length) try
return;
if (participants.TryAdd(msg.Author, vote))
{ {
try { await ((IMessageChannel)ch).SendMessageAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false); } catch { } IMessageChannel ch;
if (isPublic)
{
//if public, channel must be the same the poll started in
if (originalMessage.Channel.Id != imsg.Channel.Id)
return;
ch = imsg.Channel;
} }
else
{
//if private, channel must be dm channel
if ((ch = msg.Channel as IDMChannel) == null)
return;
// user must be a member of the guild this poll is in
var guildUsers = await guild.GetUsersAsync().ConfigureAwait(false);
if (!guildUsers.Any(u => u.Id == imsg.Author.Id))
return;
}
//user can vote only once
if (participants.TryAdd(msg.Author.Id, vote))
{
if (!isPublic)
{
await ch.SendMessageAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false);
}
else
{
var toDelete = await ch.SendMessageAsync($"{msg.Author.Mention} cast their vote.").ConfigureAwait(false);
await Task.Delay(5000);
await toDelete.DeleteAsync().ConfigureAwait(false);
}
}
}
catch { }
}); });
return Task.CompletedTask; return Task.CompletedTask;
} }

View File

@ -87,11 +87,6 @@ namespace NadekoBot.Modules.Searches
url += $"/{width}/{height}"; url += $"/{width}/{height}";
//using (var http = new HttpClient())
//{
// var res = await http.GetStreamAsync(url).ConfigureAwait(false);
// await channel.SendFileAsync()
//}
await channel.SendMessageAsync(url).ConfigureAwait(false); await channel.SendMessageAsync(url).ConfigureAwait(false);
} }
} }

View File

@ -4443,7 +4443,7 @@ namespace NadekoBot.Resources {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Creates a poll, only person who has manage server permission can do it.. /// Looks up a localized string similar to Creates a poll which requires users to send the number of the voting option to the bot..
/// </summary> /// </summary>
public static string poll_desc { public static string poll_desc {
get { get {
@ -4514,6 +4514,33 @@ namespace NadekoBot.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to publicpoll ppoll.
/// </summary>
public static string publicpoll_cmd {
get {
return ResourceManager.GetString("publicpoll_cmd", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Creates a public poll which requires users to type a number of the voting option in the channel command is ran in..
/// </summary>
public static string publicpoll_desc {
get {
return ResourceManager.GetString("publicpoll_desc", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to `{0}ppoll Question?;Answer1;Answ 2;A_3`.
/// </summary>
public static string publicpoll_usage {
get {
return ResourceManager.GetString("publicpoll_usage", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to queue q yq. /// Looks up a localized string similar to queue q yq.
/// </summary> /// </summary>

View File

@ -1336,7 +1336,7 @@
<value>poll</value> <value>poll</value>
</data> </data>
<data name="poll_desc" xml:space="preserve"> <data name="poll_desc" xml:space="preserve">
<value>Creates a poll, only person who has manage server permission can do it.</value> <value>Creates a poll which requires users to send the number of the voting option to the bot.</value>
</data> </data>
<data name="poll_usage" xml:space="preserve"> <data name="poll_usage" xml:space="preserve">
<value>`{0}poll Question?;Answer1;Answ 2;A_3`</value> <value>`{0}poll Question?;Answer1;Answ 2;A_3`</value>
@ -2538,4 +2538,13 @@
<data name="togethertube_usage" xml:space="preserve"> <data name="togethertube_usage" xml:space="preserve">
<value>`{0}totube`</value> <value>`{0}totube`</value>
</data> </data>
<data name="publicpoll_cmd" xml:space="preserve">
<value>publicpoll ppoll</value>
</data>
<data name="publicpoll_desc" xml:space="preserve">
<value>Creates a public poll which requires users to type a number of the voting option in the channel command is ran in.</value>
</data>
<data name="publicpoll_usage" xml:space="preserve">
<value>`{0}ppoll Question?;Answer1;Answ 2;A_3`</value>
</data>
</root> </root>

View File

@ -173,6 +173,10 @@ namespace NadekoBot.Services
{ {
if (msg.Channel is IPrivateChannel) if (msg.Channel is IPrivateChannel)
{ {
//rofl, gotta do this to prevent this message from occuring on polls
int vote;
if (int.TryParse(msg.Content, out vote)) return;
await msg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false); await msg.Channel.SendMessageAsync(Help.DMHelpString).ConfigureAwait(false);
await DMForwardCommands.HandleDMForwarding(msg, ownerChannels); await DMForwardCommands.HandleDMForwarding(msg, ownerChannels);