Merge pull request #238 from appelemac/patch-4

Custom Reactions
This commit is contained in:
Master Kwoth 2016-05-05 16:10:55 +02:00
commit 2a63e5bbda
8 changed files with 332 additions and 233 deletions

View File

@ -28,6 +28,7 @@ namespace NadekoBot.Modules.Administration
commands.Add(new SelfAssignedRolesCommand(this));
commands.Add(new Remind(this));
commands.Add(new InfoCommands(this));
commands.Add(new CustomReactionsCommands(this));
}
public override string Prefix { get; } = NadekoBot.Config.CommandPrefixes.Administration;

View File

@ -0,0 +1,131 @@
using NadekoBot.Classes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Discord.Commands;
using NadekoBot.Modules.Permissions.Classes;
using Discord;
namespace NadekoBot.Modules.Administration.Commands
{
class CustomReactionsCommands : DiscordCommand
{
public CustomReactionsCommands(DiscordModule module) : base(module)
{
}
internal override void Init(CommandGroupBuilder cgb)
{
var Prefix = Module.Prefix;
cgb.CreateCommand(Prefix + "addcustomreaction")
.Alias(Prefix + "acr")
.Description($"Add a custom reaction. **Owner Only!**\n**Usage**: {Prefix}acr \"hello\" I love saying hello to %user%")
.AddCheck(SimpleCheckers.OwnerOnly())
.Parameter("name", ParameterType.Required)
.Parameter("message", ParameterType.Unparsed)
.Do(async e =>
{
var name = e.GetArg("name");
var message = e.GetArg("message").Trim();
if (string.IsNullOrWhiteSpace(message))
{
await e.Channel.SendMessage($"Incorrect command usage. See -h {Prefix}acr for correct formatting").ConfigureAwait(false);
return;
}
try
{
NadekoBot.Config.CustomReactions[name].Add(message);
}
catch (KeyNotFoundException)
{
NadekoBot.Config.CustomReactions.Add(name, new System.Collections.Generic.List<string>() { message });
}
finally
{
Classes.JSONModels.ConfigHandler.SaveConfig();
}
await e.Channel.SendMessage($"Added {name} : {message}").ConfigureAwait(false);
});
cgb.CreateCommand(Prefix + "listcustomreactions")
.Alias(Prefix + "lcr")
.Description("Lists all current custom reactions (paginated with 5 commands per page).\n**Usage**:.lcr 1")
.Parameter("num", ParameterType.Required)
.Do(async e =>
{
int num;
if (!int.TryParse(e.GetArg("num"), out num)) return;
string result = getCustomsOnPage(num -1); //People prefer starting with 1
await e.Channel.SendMessage(result);
});
cgb.CreateCommand(Prefix + "deletecustomreaction")
.Alias(Prefix + "dcr")
.Description("Deletes a custome reaction with given name (and index)")
.Parameter("name", ParameterType.Required)
.Parameter("index", ParameterType.Optional)
.Do(async e =>
{
var name = e.GetArg("name");
if (!NadekoBot.Config.CustomReactions.ContainsKey(name))
{
await e.Channel.SendMessage("Could not find given commandname");
return;
}
string message = "";
int index;
if (int.TryParse(e.GetArg("index") ?? "", out index))
{
try
{
NadekoBot.Config.CustomReactions[name].RemoveAt(index - 1);
if (!NadekoBot.Config.CustomReactions[name].Any())
{
NadekoBot.Config.CustomReactions.Remove(name);
}
message = $"Deleted response #{index} from {name}";
}
catch (ArgumentOutOfRangeException)
{
await e.Channel.SendMessage("Index given was out of range").ConfigureAwait(false);
return;
}
}
else
{
NadekoBot.Config.CustomReactions.Remove(name);
message = $"Deleted custom reaction \"{name}\"";
}
Classes.JSONModels.ConfigHandler.SaveConfig();
await e.Channel.SendMessage(message);
});
}
private readonly int ItemsPerPage = 5;
private string getCustomsOnPage(int page)
{
var items = NadekoBot.Config.CustomReactions.Skip(page * ItemsPerPage).Take(ItemsPerPage);
if(!items.Any())
{
return $"No items on page {page}.";
}
string message = $"Custom reactions of page {page + 1}:";
foreach (var cr in items)
{
message += $"\n**\"{Format.Escape(cr.Key)}\"**:";
int i = 1;
foreach (var reaction in cr.Value)
{
message += "\n " + i++ + "." + Format.Code(reaction);
}
}
return message;
}
}
}

