NadekoBot/src/NadekoBot/Modules/CustomReactions/CustomReactions.cs

321 lines
15 KiB
C#
Raw Normal View History

using System.Linq;
using System.Threading.Tasks;
using Discord.Commands;
using NadekoBot.Services;
using NadekoBot.Attributes;
using System.Collections.Concurrent;
using NadekoBot.Services.Database.Models;
using Discord;
using NadekoBot.Extensions;
namespace NadekoBot.Modules.CustomReactions
{
2016-12-15 04:22:12 +00:00
[NadekoModule("CustomReactions", ".")]
public class CustomReactions : DiscordModule
{
public static ConcurrentHashSet<CustomReaction> GlobalReactions { get; } = new ConcurrentHashSet<CustomReaction>();
public static ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>> GuildReactions { get; } = new ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>>();
2016-12-15 04:22:12 +00:00
public static ConcurrentDictionary<string, uint> ReactionStats { get; } = new ConcurrentDictionary<string, uint>();
static CustomReactions()
{
using (var uow = DbHandler.UnitOfWork())
{
2016-10-08 02:35:39 +00:00
var items = uow.CustomReactions.GetAll();
2016-10-14 22:56:29 +00:00
GuildReactions = new ConcurrentDictionary<ulong, ConcurrentHashSet<CustomReaction>>(items.Where(g => g.GuildId != null && g.GuildId != 0).GroupBy(k => k.GuildId.Value).ToDictionary(g => g.Key, g => new ConcurrentHashSet<CustomReaction>(g)));
GlobalReactions = new ConcurrentHashSet<CustomReaction>(items.Where(g => g.GuildId == null || g.GuildId == 0));
}
}
2016-12-08 17:35:34 +00:00
public CustomReactions() : base()
{
}
2016-10-08 18:24:34 +00:00
2016-12-15 04:22:12 +00:00
public void ClearStats() => ReactionStats.Clear();
2016-12-16 18:43:57 +00:00
public static async Task<bool> TryExecuteCustomReaction()
{
2016-12-16 21:44:26 +00:00
//var channel = Context.Channel as ITextChannel;
if (channel == null)
return false;
2016-10-08 18:24:34 +00:00
var content = umsg.Content.Trim().ToLowerInvariant();
ConcurrentHashSet<CustomReaction> reactions;
2016-12-16 21:44:26 +00:00
//GuildReactions.TryGetValue(channel.Guild.Id, out reactions);
GuildReactions.TryGetValue(Context.Guild.Id, out reactions);
if (reactions != null && reactions.Any())
{
2016-12-15 04:22:12 +00:00
var reaction = reactions.Where(cr =>
{
var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%");
var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant();
return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger);
}).Shuffle().FirstOrDefault();
if (reaction != null)
2016-10-08 18:24:34 +00:00
{
2016-12-15 04:22:12 +00:00
if (reaction.Response != "-")
2016-12-16 21:44:26 +00:00
try { await Context.Channel.SendMessageAsync(reaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { }
2016-12-15 04:22:12 +00:00
ReactionStats.AddOrUpdate(reaction.Trigger, 1, (k, old) => ++old);
return true;
}
}
var greaction = GlobalReactions.Where(cr =>
{
var hasTarget = cr.Response.ToLowerInvariant().Contains("%target%");
var trigger = cr.TriggerWithContext(umsg).Trim().ToLowerInvariant();
return ((hasTarget && content.StartsWith(trigger + " ")) || content == trigger);
}).Shuffle().FirstOrDefault();
2016-10-16 03:22:54 +00:00
if (greaction != null)
{
2016-12-16 21:44:26 +00:00
try { await Context.Channel.SendMessageAsync(greaction.ResponseWithContext(umsg)).ConfigureAwait(false); } catch { }
2016-12-15 04:22:12 +00:00
ReactionStats.AddOrUpdate(greaction.Trigger, 1, (k, old) => ++old);
return true;
}
return false;
}
[NadekoCommand, Usage, Description, Aliases]
public async Task AddCustReact(IUserMessage imsg, string key, [Remainder] string message)
{
2016-12-16 21:44:26 +00:00
//var channel = Context.Channel as ITextChannel;
if (string.IsNullOrWhiteSpace(message) || string.IsNullOrWhiteSpace(key))
return;
key = key.ToLowerInvariant();
2016-12-16 18:43:57 +00:00
if ((channel == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (channel != null && !((IGuildUser)Context.User).GuildPermissions.Administrator))
{
2016-12-16 18:43:57 +00:00
try { await Context.Channel.SendErrorAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { }
return;
}
var cr = new CustomReaction()
{
GuildId = channel?.Guild.Id,
IsRegex = false,
Trigger = key,
Response = message,
};
using (var uow = DbHandler.UnitOfWork())
{
uow.CustomReactions.Add(cr);
await uow.CompleteAsync().ConfigureAwait(false);
}
if (channel == null)
{
GlobalReactions.Add(cr);
}
else
{
2016-12-16 21:44:26 +00:00
var reactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>());
//var reactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
reactions.Add(cr);
}
2016-12-16 18:43:57 +00:00
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
2016-12-11 16:18:25 +00:00
.WithTitle("New Custom Reaction")
.WithDescription($"#{cr.Id}")
.AddField(efb => efb.WithName("Trigger").WithValue(key))
.AddField(efb => efb.WithName("Response").WithValue(message))
2016-12-16 18:43:57 +00:00
).ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
2016-11-12 19:58:33 +00:00
[Priority(0)]
public async Task ListCustReact(IUserMessage imsg, int page = 1)
{
2016-12-16 21:44:26 +00:00
//var channel = Context.Channel as ITextChannel;
if (page < 1 || page > 1000)
return;
ConcurrentHashSet<CustomReaction> customReactions;
if (channel == null)
customReactions = GlobalReactions;
else
2016-12-16 21:44:26 +00:00
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>());
//customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
if (customReactions == null || !customReactions.Any())
2016-12-16 18:43:57 +00:00
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
else
2016-12-16 18:43:57 +00:00
await Context.Channel.SendConfirmAsync(
2016-12-11 16:18:25 +00:00
$"Page {page} of custom reactions:",
string.Join("\n", customReactions.OrderBy(cr => cr.Trigger)
.Skip((page - 1) * 20)
.Take(20)
.Select(cr => $"`#{cr.Id}` `Trigger:` {cr.Trigger}")))
.ConfigureAwait(false);
2016-11-12 19:58:33 +00:00
}
public enum All
{
All
}
[NadekoCommand, Usage, Description, Aliases]
[Priority(1)]
public async Task ListCustReact(IUserMessage imsg, All x)
{
2016-12-16 21:44:26 +00:00
//var channel = Context.Channel as ITextChannel;
2016-11-12 19:58:33 +00:00
ConcurrentHashSet<CustomReaction> customReactions;
if (channel == null)
customReactions = GlobalReactions;
else
2016-12-16 21:44:26 +00:00
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>());
//customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
2016-11-12 19:58:33 +00:00
if (customReactions == null || !customReactions.Any())
2016-12-16 18:43:57 +00:00
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
2016-11-12 19:58:33 +00:00
else
{
var txtStream = await customReactions.GroupBy(cr => cr.Trigger)
.OrderBy(cr => cr.Key)
2016-12-01 22:19:19 +00:00
.Select(cr => new { Trigger = cr.Key, Responses = cr.Select(y => y.Response).ToList() })
2016-11-12 19:58:33 +00:00
.ToJson()
.ToStream()
.ConfigureAwait(false);
if (channel == null) // its a private one, just send back
2016-12-16 18:43:57 +00:00
await Context.Channel.SendFileAsync(txtStream, "customreactions.txt", "List of all custom reactions").ConfigureAwait(false);
2016-11-12 19:58:33 +00:00
else
2016-12-16 18:43:57 +00:00
await ((IGuildUser)Context.User).SendFileAsync(txtStream, "customreactions.txt", "List of all custom reactions").ConfigureAwait(false);
2016-11-12 19:58:33 +00:00
}
}
[NadekoCommand, Usage, Description, Aliases]
public async Task ListCustReactG(IUserMessage imsg, int page = 1)
{
2016-12-16 21:44:26 +00:00
//var channel = Context.Channel as ITextChannel;
2016-11-12 19:58:33 +00:00
if (page < 1 || page > 10000)
return;
ConcurrentHashSet<CustomReaction> customReactions;
if (channel == null)
customReactions = GlobalReactions;
else
2016-12-16 21:44:26 +00:00
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>());
//customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
2016-11-12 19:58:33 +00:00
if (customReactions == null || !customReactions.Any())
2016-12-16 18:43:57 +00:00
await Context.Channel.SendErrorAsync("No custom reactions found").ConfigureAwait(false);
2016-11-12 19:58:33 +00:00
else
2016-12-16 18:43:57 +00:00
await Context.Channel.SendConfirmAsync($"Page {page} of custom reactions (grouped):",
2016-11-12 19:58:33 +00:00
string.Join("\r\n", customReactions
2016-12-15 04:22:12 +00:00
.GroupBy(cr => cr.Trigger)
2016-11-12 19:58:33 +00:00
.OrderBy(cr => cr.Key)
.Skip((page - 1) * 20)
.Take(20)
.Select(cr => $"**{cr.Key.Trim().ToLowerInvariant()}** `x{cr.Count()}`")))
.ConfigureAwait(false);
}
[NadekoCommand, Usage, Description, Aliases]
public async Task ShowCustReact(IUserMessage imsg, int id)
{
2016-12-16 21:44:26 +00:00
//var channel = Context.Channel as ITextChannel;
ConcurrentHashSet<CustomReaction> customReactions;
if (channel == null)
customReactions = GlobalReactions;
else
2016-12-16 21:44:26 +00:00
customReactions = GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>());
//customReactions = GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>());
var found = customReactions.FirstOrDefault(cr => cr.Id == id);
if (found == null)
2016-12-16 18:43:57 +00:00
await Context.Channel.SendErrorAsync("No custom reaction found with that id.").ConfigureAwait(false);
else
{
2016-12-16 18:43:57 +00:00
await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor)
2016-12-11 16:18:25 +00:00
.WithDescription($"#{id}")
.AddField(efb => efb.WithName("Trigger").WithValue(found.Trigger))
2016-12-15 04:22:12 +00:00
.AddField(efb => efb.WithName("Response").WithValue(found.Response + "\n```css\n" + found.Response + "```"))
2016-12-16 18:43:57 +00:00
).ConfigureAwait(false);
}
}
[NadekoCommand, Usage, Description, Aliases]
public async Task DelCustReact(IUserMessage imsg, int id)
{
2016-12-16 21:44:26 +00:00
//var channel = Context.Channel as ITextChannel;
2016-12-16 18:43:57 +00:00
if ((channel == null && !NadekoBot.Credentials.IsOwner(Context.User)) || (channel != null && !((IGuildUser)Context.User).GuildPermissions.Administrator))
{
2016-12-16 18:43:57 +00:00
try { await Context.Channel.SendErrorAsync("Insufficient permissions. Requires Bot ownership for global custom reactions, and Administrator for guild custom reactions."); } catch { }
return;
}
var success = false;
CustomReaction toDelete;
using (var uow = DbHandler.UnitOfWork())
{
toDelete = uow.CustomReactions.Get(id);
if (toDelete == null) //not found
return;
2016-10-15 12:40:01 +00:00
if ((toDelete.GuildId == null || toDelete.GuildId == 0) && channel == null)
{
uow.CustomReactions.Remove(toDelete);
2016-10-10 04:38:20 +00:00
GlobalReactions.RemoveWhere(cr => cr.Id == toDelete.Id);
success = true;
}
2016-10-15 12:40:01 +00:00
else if ((toDelete.GuildId != null && toDelete.GuildId != 0) && channel?.Guild.Id == toDelete.GuildId)
{
uow.CustomReactions.Remove(toDelete);
2016-12-16 21:44:26 +00:00
GuildReactions.GetOrAdd(Context.Guild.Id, new ConcurrentHashSet<CustomReaction>()).RemoveWhere(cr => cr.Id == toDelete.Id);
//GuildReactions.GetOrAdd(channel.Guild.Id, new ConcurrentHashSet<CustomReaction>()).RemoveWhere(cr => cr.Id == toDelete.Id);
success = true;
}
2016-12-15 04:22:12 +00:00
if (success)
await uow.CompleteAsync().ConfigureAwait(false);
}
if (success)
2016-12-16 18:43:57 +00:00
await Context.Channel.SendConfirmAsync("Deleted custom reaction", toDelete.ToString()).ConfigureAwait(false);
else
2016-12-16 18:43:57 +00:00
await Context.Channel.SendErrorAsync("Failed to find that custom reaction.").ConfigureAwait(false);
}
2016-12-15 04:22:12 +00:00
[NadekoCommand, Usage, Description, Aliases]
public async Task CrStatsClear(IUserMessage imsg, string trigger = null)
{
if (string.IsNullOrWhiteSpace(trigger))
{
ClearStats();
2016-12-16 18:43:57 +00:00
await Context.Channel.SendConfirmAsync($"Custom reaction stats cleared.").ConfigureAwait(false);
2016-12-15 04:22:12 +00:00
}
else
{
uint throwaway;
if (ReactionStats.TryRemove(trigger, out throwaway))
{
2016-12-16 18:43:57 +00:00
await Context.Channel.SendConfirmAsync($"Stats cleared for `{trigger}` custom reaction.").ConfigureAwait(false);
2016-12-15 04:22:12 +00:00
}
else
{
2016-12-16 18:43:57 +00:00
await Context.Channel.SendErrorAsync("No stats for that trigger found, no action taken.").ConfigureAwait(false);
2016-12-15 04:22:12 +00:00
}
}
}
[NadekoCommand, Usage, Description, Aliases]
public async Task CrStats(IUserMessage imsg, int page = 1)
{
if (page < 1)
return;
2016-12-16 18:43:57 +00:00
await Context.Channel.EmbedAsync(ReactionStats.OrderByDescending(x => x.Value)
2016-12-15 04:22:12 +00:00
.Skip((page - 1)*9)
.Take(9)
.Aggregate(new EmbedBuilder().WithColor(NadekoBot.OkColor).WithTitle($"Custom Reaction stats page #{page}"),
(agg, cur) => agg.AddField(efb => efb.WithName(cur.Key).WithValue(cur.Value.ToString()).WithIsInline(true)))
2016-12-16 18:43:57 +00:00
)
2016-12-15 04:22:12 +00:00
.ConfigureAwait(false);
}
}
}