Added some json files back. Added command cooldowns. Fixed some stuff

This commit is contained in:
Kwoth 2016-10-08 00:23:41 +02:00
parent ccb287c238
commit 8dbc7075da
15 changed files with 28795 additions and 1 deletions

View File

@ -0,0 +1,737 @@
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using NadekoBot.Services.Database.Impl;
namespace NadekoBot.Migrations
{
[DbContext(typeof(NadekoSqliteContext))]
[Migration("20161007210034_cmdcds")]
partial class cmdcds
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
modelBuilder
.HasAnnotation("ProductVersion", "1.0.0-rtm-21431");
modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<ulong>("ItemId");
b.Property<int>("Type");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("BlacklistItem");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.BotConfig", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("BufferSize");
b.Property<float>("CurrencyGenerationChance");
b.Property<int>("CurrencyGenerationCooldown");
b.Property<string>("CurrencyName");
b.Property<string>("CurrencyPluralName");
b.Property<string>("CurrencySign");
b.Property<string>("DMHelpString");
b.Property<bool>("DontJoinServers");
b.Property<bool>("ForwardMessages");
b.Property<bool>("ForwardToAllOwners");
b.Property<string>("HelpString");
b.Property<int>("MigrationVersion");
b.Property<string>("RemindMessageFormat");
b.Property<bool>("RotatingStatuses");
b.HasKey("Id");
b.ToTable("BotConfig");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("BaseDestroyed");
b.Property<string>("CallUser");
b.Property<int>("ClashWarId");
b.Property<int>("Stars");
b.Property<DateTime>("TimeAdded");
b.HasKey("Id");
b.HasIndex("ClashWarId");
b.ToTable("ClashCallers");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashWar", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<string>("EnemyClan");
b.Property<ulong>("GuildId");
b.Property<int>("Size");
b.Property<DateTime>("StartedAt");
b.Property<int>("WarState");
b.HasKey("Id");
b.ToTable("ClashOfClans");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("CommandName");
b.Property<int?>("GuildConfigId");
b.Property<int>("Seconds");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("CommandCooldown");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ConvertUnit", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("InternalTrigger");
b.Property<decimal>("Modifier");
b.Property<string>("UnitType");
b.HasKey("Id");
b.ToTable("ConversionUnits");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Currency", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<long>("Amount");
b.Property<ulong>("UserId");
b.HasKey("Id");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("Currency");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Donator", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("Amount");
b.Property<string>("Name");
b.Property<ulong>("UserId");
b.HasKey("Id");
b.HasIndex("UserId")
.IsUnique();
b.ToTable("Donators");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<string>("Text");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("EightBallResponses");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("GuildConfigId");
b.Property<int?>("GuildConfigId1");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.HasIndex("GuildConfigId1");
b.ToTable("FilterChannelId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("GuildConfigId");
b.Property<string>("Word");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("FilteredWord");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("GuildConfigId");
b.Property<ulong>("GuildId");
b.Property<bool>("LastStatus");
b.Property<int>("Type");
b.Property<string>("Username");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("FollowedStream");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("AutoAssignRoleId");
b.Property<bool>("AutoDeleteByeMessages");
b.Property<bool>("AutoDeleteGreetMessages");
b.Property<int>("AutoDeleteGreetMessagesTimer");
b.Property<bool>("AutoDeleteSelfAssignedRoleMessages");
b.Property<ulong>("ByeMessageChannelId");
b.Property<string>("ChannelByeMessageText");
b.Property<string>("ChannelGreetMessageText");
b.Property<float>("DefaultMusicVolume");
b.Property<bool>("DeleteMessageOnCommand");
b.Property<string>("DmGreetMessageText");
b.Property<bool>("ExclusiveSelfAssignedRoles");
b.Property<bool>("FilterInvites");
b.Property<bool>("FilterWords");
b.Property<ulong?>("GenerateCurrencyChannelId");
b.Property<ulong>("GreetMessageChannelId");
b.Property<ulong>("GuildId");
b.Property<int?>("LogSettingId");
b.Property<string>("PermissionRole");
b.Property<int?>("RootPermissionId");
b.Property<bool>("SendChannelByeMessage");
b.Property<bool>("SendChannelGreetMessage");
b.Property<bool>("SendDmGreetMessage");
b.Property<bool>("VerbosePermissions");
b.Property<bool>("VoicePlusTextEnabled");
b.HasKey("Id");
b.HasIndex("GuildId")
.IsUnique();
b.HasIndex("LogSettingId");
b.HasIndex("RootPermissionId");
b.ToTable("GuildConfigs");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("LogSettingId");
b.HasKey("Id");
b.HasIndex("LogSettingId");
b.ToTable("IgnoredLogChannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<int?>("LogSettingId");
b.HasKey("Id");
b.HasIndex("LogSettingId");
b.ToTable("IgnoredVoicePresenceCHannels");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.LogSetting", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<bool>("ChannelCreated");
b.Property<bool>("ChannelDestroyed");
b.Property<ulong>("ChannelId");
b.Property<bool>("ChannelUpdated");
b.Property<bool>("IsLogging");
b.Property<bool>("LogUserPresence");
b.Property<bool>("LogVoicePresence");
b.Property<bool>("MessageDeleted");
b.Property<bool>("MessageUpdated");
b.Property<bool>("UserBanned");
b.Property<bool>("UserJoined");
b.Property<bool>("UserLeft");
b.Property<ulong>("UserPresenceChannelId");
b.Property<bool>("UserUnbanned");
b.Property<bool>("UserUpdated");
b.Property<ulong>("VoicePresenceChannelId");
b.HasKey("Id");
b.ToTable("LogSettings");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int>("BotConfigId");
b.Property<string>("ModuleName");
b.Property<string>("Prefix");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("ModulePrefixes");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.MusicPlaylist", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Author");
b.Property<ulong>("AuthorId");
b.Property<string>("Name");
b.HasKey("Id");
b.ToTable("MusicPlaylists");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("NextId");
b.Property<int>("PrimaryTarget");
b.Property<ulong>("PrimaryTargetId");
b.Property<int>("SecondaryTarget");
b.Property<string>("SecondaryTargetName");
b.Property<bool>("State");
b.HasKey("Id");
b.HasIndex("NextId")
.IsUnique();
b.ToTable("Permission");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<string>("Status");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("PlayingStatus");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("MusicPlaylistId");
b.Property<string>("Provider");
b.Property<int>("ProviderType");
b.Property<string>("Query");
b.Property<string>("Title");
b.Property<string>("Uri");
b.HasKey("Id");
b.HasIndex("MusicPlaylistId");
b.ToTable("PlaylistSong");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Quote", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("AuthorId");
b.Property<string>("AuthorName")
.IsRequired();
b.Property<ulong>("GuildId");
b.Property<string>("Keyword")
.IsRequired();
b.Property<string>("Text")
.IsRequired();
b.HasKey("Id");
b.ToTable("Quotes");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<int?>("BotConfigId");
b.Property<string>("Icon");
b.Property<string>("Name");
b.HasKey("Id");
b.HasIndex("BotConfigId");
b.ToTable("RaceAnimals");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Reminder", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<bool>("IsPrivate");
b.Property<string>("Message");
b.Property<ulong>("ServerId");
b.Property<ulong>("UserId");
b.Property<DateTime>("When");
b.HasKey("Id");
b.ToTable("Reminders");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Repeater", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("ChannelId");
b.Property<ulong>("GuildId");
b.Property<TimeSpan>("Interval");
b.Property<string>("Message");
b.HasKey("Id");
b.HasIndex("ChannelId")
.IsUnique();
b.ToTable("Repeaters");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.SelfAssignedRole", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<ulong>("GuildId");
b.Property<ulong>("RoleId");
b.HasKey("Id");
b.HasIndex("GuildId", "RoleId")
.IsUnique();
b.ToTable("SelfAssignableRoles");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.TypingArticle", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("Author");
b.Property<string>("Text");
b.HasKey("Id");
b.ToTable("TypingArticles");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.BlacklistItem", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("Blacklist")
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ClashCaller", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.ClashWar", "ClashWar")
.WithMany("Bases")
.HasForeignKey("ClashWarId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("CommandCooldowns")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("EightBallResponses")
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilterChannelId", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FilterInvitesChannelIds")
.HasForeignKey("GuildConfigId");
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FilterWordsChannelIds")
.HasForeignKey("GuildConfigId1");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FilteredWord", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FilteredWords")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.FollowedStream", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("FollowedStreams")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.GuildConfig", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
.WithMany()
.HasForeignKey("LogSettingId");
b.HasOne("NadekoBot.Services.Database.Models.Permission", "RootPermission")
.WithMany()
.HasForeignKey("RootPermissionId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredLogChannel", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
.WithMany("IgnoredChannels")
.HasForeignKey("LogSettingId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.IgnoredVoicePresenceChannel", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.LogSetting", "LogSetting")
.WithMany("IgnoredVoicePresenceChannelIds")
.HasForeignKey("LogSettingId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ModulePrefix", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig", "BotConfig")
.WithMany("ModulePrefixes")
.HasForeignKey("BotConfigId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.Permission", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.Permission", "Next")
.WithOne("Previous")
.HasForeignKey("NadekoBot.Services.Database.Models.Permission", "NextId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlayingStatus", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("RotatingStatusMessages")
.HasForeignKey("BotConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.PlaylistSong", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.MusicPlaylist")
.WithMany("Songs")
.HasForeignKey("MusicPlaylistId")
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.RaceAnimal", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")
.WithMany("RaceAnimals")
.HasForeignKey("BotConfigId");
});
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Migrations;
namespace NadekoBot.Migrations
{
public partial class cmdcds : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "CommandCooldown",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("Autoincrement", true),
CommandName = table.Column<string>(nullable: true),
GuildConfigId = table.Column<int>(nullable: true),
Seconds = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_CommandCooldown", x => x.Id);
table.ForeignKey(
name: "FK_CommandCooldown_GuildConfigs_GuildConfigId",
column: x => x.GuildConfigId,
principalTable: "GuildConfigs",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
migrationBuilder.CreateIndex(
name: "IX_CommandCooldown_GuildConfigId",
table: "CommandCooldown",
column: "GuildConfigId");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "CommandCooldown");
}
}
}

View File

@ -115,6 +115,24 @@ namespace NadekoBot.Migrations
b.ToTable("ClashOfClans");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd();
b.Property<string>("CommandName");
b.Property<int?>("GuildConfigId");
b.Property<int>("Seconds");
b.HasKey("Id");
b.HasIndex("GuildConfigId");
b.ToTable("CommandCooldown");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.ConvertUnit", b =>
{
b.Property<int>("Id")
@ -613,6 +631,13 @@ namespace NadekoBot.Migrations
.OnDelete(DeleteBehavior.Cascade);
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.CommandCooldown", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.GuildConfig")
.WithMany("CommandCooldowns")
.HasForeignKey("GuildConfigId");
});
modelBuilder.Entity("NadekoBot.Services.Database.Models.EightBallResponse", b =>
{
b.HasOne("NadekoBot.Services.Database.Models.BotConfig")

View File

@ -30,7 +30,7 @@ namespace NadekoBot.Modules.Games.Trivia
public void Reload()
{
var arr = JArray.Parse(File.ReadAllText("data/triviaquestions.json"));
var arr = JArray.Parse(File.ReadAllText("data/questions.json"));
foreach (var item in arr)
{

View File

@ -0,0 +1,122 @@
using Discord;
using Discord.Commands;
using NadekoBot.Attributes;
using NadekoBot.Extensions;
using NadekoBot.Services;
using NadekoBot.Services.Database;
using NadekoBot.Services.Database.Models;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Modules.Permissions
{
public partial class Permissions
{
public class ActiveCooldown
{
public string Command { get; set; }
public ulong UserId { get; set; }
}
[Group]
public class CmdCdsCommands
{
public static ConcurrentDictionary<ulong, HashSet<CommandCooldown>> commandCooldowns { get; }
private static ConcurrentDictionary<ulong, HashSet<ActiveCooldown>> activeCooldowns = new ConcurrentDictionary<ulong, HashSet<ActiveCooldown>>();
static CmdCdsCommands()
{
using (var uow = DbHandler.UnitOfWork())
{
var configs = uow.GuildConfigs.GetAll();
commandCooldowns = new ConcurrentDictionary<ulong, HashSet<CommandCooldown>>(configs.ToDictionary(k => k.GuildId, v => v.CommandCooldowns));
}
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task CmdCooldown(IUserMessage imsg, Command command, int secs)
{
var channel = (ITextChannel)imsg.Channel;
if (secs < 0 || secs > 3600)
{
await channel.SendMessageAsync("Invalid second parameter. (Must be a number between 0 and 3600)").ConfigureAwait(false);
return;
}
using (var uow = DbHandler.UnitOfWork())
{
var config = uow.GuildConfigs.For(channel.Guild.Id);
var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new HashSet<CommandCooldown>());
config.CommandCooldowns.RemoveWhere(cc => cc.CommandName == command.Text.ToLowerInvariant());
localSet.RemoveWhere(cc => cc.CommandName == command.Text.ToLowerInvariant());
if (secs != 0)
{
var cc = new CommandCooldown()
{
CommandName = command.Text.ToLowerInvariant(),
Seconds = secs,
};
config.CommandCooldowns.Add(cc);
localSet.Add(cc);
}
await uow.CompleteAsync().ConfigureAwait(false);
}
if (secs == 0)
{
var activeCds = activeCooldowns.GetOrAdd(channel.Guild.Id, new HashSet<ActiveCooldown>());
activeCds.RemoveWhere(ac => ac.Command == command.Text.ToLowerInvariant());
await channel.SendMessageAsync($"Command **{command}** has no coooldown now and all existing cooldowns have been cleared.").ConfigureAwait(false);
}
else
await channel.SendMessageAsync($"Command **{command}** now has a **{secs} {(secs == 1 ? "second" : "seconds")}** cooldown.").ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
[RequireContext(ContextType.Guild)]
public async Task AllCmdCooldowns(IUserMessage imsg)
{
var channel = (ITextChannel)imsg.Channel;
var localSet = commandCooldowns.GetOrAdd(channel.Guild.Id, new HashSet<CommandCooldown>());
if (!localSet.Any())
await channel.SendMessageAsync("`No command cooldowns set.`").ConfigureAwait(false);
else
await channel.SendTableAsync("", localSet.Select(c => c.CommandName + ": " + c.Seconds + " secs"), s => $"{s,-30}", 2).ConfigureAwait(false);
}
public static bool HasCooldown(Command cmd, IGuild guild, IUser user)
{
var cmdcds = CmdCdsCommands.commandCooldowns.GetOrAdd(guild.Id, new HashSet<CommandCooldown>());
CommandCooldown cdRule;
if ((cdRule = cmdcds.FirstOrDefault(cc => cc.CommandName == cmd.Text.ToLowerInvariant())) != null)
{
var activeCdsForGuild = activeCooldowns.GetOrAdd(guild.Id, new HashSet<ActiveCooldown>());
if (activeCdsForGuild.FirstOrDefault(ac => ac.UserId == user.Id && ac.Command == cmd.Text.ToLowerInvariant()) != null)
{
return true;
}
else
{
activeCdsForGuild.Add(new ActiveCooldown()
{
UserId = user.Id,
Command = cmd.Text.ToLowerInvariant(),
});
var t = Task.Run(async () =>
{
await Task.Delay(cdRule.Seconds * 1000);
activeCdsForGuild.RemoveWhere(ac => ac.Command == cmd.Text.ToLowerInvariant() && ac.UserId == user.Id);
});
}
}
return false;
}
}
}
}

View File

@ -14,6 +14,8 @@ using NadekoBot.Modules.Permissions;
using Microsoft.Data.Sqlite;
using Discord.Net;
using NadekoBot.Extensions;
using static NadekoBot.Modules.Permissions.Permissions;
using System.Collections.Concurrent;
namespace NadekoBot.Services
{
@ -90,6 +92,8 @@ namespace NadekoBot.Services
}
}
try
{
bool verbose = false;
@ -235,6 +239,10 @@ namespace NadekoBot.Services
}
}
if (CmdCdsCommands.HasCooldown(cmd, guild, user))
return new Tuple<Command, IResult>(cmd, SearchResult.FromError(CommandError.Exception, $"That command is on cooldown for you."));
return new Tuple<Command, IResult>(commands[i], await commands[i].Execute(message, parseResult));
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NadekoBot.Services.Database.Models
{
public class CommandCooldown : DbEntity
{
public int Seconds { get; set; }
public string CommandName { get; set; }
}
}

View File

@ -47,6 +47,8 @@ namespace NadekoBot.Services.Database.Models
public bool VerbosePermissions { get; set; }
public string PermissionRole { get; set; } = "Nadeko";
public HashSet<CommandCooldown> CommandCooldowns { get; set; } = new HashSet<CommandCooldown>();
//filtering
public bool FilterInvites { get; set; }
public HashSet<FilterChannelId> FilterInvitesChannelIds { get; set; }

View File

@ -27,6 +27,7 @@ namespace NadekoBot.Services.Database.Repositories.Impl
.Include(gc => gc.FilterInvitesChannelIds)
.Include(gc => gc.FilterWordsChannelIds)
.Include(gc => gc.FilteredWords)
.Include(gc => gc.CommandCooldowns)
.ToList();
/// <summary>
@ -44,6 +45,7 @@ namespace NadekoBot.Services.Database.Repositories.Impl
.Include(gc => gc.FilterInvitesChannelIds)
.Include(gc => gc.FilterWordsChannelIds)
.Include(gc => gc.FilteredWords)
.Include(gc => gc.CommandCooldowns)
.FirstOrDefault(c => c.GuildId == guildId);
if (config == null)

View File

@ -0,0 +1,314 @@
[
{
"Name": "Ace of Spades",
"Description": "An ace of spades from a standard card deck. No matter where you store it on your body, you will always be able to find it in your right sleeve afterwards."
},
{
"Name": "Arrow of Euarere",
"Description": "A silver arrow, suspended on a string. It always points to the person holding the string."
},
{
"Name": "Amulet of Extra Amulet Slot",
"Description": "This amulet allows you to gain the benefit from two magical amulets rather than one. It cannot be further enchanted."
},
{
"Name": "Amulet of Feather Fall",
"Description": "When worn, this amulet turns into a feather and falls to the ground."
},
{
"Name": "Anti-Matches",
"Description": "A box of matches. Striking one will make it begin to drip water from the tip while the match shrivels away. The amount of water a match releases is about enough to fill a tablespoon."
},
{
"Name": "The Artist's Bludgeon",
"Description": "Inanimate objects hit with this bludgeon will receive no damage; they will however change color."
},
{
"Name": "Attentive Guardsman's Pike",
"Description": "These ornate and deadly-looking ceremonial pikes are reach weapons and appear to weigh at least 20 lbs, not counting the weight of the fluttering banners that can be unfurled for parade use. Constructed of shadowstuff, they weigh one pound, and inflict only a single point of damage on an attack, being almost entirely for show, although they also have the unique property of remaining in place when set (although unable to support more than 20 lbs), allowing a 'resting his eyes' guardsman to prop it up and leave it standing under its own power, while his hand sags off of it."
},
{
"Name": "Attentive Guardsman's Tabard",
"Description": "A dozen of these tabards were fashioned for palace guardsmen in the Empire of Sard, 250 miles from the nearest enemy. The bearer is placed under a glamour that causes him to appear alert and awake, even if his eyes are closed and he is snoring lightly."
},
{
"Name": "Axe of Big Numbers",
"Description": "This axe shouts \"Big numbers baby, come on!\" whenever it is swung, but always deals 1 damage or less."
},
{
"Name": "Axe of Empathy",
"Description": "Every time you hit something with this +5 greataxe, you get dealt an equal amount of damage. Both you and the thing you hit are then healed the amount of damage dealt by the axe, even if either are dead. The Axe hopes you have learned your lesson."
},
{
"Name": "Axe of Pain",
"Description": "The axe is always moaning and groaning with pain."
},
{
"Name": "Baby Oil",
"Description": "An aphrodisiac made from the finest mashed babies. Strangely unpopular in the upper planes, the judgmental prudes."
},
{
"Name": "Bag of Faerie Gold",
"Description": "This sack appears to be full of gold coins and jewels. When one attempts to spend them, however, the glamour on them soon vanishes, revealing them to be nothing but leaves and pebbles. Obviously, most shopkeepers will not be happy about this, and no amount of 'we didn't know, I swear!' will change their mind."
},
{
"Name": "Bag of Holding",
"Description": "This item functions as a normal backpack, however when attempting to retrieve an item, a calm female voice tells them there is a wait time of 4d10 minutes before they can retrieve their item (actual time is stated time plus 6d6 additional minutes). During this wait, the bag plays either annoying muzak or advertisements for the bag's creator's other products/services. Upon attempting to retrieve an item, there is a chance that the wrong item is retrieved, or that the intended item is simply missing. Obtaining the original item requires an additional 4d10+6d6 minutes and has only a 5% chance of success."
},
{
"Name": "Bag of Holding (Alternate)",
"Description": "This sack needs a hug!"
},
{
"Name": "Bag of Trading",
"Description": "You can take one thing out of the bag for each object you put in the bag. However, you have no control over what you get, and there are no trade-backs. Past research seems to imply there's some sort of correlation to what gets you what, but it's extremely convoluted and far from understood."
},
{
"Name": "Bag of Trick",
"Description": "This bag operates like a Bag of Tricks, except it only works once a week and produces a rat each time itis used."
},
{
"Name": "Bag of Unholding",
"Description": "Quite a large backpack but even the smallest item doesn't fit."
},
{
"Name": "Bagpipe of Stealth",
"Description": "Grants the user invisibility as long as it is being played."
},
{
"Name": "Ball of Eyes",
"Description": "A snow-globe filled with miniature eyeballs. When shaken, it grants the user a blurry, jittery vision of some future event."
},
{
"Name": "Banana Walkie-Talkies",
"Description": "There exist two, and only two, of these items in the world. One of which is possessed by a cranky and lonely half-orc. It appears to be an innocuous wooden banana with a coat of faded yellow paint. When an end (doesn't matter which one) is placed against your ear, you can hear a ringing followed by a *click* and a half-orc yelling at you for waking him up at this ungodly hour. If you drop the banana or \"hang up,\" the call ends. If you stay and listen, the half-orc will yell at you, call out obscenities, and start going on about his daily problems and mishaps in his love life. Every so often (2% chance/day), the banana will ring while you are sleeping and the half-orc will want to talk to you about his problems."
},
{
"Name": "Barrel of Holding",
"Description": "This large wooden barrel measuring &#8730;(12/&#960;) feet in diameter and 5 feet in height can hold up to 15 cubic feet of matter."
},
{
"Name": "Beam Sword of Severed Nerves",
"Description": "A beam sword. It cannot cut anything but nerve strings. Will pass through any other material leaving no harm."
},
{
"Name": "Belt of Pants",
"Description": "This belt creates illusory pants on the wearer. The wearer can suppress the illusion at will"
},
{
"Name": "Belt of Tightening",
"Description": "Every time you put this belt on, all of your clothes permanently shrink a fraction of a millimeter. The effect is compound."
},
{
"Name": "Belt of Unbathed Breath",
"Description": "When worn around the waist, allows the user to breathe underwater. Does not function when wet."
},
{
"Name": "Boogie Skeleton",
"Description": "This pile of bones is small, such as one that might be obtained from a bird or a toad, though it can look as though it came from any creature. When a song is sung or played in the vicinity of the skeleton, it begins to dance appropriately. As soon as the music stops, it collapses into the pile of bones again. The skeleton, when dancing, can be no larger than Diminutive."
},
{
"Name": "Book of Canon",
"Description": "A book that automatically transforms into a copy of the sacred text of any religion, translated into the language the user is most familiar with."
},
{
"Name": "Book of Confusion",
"Description": "The letters in this book always appear to be upside down, even if viewed from different directions at the same time. The book is a bad novel about zombies."
},
{
"Name": "Book of Curses",
"Description": "When opened, the book verbally berates anyone in the immediate vicinity, calling into question their combat ability, intellect, personal hygiene, lineage and profession of their mothers, and other delightful insults. Once closed the book continues shouting (although it is muffled) until placed inside a bag or some other similar container for 1d4+1 minutes and ignored. Replying to the book in any other way causes the insults to get louder and more childish the more time you spend replying to it."
},
{
"Name": "Book of Exalted Deeds",
"Description": "Contains a listing of some of the finest houses ever sold and the specifics of the titles to the properties."
},
{
"Name": "Boots of Levitation",
"Description": "These boots levitate a few inches off the ground when not worn."
},
{
"Name": "Boots of Stylishness",
"Description": "Knee high black boots that are always clean and shiny. They never take in water, thus feet are always dry."
},
{
"Name": "Boots of Walking",
"Description": "The wearer of the boots cannot run, nor can he take a double move action, and takes a -5 to Tumble checks. These boots are made for walkin', and that's just what they'll do."
},
{
"Name": "Bottle of Air",
"Description": "It's a bottle. Full of air. Congratulations."
},
{
"Name": "Bottomless Beer Mug",
"Description": "Any liquid poured into this mug treats the bottom as incorporeal, but solid objects don't"
},
{
"Name": "Bowl of Comfortable Warmth",
"Description": "Any liquid in the bowl will feel comfortably warm, so icy cold water will feel like it's a bit over room temperature. Do note, however, that it's still icy cold water, it just feels warmer."
},
{
"Name": "Breastplate of Secret Detection",
"Description": "If the wearer of this breastplate gains a piece of information that is somehow connected to the concealment of a hidden conspiracy or plot, a live and still wet red herring forms on the inside of the armor."
},
{
"Name": "Bullying Gloves",
"Description": "At random intervals, these gloves instil the wearer with a near-irresistible urge to hit themselves."
},
{
"Name": "Bunyans Belt",
"Description": "When worn, causes an enormous, bushy black beard to appear on the wearers face."
},
{
"Name": "Cape of Resistance",
"Description": "When this item is placed on any living thing it somehow manages to fall off, untie itself, slip past the owners neck entirely, or otherwise avoid being worn."
},
{
"Name": "Case of the Litigator",
"Description": "Translates any document placed in the case into legal jargon; non-reversible. Does not confer the ability to understand legal jargon."
},
{
"Name": "Cat of Schrodinger",
"Description": "When this cat is not being observed in any way it is both dead and alive. When something observes it, it suddenly becomes either dead or alive with a 50% chance of either."
},
{
"Name": "Chair of Steadiness",
"Description": "This chair can be moved but cannot be tipped over by anything less than a DC 35 Strength check."
},
{
"Name": "Charles",
"Description": "This small, unremarkable figurine of a gnome refuses to be called anything but Charles. No other name will leave the lips of the speaker. It has no other powers."
},
{
"Name": "Chime of Interruption",
"Description": "This instrument can be struck once every round, which takes a standard action. On any round the chime is activated the user may ready one action without spending an action to do so."
},
{
"Name": "Chime of Opening",
"Description": "Commonly affixed to or near doors, when pressed it emits a sound on the interior of the owners home to let them know guests have arrived."
},
{
"Name": "Chime of Opening (Alternate)",
"Description": "When struck against a solid surface, this chime emits a loud click, and opens along its length, to reveal a tiny compartment adequate to conceal a single 'smoke' worth of pipeweed or a blowgun needle. When the compartment is closed, it is seamless and can be detected only with a DC 20 Search check. If hit with an instrument such as a small mallet, it chimes."
},
{
"Name": "Cloak of Billowing",
"Description": "This black and silver cloak will always billow dramatically behind the wearer, it has no other effects."
},
{
"Name": "Cloak of Displacement, Minor",
"Description": "This item appears to be a normal cloak, but when worn by a character its magical properties distort and warp reality. When any attack is made against the wearer the cloak has a 20% chance of falling off, no matter how it is secured."
},
{
"Name": "Compacting hammer",
"Description": "The force imparted by it is multiplied, but is spread around the surface of a struck object facing inward."
},
{
"Name": "Cymbal of Symbols",
"Description": "This musical instrument enables the user to comprehend dead languages, but only while they are deafened by noise."
},
{
"Name": "Dagger of Told Secrets",
"Description": "A simple-looking dagger. If used to backstab someone to death, it will whisper your most embarrassing secret to that person."
},
{
"Name": "Dagger of Untold Secrets",
"Description": "A simple looking dagger. If used to backstab someone to death, it will whisper the most embarrassing secret of that person to you."
},
{
"Name": "Decanter of Endless Sorrow",
"Description": "A pewter flask that produces limitless alcohol when held to their lips by someone who is troubled. It gets them drunk but they never feel any better."
},
{
"Name": "Diadem of Brothaurity",
"Description": "When wearing this headpiece, you are as elegant and well-spoken as a famous diplomat or regent, but you can't stop calling everyone bro."
},
{
"Name": "Enchanted Book of Collected Stories",
"Description": "Opening this will cause miniature creatures/people to pour out and preform a chapter from the book much like a theater."
},
{
"Name": "Fade to Black Belt",
"Description": "The wearer of this belt will be unable to remember any sexual encounter begun while they were wearing the belt."
},
{
"Name": "Focusing Ring",
"Description": "The digit on which this ring is worn can be viewed in extremely high definition from a great distance."
},
{
"Name": "Gloves of Tinkering",
"Description": "Wearing the gloves will make you able to almost repair any broken item. However, you will always end up with pieces from the item that don't seem to fit anywhere."
},
{
"Name": "Greater Staff of Random Summoning",
"Description": "Summons a random creature at a random place. You could be summoning a giant Ogre on the other side of the globe for all you know."
},
{
"Name": "Hoarder's Wand",
"Description": "Does nothing but for some reason you think it might be important later in your quest."
},
{
"Name": "Hood of Offensive Facades",
"Description": "This hood will change your identity in the eyes of others to the appearance of the person they most personally dislike."
},
{
"Name": "Hood Of Worrisome Facades",
"Description": "This hood will change your identity in the eyes of others, however the identity used will be random."
},
{
"Name": "Indestructible Notebook of Memories",
"Description": "This otherwise normal notepad of normal notepad size cannot be damaged or destroyed, and anything written in it cannot be obscured or defaced. It also has unlimited pages despite its finite size. However, the data it holds only lasts as long as the writer independently remembers it, and decays in exact proportion to the relevant memories. Remember who and when, but not where? Then the words describing the location in that particular entry are the only ones gone."
},
{
"Name": "Intransigent Rod",
"Description": "When the button on this artifact is pressed in, the holder's opinions solidify and they become impossible to convince."
},
{
"Name": "Lunchbox of Delicious Unfulfillment",
"Description": "This lunchbox will hold whatever food you desire. However you will never get full and the food will deliver no nourishment."
},
{
"Name": "The Mattress of Poverty",
"Description": "No matter how you fluff this gorgeous, thick, mattress, you will always sleep on the thin part of it."
},
{
"Name": "Mug O' Dissatisfaction",
"Description": "A mug that always produces a steaming hot cup of coffee or tea when tapped on the bottom. It conjures the opposite of what the tapper prefers, so if you like tea you get coffee and vice versa. Handing the full mug to another person will make the drink in it transform to the opposite of that persons preferences."
},
{
"Name": "Murder Dagger",
"Description": "All damage it would deal is instead replaced by the target being harassed by crows for that many hours."
},
{
"Name": "Needle Of Learned Compromise",
"Description": "This needle will create beautiful tattoos of any design, however they hurt a tiny bit more. When used to sew it is entirely normal."
},
{
"Name": "Portable Dark Tavern Corner",
"Description": "Consisting of two wooden boards connected by a hinge, this artifact draws those nearby into assuming it is a perfect spot to conduct seedy business."
},
{
"Name": "Ring of First Impression",
"Description": "Wearing the ring will make you able to perform a perfect handshake with the hand wearing it."
},
{
"Name": "Sack of Hive Eggs",
"Description": "Crushing one of the numerous tiny eggs will cause the thoughts of everybody in the proximity to merge. Everybody can hear what you think and you can hear everybody."
},
{
"Name": "Shoes of the Restless Traveler",
"Description": "These shoes allow their user to run for miles without feeling fatigue, but if they try to do anything else with it (walk, sit down, jump), they will instantly trip"
},
{
"Name": "Sword of Parrying",
"Description": "Parries every attack, swinging it yourself will force it to \"parry\" your opponents weapon/attack even though he/she/it is defenseless."
},
{
"Name": "Vorpal Grindstone",
"Description": "It can \"sharpen\" any object to become vorpal. Any object."
},
{
"Name": "The Water Hat",
"Description": "A small red hat, when worn, causes water to pour from the wearer's fingers at the speed "
}
]

View File

@ -0,0 +1,21 @@
Copyright (c) 2011-2016 Guangcong Luo and other contributors
http://pokemonshowdown.com/
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell 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 copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,302 @@
[
{
"Question": "What do you call a gnome priest",
"Answer": "A compact disc"
},
{
"Question": "Why does the best raiding guild smell so bad?",
"Answer": "because they never wipe"
},
{
"Question": "Why are boomkins neither overpowered or underpowered?",
"Answer": "Because they're Balanced"
},
{
"Question": "Who is George of the Jungle's other brother?",
"Answer": "Mark of the Wild."
},
{
"Question": "What do you call a masturbating Tauren?",
"Answer": "Beef Stroganoff."
},
{
"Question": "What's a rogue's favourite drink?",
"Answer": "Subtle Tea"
},
{
"Question": "Classic: What to noobs and Rogues have in common?",
"Answer": "They both pick locks!"
},
{
"Question": "What did Gul'dan do when he tripped?",
"Answer": "He fel"
},
{
"Question": "Why didn't the warrior cross the road?",
"Answer": "No Path Available."
},
{
"Question": "What are the chances we have gotten the last content patch before a new expansion?",
"Answer": "Slim Tanaan"
},
{
"Question": "A trolls greeting is usually \"Eyy mon\" but what is a goblins?",
"Answer": "\"\"Mon-eyy\"\n\n^^^^^I'm ^^^^^so ^^^^^sorry..."
},
{
"Question": "What musician is from ICC?",
"Answer": "What musician is from ICC?\n\nAn Arthas formally known as Prince."
},
{
"Question": "How do you know if someone's been playing since Vanilla?",
"Answer": "Don't worry, they'll tell you"
},
{
"Question": "What happens when a demon forgets his sunscreen?",
"Answer": "He ends up with a Burning Region"
},
{
"Question": "Why are raid guilds recruiting rogues for the illidan fight?",
"Answer": "Because they are the only ones that have the required [preparation](http://www.wowhead.com/spell=14185/preparation) "
},
{
"Question": "How does Naxxramas fly?",
"Answer": "with it's four wings"
},
{
"Question": "Why is the Cenarion Circle neutral?",
"Answer": "Circles have no sides."
},
{
"Question": "Why are rogues the best bar tenders?",
"Answer": "Because they always have cheap shots "
},
{
"Question": "What does Gul'dan like on his hotdogs?",
"Answer": "EVERYTHIIING"
},
{
"Question": "Why did a Felhunter win the spelling competition?",
"Answer": "Nobody else could Spell Lock."
},
{
"Question": "What do you call it when Illidan teaches demon hunters in Legion?",
"Answer": "a 'demon-stration\""
},
{
"Question": "Why can't Paladins work out at Planet Fitness?",
"Answer": "Because it's a Judgement-free zone. "
},
{
"Question": "Why didn't the rogue like his dagger upgrade?",
"Answer": "He wasn't a fan of knives."
},
{
"Question": "What do you call a Gilnean church?",
"Answer": "Worgenized religion"
},
{
"Question": "Why were night elves chosen to be Azeroth's first astronauts?",
"Answer": "Because they know the moon well."
},
{
"Question": "Why are gnomes unable to be paladins?",
"Answer": "Because they cannot reach the Light."
},
{
"Question": "Did you hear about the resto druid who never plays guardian?",
"Answer": "You could say he's all bark, no bite."
},
{
"Question": "What's a dwarf rogue's favorite car?",
"Answer": "Dodge Ram"
},
{
"Question": "Why can you only wear 1 shoe in the Emerald nightmare?",
"Answer": "Because they'll steal Ursoc."
},
{
"Question": "Why is it impossible for a paladin and a rogue to make a baby?",
"Answer": "Because paladins use protection and rogues do it from behind.\n\n\nTaken from Sodapoppin's stream on December 1st. "
},
{
"Question": "Do you know how Illidan hurt his knee?",
"Answer": "(this one came through in a GM ticket response today)\n\nDo you know how Illidan hurt his knee?\nIt's simple really...\nHe fel."
},
{
"Question": "Icecrown Citadel was a pretty cool raid.",
"Answer": "Even thinking about it gives me the chills."
},
{
"Question": "Why do Blood Elves tan so quickly?",
"Answer": "They use the Sun Well."
},
{
"Question": "What is Bolvar doing lately?",
"Answer": "Just chillin."
},
{
"Question": "What do you call 4 Mogu rolling down a hill?",
"Answer": "The Rolling Stones."
},
{
"Question": "What is Taran Zhu's favorite cooking utensil?",
"Answer": "The Shado-Pan"
},
{
"Question": "What do you call a vapid celebrity female night elf?",
"Answer": "Kim Darnassian"
},
{
"Question": "Warlords of Draenor was an emotional expansion.",
"Answer": "Even the raids were in tiers."
},
{
"Question": "Why do hunters never get married?",
"Answer": "Because they're always dis-engaging."
},
{
"Question": "What do you call a kind warlock",
"Answer": "Affection lock"
},
{
"Question": "Which dragon has the dream job?",
"Answer": "Ysera"
},
{
"Question": "Healers are like artists",
"Answer": "No one appreciates them until they are dead"
},
{
"Question": "What did Illidan say when one of his group mates' gear was broken?",
"Answer": "\"YOU ARE NOT REPAIRED!\""
},
{
"Question": "Why do bars hate rogues?",
"Answer": "Because they only want cheap shots."
},
{
"Question": "What did the game tell the hunter when he dinged 40?",
"Answer": "You've got mail."
},
{
"Question": "What do you call a Blood Elf who eats his vegetables?",
"Answer": "Kale'Thas"
},
{
"Question": "Where do murlocs store their gold and treasure?",
"Answer": "At the River Bank!"
},
{
"Question": "Why did the undead smell bad?",
"Answer": "He had no nose."
},
{
"Question": "How did the Rogue one-shot Illidan?",
"Answer": "By using Preparation."
},
{
"Question": "What's the first thing Illidan sees when he wakes up?",
"Answer": "NOTHING"
},
{
"Question": "Why are all paladins so clean?",
"Answer": "...Because they are always taking Bubble baths!"
},
{
"Question": "did you hear about the monk serial killer",
"Answer": "the murders were pre-meditated"
},
{
"Question": "How much does a serving of Pandaren Cuisine weigh?",
"Answer": "About wonton."
},
{
"Question": "What injury did Gul'Dan get from playing Tennis.",
"Answer": "Twisted Nethers."
},
{
"Question": "A patch day joke...",
"Answer": ""
},
{
"Question": "Yo mamma so fat....",
"Answer": "I tried to shadowstep her and I got a loading screen!"
},
{
"Question": "What do you call it when the Argent Tournament catches on fire?",
"Answer": "Burning Crusade."
},
{
"Question": "Why can you never take a good photo of a hunter?",
"Answer": "They are always out of focus"
},
{
"Question": "How do impoverished Warriors get their weapons?",
"Answer": "They Rend-to-own."
},
{
"Question": "What is Arthas' favorite camping snack?",
"Answer": "Frostsmores"
},
{
"Question": "Why are warlocks faster after drinking vodka?",
"Answer": "Because it gives them a Burning Rush!"
},
{
"Question": "How did the druid catch a fish?",
"Answer": "With his bear hands"
},
{
"Question": "I really want the new Grove Warden moose mount...",
"Answer": "...you could say, I consider it a moost-have."
},
{
"Question": "Heard about the time Millhouse Manastorm went to Blackrock Foundry to visit Oregorger?",
"Answer": "Me neither, but I've been assured there will be gnome ore puns."
},
{
"Question": "So my new maid is a rogue...",
"Answer": "... And she keeps on using [Vanish](http://www.brockaghltd.com/wp/wp-content/uploads/2014/11/31900_4.1405683094.jpg) even when I told her I prefer [Resolve](http://ecx.images-amazon.com/images/I/81a1jTSQzbL._SY355_.jpg). I'm so fed up with her!"
},
{
"Question": "What do fire elementals eat for breakfast?",
"Answer": "Ragnar-Os"
},
{
"Question": "Why do mages share cane's?",
"Answer": "Because it's not his cane, it's R Cane!"
},
{
"Question": "Why do sneaky rogues prefer leather armor?",
"Answer": "because it's made of hide"
},
{
"Question": "What's it called when an animal spirit crashes your UI?",
"Answer": "A LOA error."
},
{
"Question": "Man the 90s take so long",
"Answer": ""
},
{
"Question": "What is Shadow-Lord Iskar's problem?",
"Answer": "Every time we get to him in Hellfire Citadel, he always seems to be in a Fowl mood."
},
{
"Question": "Why are hunters bad at photography?",
"Answer": "They have no focus."
},
{
"Question": "Why are paladins good at photography?",
"Answer": "They use the Light"
},
{
"Question": "How did the paladins get clean?",
"Answer": "They had a bubble bath!"
},
{
"Question": "You need to be careful of the trees in this game.",
"Answer": "Some of them are shady."
}
]