View File

@ -33,48 +33,7 @@ namespace NadekoBot.Modules.Conversations
manager.CreateCommands("", cgb =>
{
cgb.AddCheck(PermissionChecker.Instance);
cgb.CreateCommand("e")
.Description("You did it. Or someone else!")
.Parameter("other", ParameterType.Unparsed)
.Do(async e =>
{
var other = e.GetArg("other");
if (string.IsNullOrWhiteSpace(other))
await e.Channel.SendMessage($"{e.User.Name} did it. 😒 🔫").ConfigureAwait(false);
else
await e.Channel.SendMessage($"{other} did it. 😒 🔫").ConfigureAwait(false);
});
cgb.CreateCommand("comeatmebro")
.Description("Come at me bro (ง’̀-‘́)ง \n**Usage**: comeatmebro {target}")
.Parameter("target", ParameterType.Optional)
.Do(async e =>
{
var usr = e.Server.FindUsers(e.GetArg("target")).FirstOrDefault();
if (usr == null)
{
await e.Channel.SendMessage("(ง’̀-‘́)ง").ConfigureAwait(false);
return;
}
await e.Channel.SendMessage($"{usr.Mention} (ง’̀-‘́)ง").ConfigureAwait(false);
});
cgb.CreateCommand("\\o\\")
.Description("Nadeko replies with /o/")
.Do(async e => await e.Channel.SendMessage(e.User.Mention + "/o/").ConfigureAwait(false));
cgb.CreateCommand("/o/")
.Description("Nadeko replies with \\o\\")
.Do(async e => await e.Channel.SendMessage(e.User.Mention + "\\o\\").ConfigureAwait(false));
cgb.CreateCommand("moveto")
.Description("Suggests moving the conversation.\n**Usage**: moveto #spam")
.Parameter("target", ParameterType.Unparsed)
.Do(async e => await e.Channel.SendMessage($"(👉 ͡° ͜ʖ ͡°)👉 {e.GetArg("target")}"));
cgb.CreateCommand("..")
.Description("Adds a new quote with the specified name (single word) and message (no limit).\n**Usage**: .. abc My message")
.Parameter("keyword", ParameterType.Required)
@ -180,124 +139,7 @@ namespace NadekoBot.Modules.Conversations
await e.Channel.SendMessage(e.User.Mention + " I am sad. My Master is not with me.").ConfigureAwait(false);
}
});
cgb.CreateCommand("insult")
.Parameter("mention", ParameterType.Required)
.Description("Insults @X person.\n**Usage**: @NadekoBot insult @X.")
.Do(async e =>
{
var u = e.Channel.FindUsers(e.GetArg("mention")).FirstOrDefault();
if (u == null)
{
await e.Channel.SendMessage("Invalid user specified.").ConfigureAwait(false);
return;
}
if (NadekoBot.IsOwner(u.Id))
{
await e.Channel.SendMessage("I would never insult my master <3").ConfigureAwait(false);
return;
}
await e.Channel.SendMessage(u.Mention + NadekoBot.Locale.Insults[rng.Next(0, NadekoBot.Locale.Insults.Length)]).ConfigureAwait(false);
});
cgb.CreateCommand("praise")
.Description("Praises @X person.\n**Usage**: @NadekoBot praise @X.")
.Parameter("mention", ParameterType.Required)
.Do(async e =>
{
var u = e.Channel.FindUsers(e.GetArg("mention")).FirstOrDefault();
if (u == null)
{
await e.Channel.SendMessage("Invalid user specified.").ConfigureAwait(false);
return;
}
if (NadekoBot.IsOwner(u.Id))
{
await e.Channel.SendMessage(e.User.Mention + " I don't need your permission to praise my beloved Master <3").ConfigureAwait(false);
return;
}
await e.Channel.SendMessage(u.Mention + NadekoBot.Locale.Praises[rng.Next(0, NadekoBot.Locale.Praises.Length)]).ConfigureAwait(false);
});
cgb.CreateCommand("pat")
.Description("Pat someone ^_^")
.Parameter("user", ParameterType.Unparsed)
.Do(async e =>
{
var userStr = e.GetArg("user");
if (string.IsNullOrWhiteSpace(userStr) || !e.Message.MentionedUsers.Any()) return;
var user = e.Server.FindUsers(userStr).FirstOrDefault();
if (user == null)
return;
try
{
await e.Channel.SendMessage(
$"{user.Mention} " +
$"{NadekoBot.Config.PatResponses[rng.Next(0, NadekoBot.Config.PatResponses.Length)]}")
.ConfigureAwait(false);
}
catch
{
await e.Channel.SendMessage("Error while handling PatResponses check your data/config.json").ConfigureAwait(false);
}
});
cgb.CreateCommand("cry")
.Description("Tell Nadeko to cry. You are a heartless monster if you use this command.")
.Do(async e =>
{
try
{
await
e.Channel.SendMessage(
$"(•̥́ _•ૅ。)\n{NadekoBot.Config.CryResponses[rng.Next(0, NadekoBot.Config.CryResponses.Length)]}")
.ConfigureAwait(false);
}
catch
{
await e.Channel.SendMessage("Error while handling CryResponses check your data/config.json").ConfigureAwait(false);
}
});
cgb.CreateCommand("disguise")
.Description("Tell Nadeko to disguise herself.")
.Do(async e =>
{
try
{
await
e.Channel.SendMessage(
$"{NadekoBot.Config.DisguiseResponses[rng.Next(0, NadekoBot.Config.DisguiseResponses.Length)]}")
.ConfigureAwait(false);
}
catch
{
await e.Channel.SendMessage("Error while handling DisguiseResponses check your data/config.json")
.ConfigureAwait(false);
}
});
cgb.CreateCommand("are you real")
.Description("Useless.")
.Do(async e =>
{
await e.Channel.SendMessage(e.User.Mention + " I will be soon.").ConfigureAwait(false);
});
cgb.CreateCommand("are you there")
.Description("Checks if Nadeko is operational.")
.Alias("!", "?")
.Do(SayYes());
cgb.CreateCommand("draw")
.Description("Nadeko instructs you to type $draw. Gambling functions start with $")
.Do(async e =>
{
await e.Channel.SendMessage("Sorry, I don't gamble, type $draw for that function.").ConfigureAwait(false);
});
cgb.CreateCommand("fire")
.Description("Shows a unicode fire message. Optional parameter [x] tells her how many times to repeat the fire.\n**Usage**: @NadekoBot fire [x]")
.Parameter("times", ParameterType.Optional)
@ -394,28 +236,7 @@ namespace NadekoBot.Modules.Conversations
else
await e.Channel.SendMessage("I can't find a message mentioning you.").ConfigureAwait(false);
});
cgb.CreateCommand("bb")
.Description("Says bye to someone.\n**Usage**: @NadekoBot bb @X")
.Parameter("ppl", ParameterType.Unparsed)
.Do(async e =>
{
var str = "Bye";
foreach (var u in e.Message.MentionedUsers)
{
if (u.Id != NadekoBot.Client.CurrentUser.Id)
str += " " + u.Mention;
}
await e.Channel.SendMessage(str).ConfigureAwait(false);
});
cgb.CreateCommand("call")
.Description("Useless. Writes calling @X to chat.\n**Usage**: @NadekoBot call @X ")
.Parameter("who", ParameterType.Required)
.Do(async e =>
{
await e.Channel.SendMessage("Calling " + e.Args[0] + "...").ConfigureAwait(false);
});
cgb.CreateCommand("hide")
.Description("Hides Nadeko in plain sight!11!!")
.Do(async e =>

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Discord.Modules;
using Discord.Commands;
using NadekoBot.Modules.Permissions.Classes;
using NadekoBot.Extensions;
using System.Text.RegularExpressions;
namespace NadekoBot.Modules.CustomReactions
{
class CustomReactionsModule : DiscordModule
{
public override string Prefix { get; } = "";
public override void Install(ModuleManager manager)
{
manager.CreateCommands("", cgb =>
{
cgb.AddCheck(PermissionChecker.Instance);
Random range = new Random();
Dictionary<string, Func<CommandEventArgs, string>> commandFuncs = new Dictionary<string, Func<CommandEventArgs, string>>
{
{"%rng%", (e) => range.Next().ToString()},
{"%mention%", (e) => NadekoBot.BotMention },
{"%user%", e => e.User.Mention },
{"%target%", e => e.GetArg("args")?.Trim() ?? "" },
};
foreach (var command in NadekoBot.Config.CustomReactions)
{
var commandName = command.Key.Replace("%mention%", NadekoBot.BotMention);
var c = cgb.CreateCommand(commandName);
c.Description($"Custom reaction.\n**Usage**:{command.Key}");
c.Parameter("args", ParameterType.Unparsed);
c.Do(async e =>
{
string str = command.Value[range.Next(0, command.Value.Count())];
commandFuncs.Keys.ForEach(k => str = str.Replace(k, commandFuncs[k](e)));
await e.Channel.SendMessage(str).ConfigureAwait(false);
});
}
});
}
}
}

View File

@ -26,6 +26,7 @@ using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NadekoBot.Modules.CustomReactions;
namespace NadekoBot
{
@ -183,7 +184,7 @@ namespace NadekoBot
modules.Add(new ClashOfClansModule(), "ClashOfClans", ModuleFilter.None);
modules.Add(new PokemonModule(), "Pokegame", ModuleFilter.None);
modules.Add(new TranslatorModule(), "Translator", ModuleFilter.None);
//modules.Add(new ProgrammingModule(), "Programming", ModuleFilter.None);
modules.Add(new CustomReactionsModule(), "Customreactions", ModuleFilter.None);
if (!string.IsNullOrWhiteSpace(Creds.TrelloAppKey))
modules.Add(new TrelloModule(), "Trello", ModuleFilter.None);
@ -293,4 +294,4 @@ namespace NadekoBot
}
}
//95520984584429568 meany
//95520984584429568 meany

