Games module file, poll command converted
This commit is contained in:
129
src/NadekoBot/Modules/Games/Commands/BetrayGame.cs
Normal file
129
src/NadekoBot/Modules/Games/Commands/BetrayGame.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Classes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Games.Commands
|
||||
{
|
||||
class BetrayGame : DiscordCommand
|
||||
{
|
||||
public BetrayGame(DiscordModule module) : base(module) { }
|
||||
|
||||
private enum Answers
|
||||
{
|
||||
Cooperate,
|
||||
Betray
|
||||
}
|
||||
internal override void Init(CommandGroupBuilder cgb)
|
||||
{
|
||||
cgb.CreateCommand(Module.Prefix + "betray")
|
||||
.Description("BETRAY GAME. Betray nadeko next turn." +
|
||||
"If Nadeko cooperates - you get extra points, nadeko loses a LOT." +
|
||||
$"If Nadeko betrays - you both lose some points. | `{Prefix}betray`")
|
||||
.Do(async e =>
|
||||
{
|
||||
await ReceiveAnswer(e, Answers.Betray).ConfigureAwait(false);
|
||||
});
|
||||
|
||||
cgb.CreateCommand(Module.Prefix + "cooperate")
|
||||
.Description("BETRAY GAME. Cooperate with nadeko next turn." +
|
||||
"If Nadeko cooperates - you both get bonus points." +
|
||||
$"If Nadeko betrays - you lose A LOT, nadeko gets extra. | `{Prefix}cooperater`")
|
||||
.Do(async e =>
|
||||
{
|
||||
|
||||
await ReceiveAnswer(e, Answers.Cooperate).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
|
||||
private int userPoints = 0;
|
||||
|
||||
private int UserPoints {
|
||||
get { return userPoints; }
|
||||
set {
|
||||
if (value < 0)
|
||||
userPoints = 0;
|
||||
userPoints = value;
|
||||
}
|
||||
}
|
||||
private int nadekoPoints = 0;
|
||||
private int NadekoPoints {
|
||||
get { return nadekoPoints; }
|
||||
set {
|
||||
if (value < 0)
|
||||
nadekoPoints = 0;
|
||||
nadekoPoints = value;
|
||||
}
|
||||
}
|
||||
|
||||
private int round = 0;
|
||||
private Answers NextAnswer = Answers.Cooperate;
|
||||
private async Task ReceiveAnswer(CommandEventArgs e, Answers userAnswer)
|
||||
{
|
||||
var response = userAnswer == Answers.Betray
|
||||
? ":no_entry: `You betrayed nadeko` - you monster."
|
||||
: ":ok: `You cooperated with nadeko.` ";
|
||||
var currentAnswer = NextAnswer;
|
||||
var nadekoResponse = currentAnswer == Answers.Betray
|
||||
? ":no_entry: `aww Nadeko betrayed you` - she is so cute"
|
||||
: ":ok: `Nadeko cooperated.`";
|
||||
NextAnswer = userAnswer;
|
||||
if (userAnswer == Answers.Betray && currentAnswer == Answers.Betray)
|
||||
{
|
||||
NadekoPoints--;
|
||||
UserPoints--;
|
||||
}
|
||||
else if (userAnswer == Answers.Cooperate && currentAnswer == Answers.Cooperate)
|
||||
{
|
||||
NadekoPoints += 2;
|
||||
UserPoints += 2;
|
||||
}
|
||||
else if (userAnswer == Answers.Betray && currentAnswer == Answers.Cooperate)
|
||||
{
|
||||
NadekoPoints -= 3;
|
||||
UserPoints += 3;
|
||||
}
|
||||
else if (userAnswer == Answers.Cooperate && currentAnswer == Answers.Betray)
|
||||
{
|
||||
NadekoPoints += 3;
|
||||
UserPoints -= 3;
|
||||
}
|
||||
|
||||
await imsg.Channel.SendMessageAsync($"**ROUND {++round}**\n" +
|
||||
$"{response}\n" +
|
||||
$"{nadekoResponse}\n" +
|
||||
$"--------------------------------\n" +
|
||||
$"Nadeko has {NadekoPoints} points." +
|
||||
$"You have {UserPoints} points." +
|
||||
$"--------------------------------\n")
|
||||
.ConfigureAwait(false);
|
||||
if (round < 10) return;
|
||||
if (nadekoPoints == userPoints)
|
||||
await imsg.Channel.SendMessageAsync("Its a draw").ConfigureAwait(false);
|
||||
else if (nadekoPoints > userPoints)
|
||||
await imsg.Channel.SendMessageAsync("Nadeko won.").ConfigureAwait(false);
|
||||
else
|
||||
await imsg.Channel.SendMessageAsync("You won.").ConfigureAwait(false);
|
||||
nadekoPoints = 0;
|
||||
userPoints = 0;
|
||||
round = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class BetraySetting
|
||||
{
|
||||
private string Story = $"{0} have robbed a bank and got captured by a police." +
|
||||
$"Investigators gave you a choice:\n" +
|
||||
$"You can either >COOPERATE with your friends and " +
|
||||
$"not tell them who's idea it was, OR you can >BETRAY your" +
|
||||
$"friends. Depending on their answers your penalty will vary.";
|
||||
|
||||
public int DoubleCoop = 1;
|
||||
public int DoubleBetray = -1;
|
||||
public int BetrayCoop_Betrayer = 3;
|
||||
public int BetrayCoop_Cooperater = -3;
|
||||
|
||||
public string GetStory(IEnumerable<string> names) => String.Format(Story, string.Join(", ", names));
|
||||
}
|
||||
}
|
125
src/NadekoBot/Modules/Games/Commands/Bomberman.cs
Normal file
125
src/NadekoBot/Modules/Games/Commands/Bomberman.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Classes;
|
||||
using System.Text;
|
||||
using System.Timers;
|
||||
using static NadekoBot.Modules.Games.Commands.Bomberman;
|
||||
|
||||
namespace NadekoBot.Modules.Games.Commands
|
||||
{
|
||||
class Bomberman : DiscordCommand
|
||||
{
|
||||
public Field[,] board = new Field[15, 15];
|
||||
|
||||
public BombermanPlayer[] players = new BombermanPlayer[4];
|
||||
|
||||
public Channel gameChannel = null;
|
||||
|
||||
public Message godMsg = null;
|
||||
|
||||
public int curI = 5;
|
||||
public int curJ = 5;
|
||||
|
||||
|
||||
public Bomberman(DiscordModule module) : base(module)
|
||||
{
|
||||
for (int i = 0; i < 15; i++)
|
||||
{
|
||||
for (int j = 0; j < 15; j++)
|
||||
{
|
||||
board[i, j] = new Field();
|
||||
}
|
||||
}
|
||||
NadekoBot.Client.MessageReceived += (s, e) =>
|
||||
{
|
||||
if (e.Channel != gameChannel ||
|
||||
e.User.Id == NadekoBot.Client.CurrentUser.Id)
|
||||
return;
|
||||
|
||||
if (e.Message.Text == "w")
|
||||
{
|
||||
board[curI - 1, curJ] = board[curI--, curJ];
|
||||
board[curI + 1, curJ].player = null;
|
||||
}
|
||||
else if (e.Message.Text == "s")
|
||||
{
|
||||
board[curI + 1, curJ] = board[curI++, curJ];
|
||||
board[curI - 1, curJ].player = null;
|
||||
}
|
||||
else if (e.Message.Text == "a")
|
||||
{
|
||||
board[curI, curJ - 1] = board[curI, curJ--];
|
||||
board[curI, curJ + 1].player = null;
|
||||
}
|
||||
else if (e.Message.Text == "d")
|
||||
{
|
||||
board[curI, curJ + 1] = board[curI, curJ++];
|
||||
board[curI, curJ - 1].player = null;
|
||||
}
|
||||
|
||||
e.Message.Delete();
|
||||
};
|
||||
|
||||
var t = new Timer();
|
||||
t.Elapsed += async (s, e) =>
|
||||
{
|
||||
if (gameChannel == null)
|
||||
return;
|
||||
|
||||
var boardStr = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < 15; i++)
|
||||
{
|
||||
for (int j = 0; j < 15; j++)
|
||||
{
|
||||
boardStr.Append(board[i, j].ToString());
|
||||
}
|
||||
boardStr.AppendLine();
|
||||
}
|
||||
if (godMsg.Id != 0)
|
||||
await godMsg.Edit(boardStr.ToString()).ConfigureAwait(false);
|
||||
|
||||
};
|
||||
t.Interval = 1000;
|
||||
t.Start();
|
||||
|
||||
}
|
||||
|
||||
internal override void Init(CommandGroupBuilder cgb)
|
||||
{
|
||||
//cgb.CreateCommand(Module.Prefix + "bomb")
|
||||
// .Description("Bomberman start")
|
||||
// .Do(async e =>
|
||||
// {
|
||||
// if (gameChannel != null)
|
||||
// return;
|
||||
// godMsg = await imsg.Channel.SendMessageAsync("GAME START IN 1 SECOND....").ConfigureAwait(false);
|
||||
// gameChannel = e.Channel;
|
||||
// players[0] = new BombermanPlayer
|
||||
// {
|
||||
// User = e.User,
|
||||
// };
|
||||
|
||||
// board[5, 5].player = players[0];
|
||||
// });
|
||||
}
|
||||
|
||||
public class BombermanPlayer
|
||||
{
|
||||
public User User = null;
|
||||
public string Icon = "👳";
|
||||
|
||||
internal void MoveLeft()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal struct Field
|
||||
{
|
||||
public BombermanPlayer player;
|
||||
|
||||
public override string ToString() => player?.Icon ?? "⬜";
|
||||
}
|
||||
}
|
317
src/NadekoBot/Modules/Games/Commands/Leet.cs
Normal file
317
src/NadekoBot/Modules/Games/Commands/Leet.cs
Normal file
@@ -0,0 +1,317 @@
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Classes;
|
||||
using NadekoBot.Extensions;
|
||||
using System.Text;
|
||||
|
||||
//taken from
|
||||
//http://www.codeproject.com/Tips/207582/L-t-Tr-nsl-t-r-Leet-Translator (thanks)
|
||||
// because i don't want to waste my time on this cancerous command
|
||||
namespace NadekoBot.Modules.Games.Commands
|
||||
{
|
||||
internal class Leet : DiscordCommand
|
||||
{
|
||||
public Leet(DiscordModule module) : base(module)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Translate text to Leet - Extension methods for string class
|
||||
/// </summary>
|
||||
/// <param name="text">Orginal text</param>
|
||||
/// <param name="degree">Degree of translation (1 - 3)</param>
|
||||
/// <returns>Leet translated text</returns>
|
||||
public static string ToLeet(string text, int degree = 1) =>
|
||||
Translate(text, degree);
|
||||
|
||||
/// <summary>
|
||||
/// Translate text to Leet
|
||||
/// </summary>
|
||||
/// <param name="text">Orginal text</param>
|
||||
/// <param name="degree">Degree of translation (1 - 3)</param>
|
||||
/// <returns>Leet translated text</returns>
|
||||
public static string Translate(string text, int degree = 1)
|
||||
{
|
||||
if (degree > 6)
|
||||
degree = 6;
|
||||
if (degree <= 0)
|
||||
return text;
|
||||
|
||||
// StringBuilder to store result.
|
||||
StringBuilder sb = new StringBuilder(text.Length);
|
||||
foreach (char c in text)
|
||||
{
|
||||
#region Degree 1
|
||||
if (degree == 1)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'a': sb.Append("4"); break;
|
||||
case 'e': sb.Append("3"); break;
|
||||
case 'i': sb.Append("1"); break;
|
||||
case 'o': sb.Append("0"); break;
|
||||
case 'A': sb.Append("4"); break;
|
||||
case 'E': sb.Append("3"); break;
|
||||
case 'I': sb.Append("1"); break;
|
||||
case 'O': sb.Append("0"); break;
|
||||
default: sb.Append(c); break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Degree 2
|
||||
else if (degree == 2)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'a': sb.Append("4"); break;
|
||||
case 'e': sb.Append("3"); break;
|
||||
case 'i': sb.Append("1"); break;
|
||||
case 'o': sb.Append("0"); break;
|
||||
case 'A': sb.Append("4"); break;
|
||||
case 'E': sb.Append("3"); break;
|
||||
case 'I': sb.Append("1"); break;
|
||||
case 'O': sb.Append("0"); break;
|
||||
case 's': sb.Append("$"); break;
|
||||
case 'S': sb.Append("$"); break;
|
||||
case 'l': sb.Append("£"); break;
|
||||
case 'L': sb.Append("£"); break;
|
||||
case 'c': sb.Append("("); break;
|
||||
case 'C': sb.Append("("); break;
|
||||
case 'y': sb.Append("¥"); break;
|
||||
case 'Y': sb.Append("¥"); break;
|
||||
case 'u': sb.Append("µ"); break;
|
||||
case 'U': sb.Append("µ"); break;
|
||||
case 'd': sb.Append("Ð"); break;
|
||||
case 'D': sb.Append("Ð"); break;
|
||||
default: sb.Append(c); break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Degree 3
|
||||
else if (degree == 3)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'a': sb.Append("4"); break;
|
||||
case 'e': sb.Append("3"); break;
|
||||
case 'i': sb.Append("1"); break;
|
||||
case 'o': sb.Append("0"); break;
|
||||
case 'A': sb.Append("4"); break;
|
||||
case 'E': sb.Append("3"); break;
|
||||
case 'I': sb.Append("1"); break;
|
||||
case 'O': sb.Append("0"); break;
|
||||
case 'k': sb.Append("|{"); break;
|
||||
case 'K': sb.Append("|{"); break;
|
||||
case 's': sb.Append("$"); break;
|
||||
case 'S': sb.Append("$"); break;
|
||||
case 'g': sb.Append("9"); break;
|
||||
case 'G': sb.Append("9"); break;
|
||||
case 'l': sb.Append("£"); break;
|
||||
case 'L': sb.Append("£"); break;
|
||||
case 'c': sb.Append("("); break;
|
||||
case 'C': sb.Append("("); break;
|
||||
case 't': sb.Append("7"); break;
|
||||
case 'T': sb.Append("7"); break;
|
||||
case 'z': sb.Append("2"); break;
|
||||
case 'Z': sb.Append("2"); break;
|
||||
case 'y': sb.Append("¥"); break;
|
||||
case 'Y': sb.Append("¥"); break;
|
||||
case 'u': sb.Append("µ"); break;
|
||||
case 'U': sb.Append("µ"); break;
|
||||
case 'f': sb.Append("ƒ"); break;
|
||||
case 'F': sb.Append("ƒ"); break;
|
||||
case 'd': sb.Append("Ð"); break;
|
||||
case 'D': sb.Append("Ð"); break;
|
||||
default: sb.Append(c); break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Degree 4
|
||||
else if (degree == 4)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'a': sb.Append("4"); break;
|
||||
case 'e': sb.Append("3"); break;
|
||||
case 'i': sb.Append("1"); break;
|
||||
case 'o': sb.Append("0"); break;
|
||||
case 'A': sb.Append("4"); break;
|
||||
case 'E': sb.Append("3"); break;
|
||||
case 'I': sb.Append("1"); break;
|
||||
case 'O': sb.Append("0"); break;
|
||||
case 'k': sb.Append("|{"); break;
|
||||
case 'K': sb.Append("|{"); break;
|
||||
case 's': sb.Append("$"); break;
|
||||
case 'S': sb.Append("$"); break;
|
||||
case 'g': sb.Append("9"); break;
|
||||
case 'G': sb.Append("9"); break;
|
||||
case 'l': sb.Append("£"); break;
|
||||
case 'L': sb.Append("£"); break;
|
||||
case 'c': sb.Append("("); break;
|
||||
case 'C': sb.Append("("); break;
|
||||
case 't': sb.Append("7"); break;
|
||||
case 'T': sb.Append("7"); break;
|
||||
case 'z': sb.Append("2"); break;
|
||||
case 'Z': sb.Append("2"); break;
|
||||
case 'y': sb.Append("¥"); break;
|
||||
case 'Y': sb.Append("¥"); break;
|
||||
case 'u': sb.Append("µ"); break;
|
||||
case 'U': sb.Append("µ"); break;
|
||||
case 'f': sb.Append("ƒ"); break;
|
||||
case 'F': sb.Append("ƒ"); break;
|
||||
case 'd': sb.Append("Ð"); break;
|
||||
case 'D': sb.Append("Ð"); break;
|
||||
case 'n': sb.Append(@"|\\|"); break;
|
||||
case 'N': sb.Append(@"|\\|"); break;
|
||||
case 'w': sb.Append(@"\\/\\/"); break;
|
||||
case 'W': sb.Append(@"\\/\\/"); break;
|
||||
case 'h': sb.Append(@"|-|"); break;
|
||||
case 'H': sb.Append(@"|-|"); break;
|
||||
case 'v': sb.Append(@"\\/"); break;
|
||||
case 'V': sb.Append(@"\\/"); break;
|
||||
case 'm': sb.Append(@"|\\/|"); break;
|
||||
case 'M': sb.Append(@"|\/|"); break;
|
||||
default: sb.Append(c); break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Degree 5
|
||||
else if (degree == 5)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'a': sb.Append("4"); break;
|
||||
case 'e': sb.Append("3"); break;
|
||||
case 'i': sb.Append("1"); break;
|
||||
case 'o': sb.Append("0"); break;
|
||||
case 'A': sb.Append("4"); break;
|
||||
case 'E': sb.Append("3"); break;
|
||||
case 'I': sb.Append("1"); break;
|
||||
case 'O': sb.Append("0"); break;
|
||||
case 's': sb.Append("$"); break;
|
||||
case 'S': sb.Append("$"); break;
|
||||
case 'g': sb.Append("9"); break;
|
||||
case 'G': sb.Append("9"); break;
|
||||
case 'l': sb.Append("£"); break;
|
||||
case 'L': sb.Append("£"); break;
|
||||
case 'c': sb.Append("("); break;
|
||||
case 'C': sb.Append("("); break;
|
||||
case 't': sb.Append("7"); break;
|
||||
case 'T': sb.Append("7"); break;
|
||||
case 'z': sb.Append("2"); break;
|
||||
case 'Z': sb.Append("2"); break;
|
||||
case 'y': sb.Append("¥"); break;
|
||||
case 'Y': sb.Append("¥"); break;
|
||||
case 'u': sb.Append("µ"); break;
|
||||
case 'U': sb.Append("µ"); break;
|
||||
case 'f': sb.Append("ƒ"); break;
|
||||
case 'F': sb.Append("ƒ"); break;
|
||||
case 'd': sb.Append("Ð"); break;
|
||||
case 'D': sb.Append("Ð"); break;
|
||||
case 'n': sb.Append(@"|\\|"); break;
|
||||
case 'N': sb.Append(@"|\\|"); break;
|
||||
case 'w': sb.Append(@"\\/\\/"); break;
|
||||
case 'W': sb.Append(@"\\/\\/"); break;
|
||||
case 'h': sb.Append("|-|"); break;
|
||||
case 'H': sb.Append("|-|"); break;
|
||||
case 'v': sb.Append("\\/"); break;
|
||||
case 'V': sb.Append(@"\\/"); break;
|
||||
case 'k': sb.Append("|{"); break;
|
||||
case 'K': sb.Append("|{"); break;
|
||||
case 'r': sb.Append("®"); break;
|
||||
case 'R': sb.Append("®"); break;
|
||||
case 'm': sb.Append(@"|\\/|"); break;
|
||||
case 'M': sb.Append(@"|\\/|"); break;
|
||||
case 'b': sb.Append("ß"); break;
|
||||
case 'B': sb.Append("ß"); break;
|
||||
case 'q': sb.Append("Q"); break;
|
||||
case 'Q': sb.Append("Q¸"); break;
|
||||
case 'x': sb.Append(")("); break;
|
||||
case 'X': sb.Append(")("); break;
|
||||
default: sb.Append(c); break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
#region Degree 6
|
||||
else if (degree == 6)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'a': sb.Append("4"); break;
|
||||
case 'e': sb.Append("3"); break;
|
||||
case 'i': sb.Append("1"); break;
|
||||
case 'o': sb.Append("0"); break;
|
||||
case 'A': sb.Append("4"); break;
|
||||
case 'E': sb.Append("3"); break;
|
||||
case 'I': sb.Append("1"); break;
|
||||
case 'O': sb.Append("0"); break;
|
||||
case 's': sb.Append("$"); break;
|
||||
case 'S': sb.Append("$"); break;
|
||||
case 'g': sb.Append("9"); break;
|
||||
case 'G': sb.Append("9"); break;
|
||||
case 'l': sb.Append("£"); break;
|
||||
case 'L': sb.Append("£"); break;
|
||||
case 'c': sb.Append("("); break;
|
||||
case 'C': sb.Append("("); break;
|
||||
case 't': sb.Append("7"); break;
|
||||
case 'T': sb.Append("7"); break;
|
||||
case 'z': sb.Append("2"); break;
|
||||
case 'Z': sb.Append("2"); break;
|
||||
case 'y': sb.Append("¥"); break;
|
||||
case 'Y': sb.Append("¥"); break;
|
||||
case 'u': sb.Append("µ"); break;
|
||||
case 'U': sb.Append("µ"); break;
|
||||
case 'f': sb.Append("ƒ"); break;
|
||||
case 'F': sb.Append("ƒ"); break;
|
||||
case 'd': sb.Append("Ð"); break;
|
||||
case 'D': sb.Append("Ð"); break;
|
||||
case 'n': sb.Append(@"|\\|"); break;
|
||||
case 'N': sb.Append(@"|\\|"); break;
|
||||
case 'w': sb.Append(@"\\/\\/"); break;
|
||||
case 'W': sb.Append(@"\\/\\/"); break;
|
||||
case 'h': sb.Append("|-|"); break;
|
||||
case 'H': sb.Append("|-|"); break;
|
||||
case 'v': sb.Append(@"\\/"); break;
|
||||
case 'V': sb.Append(@"\\/"); break;
|
||||
case 'k': sb.Append("|{"); break;
|
||||
case 'K': sb.Append("|{"); break;
|
||||
case 'r': sb.Append("®"); break;
|
||||
case 'R': sb.Append("®"); break;
|
||||
case 'm': sb.Append(@"|\\/|"); break;
|
||||
case 'M': sb.Append(@"|\\/|"); break;
|
||||
case 'b': sb.Append("ß"); break;
|
||||
case 'B': sb.Append("ß"); break;
|
||||
case 'j': sb.Append("_|"); break;
|
||||
case 'J': sb.Append("_|"); break;
|
||||
case 'P': sb.Append("|°"); break;
|
||||
case 'q': sb.Append("¶"); break;
|
||||
case 'Q': sb.Append("¶¸"); break;
|
||||
case 'x': sb.Append(")("); break;
|
||||
case 'X': sb.Append(")("); break;
|
||||
default: sb.Append(c); break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
return sb.ToString().TrimTo(1995); // Return result.
|
||||
}
|
||||
|
||||
internal override void Init(CommandGroupBuilder cgb)
|
||||
{
|
||||
cgb.CreateCommand(Module.Prefix + "leet")
|
||||
.Description($"Converts a text to leetspeak with 6 (1-6) severity levels | `{Module.Prefix}leet 3 Hello`")
|
||||
.Parameter("level", ParameterType.Required)
|
||||
.Parameter("text", ParameterType.Unparsed)
|
||||
.Do(async e =>
|
||||
{
|
||||
var text = e.GetArg("text")?.Trim();
|
||||
var levelStr = e.GetArg("level")?.Trim();
|
||||
int level;
|
||||
if (!int.TryParse(levelStr, out level))
|
||||
return;
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return;
|
||||
await imsg.Channel.SendMessageAsync(ToLeet(text, level)).ConfigureAwait(false);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
166
src/NadekoBot/Modules/Games/Commands/PlantPick.cs
Normal file
166
src/NadekoBot/Modules/Games/Commands/PlantPick.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Classes;
|
||||
using NadekoBot.Extensions;
|
||||
using NadekoBot.Modules.Permissions.Classes;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Games.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Flower picking/planting idea is given to me by its
|
||||
/// inceptor Violent Crumble from Game Developers League discord server
|
||||
/// (he has !cookie and !nom) Thanks a lot Violent!
|
||||
/// Check out GDL (its a growing gamedev community):
|
||||
/// https://discord.gg/0TYNJfCU4De7YIk8
|
||||
/// </summary>
|
||||
class PlantPick : DiscordCommand
|
||||
{
|
||||
|
||||
private Random rng;
|
||||
public PlantPick(DiscordModule module) : base(module)
|
||||
{
|
||||
NadekoBot.Client.MessageReceived += PotentialFlowerGeneration;
|
||||
rng = new Random();
|
||||
}
|
||||
|
||||
private static readonly ConcurrentDictionary<ulong, DateTime> plantpickCooldowns = new ConcurrentDictionary<ulong, DateTime>();
|
||||
|
||||
private async void PotentialFlowerGeneration(object sender, Discord.MessageEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.Server == null || e.Channel.IsPrivate || e.Message.IsAuthor)
|
||||
return;
|
||||
var config = Classes.SpecificConfigurations.Default.Of(e.Server.Id);
|
||||
var now = DateTime.Now;
|
||||
int cd;
|
||||
DateTime lastSpawned;
|
||||
if (config.GenerateCurrencyChannels.TryGetValue(e.Channel.Id, out cd))
|
||||
if (!plantpickCooldowns.TryGetValue(e.Channel.Id, out lastSpawned) || (lastSpawned + new TimeSpan(0, cd, 0)) < now)
|
||||
{
|
||||
var rnd = Math.Abs(rng.Next(0,101));
|
||||
if (rnd == 0)
|
||||
{
|
||||
var msgs = new[] { await e.Channel.SendFile(GetRandomCurrencyImagePath()), await imsg.Channel.SendMessageAsync($"❗ A random {NadekoBot.Config.CurrencyName} appeared! Pick it up by typing `>pick`") };
|
||||
plantedFlowerChannels.AddOrUpdate(e.Channel.Id, msgs, (u, m) => { m.ForEach(async msgToDelete => { try { await msgToDelete.Delete(); } catch { } }); return msgs; });
|
||||
plantpickCooldowns.AddOrUpdate(e.Channel.Id, now, (i, d) => now);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
//channelid/messageid pair
|
||||
ConcurrentDictionary<ulong, IEnumerable<Message>> plantedFlowerChannels = new ConcurrentDictionary<ulong, IEnumerable<Message>>();
|
||||
|
||||
private SemaphoreSlim locker = new SemaphoreSlim(1,1);
|
||||
|
||||
internal override void Init(CommandGroupBuilder cgb)
|
||||
{
|
||||
cgb.CreateCommand(Module.Prefix + "pick")
|
||||
.Description($"Picks a flower planted in this channel. | `{Prefix}pick`")
|
||||
.Do(async e =>
|
||||
{
|
||||
IEnumerable<Message> msgs;
|
||||
|
||||
await e.Message.Delete().ConfigureAwait(false);
|
||||
if (!plantedFlowerChannels.TryRemove(e.Channel.Id, out msgs))
|
||||
return;
|
||||
|
||||
foreach(var msgToDelete in msgs)
|
||||
await msgToDelete.Delete().ConfigureAwait(false);
|
||||
|
||||
await FlowersHandler.AddFlowersAsync(e.User, "Picked a flower.", 1, true).ConfigureAwait(false);
|
||||
var msg = await imsg.Channel.SendMessageAsync($"**{e.User.Name}** picked a {NadekoBot.Config.CurrencyName}!").ConfigureAwait(false);
|
||||
ThreadPool.QueueUserWorkItem(async (state) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(10000).ConfigureAwait(false);
|
||||
await msg.Delete().ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
});
|
||||
});
|
||||
|
||||
cgb.CreateCommand(Module.Prefix + "plant")
|
||||
.Description($"Spend a flower to plant it in this channel. (If bot is restarted or crashes, flower will be lost) | `{Prefix}plant`")
|
||||
.Do(async e =>
|
||||
{
|
||||
await locker.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (plantedFlowerChannels.ContainsKey(e.Channel.Id))
|
||||
{
|
||||
await imsg.Channel.SendMessageAsync($"There is already a {NadekoBot.Config.CurrencyName} in this channel.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var removed = await FlowersHandler.RemoveFlowers(e.User, "Planted a flower.", 1, true).ConfigureAwait(false);
|
||||
if (!removed)
|
||||
{
|
||||
await imsg.Channel.SendMessageAsync($"You don't have any {NadekoBot.Config.CurrencyName}s.").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
var file = GetRandomCurrencyImagePath();
|
||||
Message msg;
|
||||
if (file == null)
|
||||
msg = await imsg.Channel.SendMessageAsync(NadekoBot.Config.CurrencySign).ConfigureAwait(false);
|
||||
else
|
||||
msg = await e.Channel.SendFile(file).ConfigureAwait(false);
|
||||
var vowelFirst = new[] { 'a', 'e', 'i', 'o', 'u' }.Contains(NadekoBot.Config.CurrencyName[0]);
|
||||
var msg2 = await imsg.Channel.SendMessageAsync($"Oh how Nice! **{e.User.Name}** planted {(vowelFirst ? "an" : "a")} {NadekoBot.Config.CurrencyName}. Pick it using {Module.Prefix}pick").ConfigureAwait(false);
|
||||
plantedFlowerChannels.TryAdd(e.Channel.Id, new[] { msg, msg2 });
|
||||
}
|
||||
finally { locker.Release(); }
|
||||
});
|
||||
|
||||
cgb.CreateCommand(Prefix + "gencurrency")
|
||||
.Alias(Prefix + "gc")
|
||||
.Description($"Toggles currency generation on this channel. Every posted message will have 2% chance to spawn a {NadekoBot.Config.CurrencyName}. Optional parameter cooldown time in minutes, 5 minutes by default. Requires Manage Messages permission. | `{Prefix}gc` or `{Prefix}gc 60`")
|
||||
.AddCheck(SimpleCheckers.ManageMessages())
|
||||
.Parameter("cd", ParameterType.Unparsed)
|
||||
.Do(async e =>
|
||||
{
|
||||
var cdStr = e.GetArg("cd");
|
||||
int cd = 2;
|
||||
if (!int.TryParse(cdStr, out cd) || cd < 0)
|
||||
{
|
||||
cd = 2;
|
||||
}
|
||||
var config = SpecificConfigurations.Default.Of(e.Server.Id);
|
||||
int throwaway;
|
||||
if (config.GenerateCurrencyChannels.TryRemove(e.Channel.Id, out throwaway))
|
||||
{
|
||||
await imsg.Channel.SendMessageAsync("`Currency generation disabled on this channel.`").ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (config.GenerateCurrencyChannels.TryAdd(e.Channel.Id, cd))
|
||||
await imsg.Channel.SendMessageAsync($"`Currency generation enabled on this channel. Cooldown is {cd} minutes.`").ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private string GetRandomCurrencyImagePath() =>
|
||||
Directory.GetFiles("data/currency_images").OrderBy(s => rng.Next()).FirstOrDefault();
|
||||
|
||||
int GetRandomNumber()
|
||||
{
|
||||
using (RNGCryptoServiceProvider rg = new RNGCryptoServiceProvider())
|
||||
{
|
||||
byte[] rno = new byte[4];
|
||||
rg.GetBytes(rno);
|
||||
int randomvalue = BitConverter.ToInt32(rno, 0);
|
||||
return randomvalue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
132
src/NadekoBot/Modules/Games/Commands/PollCommand.cs
Normal file
132
src/NadekoBot/Modules/Games/Commands/PollCommand.cs
Normal file
@@ -0,0 +1,132 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Attributes;
|
||||
using NadekoBot.Classes;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Games.Commands
|
||||
{
|
||||
public partial class GamesModule
|
||||
{
|
||||
|
||||
//todo DB in the future
|
||||
public static ConcurrentDictionary<IGuild, Poll> ActivePolls = new ConcurrentDictionary<IGuild, Poll>();
|
||||
|
||||
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Poll(IMessage imsg, [Remainder] string arg)
|
||||
{
|
||||
var channel = imsg.Channel as IGuildChannel;
|
||||
|
||||
if (!(imsg.Author as IGuildUser).GuildPermissions.ManageChannels)
|
||||
return;
|
||||
if (string.IsNullOrWhiteSpace(arg) || !arg.Contains(";"))
|
||||
return;
|
||||
var data = arg.Split(';');
|
||||
if (data.Length < 3)
|
||||
return;
|
||||
|
||||
var poll = new Poll(imsg, data[0], data.Skip(1));
|
||||
if (ActivePolls.TryAdd(channel.Guild, poll))
|
||||
{
|
||||
await poll.StartPoll().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Pollend(IMessage imsg)
|
||||
{
|
||||
var channel = imsg.Channel as IGuildChannel;
|
||||
|
||||
if (!(imsg.Author as IGuildUser).GuildPermissions.ManageChannels)
|
||||
return;
|
||||
Poll poll;
|
||||
ActivePolls.TryGetValue(channel.Guild, out poll);
|
||||
await poll.StopPoll(channel).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
public class Poll
|
||||
{
|
||||
private readonly IMessage imsg;
|
||||
private readonly string[] answers;
|
||||
private ConcurrentDictionary<IUser, int> participants = new ConcurrentDictionary<IUser, int>();
|
||||
private readonly string question;
|
||||
private DateTime started;
|
||||
private CancellationTokenSource pollCancellationSource = new CancellationTokenSource();
|
||||
|
||||
public Poll(IMessage imsg, string question, IEnumerable<string> enumerable)
|
||||
{
|
||||
this.imsg = imsg;
|
||||
this.question = question;
|
||||
this.answers = enumerable as string[] ?? enumerable.ToArray();
|
||||
}
|
||||
|
||||
public async Task StartPoll()
|
||||
{
|
||||
started = DateTime.Now;
|
||||
NadekoBot.Client.MessageReceived += Vote;
|
||||
var msgToSend = $@"📃**{imsg.Author.Username}** has created a poll which requires your attention:
|
||||
|
||||
**{question}**\n";
|
||||
var num = 1;
|
||||
msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n");
|
||||
msgToSend += "\n**Private Message me with the corresponding number of the answer.**";
|
||||
await imsg.Channel.SendMessageAsync(msgToSend).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public async Task StopPoll(IGuildChannel ch)
|
||||
{
|
||||
NadekoBot.Client.MessageReceived -= Vote;
|
||||
try
|
||||
{
|
||||
var results = participants.GroupBy(kvp => kvp.Value)
|
||||
.ToDictionary(x => x.Key, x => x.Sum(kvp => 1))
|
||||
.OrderBy(kvp => kvp.Value);
|
||||
|
||||
var totalVotesCast = results.Sum(kvp => kvp.Value);
|
||||
if (totalVotesCast == 0)
|
||||
{
|
||||
await imsg.Channel.SendMessageAsync("📄 **No votes have been cast.**").ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
var closeMessage = $"--------------**POLL CLOSED**--------------\n" +
|
||||
$"📄 , here are the results:\n";
|
||||
closeMessage = results.Aggregate(closeMessage, (current, kvp) => current + $"`{kvp.Key}.` **[{answers[kvp.Key - 1]}]**" +
|
||||
$" has {kvp.Value} votes." +
|
||||
$"({kvp.Value * 1.0f / totalVotesCast * 100}%)\n");
|
||||
|
||||
await imsg.Channel.SendMessageAsync($"📄 **Total votes cast**: {totalVotesCast}\n{closeMessage}").ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error in poll game {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Vote(IMessage msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
IPrivateChannel ch;
|
||||
if ((ch = msg.Channel as IPrivateChannel) == null)
|
||||
return;
|
||||
int vote;
|
||||
if (!int.TryParse(msg.Content, out vote)) return;
|
||||
if (vote < 1 || vote > answers.Length)
|
||||
return;
|
||||
if (participants.TryAdd(msg.Author, vote))
|
||||
{
|
||||
await (ch as ITextChannel).SendMessageAsync($"Thanks for voting **{msg.Author.Username}**.").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
197
src/NadekoBot/Modules/Games/Commands/SpeedTyping.cs
Normal file
197
src/NadekoBot/Modules/Games/Commands/SpeedTyping.cs
Normal file
@@ -0,0 +1,197 @@
|
||||
//using Discord;
|
||||
//using Discord.Commands;
|
||||
//using NadekoBot.Classes;
|
||||
//using NadekoBot.DataModels;
|
||||
//using NadekoBot.Extensions;
|
||||
//using System;
|
||||
//using System.Collections.Concurrent;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Diagnostics;
|
||||
//using System.Linq;
|
||||
//using System.Threading.Tasks;
|
||||
|
||||
////todo DB
|
||||
////todo rewrite?
|
||||
//namespace NadekoBot.Modules.Games.Commands
|
||||
//{
|
||||
|
||||
// public static class SentencesProvider
|
||||
// {
|
||||
// internal static string GetRandomSentence()
|
||||
// {
|
||||
// var data = DbHandler.Instance.GetAllRows<TypingArticle>();
|
||||
// try
|
||||
// {
|
||||
// return data.ToList()[new Random().Next(0, data.Count())].Text;
|
||||
// }
|
||||
// catch
|
||||
// {
|
||||
// return "Failed retrieving data from parse. Owner didn't add any articles to type using `typeadd`.";
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// public class TypingGame
|
||||
// {
|
||||
// public const float WORD_VALUE = 4.5f;
|
||||
// private readonly Channel channel;
|
||||
// public string CurrentSentence;
|
||||
// public bool IsActive;
|
||||
// private readonly Stopwatch sw;
|
||||
// private readonly List<ulong> finishedUserIds;
|
||||
|
||||
// public TypingGame(Channel channel)
|
||||
// {
|
||||
// this.channel = channel;
|
||||
// IsActive = false;
|
||||
// sw = new Stopwatch();
|
||||
// finishedUserIds = new List<ulong>();
|
||||
// }
|
||||
|
||||
// public Channel Channell { get; internal set; }
|
||||
|
||||
// internal async Task<bool> Stop()
|
||||
// {
|
||||
// if (!IsActive) return false;
|
||||
// NadekoBot.Client.MessageReceived -= AnswerReceived;
|
||||
// finishedUserIds.Clear();
|
||||
// IsActive = false;
|
||||
// sw.Stop();
|
||||
// sw.Reset();
|
||||
// await channel.Send("Typing contest stopped").ConfigureAwait(false);
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// internal async Task Start()
|
||||
// {
|
||||
// while (true)
|
||||
// {
|
||||
// if (IsActive) return; // can't start running game
|
||||
// IsActive = true;
|
||||
// CurrentSentence = SentencesProvider.GetRandomSentence();
|
||||
// var i = (int)(CurrentSentence.Length / WORD_VALUE * 1.7f);
|
||||
// await channel.SendMessage($":clock2: Next contest will last for {i} seconds. Type the bolded text as fast as you can.").ConfigureAwait(false);
|
||||
|
||||
|
||||
// var msg = await channel.SendMessage("Starting new typing contest in **3**...").ConfigureAwait(false);
|
||||
// await Task.Delay(1000).ConfigureAwait(false);
|
||||
// await msg.Edit("Starting new typing contest in **2**...").ConfigureAwait(false);
|
||||
// await Task.Delay(1000).ConfigureAwait(false);
|
||||
// await msg.Edit("Starting new typing contest in **1**...").ConfigureAwait(false);
|
||||
// await Task.Delay(1000).ConfigureAwait(false);
|
||||
// await msg.Edit($":book:**{CurrentSentence.Replace(" ", " \x200B")}**:book:").ConfigureAwait(false);
|
||||
// sw.Start();
|
||||
// HandleAnswers();
|
||||
|
||||
// while (i > 0)
|
||||
// {
|
||||
// await Task.Delay(1000).ConfigureAwait(false);
|
||||
// i--;
|
||||
// if (!IsActive)
|
||||
// return;
|
||||
// }
|
||||
|
||||
// await Stop().ConfigureAwait(false);
|
||||
// }
|
||||
// }
|
||||
|
||||
// private void HandleAnswers()
|
||||
// {
|
||||
// NadekoBot.Client.MessageReceived += AnswerReceived;
|
||||
// }
|
||||
|
||||
// private async void AnswerReceived(object sender, MessageEventArgs e)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// if (e.Channel == null || e.Channel.Id != channel.Id || e.User.Id == NadekoBot.Client.CurrentUser.Id) return;
|
||||
|
||||
// var guess = e.Message.RawText;
|
||||
|
||||
// var distance = CurrentSentence.LevenshteinDistance(guess);
|
||||
// var decision = Judge(distance, guess.Length);
|
||||
// if (decision && !finishedUserIds.Contains(e.User.Id))
|
||||
// {
|
||||
// finishedUserIds.Add(e.User.Id);
|
||||
// await channel.Send($"{e.User.Mention} finished in **{sw.Elapsed.Seconds}** seconds with { distance } errors, **{ CurrentSentence.Length / WORD_VALUE / sw.Elapsed.Seconds * 60 }** WPM!").ConfigureAwait(false);
|
||||
// if (finishedUserIds.Count % 2 == 0)
|
||||
// {
|
||||
// await imsg.Channel.SendMessageAsync($":exclamation: `A lot of people finished, here is the text for those still typing:`\n\n:book:**{CurrentSentence}**:book:").ConfigureAwait(false);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// catch { }
|
||||
// }
|
||||
|
||||
// private bool Judge(int errors, int textLength) => errors <= textLength / 25;
|
||||
|
||||
// }
|
||||
|
||||
// internal class SpeedTyping : DiscordCommand
|
||||
// {
|
||||
|
||||
// public static ConcurrentDictionary<ulong, TypingGame> RunningContests;
|
||||
|
||||
// public SpeedTyping(DiscordModule module) : base(module)
|
||||
// {
|
||||
// RunningContests = new ConcurrentDictionary<ulong, TypingGame>();
|
||||
// }
|
||||
|
||||
// public Func<CommandEventArgs, Task> DoFunc() =>
|
||||
// async e =>
|
||||
// {
|
||||
// var game = RunningContests.GetOrAdd(e.User.Server.Id, id => new TypingGame(e.Channel));
|
||||
|
||||
// if (game.IsActive)
|
||||
// {
|
||||
// await imsg.Channel.SendMessageAsync(
|
||||
// $"Contest already running in " +
|
||||
// $"{game.Channell.Mention} channel.")
|
||||
// .ConfigureAwait(false);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// await game.Start().ConfigureAwait(false);
|
||||
// }
|
||||
// };
|
||||
|
||||
// private Func<CommandEventArgs, Task> QuitFunc() =>
|
||||
// async e =>
|
||||
// {
|
||||
// TypingGame game;
|
||||
// if (RunningContests.TryRemove(e.User.Server.Id, out game))
|
||||
// {
|
||||
// await game.Stop().ConfigureAwait(false);
|
||||
// return;
|
||||
// }
|
||||
// await imsg.Channel.SendMessageAsync("No contest to stop on this channel.").ConfigureAwait(false);
|
||||
// };
|
||||
|
||||
// internal override void Init(CommandGroupBuilder cgb)
|
||||
// {
|
||||
// cgb.CreateCommand(Module.Prefix + "typestart")
|
||||
// .Description($"Starts a typing contest. | `{Prefix}typestart`")
|
||||
// .Do(DoFunc());
|
||||
|
||||
// cgb.CreateCommand(Module.Prefix + "typestop")
|
||||
// .Description($"Stops a typing contest on the current channel. | `{Prefix}typestop`")
|
||||
// .Do(QuitFunc());
|
||||
|
||||
// cgb.CreateCommand(Module.Prefix + "typeadd")
|
||||
// .Description($"Adds a new article to the typing contest. Owner only. | `{Prefix}typeadd wordswords`")
|
||||
// .Parameter("text", ParameterType.Unparsed)
|
||||
// .Do(async e =>
|
||||
// {
|
||||
// if (!NadekoBot.IsOwner(e.User.Id) || string.IsNullOrWhiteSpace(e.GetArg("text"))) return;
|
||||
|
||||
// DbHandler.Instance.Connection.Insert(new TypingArticle
|
||||
// {
|
||||
// Text = e.GetArg("text"),
|
||||
// DateAdded = DateTime.Now
|
||||
// });
|
||||
|
||||
// await imsg.Channel.SendMessageAsync("Added new article for typing game.").ConfigureAwait(false);
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
//}
|
155
src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs
Normal file
155
src/NadekoBot/Modules/Games/Commands/Trivia/TriviaGame.cs
Normal file
@@ -0,0 +1,155 @@
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Classes;
|
||||
using NadekoBot.Extensions;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Games.Commands.Trivia
|
||||
{
|
||||
internal class TriviaGame
|
||||
{
|
||||
private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1,1);
|
||||
|
||||
private Server server { get; }
|
||||
private Channel channel { get; }
|
||||
|
||||
private int QuestionDurationMiliseconds { get; } = 30000;
|
||||
private int HintTimeoutMiliseconds { get; } = 6000;
|
||||
public bool ShowHints { get; set; } = true;
|
||||
private CancellationTokenSource triviaCancelSource { get; set; }
|
||||
|
||||
public TriviaQuestion CurrentQuestion { get; private set; }
|
||||
public HashSet<TriviaQuestion> oldQuestions { get; } = new HashSet<TriviaQuestion>();
|
||||
|
||||
public ConcurrentDictionary<User, int> Users { get; } = new ConcurrentDictionary<User, int>();
|
||||
|
||||
public bool GameActive { get; private set; } = false;
|
||||
public bool ShouldStopGame { get; private set; }
|
||||
|
||||
public int WinRequirement { get; } = 10;
|
||||
|
||||
public TriviaGame(CommandEventArgs e, bool showHints, int winReq = 10)
|
||||
{
|
||||
ShowHints = showHints;
|
||||
server = e.Server;
|
||||
channel = e.Channel;
|
||||
WinRequirement = winReq;
|
||||
Task.Run(StartGame);
|
||||
}
|
||||
|
||||
private async Task StartGame()
|
||||
{
|
||||
while (!ShouldStopGame)
|
||||
{
|
||||
// reset the cancellation source
|
||||
triviaCancelSource = new CancellationTokenSource();
|
||||
var token = triviaCancelSource.Token;
|
||||
// load question
|
||||
CurrentQuestion = TriviaQuestionPool.Instance.GetRandomQuestion(oldQuestions);
|
||||
if (CurrentQuestion == null)
|
||||
{
|
||||
await channel.SendMessage($":exclamation: Failed loading a trivia question").ConfigureAwait(false);
|
||||
await End().ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
oldQuestions.Add(CurrentQuestion); //add it to exclusion list so it doesn't show up again
|
||||
//sendquestion
|
||||
await channel.SendMessage($":question: **{CurrentQuestion.Question}**").ConfigureAwait(false);
|
||||
|
||||
//receive messages
|
||||
NadekoBot.Client.MessageReceived += PotentialGuess;
|
||||
|
||||
//allow people to guess
|
||||
GameActive = true;
|
||||
|
||||
try
|
||||
{
|
||||
//hint
|
||||
await Task.Delay(HintTimeoutMiliseconds, token).ConfigureAwait(false);
|
||||
if (ShowHints)
|
||||
await channel.SendMessage($":exclamation:**Hint:** {CurrentQuestion.GetHint()}").ConfigureAwait(false);
|
||||
|
||||
//timeout
|
||||
await Task.Delay(QuestionDurationMiliseconds - HintTimeoutMiliseconds, token).ConfigureAwait(false);
|
||||
|
||||
}
|
||||
catch (TaskCanceledException) { } //means someone guessed the answer
|
||||
GameActive = false;
|
||||
if (!triviaCancelSource.IsCancellationRequested)
|
||||
await channel.Send($":clock2: :question: **Time's up!** The correct answer was **{CurrentQuestion.Answer}**").ConfigureAwait(false);
|
||||
NadekoBot.Client.MessageReceived -= PotentialGuess;
|
||||
// load next question if game is still running
|
||||
await Task.Delay(2000).ConfigureAwait(false);
|
||||
}
|
||||
await End().ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task End()
|
||||
{
|
||||
ShouldStopGame = true;
|
||||
await channel.SendMessage("**Trivia game ended**\n" + GetLeaderboard()).ConfigureAwait(false);
|
||||
TriviaGame throwAwayValue;
|
||||
TriviaCommands.RunningTrivias.TryRemove(server.Id, out throwAwayValue);
|
||||
}
|
||||
|
||||
public async Task StopGame()
|
||||
{
|
||||
if (!ShouldStopGame)
|
||||
await channel.SendMessage(":exclamation: Trivia will stop after this question.").ConfigureAwait(false);
|
||||
ShouldStopGame = true;
|
||||
}
|
||||
|
||||
private async void PotentialGuess(object sender, MessageEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (e.Channel.IsPrivate) return;
|
||||
if (e.Server != server) return;
|
||||
if (e.User.Id == NadekoBot.Client.CurrentUser.Id) return;
|
||||
|
||||
var guess = false;
|
||||
await _guessLock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (GameActive && CurrentQuestion.IsAnswerCorrect(e.Message.Text) && !triviaCancelSource.IsCancellationRequested)
|
||||
{
|
||||
Users.TryAdd(e.User, 0); //add if not exists
|
||||
Users[e.User]++; //add 1 point to the winner
|
||||
guess = true;
|
||||
}
|
||||
}
|
||||
finally { _guessLock.Release(); }
|
||||
if (!guess) return;
|
||||
triviaCancelSource.Cancel();
|
||||
await channel.SendMessage($"☑️ {e.User.Mention} guessed it! The answer was: **{CurrentQuestion.Answer}**").ConfigureAwait(false);
|
||||
if (Users[e.User] != WinRequirement) return;
|
||||
ShouldStopGame = true;
|
||||
await channel.Send($":exclamation: We have a winner! It's {e.User.Mention}.").ConfigureAwait(false);
|
||||
// add points to the winner
|
||||
await FlowersHandler.AddFlowersAsync(e.User, "Won Trivia", 2).ConfigureAwait(false);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public string GetLeaderboard()
|
||||
{
|
||||
if (Users.Count == 0)
|
||||
return "";
|
||||
|
||||
var sb = new StringBuilder();
|
||||
sb.Append("**Leaderboard:**\n-----------\n");
|
||||
|
||||
foreach (var kvp in Users.OrderBy(kvp => kvp.Value))
|
||||
{
|
||||
sb.AppendLine($"**{kvp.Key.Name}** has {kvp.Value} points".ToString().SnPl(kvp.Value));
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
using NadekoBot.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
// THANKS @ShoMinamimoto for suggestions and coding help
|
||||
namespace NadekoBot.Modules.Games.Commands.Trivia
|
||||
{
|
||||
public class TriviaQuestion
|
||||
{
|
||||
//represents the min size to judge levDistance with
|
||||
private static readonly HashSet<Tuple<int, int>> strictness = new HashSet<Tuple<int, int>> {
|
||||
new Tuple<int, int>(9, 0),
|
||||
new Tuple<int, int>(14, 1),
|
||||
new Tuple<int, int>(19, 2),
|
||||
new Tuple<int, int>(22, 3),
|
||||
};
|
||||
public static int maxStringLength = 22;
|
||||
|
||||
public string Category;
|
||||
public string Question;
|
||||
public string Answer;
|
||||
|
||||
public TriviaQuestion(string q, string a, string c)
|
||||
{
|
||||
this.Question = q;
|
||||
this.Answer = a;
|
||||
this.Category = c;
|
||||
}
|
||||
|
||||
public string GetHint() => Answer.Scramble();
|
||||
|
||||
public bool IsAnswerCorrect(string guess)
|
||||
{
|
||||
guess = CleanGuess(guess);
|
||||
if (Answer.Equals(guess))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
Answer = CleanGuess(Answer);
|
||||
guess = CleanGuess(guess);
|
||||
if (Answer.Equals(guess))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int levDistance = Answer.LevenshteinDistance(guess);
|
||||
return JudgeGuess(Answer.Length, guess.Length, levDistance);
|
||||
}
|
||||
|
||||
private bool JudgeGuess(int guessLength, int answerLength, int levDistance)
|
||||
{
|
||||
foreach (Tuple<int, int> level in strictness)
|
||||
{
|
||||
if (guessLength <= level.Item1 || answerLength <= level.Item1)
|
||||
{
|
||||
if (levDistance <= level.Item2)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private string CleanGuess(string str)
|
||||
{
|
||||
str = " " + str.ToLower() + " ";
|
||||
str = Regex.Replace(str, "\\s+", " ");
|
||||
str = Regex.Replace(str, "[^\\w\\d\\s]", "");
|
||||
//Here's where custom modification can be done
|
||||
str = Regex.Replace(str, "\\s(a|an|the|of|in|for|to|as|at|be)\\s", " ");
|
||||
//End custom mod and cleanup whitespace
|
||||
str = Regex.Replace(str, "^\\s+", "");
|
||||
str = Regex.Replace(str, "\\s+$", "");
|
||||
//Trim the really long answers
|
||||
str = str.Length <= maxStringLength ? str : str.Substring(0, maxStringLength);
|
||||
return str;
|
||||
}
|
||||
|
||||
public override string ToString() =>
|
||||
"Question: **" + this.Question + "?**";
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace NadekoBot.Modules.Games.Commands.Trivia
|
||||
{
|
||||
public class TriviaQuestionPool
|
||||
{
|
||||
public static TriviaQuestionPool Instance { get; } = new TriviaQuestionPool();
|
||||
|
||||
public HashSet<TriviaQuestion> pool = new HashSet<TriviaQuestion>();
|
||||
|
||||
private Random rng { get; } = new Random();
|
||||
|
||||
static TriviaQuestionPool() { }
|
||||
|
||||
private TriviaQuestionPool()
|
||||
{
|
||||
Reload();
|
||||
}
|
||||
|
||||
public TriviaQuestion GetRandomQuestion(IEnumerable<TriviaQuestion> exclude)
|
||||
{
|
||||
var list = pool.Except(exclude).ToList();
|
||||
var rand = rng.Next(0, list.Count);
|
||||
return list[rand];
|
||||
}
|
||||
|
||||
internal void Reload()
|
||||
{
|
||||
var arr = JArray.Parse(File.ReadAllText("data/questions.json"));
|
||||
|
||||
foreach (var item in arr)
|
||||
{
|
||||
var tq = new TriviaQuestion(item["Question"].ToString(), item["Answer"].ToString(), item["Category"]?.ToString());
|
||||
pool.Add(tq);
|
||||
}
|
||||
var r = new Random();
|
||||
pool = new HashSet<TriviaQuestion>(pool.OrderBy(x => r.Next()));
|
||||
}
|
||||
}
|
||||
}
|
76
src/NadekoBot/Modules/Games/Commands/TriviaCommand.cs
Normal file
76
src/NadekoBot/Modules/Games/Commands/TriviaCommand.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
//using Discord.Commands;
|
||||
//using NadekoBot.Classes;
|
||||
//using NadekoBot.Modules.Games.Commands.Trivia;
|
||||
//using System;
|
||||
//using System.Collections.Concurrent;
|
||||
//using System.Linq;
|
||||
|
||||
////todo DB
|
||||
////todo Rewrite?
|
||||
|
||||
//namespace NadekoBot.Modules.Games.Commands
|
||||
//{
|
||||
// internal class TriviaCommands : DiscordCommand
|
||||
// {
|
||||
// public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias = new ConcurrentDictionary<ulong, TriviaGame>();
|
||||
|
||||
// public TriviaCommands(DiscordModule module) : base(module)
|
||||
// {
|
||||
// }
|
||||
|
||||
// internal override void Init(CommandGroupBuilder cgb)
|
||||
// {
|
||||
// cgb.CreateCommand(Module.Prefix + "t")
|
||||
// .Description($"Starts a game of trivia. You can add nohint to prevent hints." +
|
||||
// "First player to get to 10 points wins by default. You can specify a different number. 30 seconds per question." +
|
||||
// $" |`{Module.Prefix}t nohint` or `{Module.Prefix}t 5 nohint`")
|
||||
// .Parameter("args", ParameterType.Multiple)
|
||||
// .Do(async e =>
|
||||
// {
|
||||
// TriviaGame trivia;
|
||||
// if (!RunningTrivias.TryGetValue(e.Server.Id, out trivia))
|
||||
// {
|
||||
// var showHints = !e.Args.Contains("nohint");
|
||||
// var number = e.Args.Select(s =>
|
||||
// {
|
||||
// int num;
|
||||
// return new Tuple<bool, int>(int.TryParse(s, out num), num);
|
||||
// }).Where(t => t.Item1).Select(t => t.Item2).FirstOrDefault();
|
||||
// if (number < 0)
|
||||
// return;
|
||||
// var triviaGame = new TriviaGame(e, showHints, number == 0 ? 10 : number);
|
||||
// if (RunningTrivias.TryAdd(e.Server.Id, triviaGame))
|
||||
// await imsg.Channel.SendMessageAsync($"**Trivia game started! {triviaGame.WinRequirement} points needed to win.**").ConfigureAwait(false);
|
||||
// else
|
||||
// await triviaGame.StopGame().ConfigureAwait(false);
|
||||
// }
|
||||
// else
|
||||
// await imsg.Channel.SendMessageAsync("Trivia game is already running on this server.\n" + trivia.CurrentQuestion).ConfigureAwait(false);
|
||||
// });
|
||||
|
||||
// cgb.CreateCommand(Module.Prefix + "tl")
|
||||
// .Description($"Shows a current trivia leaderboard. | `{Prefix}tl`")
|
||||
// .Do(async e =>
|
||||
// {
|
||||
// TriviaGame trivia;
|
||||
// if (RunningTrivias.TryGetValue(e.Server.Id, out trivia))
|
||||
// await imsg.Channel.SendMessageAsync(trivia.GetLeaderboard()).ConfigureAwait(false);
|
||||
// else
|
||||
// await imsg.Channel.SendMessageAsync("No trivia is running on this server.").ConfigureAwait(false);
|
||||
// });
|
||||
|
||||
// cgb.CreateCommand(Module.Prefix + "tq")
|
||||
// .Description($"Quits current trivia after current question. | `{Prefix}tq`")
|
||||
// .Do(async e =>
|
||||
// {
|
||||
// TriviaGame trivia;
|
||||
// if (RunningTrivias.TryGetValue(e.Server.Id, out trivia))
|
||||
// {
|
||||
// await trivia.StopGame().ConfigureAwait(false);
|
||||
// }
|
||||
// else
|
||||
// await imsg.Channel.SendMessageAsync("No trivia is running on this server.").ConfigureAwait(false);
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
//}
|
114
src/NadekoBot/Modules/Games/GamesModule.cs
Normal file
114
src/NadekoBot/Modules/Games/GamesModule.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using Discord.Commands;
|
||||
using Discord;
|
||||
using NadekoBot.Services;
|
||||
using System.Threading.Tasks;
|
||||
using NadekoBot.Attributes;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using NadekoBot.Extensions;
|
||||
|
||||
namespace NadekoBot.Modules.Games
|
||||
{
|
||||
[Module(">", AppendSpace = false)]
|
||||
public partial class GamesModule : DiscordModule
|
||||
{
|
||||
//todo DB
|
||||
private IEnumerable<string> _8BallResponses;
|
||||
public GamesModule(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
|
||||
{
|
||||
}
|
||||
|
||||
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Choose(IMessage imsg, [Remainder] string list)
|
||||
{
|
||||
var channel = imsg.Channel as IGuildChannel;
|
||||
if (string.IsNullOrWhiteSpace(list))
|
||||
return;
|
||||
var listArr = list.Split(';');
|
||||
if (listArr.Count() < 2)
|
||||
return;
|
||||
var rng = new Random();
|
||||
await imsg.Channel.SendMessageAsync(listArr[rng.Next(0, listArr.Length)]).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task _8Ball(IMessage imsg, [Remainder] string question)
|
||||
{
|
||||
var channel = imsg.Channel as IGuildChannel;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(question))
|
||||
return;
|
||||
var rng = new Random();
|
||||
await imsg.Channel.SendMessageAsync($@":question: `Question` __**{question}**__
|
||||
🎱 `8Ball Answers` __**{_8BallResponses.Shuffle().FirstOrDefault()}**__").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Rps(IMessage imsg, string input)
|
||||
{
|
||||
var channel = imsg.Channel as IGuildChannel;
|
||||
|
||||
Func<int,string> GetRPSPick = (p) =>
|
||||
{
|
||||
if (p == 0)
|
||||
return "rocket";
|
||||
else if (p == 1)
|
||||
return "paperclip";
|
||||
else
|
||||
return "scissors";
|
||||
};
|
||||
|
||||
int pick;
|
||||
switch (input)
|
||||
{
|
||||
case "r":
|
||||
case "rock":
|
||||
case "rocket":
|
||||
pick = 0;
|
||||
break;
|
||||
case "p":
|
||||
case "paper":
|
||||
case "paperclip":
|
||||
pick = 1;
|
||||
break;
|
||||
case "scissors":
|
||||
case "s":
|
||||
pick = 2;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
var nadekoPick = new Random().Next(0, 3);
|
||||
var msg = "";
|
||||
if (pick == nadekoPick)
|
||||
msg = $"It's a draw! Both picked :{GetRPSPick(pick)}:";
|
||||
else if ((pick == 0 && nadekoPick == 1) ||
|
||||
(pick == 1 && nadekoPick == 2) ||
|
||||
(pick == 2 && nadekoPick == 0))
|
||||
msg = $"{(await NadekoBot.Client.GetCurrentUserAsync()).Mention} won! :{GetRPSPick(nadekoPick)}: beats :{GetRPSPick(pick)}:";
|
||||
else
|
||||
msg = $"{imsg.Author.Mention} won! :{GetRPSPick(pick)}: beats :{GetRPSPick(nadekoPick)}:";
|
||||
|
||||
await imsg.Channel.SendMessageAsync(msg).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Linux(IMessage imsg, string guhnoo, string loonix)
|
||||
{
|
||||
var channel = imsg.Channel as IGuildChannel;
|
||||
|
||||
await imsg.Channel.SendMessageAsync(
|
||||
$@"I'd just like to interject for moment. What you're refering to as {loonix}, is in fact, {guhnoo}/{loonix}, or as I've recently taken to calling it, {guhnoo} plus {loonix}. {loonix} is not an operating system unto itself, but rather another free component of a fully functioning {guhnoo} system made useful by the {guhnoo} corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX.
|
||||
|
||||
Many computer users run a modified version of the {guhnoo} system every day, without realizing it. Through a peculiar turn of events, the version of {guhnoo} which is widely used today is often called {loonix}, and many of its users are not aware that it is basically the {guhnoo} system, developed by the {guhnoo} Project.
|
||||
|
||||
There really is a {loonix}, and these people are using it, but it is just a part of the system they use. {loonix} is the kernel: the program in the system that allocates the machine's resources to the other programs that you run. The kernel is an essential part of an operating system, but useless by itself; it can only function in the context of a complete operating system. {loonix} is normally used in combination with the {guhnoo} operating system: the whole system is basically {guhnoo} with {loonix} added, or {guhnoo}/{loonix}. All the so-called {loonix} distributions are really distributions of {guhnoo}/{loonix}."
|
||||
).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
@@ -8,6 +8,7 @@ using NadekoBot.Services;
|
||||
|
||||
namespace NadekoBot.Modules.Translator
|
||||
{
|
||||
[Module("~", AppendSpace = false)]
|
||||
public class TranslatorModule : DiscordModule
|
||||
{
|
||||
public TranslatorModule(ILocalization loc, CommandService cmds, IBotConfiguration config, IDiscordClient client) : base(loc, cmds, config, client)
|
||||
|
@@ -1,25 +0,0 @@
|
||||
using Discord.Commands;
|
||||
using NadekoBot.Classes;
|
||||
using NadekoBot.Modules.Translator.Helpers;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NadekoBot.Modules.Translator
|
||||
{
|
||||
class ValidLanguagesCommand : DiscordCommand
|
||||
{
|
||||
public ValidLanguagesCommand(DiscordModule module) : base(module) { }
|
||||
|
||||
internal override void Init(CommandGroupBuilder cgb)
|
||||
{
|
||||
cgb.CreateCommand(Module.Prefix + "translangs")
|
||||
.Description($"List the valid languages for translation. | `{Prefix}translangs` or `{Prefix}translangs language`")
|
||||
.Parameter("search", ParameterType.Optional)
|
||||
.Do(ListLanguagesFunc());
|
||||
}
|
||||
private Func<CommandEventArgs, Task> ListLanguagesFunc() => async e =>
|
||||
{
|
||||
|
||||
};
|
||||
}
|
||||
}
|
@@ -128,6 +128,7 @@ namespace NadekoBot.Modules.Utility
|
||||
}
|
||||
}
|
||||
|
||||
//todo maybe split into 3/4 different commands with the same name
|
||||
[LocalizedCommand, LocalizedDescription, LocalizedSummary]
|
||||
[RequireContext(ContextType.Guild)]
|
||||
public async Task Prune(IMessage msg, [Remainder] string target = null)
|
||||
|
Reference in New Issue
Block a user