2016-11-15 11:01:02 +00:00
using System.Linq ;
2016-10-08 02:25:12 +00:00
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-11-15 11:01:02 +00:00
{
2016-12-15 04:22:12 +00:00
[NadekoModule("CustomReactions", ".")]
2016-10-08 02:25:12 +00:00
public class CustomReactions : DiscordModule
{
2016-10-10 04:12:22 +00:00
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 > ( ) ;
2016-10-08 02:25:12 +00:00
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-10-08 02:25:12 +00:00
}
}
2016-12-08 17:35:34 +00:00
public CustomReactions ( ) : base ( )
2016-10-08 02:25:12 +00:00
{
2016-11-05 19:03:50 +00:00
}
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-11-05 19:03:50 +00:00
{
2016-12-16 21:44:26 +00:00
//var channel = Context.Channel as ITextChannel;
2016-11-05 19:03:50 +00:00
if ( channel = = null )
return false ;
2016-10-08 18:24:34 +00:00
2016-11-05 19:03:50 +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 ) ;
2016-11-05 19:03:50 +00:00
if ( reactions ! = null & & reactions . Any ( ) )
{
2016-12-15 04:22:12 +00:00
var reaction = reactions . Where ( cr = >
{
2016-11-05 19:03:50 +00:00
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 ) ;
2016-11-05 19:03:50 +00:00
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
2016-11-05 19:03:50 +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 ) ;
2016-11-05 19:03:50 +00:00
return true ;
}
return false ;
2016-10-08 02:25:12 +00:00
}
[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;
2016-10-08 02:25:12 +00:00
if ( string . IsNullOrWhiteSpace ( message ) | | string . IsNullOrWhiteSpace ( key ) )
return ;
2016-10-10 03:04:50 +00:00
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-10-08 02:25:12 +00:00
{
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 { }
2016-10-08 02:25:12 +00:00
return ;
}
var cr = new CustomReaction ( )
{
GuildId = channel ? . Guild . Id ,
IsRegex = false ,
2016-10-10 03:04:50 +00:00
Trigger = key ,
2016-10-08 02:25:12 +00:00
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>());
2016-10-08 02:25:12 +00:00
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 ) ;
2016-10-08 02:25:12 +00:00
}
[NadekoCommand, Usage, Description, Aliases]
2016-11-12 19:58:33 +00:00
[Priority(0)]
public async Task ListCustReact ( IUserMessage imsg , int page = 1 )
2016-10-08 02:25:12 +00:00
{
2016-12-16 21:44:26 +00:00
//var channel = Context.Channel as ITextChannel;
2016-10-08 02:25:12 +00:00
if ( page < 1 | | page > 1000 )
return ;
2016-10-10 04:12:22 +00:00
ConcurrentHashSet < CustomReaction > customReactions ;
2016-10-08 02:25:12 +00:00
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-10-08 02:25:12 +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-10-08 02:25:12 +00:00
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()}`" ) ) )
2016-10-08 02:25:12 +00:00
. ConfigureAwait ( false ) ;
}
2016-10-14 08:50:04 +00:00
[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;
2016-10-14 08:50:04 +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-10-14 08:50:04 +00:00
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 ) ;
2016-10-14 08:50:04 +00:00
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 ) ;
2016-10-14 08:50:04 +00:00
}
}
2016-10-08 02:25:12 +00:00
[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-10-08 02:25:12 +00:00
2016-12-16 18:43:57 +00:00
if ( ( channel = = null & & ! NadekoBot . Credentials . IsOwner ( Context . User ) ) | | ( channel ! = null & & ! ( ( IGuildUser ) Context . User ) . GuildPermissions . Administrator ) )
2016-10-08 02:25:12 +00:00
{
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 { }
2016-10-08 02:25:12 +00:00
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 )
2016-10-08 02:25:12 +00:00
{
uow . CustomReactions . Remove ( toDelete ) ;
2016-10-10 04:38:20 +00:00
GlobalReactions . RemoveWhere ( cr = > cr . Id = = toDelete . Id ) ;
2016-10-08 02:25:12 +00:00
success = true ;
}
2016-10-15 12:40:01 +00:00
else if ( ( toDelete . GuildId ! = null & & toDelete . GuildId ! = 0 ) & & channel ? . Guild . Id = = toDelete . GuildId )
2016-10-08 02:25:12 +00:00
{
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);
2016-10-08 02:25:12 +00:00
success = true ;
}
2016-12-15 04:22:12 +00:00
if ( success )
2016-10-08 02:25:12 +00:00
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 ) ;
2016-10-08 02:25:12 +00:00
else
2016-12-16 18:43:57 +00:00
await Context . Channel . SendErrorAsync ( "Failed to find that custom reaction." ) . ConfigureAwait ( false ) ;
2016-10-08 02:25:12 +00:00
}
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 ) ;
}
2016-10-08 02:25:12 +00:00
}
}