View File

@ -120,9 +120,11 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Modules\Administration\Commands\CustomReactionsCommands.cs" />
<Compile Include="Modules\ClashOfClans\ClashOfClans.cs" />
<Compile Include="Classes\DBHandler.cs" />
<Compile Include="Classes\FlowersHandler.cs" />
<Compile Include="Modules\CustomReactions\CustomReactions.cs" />
<Compile Include="Modules\Programming\Commands\HaskellRepl.cs" />
<Compile Include="Modules\Programming\ProgrammingModule.cs" />
<Compile Include="Modules\Searches\Commands\IMDB\ImdbMovie.cs" />

View File

@ -17,7 +17,78 @@ namespace NadekoBot.Classes.JSONModels
[JsonIgnore]
public List<PokemonType> PokemonTypes { get; set; } = new List<PokemonType>();
public Dictionary<string, List<string>> CustomReactions { get; set; } = new Dictionary<string, List<string>>()
{
{@"\o\", new List<string>()
{ "/o/" } },
{"/o/", new List<string>()
{ @"\o\" } },
{"moveto", new List<string>() {
@"(👉 ͡° ͜ʖ ͡°)👉 %target%" } },
{"comeatmebro", new List<string>() {
"%target% (ง’̀-‘́)ง" } },
{"e", new List<string>() {
"%user% did it 😒 🔫",
"%target% did it 😒 🔫" } },
{"%mention% insult", new List<string>() {
"%target% You are a poop.",
"%target% You're a jerk.",
"%target% I will eat you when I get my powers back."
} },
{"%mention% praise", new List<string>()
{
"%target% You are cool.",
"%target% You are nice!",
"%target% You did a good job.",
"%target% You did something nice.",
"%target% is awesome!",
"%target% Wow."
} },
{"%mention% pat", new List<string>() {
"http://i.imgur.com/IiQwK12.gif",
"http://i.imgur.com/JCXj8yD.gif",
"http://i.imgur.com/qqBl2bm.gif",
"http://i.imgur.com/eOJlnwP.gif",
"https://45.media.tumblr.com/229ec0458891c4dcd847545c81e760a5/tumblr_mpfy232F4j1rxrpjzo1_r2_500.gif",
"https://media.giphy.com/media/KZQlfylo73AMU/giphy.gif",
"https://media.giphy.com/media/12hvLuZ7uzvCvK/giphy.gif",
"http://gallery1.anivide.com/_full/65030_1382582341.gif",
"https://49.media.tumblr.com/8e8a099c4eba22abd3ec0f70fd087cce/tumblr_nxovj9oY861ur1mffo1_500.gif ",
} },
{"%mention% cry", new List<string>()
{
"http://i.imgur.com/Xg3i1Qy.gif",
"http://i.imgur.com/3K8DRrU.gif",
"http://i.imgur.com/k58BcAv.gif",
"http://i.imgur.com/I2fLXwo.gif"
} },
{"%mention% are you real?", new List<string>()
{
"%user%, I will be soon."
} },
{"%mention% are you there?", new List<string>()
{
"Yes. :)"
} },
{"%mention% draw", new List<string>() {
"Sorry, I don't gamble, type $draw for that function."
} },
{"%mention% bb", new List<string>()
{
"Bye %target%"
} },
{"%mention% call", new List<string>() {
"Calling %target%"
} },
{"%mention% disguise", new List<string>() {
"https://cdn.discordapp.com/attachments/140007341880901632/156721710458994690/Cc5mixjUYAADgBs.jpg",
"https://cdn.discordapp.com/attachments/140007341880901632/156721715831898113/hqdefault.jpg",
"https://cdn.discordapp.com/attachments/140007341880901632/156721724430352385/okawari_01_haruka_weird_mask.jpg",
"https://cdn.discordapp.com/attachments/140007341880901632/156721728763068417/mustache-best-girl.png"
} }
};
public List<string> RotatingStatuses { get; set; } = new List<string>();
public CommandPrefixesModel CommandPrefixes { get; set; } = new CommandPrefixesModel();
public HashSet<ulong> ServerBlacklist { get; set; } = new HashSet<ulong>();
@ -55,32 +126,6 @@ namespace NadekoBot.Classes.JSONModels
"NO - It may cause disease contraction"
};
public string[] DisguiseResponses { get; set; } = {
"https://cdn.discordapp.com/attachments/140007341880901632/156721710458994690/Cc5mixjUYAADgBs.jpg",
"https://cdn.discordapp.com/attachments/140007341880901632/156721715831898113/hqdefault.jpg",
"https://cdn.discordapp.com/attachments/140007341880901632/156721724430352385/okawari_01_haruka_weird_mask.jpg",
"https://cdn.discordapp.com/attachments/140007341880901632/156721728763068417/mustache-best-girl.png"
};
public string[] CryResponses { get; set; } = {
"http://i.imgur.com/Xg3i1Qy.gif",
"http://i.imgur.com/3K8DRrU.gif",
"http://i.imgur.com/k58BcAv.gif",
"http://i.imgur.com/I2fLXwo.gif"
};
public string[] PatResponses { get; set; } = {
"http://i.imgur.com/IiQwK12.gif",
"http://i.imgur.com/JCXj8yD.gif",
"http://i.imgur.com/qqBl2bm.gif",
"http://i.imgur.com/eOJlnwP.gif",
"https://45.media.tumblr.com/229ec0458891c4dcd847545c81e760a5/tumblr_mpfy232F4j1rxrpjzo1_r2_500.gif",
"https://media.giphy.com/media/KZQlfylo73AMU/giphy.gif",
"https://media.giphy.com/media/12hvLuZ7uzvCvK/giphy.gif",
"http://gallery1.anivide.com/_full/65030_1382582341.gif",
"https://49.media.tumblr.com/8e8a099c4eba22abd3ec0f70fd087cce/tumblr_nxovj9oY861ur1mffo1_500.gif ",
};
public string CurrencySign { get; set; } = "🌸";
public string CurrencyName { get; set; } = "NadekoFlower";
public string DMHelpString { get; set; } = "Type `-h` for help.";

View File

@ -2,6 +2,75 @@
"DontJoinServers": false,
"ForwardMessages": true,
"IsRotatingStatus": false,
"CustomReactions": {
"\\o\\": [
"/o/"
],
"/o/": [
"\\o\\"
],
"moveto": [
"(👉 ͡° ͜ʖ ͡°)👉 %target%"
],
"comeatmebro": [
"%target% (ง’̀-‘́)ง"
],
"e": [
"%user% did it 😒 🔫",
"%target% did it 😒 🔫"
],
"%mention% insult": [
"%target% You are a poop.",
"%target% You're a jerk.",
"%target% I will eat you when I get my powers back."
],
"%mention% praise": [
"%target% You are cool.",
"%target% You are nice!",
"%target% You did a good job.",
"%target% You did something nice.",
"%target% is awesome!",
"%target% Wow."
],
"%mention% pat": [
"http://i.imgur.com/IiQwK12.gif",
"http://i.imgur.com/JCXj8yD.gif",
"http://i.imgur.com/qqBl2bm.gif",
"http://i.imgur.com/eOJlnwP.gif",
"https://45.media.tumblr.com/229ec0458891c4dcd847545c81e760a5/tumblr_mpfy232F4j1rxrpjzo1_r2_500.gif",
"https://media.giphy.com/media/KZQlfylo73AMU/giphy.gif",
"https://media.giphy.com/media/12hvLuZ7uzvCvK/giphy.gif",
"http://gallery1.anivide.com/_full/65030_1382582341.gif",
"https://49.media.tumblr.com/8e8a099c4eba22abd3ec0f70fd087cce/tumblr_nxovj9oY861ur1mffo1_500.gif "
],
"%mention% cry": [
"http://i.imgur.com/Xg3i1Qy.gif",
"http://i.imgur.com/3K8DRrU.gif",
"http://i.imgur.com/k58BcAv.gif",
"http://i.imgur.com/I2fLXwo.gif"
],
"%mention% are you real?": [
"%user%, I will be soon."
],
"%mention% are you there?": [
"Yes. :)"
],
"%mention% draw": [
"Sorry, I don't gamble, type $draw for that function."
],
"%mention% bb": [
"Bye %target%"
],
"%mention% call": [
"Calling %target%"
],
"%mention% disguise": [
"https://cdn.discordapp.com/attachments/140007341880901632/156721710458994690/Cc5mixjUYAADgBs.jpg",
"https://cdn.discordapp.com/attachments/140007341880901632/156721715831898113/hqdefault.jpg",
"https://cdn.discordapp.com/attachments/140007341880901632/156721724430352385/okawari_01_haruka_weird_mask.jpg",
"https://cdn.discordapp.com/attachments/140007341880901632/156721728763068417/mustache-best-girl.png"
]
},
"RotatingStatuses": [],
"CommandPrefixes": {
"Administration": ".",
@ -48,29 +117,6 @@
"Definitely no",
"NO - It may cause disease contraction"
],
"DisguiseResponses": [
"https://cdn.discordapp.com/attachments/140007341880901632/156721710458994690/Cc5mixjUYAADgBs.jpg",
"https://cdn.discordapp.com/attachments/140007341880901632/156721715831898113/hqdefault.jpg",
"https://cdn.discordapp.com/attachments/140007341880901632/156721724430352385/okawari_01_haruka_weird_mask.jpg",
"https://cdn.discordapp.com/attachments/140007341880901632/156721728763068417/mustache-best-girl.png"
],
"CryResponses": [
"http://i.imgur.com/Xg3i1Qy.gif",
"http://i.imgur.com/3K8DRrU.gif",
"http://i.imgur.com/k58BcAv.gif",
"http://i.imgur.com/I2fLXwo.gif"
],
"PatResponses": [
"http://i.imgur.com/IiQwK12.gif",
"http://i.imgur.com/JCXj8yD.gif",
"http://i.imgur.com/qqBl2bm.gif",
"http://i.imgur.com/eOJlnwP.gif",
"https://45.media.tumblr.com/229ec0458891c4dcd847545c81e760a5/tumblr_mpfy232F4j1rxrpjzo1_r2_500.gif",
"https://media.giphy.com/media/KZQlfylo73AMU/giphy.gif",
"https://media.giphy.com/media/12hvLuZ7uzvCvK/giphy.gif",
"http://gallery1.anivide.com/_full/65030_1382582341.gif",
"https://49.media.tumblr.com/8e8a099c4eba22abd3ec0f70fd087cce/tumblr_nxovj9oY861ur1mffo1_500.gif "
],
"CurrencySign": "🌸",
"CurrencyName": "NadekoFlower",
"DMHelpString": "Type `-h` for help."