Games done, admin half done
This commit is contained in:
		| @@ -7,82 +7,54 @@ using System.Linq; | |||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using NadekoBot.Services; | using NadekoBot.Services; | ||||||
| using NadekoBot.Attributes; | using NadekoBot.Attributes; | ||||||
| using Discord.WebSocket; |  | ||||||
| using NadekoBot.Services.Database.Models; | using NadekoBot.Services.Database.Models; | ||||||
| using static NadekoBot.Modules.Permissions.Permissions; | using NadekoBot.Services.Administration; | ||||||
| using System.Collections.Concurrent; |  | ||||||
| using NLog; |  | ||||||
| using NadekoBot.Modules.Permissions; |  | ||||||
|  |  | ||||||
| namespace NadekoBot.Modules.Administration | namespace NadekoBot.Modules.Administration | ||||||
| { | { | ||||||
|     [NadekoModule("Administration", ".")] |  | ||||||
|     public partial class Administration : NadekoTopLevelModule |     public partial class Administration : NadekoTopLevelModule | ||||||
|     { |     { | ||||||
|         private static readonly ConcurrentHashSet<ulong> deleteMessagesOnCommand; |         private IGuild _nadekoSupportServer; | ||||||
|  |         private readonly DbHandler _db; | ||||||
|  |         private readonly AdministrationService _admin; | ||||||
|  |  | ||||||
|         private new static readonly Logger _log; |         public Administration(DbHandler db, AdministrationService admin) | ||||||
|  |  | ||||||
|         static Administration() |  | ||||||
|         { |         { | ||||||
|             _log = LogManager.GetCurrentClassLogger(); |             _db = db; | ||||||
|             NadekoBot.CommandHandler.CommandExecuted += DelMsgOnCmd_Handler; |             _admin = admin; | ||||||
|  |  | ||||||
|             deleteMessagesOnCommand = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId)); |  | ||||||
|  |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private static Task DelMsgOnCmd_Handler(IUserMessage msg, CommandInfo cmd) |         ////todo permissions | ||||||
|         { |         //[NadekoCommand, Usage, Description, Aliases] | ||||||
|             var _ = Task.Run(async () => |         //[RequireContext(ContextType.Guild)] | ||||||
|             { |         //[RequireUserPermission(GuildPermission.Administrator)] | ||||||
|                 try |         //public async Task ResetPermissions() | ||||||
|                 { |         //{ | ||||||
|                     var channel = msg.Channel as SocketTextChannel; |         //    using (var uow = _db.UnitOfWork) | ||||||
|                     if (channel == null) |         //    { | ||||||
|                         return; |         //        var config = uow.GuildConfigs.GcWithPermissionsv2For(Context.Guild.Id); | ||||||
|                     if (deleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune" && cmd.Name != "pick") |         //        config.Permissions = Permissionv2.GetDefaultPermlist; | ||||||
|                         await msg.DeleteAsync().ConfigureAwait(false); |         //        await uow.CompleteAsync(); | ||||||
|                 } |         //        UpdateCache(config); | ||||||
|                 catch (Exception ex) |         //    } | ||||||
|                 { |         //    await ReplyConfirmLocalized("perms_reset").ConfigureAwait(false); | ||||||
|                     _log.Warn(ex, "Delmsgoncmd errored..."); |         //} | ||||||
|                 } |         //[NadekoCommand, Usage, Description, Aliases] | ||||||
|             }); |         //[OwnerOnly] | ||||||
|             return Task.CompletedTask; |         //public async Task ResetGlobalPermissions() | ||||||
|         } |         //{ | ||||||
|  |         //    using (var uow = _db.UnitOfWork) | ||||||
|  |         //    { | ||||||
|  |         //        var gc = uow.BotConfig.GetOrCreate(); | ||||||
|  |         //        gc.BlockedCommands.Clear(); | ||||||
|  |         //        gc.BlockedModules.Clear(); | ||||||
|  |  | ||||||
|         [NadekoCommand, Usage, Description, Aliases] |         //        GlobalPermissionCommands.BlockedCommands.Clear(); | ||||||
|         [RequireContext(ContextType.Guild)] |         //        GlobalPermissionCommands.BlockedModules.Clear(); | ||||||
|         [RequireUserPermission(GuildPermission.Administrator)] |         //        await uow.CompleteAsync(); | ||||||
|         public async Task ResetPermissions() |         //    } | ||||||
|         { |         //    await ReplyConfirmLocalized("global_perms_reset").ConfigureAwait(false); | ||||||
|             using (var uow = DbHandler.UnitOfWork()) |         //} | ||||||
|             { |  | ||||||
|                 var config = uow.GuildConfigs.GcWithPermissionsv2For(Context.Guild.Id); |  | ||||||
|                 config.Permissions = Permissionv2.GetDefaultPermlist; |  | ||||||
|                 await uow.CompleteAsync(); |  | ||||||
|                 UpdateCache(config); |  | ||||||
|             } |  | ||||||
|             await ReplyConfirmLocalized("perms_reset").ConfigureAwait(false); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         [NadekoCommand, Usage, Description, Aliases] |  | ||||||
|         [OwnerOnly] |  | ||||||
|         public async Task ResetGlobalPermissions() |  | ||||||
|         { |  | ||||||
|             using (var uow = DbHandler.UnitOfWork()) |  | ||||||
|             { |  | ||||||
|                 var gc = uow.BotConfig.GetOrCreate(); |  | ||||||
|                 gc.BlockedCommands.Clear(); |  | ||||||
|                 gc.BlockedModules.Clear(); |  | ||||||
|  |  | ||||||
|                 GlobalPermissionCommands.BlockedCommands.Clear(); |  | ||||||
|                 GlobalPermissionCommands.BlockedModules.Clear(); |  | ||||||
|                 await uow.CompleteAsync(); |  | ||||||
|             } |  | ||||||
|             await ReplyConfirmLocalized("global_perms_reset").ConfigureAwait(false); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         [NadekoCommand, Usage, Description, Aliases] |         [NadekoCommand, Usage, Description, Aliases] | ||||||
|         [RequireContext(ContextType.Guild)] |         [RequireContext(ContextType.Guild)] | ||||||
| @@ -91,7 +63,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|         public async Task Delmsgoncmd() |         public async Task Delmsgoncmd() | ||||||
|         { |         { | ||||||
|             bool enabled; |             bool enabled; | ||||||
|             using (var uow = DbHandler.UnitOfWork()) |             using (var uow = _db.UnitOfWork) | ||||||
|             { |             { | ||||||
|                 var conf = uow.GuildConfigs.For(Context.Guild.Id, set => set); |                 var conf = uow.GuildConfigs.For(Context.Guild.Id, set => set); | ||||||
|                 enabled = conf.DeleteMessageOnCommand = !conf.DeleteMessageOnCommand; |                 enabled = conf.DeleteMessageOnCommand = !conf.DeleteMessageOnCommand; | ||||||
| @@ -100,12 +72,12 @@ namespace NadekoBot.Modules.Administration | |||||||
|             } |             } | ||||||
|             if (enabled) |             if (enabled) | ||||||
|             { |             { | ||||||
|                 deleteMessagesOnCommand.Add(Context.Guild.Id); |                 _admin.DeleteMessagesOnCommand.Add(Context.Guild.Id); | ||||||
|                 await ReplyConfirmLocalized("delmsg_on").ConfigureAwait(false); |                 await ReplyConfirmLocalized("delmsg_on").ConfigureAwait(false); | ||||||
|             } |             } | ||||||
|             else |             else | ||||||
|             { |             { | ||||||
|                 deleteMessagesOnCommand.TryRemove(Context.Guild.Id); |                 _admin.DeleteMessagesOnCommand.TryRemove(Context.Guild.Id); | ||||||
|                 await ReplyConfirmLocalized("delmsg_off").ConfigureAwait(false); |                 await ReplyConfirmLocalized("delmsg_off").ConfigureAwait(false); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| @@ -454,19 +426,18 @@ namespace NadekoBot.Modules.Administration | |||||||
|             await Context.Channel.SendMessageAsync(send).ConfigureAwait(false); |             await Context.Channel.SendMessageAsync(send).ConfigureAwait(false); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private IGuild _nadekoSupportServer; |  | ||||||
|         [NadekoCommand, Usage, Description, Aliases] |         [NadekoCommand, Usage, Description, Aliases] | ||||||
|         public async Task Donators() |         public async Task Donators() | ||||||
|         { |         { | ||||||
|             IEnumerable<Donator> donatorsOrdered; |             IEnumerable<Donator> donatorsOrdered; | ||||||
|  |  | ||||||
|             using (var uow = DbHandler.UnitOfWork()) |             using (var uow = _db.UnitOfWork) | ||||||
|             { |             { | ||||||
|                 donatorsOrdered = uow.Donators.GetDonatorsOrdered(); |                 donatorsOrdered = uow.Donators.GetDonatorsOrdered(); | ||||||
|             } |             } | ||||||
|             await Context.Channel.SendConfirmAsync(GetText("donators"), string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false); |             await Context.Channel.SendConfirmAsync(GetText("donators"), string.Join("⭐", donatorsOrdered.Select(d => d.Name))).ConfigureAwait(false); | ||||||
|  |  | ||||||
|             _nadekoSupportServer = _nadekoSupportServer ?? NadekoBot.Client.GetGuild(117523346618318850); |             _nadekoSupportServer = _nadekoSupportServer ?? (await Context.Client.GetGuildAsync(117523346618318850)); | ||||||
|  |  | ||||||
|             var patreonRole = _nadekoSupportServer?.GetRole(236667642088259585); |             var patreonRole = _nadekoSupportServer?.GetRole(236667642088259585); | ||||||
|             if (patreonRole == null) |             if (patreonRole == null) | ||||||
| @@ -482,7 +453,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|         public async Task Donadd(IUser donator, int amount) |         public async Task Donadd(IUser donator, int amount) | ||||||
|         { |         { | ||||||
|             Donator don; |             Donator don; | ||||||
|             using (var uow = DbHandler.UnitOfWork()) |             using (var uow = _db.UnitOfWork) | ||||||
|             { |             { | ||||||
|                 don = uow.Donators.AddOrUpdateDonator(donator.Id, donator.Username, amount); |                 don = uow.Donators.AddOrUpdateDonator(donator.Id, donator.Username, amount); | ||||||
|                 await uow.CompleteAsync(); |                 await uow.CompleteAsync(); | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ using Discord.WebSocket; | |||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
| using NadekoBot.Attributes; | using NadekoBot.Attributes; | ||||||
| using NadekoBot.Services; | using NadekoBot.Services; | ||||||
|  | using NadekoBot.Services.Administration; | ||||||
| using NadekoBot.Services.Database.Models; | using NadekoBot.Services.Database.Models; | ||||||
| using NLog; | using NLog; | ||||||
| using System; | using System; | ||||||
| @@ -19,232 +20,13 @@ namespace NadekoBot.Modules.Administration | |||||||
|         [Group] |         [Group] | ||||||
|         public class MuteCommands : NadekoSubmodule |         public class MuteCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|             private static ConcurrentDictionary<ulong, string> GuildMuteRoles { get; } |             private readonly MuteService _service; | ||||||
|             private static ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; } |             private readonly DbHandler _db; | ||||||
|             private static ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, Timer>> UnmuteTimers { get; } |  | ||||||
|                 = new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, Timer>>(); |  | ||||||
|  |  | ||||||
|             public static event Action<IGuildUser, MuteType> UserMuted = delegate { }; |             public MuteCommands(MuteService service, DbHandler db) | ||||||
|             public static event Action<IGuildUser, MuteType> UserUnmuted = delegate { }; |  | ||||||
|  |  | ||||||
|  |  | ||||||
|             public enum MuteType { |  | ||||||
|                 Voice, |  | ||||||
|                 Chat, |  | ||||||
|                 All |  | ||||||
|             } |  | ||||||
|             private static readonly OverwritePermissions denyOverwrite = new OverwritePermissions(sendMessages: PermValue.Deny, attachFiles: PermValue.Deny); |  | ||||||
|  |  | ||||||
|             private static readonly new Logger _log = LogManager.GetCurrentClassLogger(); |  | ||||||
|  |  | ||||||
|             static MuteCommands() |  | ||||||
|             { |             { | ||||||
|                 var configs = NadekoBot.AllGuildConfigs; |                 _service = service; | ||||||
|                 GuildMuteRoles = new ConcurrentDictionary<ulong, string>(configs |                 _db = db; | ||||||
|                         .Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName)) |  | ||||||
|                         .ToDictionary(c => c.GuildId, c => c.MuteRoleName)); |  | ||||||
|  |  | ||||||
|                 MutedUsers = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(configs.ToDictionary( |  | ||||||
|                     k => k.GuildId, |  | ||||||
|                     v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId)) |  | ||||||
|                 )); |  | ||||||
|  |  | ||||||
|                 foreach (var conf in configs) |  | ||||||
|                 { |  | ||||||
|                     foreach (var x in conf.UnmuteTimers) |  | ||||||
|                     { |  | ||||||
|                         TimeSpan after; |  | ||||||
|                         if (x.UnmuteAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow) |  | ||||||
|                         { |  | ||||||
|                             after = TimeSpan.FromMinutes(2); |  | ||||||
|                         } |  | ||||||
|                         else |  | ||||||
|                         { |  | ||||||
|                             after = x.UnmuteAt - DateTime.UtcNow; |  | ||||||
|                         } |  | ||||||
|                         StartUnmuteTimer(conf.GuildId, x.UserId, after); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 NadekoBot.Client.UserJoined += Client_UserJoined; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             private static async Task Client_UserJoined(IGuildUser usr) |  | ||||||
|             { |  | ||||||
|                 try |  | ||||||
|                 { |  | ||||||
|                     MutedUsers.TryGetValue(usr.Guild.Id, out ConcurrentHashSet<ulong> muted); |  | ||||||
|  |  | ||||||
|                     if (muted == null || !muted.Contains(usr.Id)) |  | ||||||
|                         return; |  | ||||||
|                     await MuteUser(usr).ConfigureAwait(false); |  | ||||||
|                 } |  | ||||||
|                 catch (Exception ex) |  | ||||||
|                 { |  | ||||||
|                     LogManager.GetCurrentClassLogger().Warn(ex); |  | ||||||
|                 } |  | ||||||
|                      |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public static async Task MuteUser(IGuildUser usr) |  | ||||||
|             { |  | ||||||
|                 await usr.ModifyAsync(x => x.Mute = true).ConfigureAwait(false); |  | ||||||
|                 var muteRole = await GetMuteRole(usr.Guild); |  | ||||||
|                 if (!usr.RoleIds.Contains(muteRole.Id)) |  | ||||||
|                     await usr.AddRoleAsync(muteRole).ConfigureAwait(false); |  | ||||||
|                 StopUnmuteTimer(usr.GuildId, usr.Id); |  | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |  | ||||||
|                 { |  | ||||||
|                     var config = uow.GuildConfigs.For(usr.Guild.Id, |  | ||||||
|                         set => set.Include(gc => gc.MutedUsers) |  | ||||||
|                             .Include(gc => gc.UnmuteTimers)); |  | ||||||
|                     config.MutedUsers.Add(new MutedUserId() |  | ||||||
|                     { |  | ||||||
|                         UserId = usr.Id |  | ||||||
|                     }); |  | ||||||
|                     if (MutedUsers.TryGetValue(usr.Guild.Id, out ConcurrentHashSet<ulong> muted)) |  | ||||||
|                         muted.Add(usr.Id); |  | ||||||
|  |  | ||||||
|                     config.UnmuteTimers.RemoveWhere(x => x.UserId == usr.Id); |  | ||||||
|  |  | ||||||
|                     await uow.CompleteAsync().ConfigureAwait(false); |  | ||||||
|                 } |  | ||||||
|                 UserMuted(usr, MuteType.All); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public static async Task UnmuteUser(IGuildUser usr) |  | ||||||
|             { |  | ||||||
|                 StopUnmuteTimer(usr.GuildId, usr.Id); |  | ||||||
|                 try { await usr.ModifyAsync(x => x.Mute = false).ConfigureAwait(false); } catch { } |  | ||||||
|                 try { await usr.RemoveRoleAsync(await GetMuteRole(usr.Guild)).ConfigureAwait(false); } catch { /*ignore*/ } |  | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |  | ||||||
|                 { |  | ||||||
|                     var config = uow.GuildConfigs.For(usr.Guild.Id, set => set.Include(gc => gc.MutedUsers) |  | ||||||
|                         .Include(gc => gc.UnmuteTimers)); |  | ||||||
|                     config.MutedUsers.Remove(new MutedUserId() |  | ||||||
|                     { |  | ||||||
|                         UserId = usr.Id |  | ||||||
|                     }); |  | ||||||
|                     if (MutedUsers.TryGetValue(usr.Guild.Id, out ConcurrentHashSet<ulong> muted)) |  | ||||||
|                         muted.TryRemove(usr.Id); |  | ||||||
|  |  | ||||||
|                     config.UnmuteTimers.RemoveWhere(x => x.UserId == usr.Id); |  | ||||||
|  |  | ||||||
|                     await uow.CompleteAsync().ConfigureAwait(false); |  | ||||||
|                 } |  | ||||||
|                 UserUnmuted(usr, MuteType.All); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public static async Task<IRole>GetMuteRole(IGuild guild) |  | ||||||
|             { |  | ||||||
|                 const string defaultMuteRoleName = "nadeko-mute"; |  | ||||||
|  |  | ||||||
|                 var muteRoleName = GuildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName); |  | ||||||
|  |  | ||||||
|                 var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName); |  | ||||||
|                 if (muteRole == null) |  | ||||||
|                 { |  | ||||||
|  |  | ||||||
|                     //if it doesn't exist, create it  |  | ||||||
|                     try { muteRole = await guild.CreateRoleAsync(muteRoleName, GuildPermissions.None).ConfigureAwait(false); } |  | ||||||
|                     catch |  | ||||||
|                     { |  | ||||||
|                         //if creations fails,  maybe the name is not correct, find default one, if doesn't work, create default one |  | ||||||
|                         muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName) ?? |  | ||||||
|                             await guild.CreateRoleAsync(defaultMuteRoleName, GuildPermissions.None).ConfigureAwait(false); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 foreach (var toOverwrite in (await guild.GetTextChannelsAsync())) |  | ||||||
|                 { |  | ||||||
|                     try |  | ||||||
|                     { |  | ||||||
|                         if (!toOverwrite.PermissionOverwrites.Select(x => x.Permissions).Contains(denyOverwrite)) |  | ||||||
|                         { |  | ||||||
|                             await toOverwrite.AddPermissionOverwriteAsync(muteRole, denyOverwrite) |  | ||||||
|                                     .ConfigureAwait(false); |  | ||||||
|  |  | ||||||
|                             await Task.Delay(200).ConfigureAwait(false); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     catch |  | ||||||
|                     { |  | ||||||
|                         // ignored |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 return muteRole; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public static async Task TimedMute(IGuildUser user, TimeSpan after) |  | ||||||
|             { |  | ||||||
|                 await MuteUser(user).ConfigureAwait(false); // mute the user. This will also remove any previous unmute timers |  | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |  | ||||||
|                 { |  | ||||||
|                     var config = uow.GuildConfigs.For(user.GuildId, set => set.Include(x => x.UnmuteTimers)); |  | ||||||
|                     config.UnmuteTimers.Add(new UnmuteTimer() |  | ||||||
|                     { |  | ||||||
|                         UserId = user.Id, |  | ||||||
|                         UnmuteAt = DateTime.UtcNow + after, |  | ||||||
|                     }); // add teh unmute timer to the database |  | ||||||
|                     uow.Complete(); |  | ||||||
|                 } |  | ||||||
|                 StartUnmuteTimer(user.GuildId, user.Id, after); // start the timer |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public static void StartUnmuteTimer(ulong guildId, ulong userId, TimeSpan after) |  | ||||||
|             { |  | ||||||
|                 //load the unmute timers for this guild |  | ||||||
|                 var userUnmuteTimers = UnmuteTimers.GetOrAdd(guildId, new ConcurrentDictionary<ulong, Timer>()); |  | ||||||
|  |  | ||||||
|                 //unmute timer to be added |  | ||||||
|                 var toAdd = new Timer(async _ => |  | ||||||
|                 { |  | ||||||
|                     try |  | ||||||
|                     { |  | ||||||
|                         var guild = NadekoBot.Client.GetGuild(guildId); // load the guild |  | ||||||
|                         if (guild == null) |  | ||||||
|                         { |  | ||||||
|                             RemoveUnmuteTimerFromDb(guildId, userId); |  | ||||||
|                             return; // if guild can't be found, just remove the timer from db |  | ||||||
|                         } |  | ||||||
|                         // unmute the user, this will also remove the timer from the db |  | ||||||
|                         await UnmuteUser(guild.GetUser(userId)).ConfigureAwait(false);  |  | ||||||
|                     } |  | ||||||
|                     catch (Exception ex) |  | ||||||
|                     { |  | ||||||
|                         RemoveUnmuteTimerFromDb(guildId, userId); // if unmute errored, just remove unmute from db |  | ||||||
|                         Administration._log.Warn("Couldn't unmute user {0} in guild {1}", userId, guildId); |  | ||||||
|                         Administration._log.Warn(ex); |  | ||||||
|                     } |  | ||||||
|                 }, null, after, Timeout.InfiniteTimeSpan); |  | ||||||
|  |  | ||||||
|                 //add it, or stop the old one and add this one |  | ||||||
|                 userUnmuteTimers.AddOrUpdate(userId, (key) => toAdd, (key, old) => |  | ||||||
|                 { |  | ||||||
|                     old.Change(Timeout.Infinite, Timeout.Infinite); |  | ||||||
|                     return toAdd; |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public static void StopUnmuteTimer(ulong guildId, ulong userId) |  | ||||||
|             { |  | ||||||
|                 if (!UnmuteTimers.TryGetValue(guildId, out ConcurrentDictionary<ulong, Timer> userUnmuteTimers)) return; |  | ||||||
|  |  | ||||||
|                 if (userUnmuteTimers.TryRemove(userId, out Timer removed)) |  | ||||||
|                 { |  | ||||||
|                     removed.Change(Timeout.Infinite, Timeout.Infinite); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             private static void RemoveUnmuteTimerFromDb(ulong guildId, ulong userId) |  | ||||||
|             { |  | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |  | ||||||
|                 { |  | ||||||
|                     var config = uow.GuildConfigs.For(guildId, set => set.Include(x => x.UnmuteTimers)); |  | ||||||
|                     config.UnmuteTimers.RemoveWhere(x => x.UserId == userId); |  | ||||||
|                     uow.Complete(); |  | ||||||
|                 } |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
| @@ -257,11 +39,11 @@ namespace NadekoBot.Modules.Administration | |||||||
|                 if (string.IsNullOrWhiteSpace(name)) |                 if (string.IsNullOrWhiteSpace(name)) | ||||||
|                     return; |                     return; | ||||||
|  |  | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     var config = uow.GuildConfigs.For(Context.Guild.Id, set => set); |                     var config = uow.GuildConfigs.For(Context.Guild.Id, set => set); | ||||||
|                     config.MuteRoleName = name; |                     config.MuteRoleName = name; | ||||||
|                     GuildMuteRoles.AddOrUpdate(Context.Guild.Id, name, (id, old) => name); |                     _service.GuildMuteRoles.AddOrUpdate(Context.Guild.Id, name, (id, old) => name); | ||||||
|                     await uow.CompleteAsync().ConfigureAwait(false); |                     await uow.CompleteAsync().ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|                 await ReplyConfirmLocalized("mute_role_set").ConfigureAwait(false); |                 await ReplyConfirmLocalized("mute_role_set").ConfigureAwait(false); | ||||||
| @@ -283,7 +65,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             { |             { | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     await MuteUser(user).ConfigureAwait(false); |                     await _service.MuteUser(user).ConfigureAwait(false); | ||||||
|                     await ReplyConfirmLocalized("user_muted", Format.Bold(user.ToString())).ConfigureAwait(false); |                     await ReplyConfirmLocalized("user_muted", Format.Bold(user.ToString())).ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|                 catch |                 catch | ||||||
| @@ -303,7 +85,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                     return; |                     return; | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     await TimedMute(user, TimeSpan.FromMinutes(minutes)).ConfigureAwait(false); |                     await _service.TimedMute(user, TimeSpan.FromMinutes(minutes)).ConfigureAwait(false); | ||||||
|                     await ReplyConfirmLocalized("user_muted_time", Format.Bold(user.ToString()), minutes).ConfigureAwait(false); |                     await ReplyConfirmLocalized("user_muted_time", Format.Bold(user.ToString()), minutes).ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|                 catch (Exception ex) |                 catch (Exception ex) | ||||||
| @@ -321,7 +103,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             { |             { | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     await UnmuteUser(user).ConfigureAwait(false); |                     await _service.UnmuteUser(user).ConfigureAwait(false); | ||||||
|                     await ReplyConfirmLocalized("user_unmuted", Format.Bold(user.ToString())).ConfigureAwait(false); |                     await ReplyConfirmLocalized("user_unmuted", Format.Bold(user.ToString())).ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|                 catch |                 catch | ||||||
| @@ -337,8 +119,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             { |             { | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     await user.AddRoleAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false); |                     await _service.MuteUser(user, MuteType.Chat).ConfigureAwait(false); | ||||||
|                     UserMuted(user, MuteType.Chat); |  | ||||||
|                     await ReplyConfirmLocalized("user_chat_mute", Format.Bold(user.ToString())).ConfigureAwait(false); |                     await ReplyConfirmLocalized("user_chat_mute", Format.Bold(user.ToString())).ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|                 catch |                 catch | ||||||
| @@ -354,8 +135,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             { |             { | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     await user.RemoveRoleAsync(await GetMuteRole(Context.Guild).ConfigureAwait(false)).ConfigureAwait(false); |                     await _service.UnmuteUser(user, MuteType.Chat).ConfigureAwait(false); | ||||||
|                     UserUnmuted(user, MuteType.Chat); |  | ||||||
|                     await ReplyConfirmLocalized("user_chat_unmute", Format.Bold(user.ToString())).ConfigureAwait(false); |                     await ReplyConfirmLocalized("user_chat_unmute", Format.Bold(user.ToString())).ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|                 catch |                 catch | ||||||
| @@ -367,12 +147,11 @@ namespace NadekoBot.Modules.Administration | |||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             [RequireContext(ContextType.Guild)] |             [RequireContext(ContextType.Guild)] | ||||||
|             [RequireUserPermission(GuildPermission.MuteMembers)] |             [RequireUserPermission(GuildPermission.MuteMembers)] | ||||||
|             public async Task VoiceMute(IGuildUser user) |             public async Task VoiceMute([Remainder] IGuildUser user) | ||||||
|             { |             { | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     await user.ModifyAsync(usr => usr.Mute = true).ConfigureAwait(false); |                     await _service.MuteUser(user, MuteType.Voice).ConfigureAwait(false); | ||||||
|                     UserMuted(user, MuteType.Voice); |  | ||||||
|                     await ReplyConfirmLocalized("user_voice_mute", Format.Bold(user.ToString())).ConfigureAwait(false); |                     await ReplyConfirmLocalized("user_voice_mute", Format.Bold(user.ToString())).ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|                 catch |                 catch | ||||||
| @@ -384,12 +163,11 @@ namespace NadekoBot.Modules.Administration | |||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             [RequireContext(ContextType.Guild)] |             [RequireContext(ContextType.Guild)] | ||||||
|             [RequireUserPermission(GuildPermission.MuteMembers)] |             [RequireUserPermission(GuildPermission.MuteMembers)] | ||||||
|             public async Task VoiceUnmute(IGuildUser user) |             public async Task VoiceUnmute([Remainder] IGuildUser user) | ||||||
|             { |             { | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     await user.ModifyAsync(usr => usr.Mute = false).ConfigureAwait(false); |                     await _service.UnmuteUser(user, MuteType.Voice).ConfigureAwait(false); | ||||||
|                     UserUnmuted(user, MuteType.Voice); |  | ||||||
|                     await ReplyConfirmLocalized("user_voice_unmute", Format.Bold(user.ToString())).ConfigureAwait(false); |                     await ReplyConfirmLocalized("user_voice_unmute", Format.Bold(user.ToString())).ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|                 catch |                 catch | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ using Microsoft.EntityFrameworkCore; | |||||||
| using System.Collections.Immutable; | using System.Collections.Immutable; | ||||||
| using NadekoBot.DataStructures; | using NadekoBot.DataStructures; | ||||||
| using NLog; | using NLog; | ||||||
|  | using NadekoBot.Services.Administration; | ||||||
|  |  | ||||||
| namespace NadekoBot.Modules.Administration | namespace NadekoBot.Modules.Administration | ||||||
| { | { | ||||||
| @@ -23,34 +24,20 @@ namespace NadekoBot.Modules.Administration | |||||||
|         [Group] |         [Group] | ||||||
|         public class SelfCommands : NadekoSubmodule |         public class SelfCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|             private static volatile bool _forwardDMs; |             private readonly DbHandler _db; | ||||||
|             private static volatile bool _forwardDMsToAllOwners; |  | ||||||
|  |  | ||||||
|             private static readonly object _locker = new object(); |             private static readonly object _locker = new object(); | ||||||
|  |             private readonly SelfService _service; | ||||||
|  |             private readonly DiscordShardedClient _client; | ||||||
|  |             private readonly IImagesService _images; | ||||||
|  |  | ||||||
|             private new static readonly Logger _log; |             public SelfCommands(DbHandler db, SelfService service, DiscordShardedClient client, | ||||||
|  |                 IImagesService images) | ||||||
|             static SelfCommands() |  | ||||||
|             { |             { | ||||||
|                 _log = LogManager.GetCurrentClassLogger(); |                 _db = db; | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 _service = service; | ||||||
|                 { |                 _client = client; | ||||||
|                     var config = uow.BotConfig.GetOrCreate(); |                 _images = images; | ||||||
|                     _forwardDMs = config.ForwardMessages; |  | ||||||
|                     _forwardDMsToAllOwners = config.ForwardToAllOwners; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 var _ = Task.Run(async () => |  | ||||||
|                 { |  | ||||||
|                     while (!NadekoBot.Ready) |  | ||||||
|                         await Task.Delay(1000); |  | ||||||
|  |  | ||||||
|                     foreach (var cmd in NadekoBot.BotConfig.StartupCommands) |  | ||||||
|                     { |  | ||||||
|                         await NadekoBot.CommandHandler.ExecuteExternal(cmd.GuildId, cmd.ChannelId, cmd.CommandText); |  | ||||||
|                         await Task.Delay(400).ConfigureAwait(false); |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
| @@ -69,7 +56,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                     VoiceChannelId = guser.VoiceChannel?.Id, |                     VoiceChannelId = guser.VoiceChannel?.Id, | ||||||
|                     VoiceChannelName = guser.VoiceChannel?.Name, |                     VoiceChannelName = guser.VoiceChannel?.Name, | ||||||
|                 }; |                 }; | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     uow.BotConfig |                     uow.BotConfig | ||||||
|                        .GetOrCreate(set => set.Include(x => x.StartupCommands)) |                        .GetOrCreate(set => set.Include(x => x.StartupCommands)) | ||||||
| @@ -96,7 +83,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                     return; |                     return; | ||||||
|                 page -= 1; |                 page -= 1; | ||||||
|                 IEnumerable<StartupCommand> scmds; |                 IEnumerable<StartupCommand> scmds; | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     scmds = uow.BotConfig |                     scmds = uow.BotConfig | ||||||
|                        .GetOrCreate(set => set.Include(x => x.StartupCommands)) |                        .GetOrCreate(set => set.Include(x => x.StartupCommands)) | ||||||
| @@ -148,7 +135,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             public async Task StartupCommandRemove([Remainder] string cmdText) |             public async Task StartupCommandRemove([Remainder] string cmdText) | ||||||
|             { |             { | ||||||
|                 StartupCommand cmd; |                 StartupCommand cmd; | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     var cmds = uow.BotConfig |                     var cmds = uow.BotConfig | ||||||
|                        .GetOrCreate(set => set.Include(x => x.StartupCommands)) |                        .GetOrCreate(set => set.Include(x => x.StartupCommands)) | ||||||
| @@ -174,7 +161,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             [OwnerOnly] |             [OwnerOnly] | ||||||
|             public async Task StartupCommandsClear() |             public async Task StartupCommandsClear() | ||||||
|             { |             { | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     uow.BotConfig |                     uow.BotConfig | ||||||
|                        .GetOrCreate(set => set.Include(x => x.StartupCommands)) |                        .GetOrCreate(set => set.Include(x => x.StartupCommands)) | ||||||
| @@ -190,14 +177,13 @@ namespace NadekoBot.Modules.Administration | |||||||
|             [OwnerOnly] |             [OwnerOnly] | ||||||
|             public async Task ForwardMessages() |             public async Task ForwardMessages() | ||||||
|             { |             { | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     var config = uow.BotConfig.GetOrCreate(); |                     var config = uow.BotConfig.GetOrCreate(); | ||||||
|                     lock (_locker) |                     _service.ForwardDMs = config.ForwardMessages = !config.ForwardMessages; | ||||||
|                         _forwardDMs = config.ForwardMessages = !config.ForwardMessages; |  | ||||||
|                     uow.Complete(); |                     uow.Complete(); | ||||||
|                 } |                 } | ||||||
|                 if (_forwardDMs) |                 if (_service.ForwardDMs) | ||||||
|                     await ReplyConfirmLocalized("fwdm_start").ConfigureAwait(false); |                     await ReplyConfirmLocalized("fwdm_start").ConfigureAwait(false); | ||||||
|                 else |                 else | ||||||
|                     await ReplyConfirmLocalized("fwdm_stop").ConfigureAwait(false); |                     await ReplyConfirmLocalized("fwdm_stop").ConfigureAwait(false); | ||||||
| @@ -207,83 +193,83 @@ namespace NadekoBot.Modules.Administration | |||||||
|             [OwnerOnly] |             [OwnerOnly] | ||||||
|             public async Task ForwardToAll() |             public async Task ForwardToAll() | ||||||
|             { |             { | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     var config = uow.BotConfig.GetOrCreate(); |                     var config = uow.BotConfig.GetOrCreate(); | ||||||
|                     lock (_locker) |                     lock (_locker) | ||||||
|                         _forwardDMsToAllOwners = config.ForwardToAllOwners = !config.ForwardToAllOwners; |                         _service.ForwardDMsToAllOwners = config.ForwardToAllOwners = !config.ForwardToAllOwners; | ||||||
|                     uow.Complete(); |                     uow.Complete(); | ||||||
|                 } |                 } | ||||||
|                 if (_forwardDMsToAllOwners) |                 if (_service.ForwardDMsToAllOwners) | ||||||
|                     await ReplyConfirmLocalized("fwall_start").ConfigureAwait(false); |                     await ReplyConfirmLocalized("fwall_start").ConfigureAwait(false); | ||||||
|                 else |                 else | ||||||
|                     await ReplyConfirmLocalized("fwall_stop").ConfigureAwait(false); |                     await ReplyConfirmLocalized("fwall_stop").ConfigureAwait(false); | ||||||
|  |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             public static async Task HandleDmForwarding(IUserMessage msg, ImmutableArray<AsyncLazy<IDMChannel>> ownerChannels) |             //todo dm forwarding | ||||||
|             { |             //public async Task HandleDmForwarding(IUserMessage msg, ImmutableArray<AsyncLazy<IDMChannel>> ownerChannels) | ||||||
|                 if (_forwardDMs && ownerChannels.Length > 0) |             //{ | ||||||
|                 { |             //    if (_service.ForwardDMs && ownerChannels.Length > 0) | ||||||
|                     var title = GetTextStatic("dm_from", |             //    { | ||||||
|                                     NadekoBot.Localization.DefaultCultureInfo, |             //        var title = _strings.GetText("dm_from", | ||||||
|                                     typeof(Administration).Name.ToLowerInvariant()) + |             //                        NadekoBot.Localization.DefaultCultureInfo, | ||||||
|                                 $" [{msg.Author}]({msg.Author.Id})"; |             //                        typeof(Administration).Name.ToLowerInvariant()) + | ||||||
|  |             //                    $" [{msg.Author}]({msg.Author.Id})"; | ||||||
|  |  | ||||||
|                     var attachamentsTxt = GetTextStatic("attachments", |             //        var attachamentsTxt = GetTextStatic("attachments", | ||||||
|                         NadekoBot.Localization.DefaultCultureInfo, |             //            NadekoBot.Localization.DefaultCultureInfo, | ||||||
|                         typeof(Administration).Name.ToLowerInvariant()); |             //            typeof(Administration).Name.ToLowerInvariant()); | ||||||
|  |  | ||||||
|                     var toSend = msg.Content; |             //        var toSend = msg.Content; | ||||||
|  |  | ||||||
|                     if (msg.Attachments.Count > 0) |             //        if (msg.Attachments.Count > 0) | ||||||
|                     { |             //        { | ||||||
|                         toSend += $"\n\n{Format.Code(attachamentsTxt)}:\n" + |             //            toSend += $"\n\n{Format.Code(attachamentsTxt)}:\n" + | ||||||
|                                   string.Join("\n", msg.Attachments.Select(a => a.ProxyUrl)); |             //                      string.Join("\n", msg.Attachments.Select(a => a.ProxyUrl)); | ||||||
|                     } |             //        } | ||||||
|  |  | ||||||
|                     if (_forwardDMsToAllOwners) |  | ||||||
|                     { |  | ||||||
|                         var allOwnerChannels = await Task.WhenAll(ownerChannels |  | ||||||
|                             .Select(x => x.Value)) |  | ||||||
|                             .ConfigureAwait(false); |  | ||||||
|  |  | ||||||
|                         foreach (var ownerCh in allOwnerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id)) |  | ||||||
|                         { |  | ||||||
|                             try |  | ||||||
|                             { |  | ||||||
|                                 await ownerCh.SendConfirmAsync(title, toSend).ConfigureAwait(false); |  | ||||||
|                             } |  | ||||||
|                             catch |  | ||||||
|                             { |  | ||||||
|                                 _log.Warn("Can't contact owner with id {0}", ownerCh.Recipient.Id); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     else |  | ||||||
|                     { |  | ||||||
|                             var firstOwnerChannel = await ownerChannels[0]; |  | ||||||
|                             if (firstOwnerChannel.Recipient.Id != msg.Author.Id) |  | ||||||
|                             { |  | ||||||
|                                 try |  | ||||||
|                                 { |  | ||||||
|                                     await firstOwnerChannel.SendConfirmAsync(title, toSend).ConfigureAwait(false); |  | ||||||
|                                 } |  | ||||||
|                                 catch |  | ||||||
|                                 { |  | ||||||
|                                     // ignored |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|  |             //        if (_service.ForwardDMsToAllOwners) | ||||||
|  |             //        { | ||||||
|  |             //            var allOwnerChannels = await Task.WhenAll(ownerChannels | ||||||
|  |             //                .Select(x => x.Value)) | ||||||
|  |             //                .ConfigureAwait(false); | ||||||
|  |  | ||||||
|  |             //            foreach (var ownerCh in allOwnerChannels.Where(ch => ch.Recipient.Id != msg.Author.Id)) | ||||||
|  |             //            { | ||||||
|  |             //                try | ||||||
|  |             //                { | ||||||
|  |             //                    await ownerCh.SendConfirmAsync(title, toSend).ConfigureAwait(false); | ||||||
|  |             //                } | ||||||
|  |             //                catch | ||||||
|  |             //                { | ||||||
|  |             //                    _log.Warn("Can't contact owner with id {0}", ownerCh.Recipient.Id); | ||||||
|  |             //                } | ||||||
|  |             //            } | ||||||
|  |             //        } | ||||||
|  |             //        else | ||||||
|  |             //        { | ||||||
|  |             //                var firstOwnerChannel = await ownerChannels[0]; | ||||||
|  |             //                if (firstOwnerChannel.Recipient.Id != msg.Author.Id) | ||||||
|  |             //                { | ||||||
|  |             //                    try | ||||||
|  |             //                    { | ||||||
|  |             //                        await firstOwnerChannel.SendConfirmAsync(title, toSend).ConfigureAwait(false); | ||||||
|  |             //                    } | ||||||
|  |             //                    catch | ||||||
|  |             //                    { | ||||||
|  |             //                        // ignored | ||||||
|  |             //                    } | ||||||
|  |             //                } | ||||||
|  |             //            } | ||||||
|  |             //        } | ||||||
|  |             //    } | ||||||
|  |              | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             [OwnerOnly] |             [OwnerOnly] | ||||||
|             public async Task ConnectShard(int shardid) |             public async Task ConnectShard(int shardid) | ||||||
|             { |             { | ||||||
|                 var shard = NadekoBot.Client.GetShard(shardid); |                 var shard = _client.GetShard(shardid); | ||||||
|  |  | ||||||
|                 if (shard == null) |                 if (shard == null) | ||||||
|                 { |                 { | ||||||
| @@ -307,15 +293,15 @@ namespace NadekoBot.Modules.Administration | |||||||
|             public async Task Leave([Remainder] string guildStr) |             public async Task Leave([Remainder] string guildStr) | ||||||
|             { |             { | ||||||
|                 guildStr = guildStr.Trim().ToUpperInvariant(); |                 guildStr = guildStr.Trim().ToUpperInvariant(); | ||||||
|                 var server = NadekoBot.Client.Guilds.FirstOrDefault(g => g.Id.ToString() == guildStr) ?? |                 var server = _client.Guilds.FirstOrDefault(g => g.Id.ToString() == guildStr) ?? | ||||||
|                     NadekoBot.Client.Guilds.FirstOrDefault(g => g.Name.Trim().ToUpperInvariant() == guildStr); |                     _client.Guilds.FirstOrDefault(g => g.Name.Trim().ToUpperInvariant() == guildStr); | ||||||
|  |  | ||||||
|                 if (server == null) |                 if (server == null) | ||||||
|                 { |                 { | ||||||
|                     await ReplyErrorLocalized("no_server").ConfigureAwait(false); |                     await ReplyErrorLocalized("no_server").ConfigureAwait(false); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|                 if (server.OwnerId != NadekoBot.Client.CurrentUser.Id) |                 if (server.OwnerId != _client.CurrentUser.Id) | ||||||
|                 { |                 { | ||||||
|                     await server.LeaveAsync().ConfigureAwait(false); |                     await server.LeaveAsync().ConfigureAwait(false); | ||||||
|                     await ReplyConfirmLocalized("left_server", Format.Bold(server.Name)).ConfigureAwait(false); |                     await ReplyConfirmLocalized("left_server", Format.Bold(server.Name)).ConfigureAwait(false); | ||||||
| @@ -351,7 +337,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                 if (string.IsNullOrWhiteSpace(newName)) |                 if (string.IsNullOrWhiteSpace(newName)) | ||||||
|                     return; |                     return; | ||||||
|  |  | ||||||
|                 await NadekoBot.Client.CurrentUser.ModifyAsync(u => u.Username = newName).ConfigureAwait(false); |                 await _client.CurrentUser.ModifyAsync(u => u.Username = newName).ConfigureAwait(false); | ||||||
|  |  | ||||||
|                 await ReplyConfirmLocalized("bot_name", Format.Bold(newName)).ConfigureAwait(false); |                 await ReplyConfirmLocalized("bot_name", Format.Bold(newName)).ConfigureAwait(false); | ||||||
|             } |             } | ||||||
| @@ -360,7 +346,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             [OwnerOnly] |             [OwnerOnly] | ||||||
|             public async Task SetStatus([Remainder] SettableUserStatus status) |             public async Task SetStatus([Remainder] SettableUserStatus status) | ||||||
|             { |             { | ||||||
|                 await NadekoBot.Client.SetStatusAsync(SettableUserStatusToUserStatus(status)).ConfigureAwait(false); |                 await _client.SetStatusAsync(SettableUserStatusToUserStatus(status)).ConfigureAwait(false); | ||||||
|  |  | ||||||
|                 await ReplyConfirmLocalized("bot_status", Format.Bold(status.ToString())).ConfigureAwait(false); |                 await ReplyConfirmLocalized("bot_status", Format.Bold(status.ToString())).ConfigureAwait(false); | ||||||
|             } |             } | ||||||
| @@ -380,7 +366,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                         await sr.CopyToAsync(imgStream); |                         await sr.CopyToAsync(imgStream); | ||||||
|                         imgStream.Position = 0; |                         imgStream.Position = 0; | ||||||
|  |  | ||||||
|                         await NadekoBot.Client.CurrentUser.ModifyAsync(u => u.Avatar = new Image(imgStream)).ConfigureAwait(false); |                         await _client.CurrentUser.ModifyAsync(u => u.Avatar = new Image(imgStream)).ConfigureAwait(false); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
| @@ -391,7 +377,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             [OwnerOnly] |             [OwnerOnly] | ||||||
|             public async Task SetGame([Remainder] string game = null) |             public async Task SetGame([Remainder] string game = null) | ||||||
|             { |             { | ||||||
|                 await NadekoBot.Client.SetGameAsync(game).ConfigureAwait(false); |                 await _client.SetGameAsync(game).ConfigureAwait(false); | ||||||
|  |  | ||||||
|                 await ReplyConfirmLocalized("set_game").ConfigureAwait(false); |                 await ReplyConfirmLocalized("set_game").ConfigureAwait(false); | ||||||
|             } |             } | ||||||
| @@ -402,7 +388,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             { |             { | ||||||
|                 name = name ?? ""; |                 name = name ?? ""; | ||||||
|  |  | ||||||
|                 await NadekoBot.Client.SetGameAsync(name, url, StreamType.Twitch).ConfigureAwait(false); |                 await _client.SetGameAsync(name, url, StreamType.Twitch).ConfigureAwait(false); | ||||||
|  |  | ||||||
|                 await ReplyConfirmLocalized("set_stream").ConfigureAwait(false); |                 await ReplyConfirmLocalized("set_stream").ConfigureAwait(false); | ||||||
|             } |             } | ||||||
| @@ -418,7 +404,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                 if (ids.Length != 2) |                 if (ids.Length != 2) | ||||||
|                     return; |                     return; | ||||||
|                 var sid = ulong.Parse(ids[0]); |                 var sid = ulong.Parse(ids[0]); | ||||||
|                 var server = NadekoBot.Client.Guilds.FirstOrDefault(s => s.Id == sid); |                 var server = _client.Guilds.FirstOrDefault(s => s.Id == sid); | ||||||
|  |  | ||||||
|                 if (server == null) |                 if (server == null) | ||||||
|                     return; |                     return; | ||||||
| @@ -455,7 +441,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             [OwnerOnly] |             [OwnerOnly] | ||||||
|             public async Task Announce([Remainder] string message) |             public async Task Announce([Remainder] string message) | ||||||
|             { |             { | ||||||
|                 var channels = NadekoBot.Client.Guilds.Select(g => g.DefaultChannel).ToArray(); |                 var channels = _client.Guilds.Select(g => g.DefaultChannel).ToArray(); | ||||||
|                 if (channels == null) |                 if (channels == null) | ||||||
|                     return; |                     return; | ||||||
|                 await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync(GetText("message_from_bo", Context.User.ToString()), message))) |                 await Task.WhenAll(channels.Where(c => c != null).Select(c => c.SendConfirmAsync(GetText("message_from_bo", Context.User.ToString()), message))) | ||||||
| @@ -468,7 +454,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             [OwnerOnly] |             [OwnerOnly] | ||||||
|             public async Task ReloadImages() |             public async Task ReloadImages() | ||||||
|             { |             { | ||||||
|                 var time = await NadekoBot.Images.Reload().ConfigureAwait(false); |                 var time = _images.Reload(); | ||||||
|                 await ReplyConfirmLocalized("images_loaded", time.TotalSeconds.ToString("F3")).ConfigureAwait(false); |                 await ReplyConfirmLocalized("images_loaded", time.TotalSeconds.ToString("F3")).ConfigureAwait(false); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,14 +1,9 @@ | |||||||
| using Discord; | using Discord; | ||||||
| using Discord.Commands; | using Discord.Commands; | ||||||
| using NadekoBot.Attributes; | using NadekoBot.Attributes; | ||||||
| using NadekoBot.DataStructures; |  | ||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
| using NadekoBot.Services; | using NadekoBot.Services; | ||||||
| using NadekoBot.Services.Database.Models; | using NadekoBot.Services.Database.Models; | ||||||
| using NLog; |  | ||||||
| using System; |  | ||||||
| using System.Collections.Concurrent; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
|  |  | ||||||
| namespace NadekoBot.Modules.Administration | namespace NadekoBot.Modules.Administration | ||||||
| @@ -18,158 +13,13 @@ namespace NadekoBot.Modules.Administration | |||||||
|         [Group] |         [Group] | ||||||
|         public class ServerGreetCommands : NadekoSubmodule |         public class ServerGreetCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|             //make this to a field in the guildconfig table |             private readonly GreetSettingsService _greetService; | ||||||
|  |             private readonly DbHandler _db; | ||||||
|  |  | ||||||
|             private new static Logger _log { get; } |             public ServerGreetCommands(GreetSettingsService greetService, DbHandler db) | ||||||
|             private static readonly GreetSettingsService greetService; |  | ||||||
|  |  | ||||||
|             static ServerGreetCommands() |  | ||||||
|             { |             { | ||||||
|                 NadekoBot.Client.UserJoined += UserJoined; |                 _greetService = greetService; | ||||||
|                 NadekoBot.Client.UserLeft += UserLeft; |                 _db = db; | ||||||
|                 _log = LogManager.GetCurrentClassLogger(); |  | ||||||
|  |  | ||||||
|                 //todo di |  | ||||||
|                 greetService = NadekoBot.GreetSettingsService; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             private static Task UserLeft(IGuildUser user) |  | ||||||
|             { |  | ||||||
|                 var _ = Task.Run(async () => |  | ||||||
|                 { |  | ||||||
|                     try |  | ||||||
|                     { |  | ||||||
|                         var conf = greetService.GetOrAddSettingsForGuild(user.GuildId); |  | ||||||
|  |  | ||||||
|                         if (!conf.SendChannelByeMessage) return; |  | ||||||
|                         var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId); |  | ||||||
|  |  | ||||||
|                         if (channel == null) //maybe warn the server owner that the channel is missing |  | ||||||
|                             return; |  | ||||||
|                         CREmbed embedData; |  | ||||||
|                         if (CREmbed.TryParse(conf.ChannelByeMessageText, out embedData)) |  | ||||||
|                         { |  | ||||||
|                             embedData.PlainText = embedData.PlainText?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); |  | ||||||
|                             embedData.Description = embedData.Description?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); |  | ||||||
|                             embedData.Title = embedData.Title?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); |  | ||||||
|                             try |  | ||||||
|                             { |  | ||||||
|                                 var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false); |  | ||||||
|                                 if (conf.AutoDeleteByeMessagesTimer > 0) |  | ||||||
|                                 { |  | ||||||
|                                     toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer); |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                             catch (Exception ex) { _log.Warn(ex); } |  | ||||||
|                         } |  | ||||||
|                         else |  | ||||||
|                         { |  | ||||||
|                             var msg = conf.ChannelByeMessageText.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); |  | ||||||
|                             if (string.IsNullOrWhiteSpace(msg)) |  | ||||||
|                                 return; |  | ||||||
|                             try |  | ||||||
|                             { |  | ||||||
|                                 var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false); |  | ||||||
|                                 if (conf.AutoDeleteByeMessagesTimer > 0) |  | ||||||
|                                 { |  | ||||||
|                                     toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer); |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                             catch (Exception ex) { _log.Warn(ex); } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     catch |  | ||||||
|                     { |  | ||||||
|                         // ignored |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|                 return Task.CompletedTask; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             private static Task UserJoined(IGuildUser user) |  | ||||||
|             { |  | ||||||
|                 var _ = Task.Run(async () => |  | ||||||
|                 { |  | ||||||
|                     try |  | ||||||
|                     { |  | ||||||
|                         var conf = greetService.GetOrAddSettingsForGuild(user.GuildId); |  | ||||||
|  |  | ||||||
|                         if (conf.SendChannelGreetMessage) |  | ||||||
|                         { |  | ||||||
|                             var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.GreetMessageChannelId); |  | ||||||
|                             if (channel != null) //maybe warn the server owner that the channel is missing |  | ||||||
|                             { |  | ||||||
|  |  | ||||||
|                                 CREmbed embedData; |  | ||||||
|                                 if (CREmbed.TryParse(conf.ChannelGreetMessageText, out embedData)) |  | ||||||
|                                 { |  | ||||||
|                                     embedData.PlainText = embedData.PlainText?.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); |  | ||||||
|                                     embedData.Description = embedData.Description?.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); |  | ||||||
|                                     embedData.Title = embedData.Title?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); |  | ||||||
|                                     try |  | ||||||
|                                     { |  | ||||||
|                                         var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false); |  | ||||||
|                                         if (conf.AutoDeleteGreetMessagesTimer > 0) |  | ||||||
|                                         { |  | ||||||
|                                             toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer); |  | ||||||
|                                         } |  | ||||||
|                                     } |  | ||||||
|                                     catch (Exception ex) { _log.Warn(ex); } |  | ||||||
|                                 } |  | ||||||
|                                 else |  | ||||||
|                                 { |  | ||||||
|                                     var msg = conf.ChannelGreetMessageText.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); |  | ||||||
|                                     if (!string.IsNullOrWhiteSpace(msg)) |  | ||||||
|                                     { |  | ||||||
|                                         try |  | ||||||
|                                         { |  | ||||||
|                                             var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false); |  | ||||||
|                                             if (conf.AutoDeleteGreetMessagesTimer > 0) |  | ||||||
|                                             { |  | ||||||
|                                                 toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer); |  | ||||||
|                                             } |  | ||||||
|                                         } |  | ||||||
|                                         catch (Exception ex) { _log.Warn(ex); } |  | ||||||
|                                     } |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                         if (conf.SendDmGreetMessage) |  | ||||||
|                         { |  | ||||||
|                             var channel = await user.CreateDMChannelAsync(); |  | ||||||
|  |  | ||||||
|                             if (channel != null) |  | ||||||
|                             { |  | ||||||
|                                 CREmbed embedData; |  | ||||||
|                                 if (CREmbed.TryParse(conf.ChannelGreetMessageText, out embedData)) |  | ||||||
|                                 { |  | ||||||
|                                     embedData.PlainText = embedData.PlainText?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); |  | ||||||
|                                     embedData.Description = embedData.Description?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); |  | ||||||
|                                     embedData.Title = embedData.Title?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); |  | ||||||
|                                     try |  | ||||||
|                                     { |  | ||||||
|                                         await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false); |  | ||||||
|                                     } |  | ||||||
|                                     catch (Exception ex) { _log.Warn(ex); } |  | ||||||
|                                 } |  | ||||||
|                                 else |  | ||||||
|                                 { |  | ||||||
|                                     var msg = conf.DmGreetMessageText.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); |  | ||||||
|                                     if (!string.IsNullOrWhiteSpace(msg)) |  | ||||||
|                                     { |  | ||||||
|                                         await channel.SendConfirmAsync(msg).ConfigureAwait(false); |  | ||||||
|                                     } |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     catch |  | ||||||
|                     { |  | ||||||
|                         // ignored |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|                 return Task.CompletedTask; |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
| @@ -180,7 +30,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                 if (timer < 0 || timer > 600) |                 if (timer < 0 || timer > 600) | ||||||
|                     return; |                     return; | ||||||
|  |  | ||||||
|                 await ServerGreetCommands.SetGreetDel(Context.Guild.Id, timer).ConfigureAwait(false); |                 await _greetService.SetGreetDel(Context.Guild.Id, timer).ConfigureAwait(false); | ||||||
|  |  | ||||||
|                 if (timer > 0) |                 if (timer > 0) | ||||||
|                     await ReplyConfirmLocalized("greetdel_on", timer).ConfigureAwait(false); |                     await ReplyConfirmLocalized("greetdel_on", timer).ConfigureAwait(false); | ||||||
| @@ -188,29 +38,12 @@ namespace NadekoBot.Modules.Administration | |||||||
|                     await ReplyConfirmLocalized("greetdel_off").ConfigureAwait(false); |                     await ReplyConfirmLocalized("greetdel_off").ConfigureAwait(false); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             private static async Task SetGreetDel(ulong id, int timer) |  | ||||||
|             { |  | ||||||
|                 if (timer < 0 || timer > 600) |  | ||||||
|                     return; |  | ||||||
|  |  | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |  | ||||||
|                 { |  | ||||||
|                     var conf = uow.GuildConfigs.For(id, set => set); |  | ||||||
|                     conf.AutoDeleteGreetMessagesTimer = timer; |  | ||||||
|  |  | ||||||
|                     var toAdd = GreetSettings.Create(conf); |  | ||||||
|                     greetService.GuildConfigsCache.AddOrUpdate(id, toAdd, (key, old) => toAdd); |  | ||||||
|  |  | ||||||
|                     await uow.CompleteAsync().ConfigureAwait(false); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             [RequireContext(ContextType.Guild)] |             [RequireContext(ContextType.Guild)] | ||||||
|             [RequireUserPermission(GuildPermission.ManageGuild)] |             [RequireUserPermission(GuildPermission.ManageGuild)] | ||||||
|             public async Task Greet() |             public async Task Greet() | ||||||
|             { |             { | ||||||
|                 var enabled = await greetService.SetGreet(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false); |                 var enabled = await _greetService.SetGreet(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false); | ||||||
|  |  | ||||||
|                 if (enabled) |                 if (enabled) | ||||||
|                     await ReplyConfirmLocalized("greet_on").ConfigureAwait(false); |                     await ReplyConfirmLocalized("greet_on").ConfigureAwait(false); | ||||||
| @@ -226,7 +59,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                 if (string.IsNullOrWhiteSpace(text)) |                 if (string.IsNullOrWhiteSpace(text)) | ||||||
|                 { |                 { | ||||||
|                     string channelGreetMessageText; |                     string channelGreetMessageText; | ||||||
|                     using (var uow = DbHandler.UnitOfWork()) |                     using (var uow = _db.UnitOfWork) | ||||||
|                     { |                     { | ||||||
|                         channelGreetMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelGreetMessageText; |                         channelGreetMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelGreetMessageText; | ||||||
|                     } |                     } | ||||||
| @@ -234,7 +67,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 var sendGreetEnabled = greetService.SetGreetMessage(Context.Guild.Id, ref text); |                 var sendGreetEnabled = _greetService.SetGreetMessage(Context.Guild.Id, ref text); | ||||||
|  |  | ||||||
|                 await ReplyConfirmLocalized("greetmsg_new").ConfigureAwait(false); |                 await ReplyConfirmLocalized("greetmsg_new").ConfigureAwait(false); | ||||||
|                 if (!sendGreetEnabled) |                 if (!sendGreetEnabled) | ||||||
| @@ -246,7 +79,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             [RequireUserPermission(GuildPermission.ManageGuild)] |             [RequireUserPermission(GuildPermission.ManageGuild)] | ||||||
|             public async Task GreetDm() |             public async Task GreetDm() | ||||||
|             { |             { | ||||||
|                 var enabled = await greetService.SetGreetDm(Context.Guild.Id).ConfigureAwait(false); |                 var enabled = await _greetService.SetGreetDm(Context.Guild.Id).ConfigureAwait(false); | ||||||
|  |  | ||||||
|                 if (enabled) |                 if (enabled) | ||||||
|                     await ReplyConfirmLocalized("greetdm_on").ConfigureAwait(false); |                     await ReplyConfirmLocalized("greetdm_on").ConfigureAwait(false); | ||||||
| @@ -262,7 +95,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                 if (string.IsNullOrWhiteSpace(text)) |                 if (string.IsNullOrWhiteSpace(text)) | ||||||
|                 { |                 { | ||||||
|                     GuildConfig config; |                     GuildConfig config; | ||||||
|                     using (var uow = DbHandler.UnitOfWork()) |                     using (var uow = _db.UnitOfWork) | ||||||
|                     { |                     { | ||||||
|                         config = uow.GuildConfigs.For(Context.Guild.Id); |                         config = uow.GuildConfigs.For(Context.Guild.Id); | ||||||
|                     } |                     } | ||||||
| @@ -270,7 +103,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 var sendGreetEnabled = greetService.SetGreetDmMessage(Context.Guild.Id, ref text); |                 var sendGreetEnabled = _greetService.SetGreetDmMessage(Context.Guild.Id, ref text); | ||||||
|  |  | ||||||
|                 await ReplyConfirmLocalized("greetdmmsg_new").ConfigureAwait(false); |                 await ReplyConfirmLocalized("greetdmmsg_new").ConfigureAwait(false); | ||||||
|                 if (!sendGreetEnabled) |                 if (!sendGreetEnabled) | ||||||
| @@ -282,7 +115,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             [RequireUserPermission(GuildPermission.ManageGuild)] |             [RequireUserPermission(GuildPermission.ManageGuild)] | ||||||
|             public async Task Bye() |             public async Task Bye() | ||||||
|             { |             { | ||||||
|                 var enabled = await greetService.SetBye(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false); |                 var enabled = await _greetService.SetBye(Context.Guild.Id, Context.Channel.Id).ConfigureAwait(false); | ||||||
|  |  | ||||||
|                 if (enabled) |                 if (enabled) | ||||||
|                     await ReplyConfirmLocalized("bye_on").ConfigureAwait(false); |                     await ReplyConfirmLocalized("bye_on").ConfigureAwait(false); | ||||||
| @@ -298,7 +131,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                 if (string.IsNullOrWhiteSpace(text)) |                 if (string.IsNullOrWhiteSpace(text)) | ||||||
|                 { |                 { | ||||||
|                     string byeMessageText; |                     string byeMessageText; | ||||||
|                     using (var uow = DbHandler.UnitOfWork()) |                     using (var uow = _db.UnitOfWork) | ||||||
|                     { |                     { | ||||||
|                         byeMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelByeMessageText; |                         byeMessageText = uow.GuildConfigs.For(Context.Guild.Id, set => set).ChannelByeMessageText; | ||||||
|                     } |                     } | ||||||
| @@ -306,7 +139,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 var sendByeEnabled = greetService.SetByeMessage(Context.Guild.Id, ref text); |                 var sendByeEnabled = _greetService.SetByeMessage(Context.Guild.Id, ref text); | ||||||
|  |  | ||||||
|                 await ReplyConfirmLocalized("byemsg_new").ConfigureAwait(false); |                 await ReplyConfirmLocalized("byemsg_new").ConfigureAwait(false); | ||||||
|                 if (!sendByeEnabled) |                 if (!sendByeEnabled) | ||||||
| @@ -318,7 +151,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             [RequireUserPermission(GuildPermission.ManageGuild)] |             [RequireUserPermission(GuildPermission.ManageGuild)] | ||||||
|             public async Task ByeDel(int timer = 30) |             public async Task ByeDel(int timer = 30) | ||||||
|             { |             { | ||||||
|                 await greetService.SetByeDel(Context.Guild.Id, timer).ConfigureAwait(false); |                 await _greetService.SetByeDel(Context.Guild.Id, timer).ConfigureAwait(false); | ||||||
|  |  | ||||||
|                 if (timer > 0) |                 if (timer > 0) | ||||||
|                     await ReplyConfirmLocalized("byedel_on", timer).ConfigureAwait(false); |                     await ReplyConfirmLocalized("byedel_on", timer).ConfigureAwait(false); | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore; | |||||||
| using NadekoBot.Attributes; | using NadekoBot.Attributes; | ||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
| using NadekoBot.Services; | using NadekoBot.Services; | ||||||
|  | using NadekoBot.Services.Administration; | ||||||
| using NadekoBot.Services.Database.Models; | using NadekoBot.Services.Database.Models; | ||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| @@ -18,6 +19,16 @@ namespace NadekoBot.Modules.Administration | |||||||
|         [Group] |         [Group] | ||||||
|         public class UserPunishCommands : NadekoSubmodule |         public class UserPunishCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|  |             private readonly DbHandler _db; | ||||||
|  |             private readonly MuteService _muteService; | ||||||
|  |  | ||||||
|  |             public UserPunishCommands(DbHandler db, MuteService muteService) | ||||||
|  |             { | ||||||
|  |                 _db = db; | ||||||
|  |  | ||||||
|  |                 _muteService = muteService; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             private async Task<PunishmentAction?> InternalWarn(IGuild guild, ulong userId, string modName, string reason) |             private async Task<PunishmentAction?> InternalWarn(IGuild guild, ulong userId, string modName, string reason) | ||||||
|             { |             { | ||||||
|                 if (string.IsNullOrWhiteSpace(reason)) |                 if (string.IsNullOrWhiteSpace(reason)) | ||||||
| @@ -36,7 +47,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|  |  | ||||||
|                 int warnings = 1; |                 int warnings = 1; | ||||||
|                 List<WarningPunishment> ps; |                 List<WarningPunishment> ps; | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     ps = uow.GuildConfigs.For(guildId, set => set.Include(x => x.WarnPunishments)) |                     ps = uow.GuildConfigs.For(guildId, set => set.Include(x => x.WarnPunishments)) | ||||||
|                         .WarnPunishments; |                         .WarnPunishments; | ||||||
| @@ -62,9 +73,9 @@ namespace NadekoBot.Modules.Administration | |||||||
|                     { |                     { | ||||||
|                         case PunishmentAction.Mute: |                         case PunishmentAction.Mute: | ||||||
|                             if (p.Time == 0) |                             if (p.Time == 0) | ||||||
|                                 await MuteCommands.MuteUser(user).ConfigureAwait(false); |                                 await _muteService.MuteUser(user).ConfigureAwait(false); | ||||||
|                             else |                             else | ||||||
|                                 await MuteCommands.TimedMute(user, TimeSpan.FromMinutes(p.Time)).ConfigureAwait(false); |                                 await _muteService.TimedMute(user, TimeSpan.FromMinutes(p.Time)).ConfigureAwait(false); | ||||||
|                             break; |                             break; | ||||||
|                         case PunishmentAction.Kick: |                         case PunishmentAction.Kick: | ||||||
|                             await user.KickAsync().ConfigureAwait(false); |                             await user.KickAsync().ConfigureAwait(false); | ||||||
| @@ -147,7 +158,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                 if (page < 0) |                 if (page < 0) | ||||||
|                     return; |                     return; | ||||||
|                 Warning[] warnings; |                 Warning[] warnings; | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     warnings = uow.Warnings.For(Context.Guild.Id, userId); |                     warnings = uow.Warnings.For(Context.Guild.Id, userId); | ||||||
|                 } |                 } | ||||||
| @@ -192,7 +203,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             [RequireUserPermission(GuildPermission.BanMembers)] |             [RequireUserPermission(GuildPermission.BanMembers)] | ||||||
|             public async Task Warnclear(ulong userId) |             public async Task Warnclear(ulong userId) | ||||||
|             { |             { | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     await uow.Warnings.ForgiveAll(Context.Guild.Id, userId, Context.User.ToString()).ConfigureAwait(false); |                     await uow.Warnings.ForgiveAll(Context.Guild.Id, userId, Context.User.ToString()).ConfigureAwait(false); | ||||||
|                     uow.Complete(); |                     uow.Complete(); | ||||||
| @@ -212,7 +223,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                 if (number <= 0) |                 if (number <= 0) | ||||||
|                     return; |                     return; | ||||||
|  |  | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     var ps = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.WarnPunishments)).WarnPunishments; |                     var ps = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.WarnPunishments)).WarnPunishments; | ||||||
|                     ps.RemoveAll(x => x.Count == number); |                     ps.RemoveAll(x => x.Count == number); | ||||||
| @@ -239,7 +250,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                 if (number <= 0) |                 if (number <= 0) | ||||||
|                     return; |                     return; | ||||||
|  |  | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     var ps = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.WarnPunishments)).WarnPunishments; |                     var ps = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.WarnPunishments)).WarnPunishments; | ||||||
|                     var p = ps.FirstOrDefault(x => x.Count == number); |                     var p = ps.FirstOrDefault(x => x.Count == number); | ||||||
| @@ -260,7 +271,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             public async Task WarnPunishList() |             public async Task WarnPunishList() | ||||||
|             { |             { | ||||||
|                 WarningPunishment[] ps; |                 WarningPunishment[] ps; | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     ps = uow.GuildConfigs.For(Context.Guild.Id, gc => gc.Include(x => x.WarnPunishments)) |                     ps = uow.GuildConfigs.For(Context.Guild.Id, gc => gc.Include(x => x.WarnPunishments)) | ||||||
|                         .WarnPunishments |                         .WarnPunishments | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ using Microsoft.EntityFrameworkCore; | |||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
| using NadekoBot.Services; | using NadekoBot.Services; | ||||||
| using NadekoBot.Services.Database.Models; | using NadekoBot.Services.Database.Models; | ||||||
|  | using NadekoBot.Services.Administration; | ||||||
|  |  | ||||||
| namespace NadekoBot.Modules.Administration | namespace NadekoBot.Modules.Administration | ||||||
| { | { | ||||||
| @@ -18,86 +19,13 @@ namespace NadekoBot.Modules.Administration | |||||||
|         [Group] |         [Group] | ||||||
|         public class VcRoleCommands : NadekoSubmodule |         public class VcRoleCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|             private static ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>> VcRoles { get; } |             private readonly VcRoleService _service; | ||||||
|  |             private readonly DbHandler _db; | ||||||
|  |  | ||||||
|             static VcRoleCommands() |             public VcRoleCommands(VcRoleService service, DbHandler db) | ||||||
|             { |             { | ||||||
|                 NadekoBot.Client.UserVoiceStateUpdated += ClientOnUserVoiceStateUpdated; |                 _service = service; | ||||||
|                 VcRoles = new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>>(); |                 _db = db; | ||||||
|                 foreach (var gconf in NadekoBot.AllGuildConfigs) |  | ||||||
|                 { |  | ||||||
|                     var g = NadekoBot.Client.GetGuild(gconf.GuildId); |  | ||||||
|                     if (g == null) |  | ||||||
|                         continue; //todo delete everything from db if guild doesn't exist? |  | ||||||
|  |  | ||||||
|                     var infos = new ConcurrentDictionary<ulong, IRole>(); |  | ||||||
|                     VcRoles.TryAdd(gconf.GuildId, infos); |  | ||||||
|                     foreach (var ri in gconf.VcRoleInfos) |  | ||||||
|                     { |  | ||||||
|                         var role = g.GetRole(ri.RoleId); |  | ||||||
|                         if (role == null) |  | ||||||
|                             continue; //todo remove this entry from db |  | ||||||
|  |  | ||||||
|                         infos.TryAdd(ri.VoiceChannelId, role); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             private static Task ClientOnUserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState, |  | ||||||
|                 SocketVoiceState newState) |  | ||||||
|             { |  | ||||||
|  |  | ||||||
|                 var gusr = usr as SocketGuildUser; |  | ||||||
|                 if (gusr == null) |  | ||||||
|                     return Task.CompletedTask; |  | ||||||
|  |  | ||||||
|                 var oldVc = oldState.VoiceChannel; |  | ||||||
|                 var newVc = newState.VoiceChannel; |  | ||||||
|                 var _ = Task.Run(async () => |  | ||||||
|                 { |  | ||||||
|                     try |  | ||||||
|                     { |  | ||||||
|                         if (oldVc != newVc) |  | ||||||
|                         { |  | ||||||
|                             ulong guildId; |  | ||||||
|                             guildId = newVc?.Guild.Id ?? oldVc.Guild.Id; |  | ||||||
|  |  | ||||||
|                             if (VcRoles.TryGetValue(guildId, out ConcurrentDictionary<ulong, IRole> guildVcRoles)) |  | ||||||
|                             { |  | ||||||
|                                 //remove old |  | ||||||
|                                 if (oldVc != null && guildVcRoles.TryGetValue(oldVc.Id, out IRole role)) |  | ||||||
|                                 { |  | ||||||
|                                     if (gusr.Roles.Contains(role)) |  | ||||||
|                                     { |  | ||||||
|                                         try |  | ||||||
|                                         { |  | ||||||
|                                             await gusr.RemoveRoleAsync(role).ConfigureAwait(false); |  | ||||||
|                                             await Task.Delay(500).ConfigureAwait(false); |  | ||||||
|                                         } |  | ||||||
|                                         catch |  | ||||||
|                                         { |  | ||||||
|                                             await Task.Delay(200).ConfigureAwait(false); |  | ||||||
|                                             await gusr.RemoveRoleAsync(role).ConfigureAwait(false); |  | ||||||
|                                             await Task.Delay(500).ConfigureAwait(false); |  | ||||||
|                                         } |  | ||||||
|                                     } |  | ||||||
|                                 } |  | ||||||
|                                 //add new |  | ||||||
|                                 if (newVc != null && guildVcRoles.TryGetValue(newVc.Id, out role)) |  | ||||||
|                                 { |  | ||||||
|                                     if (!gusr.Roles.Contains(role)) |  | ||||||
|                                         await gusr.AddRoleAsync(role).ConfigureAwait(false); |  | ||||||
|                                 } |  | ||||||
|  |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     catch (Exception ex) |  | ||||||
|                     { |  | ||||||
|                         Administration._log.Warn(ex); |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|                 return Task.CompletedTask; |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
| @@ -118,14 +46,14 @@ namespace NadekoBot.Modules.Administration | |||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 var guildVcRoles = VcRoles.GetOrAdd(user.GuildId, new ConcurrentDictionary<ulong, IRole>()); |                 var guildVcRoles = _service.VcRoles.GetOrAdd(user.GuildId, new ConcurrentDictionary<ulong, IRole>()); | ||||||
|  |  | ||||||
|                 if (role == null) |                 if (role == null) | ||||||
|                 { |                 { | ||||||
|                     if (guildVcRoles.TryRemove(vc.Id, out role)) |                     if (guildVcRoles.TryRemove(vc.Id, out role)) | ||||||
|                     { |                     { | ||||||
|                         await ReplyConfirmLocalized("vcrole_removed", Format.Bold(vc.Name)).ConfigureAwait(false); |                         await ReplyConfirmLocalized("vcrole_removed", Format.Bold(vc.Name)).ConfigureAwait(false); | ||||||
|                         using (var uow = DbHandler.UnitOfWork()) |                         using (var uow = _db.UnitOfWork) | ||||||
|                         { |                         { | ||||||
|                             var conf = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.VcRoleInfos)); |                             var conf = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.VcRoleInfos)); | ||||||
|                             conf.VcRoleInfos.RemoveWhere(x => x.VoiceChannelId == vc.Id); |                             conf.VcRoleInfos.RemoveWhere(x => x.VoiceChannelId == vc.Id); | ||||||
| @@ -136,7 +64,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
|                     guildVcRoles.AddOrUpdate(vc.Id, role, (key, old) => role); |                     guildVcRoles.AddOrUpdate(vc.Id, role, (key, old) => role); | ||||||
|                     using (var uow = DbHandler.UnitOfWork()) |                     using (var uow = _db.UnitOfWork) | ||||||
|                     { |                     { | ||||||
|                         var conf = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.VcRoleInfos)); |                         var conf = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(x => x.VcRoleInfos)); | ||||||
|                         conf.VcRoleInfos.RemoveWhere(x => x.VoiceChannelId == vc.Id); // remove old one |                         conf.VcRoleInfos.RemoveWhere(x => x.VoiceChannelId == vc.Id); // remove old one | ||||||
| @@ -157,7 +85,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|             { |             { | ||||||
|                 var guild = (SocketGuild) Context.Guild; |                 var guild = (SocketGuild) Context.Guild; | ||||||
|                 string text; |                 string text; | ||||||
|                 if (VcRoles.TryGetValue(Context.Guild.Id, out ConcurrentDictionary<ulong, IRole> roles)) |                 if (_service.VcRoles.TryGetValue(Context.Guild.Id, out ConcurrentDictionary<ulong, IRole> roles)) | ||||||
|                 { |                 { | ||||||
|                     if (!roles.Any()) |                     if (!roles.Any()) | ||||||
|                     { |                     { | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ using Discord.WebSocket; | |||||||
| using NadekoBot.Attributes; | using NadekoBot.Attributes; | ||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
| using NadekoBot.Services; | using NadekoBot.Services; | ||||||
|  | using NadekoBot.Services.Administration; | ||||||
| using NLog; | using NLog; | ||||||
| using System; | using System; | ||||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||||
| @@ -21,140 +22,15 @@ namespace NadekoBot.Modules.Administration | |||||||
|         [Group] |         [Group] | ||||||
|         public class VoicePlusTextCommands : NadekoSubmodule |         public class VoicePlusTextCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|             private new static readonly Logger _log; |             private readonly VplusTService _service; | ||||||
|  |             private readonly DbHandler _db; | ||||||
|  |  | ||||||
|             private static readonly Regex _channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled); |             public VoicePlusTextCommands(VplusTService service, DbHandler db) | ||||||
|  |  | ||||||
|             private static readonly ConcurrentHashSet<ulong> _voicePlusTextCache; |  | ||||||
|             private static readonly ConcurrentDictionary<ulong, SemaphoreSlim> _guildLockObjects = new ConcurrentDictionary<ulong, SemaphoreSlim>(); |  | ||||||
|             static VoicePlusTextCommands() |  | ||||||
|             { |             { | ||||||
|                 _log = LogManager.GetCurrentClassLogger(); |                 _service = service; | ||||||
|                 var sw = Stopwatch.StartNew(); |                 _db = db; | ||||||
|  |  | ||||||
|                 _voicePlusTextCache = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId)); |  | ||||||
|                 NadekoBot.Client.UserVoiceStateUpdated += UserUpdatedEventHandler; |  | ||||||
|  |  | ||||||
|                 sw.Stop(); |  | ||||||
|                 _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             private static Task UserUpdatedEventHandler(SocketUser iuser, SocketVoiceState before, SocketVoiceState after) |  | ||||||
|             { |  | ||||||
|                 var user = (iuser as SocketGuildUser); |  | ||||||
|                 var guild = user?.Guild; |  | ||||||
|  |  | ||||||
|                 if (guild == null) |  | ||||||
|                     return Task.CompletedTask; |  | ||||||
|  |  | ||||||
|                 var botUserPerms = guild.CurrentUser.GuildPermissions; |  | ||||||
|  |  | ||||||
|                 if (before.VoiceChannel == after.VoiceChannel) |  | ||||||
|                     return Task.CompletedTask; |  | ||||||
|  |  | ||||||
|                 if (!_voicePlusTextCache.Contains(guild.Id)) |  | ||||||
|                     return Task.CompletedTask; |  | ||||||
|  |  | ||||||
|                 var _ = Task.Run(async () => |  | ||||||
|                 { |  | ||||||
|                     try |  | ||||||
|                     { |  | ||||||
|  |  | ||||||
|                         if (!botUserPerms.ManageChannels || !botUserPerms.ManageRoles) |  | ||||||
|                         { |  | ||||||
|                             try |  | ||||||
|                             { |  | ||||||
|                                 await guild.Owner.SendErrorAsync( |  | ||||||
|                                     GetTextStatic("vt_exit", |  | ||||||
|                                         NadekoBot.Localization.GetCultureInfo(guild), |  | ||||||
|                                         typeof(Administration).Name.ToLowerInvariant(), |  | ||||||
|                                         Format.Bold(guild.Name))).ConfigureAwait(false); |  | ||||||
|                             } |  | ||||||
|                             catch |  | ||||||
|                             { |  | ||||||
|                                 // ignored |  | ||||||
|                             } |  | ||||||
|                             using (var uow = DbHandler.UnitOfWork()) |  | ||||||
|                             { |  | ||||||
|                                 uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false; |  | ||||||
|                                 _voicePlusTextCache.TryRemove(guild.Id); |  | ||||||
|                                 await uow.CompleteAsync().ConfigureAwait(false); |  | ||||||
|                             } |  | ||||||
|                             return; |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                         var semaphore = _guildLockObjects.GetOrAdd(guild.Id, (key) => new SemaphoreSlim(1, 1)); |  | ||||||
|  |  | ||||||
|                         try |  | ||||||
|                         { |  | ||||||
|                             await semaphore.WaitAsync().ConfigureAwait(false); |  | ||||||
|  |  | ||||||
|                             var beforeVch = before.VoiceChannel; |  | ||||||
|                             if (beforeVch != null) |  | ||||||
|                             { |  | ||||||
|                                 var beforeRoleName = GetRoleName(beforeVch); |  | ||||||
|                                 var beforeRole = guild.Roles.FirstOrDefault(x => x.Name == beforeRoleName); |  | ||||||
|                                 if (beforeRole != null) |  | ||||||
|                                     try |  | ||||||
|                                     { |  | ||||||
|                                         _log.Info("Removing role " + beforeRoleName + " from user " + user.Username); |  | ||||||
|                                         await user.RemoveRoleAsync(beforeRole).ConfigureAwait(false); |  | ||||||
|                                         await Task.Delay(200).ConfigureAwait(false); |  | ||||||
|                                     } |  | ||||||
|                                     catch (Exception ex) |  | ||||||
|                                     { |  | ||||||
|                                         _log.Warn(ex); |  | ||||||
|                                     } |  | ||||||
|                             } |  | ||||||
|                             var afterVch = after.VoiceChannel; |  | ||||||
|                             if (afterVch != null && guild.AFKChannel?.Id != afterVch.Id) |  | ||||||
|                             { |  | ||||||
|                                 var roleName = GetRoleName(afterVch); |  | ||||||
|                                 var roleToAdd = guild.Roles.FirstOrDefault(x => x.Name == roleName) ?? |  | ||||||
|                                                   (IRole) await guild.CreateRoleAsync(roleName, GuildPermissions.None).ConfigureAwait(false); |  | ||||||
|  |  | ||||||
|                                 ITextChannel textChannel = guild.TextChannels |  | ||||||
|                                                             .FirstOrDefault(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant()); |  | ||||||
|                                 if (textChannel == null) |  | ||||||
|                                 { |  | ||||||
|                                     var created = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false)); |  | ||||||
|  |  | ||||||
|                                     try { await guild.CurrentUser.AddRoleAsync(roleToAdd).ConfigureAwait(false); } catch {/*ignored*/} |  | ||||||
|                                     await Task.Delay(50).ConfigureAwait(false); |  | ||||||
|                                     await created.AddPermissionOverwriteAsync(roleToAdd, new OverwritePermissions( |  | ||||||
|                                         readMessages: PermValue.Allow, |  | ||||||
|                                         sendMessages: PermValue.Allow)) |  | ||||||
|                                             .ConfigureAwait(false); |  | ||||||
|                                     await Task.Delay(50).ConfigureAwait(false); |  | ||||||
|                                     await created.AddPermissionOverwriteAsync(guild.EveryoneRole, new OverwritePermissions( |  | ||||||
|                                         readMessages: PermValue.Deny, |  | ||||||
|                                         sendMessages: PermValue.Deny)) |  | ||||||
|                                             .ConfigureAwait(false); |  | ||||||
|                                     await Task.Delay(50).ConfigureAwait(false); |  | ||||||
|                                 } |  | ||||||
|                                 _log.Warn("Adding role " + roleToAdd.Name + " to user " + user.Username); |  | ||||||
|                                 await user.AddRoleAsync(roleToAdd).ConfigureAwait(false); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                         finally |  | ||||||
|                         { |  | ||||||
|                             semaphore.Release(); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     catch (Exception ex) |  | ||||||
|                     { |  | ||||||
|                         _log.Warn(ex); |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|                 return Task.CompletedTask; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             private static string GetChannelName(string voiceName) => |  | ||||||
|                 _channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice"; |  | ||||||
|  |  | ||||||
|             private static string GetRoleName(IVoiceChannel ch) => |  | ||||||
|                 "nvoice-" + ch.Id; |  | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             [RequireContext(ContextType.Guild)] |             [RequireContext(ContextType.Guild)] | ||||||
|             [RequireUserPermission(GuildPermission.ManageRoles)] |             [RequireUserPermission(GuildPermission.ManageRoles)] | ||||||
| @@ -184,7 +60,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     bool isEnabled; |                     bool isEnabled; | ||||||
|                     using (var uow = DbHandler.UnitOfWork()) |                     using (var uow = _db.UnitOfWork) | ||||||
|                     { |                     { | ||||||
|                         var conf = uow.GuildConfigs.For(guild.Id, set => set); |                         var conf = uow.GuildConfigs.For(guild.Id, set => set); | ||||||
|                         isEnabled = conf.VoicePlusTextEnabled = !conf.VoicePlusTextEnabled; |                         isEnabled = conf.VoicePlusTextEnabled = !conf.VoicePlusTextEnabled; | ||||||
| @@ -192,7 +68,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                     } |                     } | ||||||
|                     if (!isEnabled) |                     if (!isEnabled) | ||||||
|                     { |                     { | ||||||
|                         _voicePlusTextCache.TryRemove(guild.Id); |                         _service.VoicePlusTextCache.TryRemove(guild.Id); | ||||||
|                         foreach (var textChannel in (await guild.GetTextChannelsAsync().ConfigureAwait(false)).Where(c => c.Name.EndsWith("-voice"))) |                         foreach (var textChannel in (await guild.GetTextChannelsAsync().ConfigureAwait(false)).Where(c => c.Name.EndsWith("-voice"))) | ||||||
|                         { |                         { | ||||||
|                             try { await textChannel.DeleteAsync().ConfigureAwait(false); } catch { } |                             try { await textChannel.DeleteAsync().ConfigureAwait(false); } catch { } | ||||||
| @@ -207,7 +83,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                         await ReplyConfirmLocalized("vt_disabled").ConfigureAwait(false); |                         await ReplyConfirmLocalized("vt_disabled").ConfigureAwait(false); | ||||||
|                         return; |                         return; | ||||||
|                     } |                     } | ||||||
|                     _voicePlusTextCache.Add(guild.Id); |                     _service.VoicePlusTextCache.Add(guild.Id); | ||||||
|                     await ReplyConfirmLocalized("vt_enabled").ConfigureAwait(false); |                     await ReplyConfirmLocalized("vt_enabled").ConfigureAwait(false); | ||||||
|  |  | ||||||
|                 } |                 } | ||||||
| @@ -236,7 +112,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                 var voiceChannels = await guild.GetVoiceChannelsAsync().ConfigureAwait(false); |                 var voiceChannels = await guild.GetVoiceChannelsAsync().ConfigureAwait(false); | ||||||
|  |  | ||||||
|                 var boundTextChannels = textChannels.Where(c => c.Name.EndsWith("-voice")); |                 var boundTextChannels = textChannels.Where(c => c.Name.EndsWith("-voice")); | ||||||
|                 var validTxtChannelNames = new HashSet<string>(voiceChannels.Select(c => GetChannelName(c.Name).ToLowerInvariant())); |                 var validTxtChannelNames = new HashSet<string>(voiceChannels.Select(c => _service.GetChannelName(c.Name).ToLowerInvariant())); | ||||||
|                 var invalidTxtChannels = boundTextChannels.Where(c => !validTxtChannelNames.Contains(c.Name)); |                 var invalidTxtChannels = boundTextChannels.Where(c => !validTxtChannelNames.Contains(c.Name)); | ||||||
|  |  | ||||||
|                 foreach (var c in invalidTxtChannels) |                 foreach (var c in invalidTxtChannels) | ||||||
| @@ -246,7 +122,7 @@ namespace NadekoBot.Modules.Administration | |||||||
|                 } |                 } | ||||||
|                  |                  | ||||||
|                 var boundRoles = guild.Roles.Where(r => r.Name.StartsWith("nvoice-")); |                 var boundRoles = guild.Roles.Where(r => r.Name.StartsWith("nvoice-")); | ||||||
|                 var validRoleNames = new HashSet<string>(voiceChannels.Select(c => GetRoleName(c).ToLowerInvariant())); |                 var validRoleNames = new HashSet<string>(voiceChannels.Select(c => _service.GetRoleName(c).ToLowerInvariant())); | ||||||
|                 var invalidRoles = boundRoles.Where(r => !validRoleNames.Contains(r.Name)); |                 var invalidRoles = boundRoles.Where(r => !validRoleNames.Contains(r.Name)); | ||||||
|  |  | ||||||
|                 foreach (var r in invalidRoles) |                 foreach (var r in invalidRoles) | ||||||
|   | |||||||
| @@ -21,9 +21,16 @@ namespace NadekoBot.Modules.Games | |||||||
|         [Group] |         [Group] | ||||||
|         public class Acropobia : NadekoSubmodule |         public class Acropobia : NadekoSubmodule | ||||||
|         { |         { | ||||||
|  |             private readonly DiscordShardedClient _client; | ||||||
|  |  | ||||||
|             //channelId, game |             //channelId, game | ||||||
|             public static ConcurrentDictionary<ulong, AcrophobiaGame> AcrophobiaGames { get; } = new ConcurrentDictionary<ulong, AcrophobiaGame>(); |             public static ConcurrentDictionary<ulong, AcrophobiaGame> AcrophobiaGames { get; } = new ConcurrentDictionary<ulong, AcrophobiaGame>(); | ||||||
|  |  | ||||||
|  |             public Acropobia(DiscordShardedClient client) | ||||||
|  |             { | ||||||
|  |                 _client = client; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             [RequireContext(ContextType.Guild)] |             [RequireContext(ContextType.Guild)] | ||||||
|             public async Task Acro(int time = 60) |             public async Task Acro(int time = 60) | ||||||
| @@ -32,7 +39,7 @@ namespace NadekoBot.Modules.Games | |||||||
|                     return; |                     return; | ||||||
|                 var channel = (ITextChannel)Context.Channel; |                 var channel = (ITextChannel)Context.Channel; | ||||||
|  |  | ||||||
|                 var game = new AcrophobiaGame(channel, time); |                 var game = new AcrophobiaGame(_client, _strings, channel, time); | ||||||
|                 if (AcrophobiaGames.TryAdd(channel.Id, game)) |                 if (AcrophobiaGames.TryAdd(channel.Id, game)) | ||||||
|                 { |                 { | ||||||
|                     try |                     try | ||||||
| @@ -59,6 +66,7 @@ namespace NadekoBot.Modules.Games | |||||||
|             Voting |             Voting | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         //todo Isolate, this shouldn't print or anything like that. | ||||||
|         public class AcrophobiaGame |         public class AcrophobiaGame | ||||||
|         { |         { | ||||||
|             private readonly ITextChannel _channel; |             private readonly ITextChannel _channel; | ||||||
| @@ -79,10 +87,14 @@ namespace NadekoBot.Modules.Games | |||||||
|             //text, votes |             //text, votes | ||||||
|             private readonly ConcurrentDictionary<string, int> _votes = new ConcurrentDictionary<string, int>(); |             private readonly ConcurrentDictionary<string, int> _votes = new ConcurrentDictionary<string, int>(); | ||||||
|             private readonly Logger _log; |             private readonly Logger _log; | ||||||
|  |             private readonly DiscordShardedClient _client; | ||||||
|  |             private readonly NadekoStrings _strings; | ||||||
|  |  | ||||||
|             public AcrophobiaGame(ITextChannel channel, int time) |             public AcrophobiaGame(DiscordShardedClient client, NadekoStrings strings, ITextChannel channel, int time) | ||||||
|             { |             { | ||||||
|                 _log = LogManager.GetCurrentClassLogger(); |                 _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |                 _client = client; | ||||||
|  |                 _strings = strings; | ||||||
|  |  | ||||||
|                 _channel = channel; |                 _channel = channel; | ||||||
|                 _time = time; |                 _time = time; | ||||||
| @@ -123,7 +135,7 @@ $@"-- | |||||||
|  |  | ||||||
|             public async Task Run() |             public async Task Run() | ||||||
|             { |             { | ||||||
|                 NadekoBot.Client.MessageReceived += PotentialAcro; |                 _client.MessageReceived += PotentialAcro; | ||||||
|                 var embed = GetEmbed(); |                 var embed = GetEmbed(); | ||||||
|  |  | ||||||
|                 //SUBMISSIONS PHASE |                 //SUBMISSIONS PHASE | ||||||
| @@ -292,14 +304,14 @@ $@"-- | |||||||
|  |  | ||||||
|             public void EnsureStopped() |             public void EnsureStopped() | ||||||
|             { |             { | ||||||
|                 NadekoBot.Client.MessageReceived -= PotentialAcro; |                 _client.MessageReceived -= PotentialAcro; | ||||||
|                 if (!_source.IsCancellationRequested) |                 if (!_source.IsCancellationRequested) | ||||||
|                     _source.Cancel(); |                     _source.Cancel(); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             private string GetText(string key, params object[] replacements) |             private string GetText(string key, params object[] replacements) | ||||||
|                 => GetTextStatic(key, |                 => _strings.GetText(key, | ||||||
|                     NadekoBot.Localization.GetCultureInfo(_channel.Guild), |                     _channel.Guild.Id, | ||||||
|                     typeof(Games).Name.ToLowerInvariant(), |                     typeof(Games).Name.ToLowerInvariant(), | ||||||
|                     replacements); |                     replacements); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,16 +1,10 @@ | |||||||
| using Discord; | using Discord; | ||||||
| using Discord.Commands; | using Discord.Commands; | ||||||
| using NadekoBot.Attributes; | using NadekoBot.Attributes; | ||||||
| using NadekoBot.Extensions; |  | ||||||
| using NadekoBot.Services; | using NadekoBot.Services; | ||||||
| using NLog; |  | ||||||
| using System; | using System; | ||||||
| using System.Collections.Concurrent; |  | ||||||
| using System.Diagnostics; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Net.Http; |  | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using Newtonsoft.Json; | using NadekoBot.Services.Games; | ||||||
|  |  | ||||||
| namespace NadekoBot.Modules.Games | namespace NadekoBot.Modules.Games | ||||||
| { | { | ||||||
| @@ -19,72 +13,13 @@ namespace NadekoBot.Modules.Games | |||||||
|         [Group] |         [Group] | ||||||
|         public class CleverBotCommands : NadekoSubmodule |         public class CleverBotCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|             private new static Logger _log { get; } |             private readonly DbHandler _db; | ||||||
|  |             private readonly GamesService _games; | ||||||
|  |  | ||||||
|             public static ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; } |             public CleverBotCommands(DbHandler db, GamesService games) | ||||||
|  |  | ||||||
|             static CleverBotCommands() |  | ||||||
|             { |             { | ||||||
|                 _log = LogManager.GetCurrentClassLogger(); |                 _db = db; | ||||||
|                 var sw = Stopwatch.StartNew(); |                 _games = games; | ||||||
|                  |  | ||||||
|                 CleverbotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>( |  | ||||||
|                     NadekoBot.AllGuildConfigs |  | ||||||
|                         .Where(gc => gc.CleverbotEnabled) |  | ||||||
|                         .ToDictionary(gc => gc.GuildId, gc => new Lazy<ChatterBotSession>(() => new ChatterBotSession(gc.GuildId), true))); |  | ||||||
|  |  | ||||||
|                 sw.Stop(); |  | ||||||
|                 _log.Debug($"Loaded in {sw.Elapsed.TotalSeconds:F2}s"); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public static string PrepareMessage(IUserMessage msg, out ChatterBotSession cleverbot) |  | ||||||
|             { |  | ||||||
|                 var channel = msg.Channel as ITextChannel; |  | ||||||
|                 cleverbot = null; |  | ||||||
|  |  | ||||||
|                 if (channel == null) |  | ||||||
|                     return null; |  | ||||||
|  |  | ||||||
|                 Lazy<ChatterBotSession> lazyCleverbot; |  | ||||||
|                 if (!CleverbotGuilds.TryGetValue(channel.Guild.Id, out lazyCleverbot)) |  | ||||||
|                     return null; |  | ||||||
|  |  | ||||||
|                 cleverbot = lazyCleverbot.Value; |  | ||||||
|  |  | ||||||
|                 var nadekoId = NadekoBot.Client.CurrentUser.Id; |  | ||||||
|                 var normalMention = $"<@{nadekoId}> "; |  | ||||||
|                 var nickMention = $"<@!{nadekoId}> "; |  | ||||||
|                 string message; |  | ||||||
|                 if (msg.Content.StartsWith(normalMention)) |  | ||||||
|                 { |  | ||||||
|                     message = msg.Content.Substring(normalMention.Length).Trim(); |  | ||||||
|                 } |  | ||||||
|                 else if (msg.Content.StartsWith(nickMention)) |  | ||||||
|                 { |  | ||||||
|                     message = msg.Content.Substring(nickMention.Length).Trim(); |  | ||||||
|                 } |  | ||||||
|                 else |  | ||||||
|                 { |  | ||||||
|                     return null; |  | ||||||
|                 } |  | ||||||
|  |  | ||||||
|                 return message; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public static async Task<bool> TryAsk(ChatterBotSession cleverbot, ITextChannel channel, string message) |  | ||||||
|             { |  | ||||||
|                 await channel.TriggerTypingAsync().ConfigureAwait(false); |  | ||||||
|  |  | ||||||
|                 var response = await cleverbot.Think(message).ConfigureAwait(false); |  | ||||||
|                 try |  | ||||||
|                 { |  | ||||||
|                     await channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false); |  | ||||||
|                 } |  | ||||||
|                 catch |  | ||||||
|                 { |  | ||||||
|                     await channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false); // try twice :\ |  | ||||||
|                 } |  | ||||||
|                 return true; |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
| @@ -94,10 +29,9 @@ namespace NadekoBot.Modules.Games | |||||||
|             { |             { | ||||||
|                 var channel = (ITextChannel)Context.Channel; |                 var channel = (ITextChannel)Context.Channel; | ||||||
|  |  | ||||||
|                 Lazy<ChatterBotSession> throwaway; |                 if (_games.CleverbotGuilds.TryRemove(channel.Guild.Id, out Lazy<ChatterBotSession> throwaway)) | ||||||
|                 if (CleverbotGuilds.TryRemove(channel.Guild.Id, out throwaway)) |  | ||||||
|                 { |                 { | ||||||
|                     using (var uow = DbHandler.UnitOfWork()) |                     using (var uow = _db.UnitOfWork) | ||||||
|                     { |                     { | ||||||
|                         uow.GuildConfigs.SetCleverbotEnabled(Context.Guild.Id, false); |                         uow.GuildConfigs.SetCleverbotEnabled(Context.Guild.Id, false); | ||||||
|                         await uow.CompleteAsync().ConfigureAwait(false); |                         await uow.CompleteAsync().ConfigureAwait(false); | ||||||
| @@ -106,9 +40,9 @@ namespace NadekoBot.Modules.Games | |||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 CleverbotGuilds.TryAdd(channel.Guild.Id, new Lazy<ChatterBotSession>(() => new ChatterBotSession(Context.Guild.Id), true)); |                 _games.CleverbotGuilds.TryAdd(channel.Guild.Id, new Lazy<ChatterBotSession>(() => new ChatterBotSession(Context.Guild.Id), true)); | ||||||
|  |  | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     uow.GuildConfigs.SetCleverbotEnabled(Context.Guild.Id, true); |                     uow.GuildConfigs.SetCleverbotEnabled(Context.Guild.Id, true); | ||||||
|                     await uow.CompleteAsync().ConfigureAwait(false); |                     await uow.CompleteAsync().ConfigureAwait(false); | ||||||
| @@ -118,41 +52,6 @@ namespace NadekoBot.Modules.Games | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public class ChatterBotSession |         | ||||||
|         { |  | ||||||
|             private static NadekoRandom rng { get; } = new NadekoRandom(); |  | ||||||
|             public string ChatterbotId { get; } |  | ||||||
|             public string ChannelId { get; } |  | ||||||
|             private int _botId = 6; |  | ||||||
|  |  | ||||||
|             public ChatterBotSession(ulong channelId) |  | ||||||
|             { |  | ||||||
|                 ChannelId = channelId.ToString().ToBase64(); |  | ||||||
|                 ChatterbotId = rng.Next(0, 1000000).ToString().ToBase64(); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             private string apiEndpoint => "http://api.program-o.com/v2/chatbot/" + |  | ||||||
|                                           $"?bot_id={_botId}&" + |  | ||||||
|                                           "say={0}&" + |  | ||||||
|                                           $"convo_id=nadekobot_{ChatterbotId}_{ChannelId}&" + |  | ||||||
|                                           "format=json"; |  | ||||||
|  |  | ||||||
|             public async Task<string> Think(string message) |  | ||||||
|             { |  | ||||||
|                 using (var http = new HttpClient()) |  | ||||||
|                 { |  | ||||||
|                     var res = await http.GetStringAsync(string.Format(apiEndpoint, message)).ConfigureAwait(false); |  | ||||||
|                     var cbr = JsonConvert.DeserializeObject<ChatterBotResponse>(res); |  | ||||||
|                     //Console.WriteLine(cbr.Convo_id); |  | ||||||
|                     return cbr.BotSay.Replace("<br/>", "\n"); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public class ChatterBotResponse |  | ||||||
|         { |  | ||||||
|             public string Convo_id { get; set; } |  | ||||||
|             public string BotSay { get; set; } |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -56,6 +56,7 @@ namespace NadekoBot.Modules.Games.Hangman | |||||||
|     public class HangmanGame: IDisposable |     public class HangmanGame: IDisposable | ||||||
|     { |     { | ||||||
|         private readonly Logger _log; |         private readonly Logger _log; | ||||||
|  |         private readonly DiscordShardedClient _client; | ||||||
|  |  | ||||||
|         public IMessageChannel GameChannel { get; } |         public IMessageChannel GameChannel { get; } | ||||||
|         public HashSet<char> Guesses { get; } = new HashSet<char>(); |         public HashSet<char> Guesses { get; } = new HashSet<char>(); | ||||||
| @@ -81,9 +82,11 @@ namespace NadekoBot.Modules.Games.Hangman | |||||||
|  |  | ||||||
|         public event Action<HangmanGame> OnEnded; |         public event Action<HangmanGame> OnEnded; | ||||||
|  |  | ||||||
|         public HangmanGame(IMessageChannel channel, string type) |         public HangmanGame(DiscordShardedClient client, IMessageChannel channel, string type) | ||||||
|         { |         { | ||||||
|             _log = LogManager.GetCurrentClassLogger(); |             _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |             _client = client; | ||||||
|  |  | ||||||
|             this.GameChannel = channel; |             this.GameChannel = channel; | ||||||
|             this.TermType = type.ToTitleCase(); |             this.TermType = type.ToTitleCase(); | ||||||
|         } |         } | ||||||
| @@ -95,12 +98,12 @@ namespace NadekoBot.Modules.Games.Hangman | |||||||
|             if (this.Term == null) |             if (this.Term == null) | ||||||
|                 throw new KeyNotFoundException("Can't find a term with that type. Use hangmanlist command."); |                 throw new KeyNotFoundException("Can't find a term with that type. Use hangmanlist command."); | ||||||
|             // start listening for answers when game starts |             // start listening for answers when game starts | ||||||
|             NadekoBot.Client.MessageReceived += PotentialGuess; |             _client.MessageReceived += PotentialGuess; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public async Task End() |         public async Task End() | ||||||
|         { |         { | ||||||
|             NadekoBot.Client.MessageReceived -= PotentialGuess; |             _client.MessageReceived -= PotentialGuess; | ||||||
|             OnEnded(this); |             OnEnded(this); | ||||||
|             var toSend = "Game ended. You **" + (Errors >= MaxErrors ? "LOSE" : "WIN") + "**!\n" + GetHangman(); |             var toSend = "Game ended. You **" + (Errors >= MaxErrors ? "LOSE" : "WIN") + "**!\n" + GetHangman(); | ||||||
|             var embed = new EmbedBuilder().WithTitle("Hangman Game") |             var embed = new EmbedBuilder().WithTitle("Hangman Game") | ||||||
| @@ -199,7 +202,7 @@ namespace NadekoBot.Modules.Games.Hangman | |||||||
|  |  | ||||||
|         public void Dispose() |         public void Dispose() | ||||||
|         { |         { | ||||||
|             NadekoBot.Client.MessageReceived -= PotentialGuess; |             _client.MessageReceived -= PotentialGuess; | ||||||
|             OnEnded = null; |             OnEnded = null; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ using System.Collections.Concurrent; | |||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using NadekoBot.Modules.Games.Hangman; | using NadekoBot.Modules.Games.Hangman; | ||||||
| using Discord; | using Discord; | ||||||
|  | using Discord.WebSocket; | ||||||
|  |  | ||||||
| namespace NadekoBot.Modules.Games | namespace NadekoBot.Modules.Games | ||||||
| { | { | ||||||
| @@ -14,6 +15,13 @@ namespace NadekoBot.Modules.Games | |||||||
|         [Group] |         [Group] | ||||||
|         public class HangmanCommands : NadekoSubmodule |         public class HangmanCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|  |             private readonly DiscordShardedClient _client; | ||||||
|  |  | ||||||
|  |             public HangmanCommands(DiscordShardedClient client) | ||||||
|  |             { | ||||||
|  |                 _client = client; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             //channelId, game |             //channelId, game | ||||||
|             public static ConcurrentDictionary<ulong, HangmanGame> HangmanGames { get; } = new ConcurrentDictionary<ulong, HangmanGame>(); |             public static ConcurrentDictionary<ulong, HangmanGame> HangmanGames { get; } = new ConcurrentDictionary<ulong, HangmanGame>(); | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
| @@ -25,7 +33,7 @@ namespace NadekoBot.Modules.Games | |||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             public async Task Hangman([Remainder]string type = "All") |             public async Task Hangman([Remainder]string type = "All") | ||||||
|             { |             { | ||||||
|                 var hm = new HangmanGame(Context.Channel, type); |                 var hm = new HangmanGame(_client, Context.Channel, type); | ||||||
|  |  | ||||||
|                 if (!HangmanGames.TryAdd(Context.Channel.Id, hm)) |                 if (!HangmanGames.TryAdd(Context.Channel.Id, hm)) | ||||||
|                 { |                 { | ||||||
| @@ -35,8 +43,7 @@ namespace NadekoBot.Modules.Games | |||||||
|  |  | ||||||
|                 hm.OnEnded += g => |                 hm.OnEnded += g => | ||||||
|                 { |                 { | ||||||
|                     HangmanGame throwaway; |                     HangmanGames.TryRemove(g.GameChannel.Id, out HangmanGame throwaway); | ||||||
|                     HangmanGames.TryRemove(g.GameChannel.Id, out throwaway); |  | ||||||
|                 }; |                 }; | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
| @@ -45,8 +52,7 @@ namespace NadekoBot.Modules.Games | |||||||
|                 catch (Exception ex) |                 catch (Exception ex) | ||||||
|                 { |                 { | ||||||
|                     try { await Context.Channel.SendErrorAsync(GetText("hangman_start_errored") + " " + ex.Message).ConfigureAwait(false); } catch { } |                     try { await Context.Channel.SendErrorAsync(GetText("hangman_start_errored") + " " + ex.Message).ConfigureAwait(false); } catch { } | ||||||
|                     HangmanGame throwaway; |                     HangmanGames.TryRemove(Context.Channel.Id, out HangmanGame throwaway); | ||||||
|                     HangmanGames.TryRemove(Context.Channel.Id, out throwaway); |  | ||||||
|                     throwaway.Dispose(); |                     throwaway.Dispose(); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|   | |||||||
							
								
								
									
										147
									
								
								src/NadekoBot/Modules/Games/Commands/Models/TypingGame.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								src/NadekoBot/Modules/Games/Commands/Models/TypingGame.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | |||||||
|  | using Discord; | ||||||
|  | using Discord.WebSocket; | ||||||
|  | using NadekoBot.Extensions; | ||||||
|  | using NadekoBot.Services; | ||||||
|  | using NadekoBot.Services.Games; | ||||||
|  | using NLog; | ||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Diagnostics; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Modules.Games.Models | ||||||
|  | { | ||||||
|  |     public class TypingGame | ||||||
|  |     { | ||||||
|  |         public const float WORD_VALUE = 4.5f; | ||||||
|  |         public ITextChannel Channel { get; } | ||||||
|  |         public string CurrentSentence { get; private set; } | ||||||
|  |         public bool IsActive { get; private set; } | ||||||
|  |         private readonly Stopwatch sw; | ||||||
|  |         private readonly List<ulong> finishedUserIds; | ||||||
|  |         private readonly DiscordShardedClient _client; | ||||||
|  |         private readonly GamesService _games; | ||||||
|  |  | ||||||
|  |         private Logger _log { get; } | ||||||
|  |  | ||||||
|  |         public TypingGame(GamesService games, DiscordShardedClient client, ITextChannel channel) | ||||||
|  |         { | ||||||
|  |             _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |             _games = games; | ||||||
|  |             _client = client; | ||||||
|  |  | ||||||
|  |             this.Channel = channel; | ||||||
|  |             IsActive = false; | ||||||
|  |             sw = new Stopwatch(); | ||||||
|  |             finishedUserIds = new List<ulong>(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public async Task<bool> Stop() | ||||||
|  |         { | ||||||
|  |             if (!IsActive) return false; | ||||||
|  |             _client.MessageReceived -= AnswerReceived; | ||||||
|  |             finishedUserIds.Clear(); | ||||||
|  |             IsActive = false; | ||||||
|  |             sw.Stop(); | ||||||
|  |             sw.Reset(); | ||||||
|  |             try { await Channel.SendConfirmAsync("Typing contest stopped.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public async Task Start() | ||||||
|  |         { | ||||||
|  |             if (IsActive) return; // can't start running game | ||||||
|  |             IsActive = true; | ||||||
|  |             CurrentSentence = GetRandomSentence(); | ||||||
|  |             var i = (int)(CurrentSentence.Length / WORD_VALUE * 1.7f); | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 await Channel.SendConfirmAsync($@":clock2: Next contest will last for {i} seconds. Type the bolded text as fast as you can.").ConfigureAwait(false); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |                 var msg = await Channel.SendMessageAsync("Starting new typing contest in **3**...").ConfigureAwait(false); | ||||||
|  |                 await Task.Delay(1000).ConfigureAwait(false); | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     await msg.ModifyAsync(m => m.Content = "Starting new typing contest in **2**...").ConfigureAwait(false); | ||||||
|  |                     await Task.Delay(1000).ConfigureAwait(false); | ||||||
|  |                     await msg.ModifyAsync(m => m.Content = "Starting new typing contest in **1**...").ConfigureAwait(false); | ||||||
|  |                     await Task.Delay(1000).ConfigureAwait(false); | ||||||
|  |                 } | ||||||
|  |                 catch (Exception ex) { _log.Warn(ex); } | ||||||
|  |  | ||||||
|  |                 await msg.ModifyAsync(m => m.Content = Format.Bold(Format.Sanitize(CurrentSentence.Replace(" ", " \x200B")).SanitizeMentions())).ConfigureAwait(false); | ||||||
|  |                 sw.Start(); | ||||||
|  |                 HandleAnswers(); | ||||||
|  |  | ||||||
|  |                 while (i > 0) | ||||||
|  |                 { | ||||||
|  |                     await Task.Delay(1000).ConfigureAwait(false); | ||||||
|  |                     i--; | ||||||
|  |                     if (!IsActive) | ||||||
|  |                         return; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |             } | ||||||
|  |             catch { } | ||||||
|  |             finally | ||||||
|  |             { | ||||||
|  |                 await Stop().ConfigureAwait(false); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public string GetRandomSentence() | ||||||
|  |         { | ||||||
|  |             if (_games.TypingArticles.Any()) | ||||||
|  |                 return _games.TypingArticles[new NadekoRandom().Next(0, _games.TypingArticles.Count)].Text; | ||||||
|  |             else | ||||||
|  |                 return $"No typing articles found. Use {NadekoBot.Prefix}typeadd command to add a new article for typing."; | ||||||
|  |  | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void HandleAnswers() | ||||||
|  |         { | ||||||
|  |             _client.MessageReceived += AnswerReceived; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private async Task AnswerReceived(SocketMessage imsg) | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 if (imsg.Author.IsBot) | ||||||
|  |                     return; | ||||||
|  |                 var msg = imsg as SocketUserMessage; | ||||||
|  |                 if (msg == null) | ||||||
|  |                     return; | ||||||
|  |  | ||||||
|  |                 if (this.Channel == null || this.Channel.Id != msg.Channel.Id) return; | ||||||
|  |  | ||||||
|  |                 var guess = msg.Content; | ||||||
|  |  | ||||||
|  |                 var distance = CurrentSentence.LevenshteinDistance(guess); | ||||||
|  |                 var decision = Judge(distance, guess.Length); | ||||||
|  |                 if (decision && !finishedUserIds.Contains(msg.Author.Id)) | ||||||
|  |                 { | ||||||
|  |                     var elapsed = sw.Elapsed; | ||||||
|  |                     var wpm = CurrentSentence.Length / WORD_VALUE / elapsed.TotalSeconds * 60; | ||||||
|  |                     finishedUserIds.Add(msg.Author.Id); | ||||||
|  |                     await this.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() | ||||||
|  |                         .WithTitle($"{msg.Author} finished the race!") | ||||||
|  |                         .AddField(efb => efb.WithName("Place").WithValue($"#{finishedUserIds.Count}").WithIsInline(true)) | ||||||
|  |                         .AddField(efb => efb.WithName("WPM").WithValue($"{wpm:F1} *[{elapsed.TotalSeconds:F2}sec]*").WithIsInline(true)) | ||||||
|  |                         .AddField(efb => efb.WithName("Errors").WithValue(distance.ToString()).WithIsInline(true))) | ||||||
|  |                             .ConfigureAwait(false); | ||||||
|  |                     if (finishedUserIds.Count % 4 == 0) | ||||||
|  |                     { | ||||||
|  |                         await this.Channel.SendConfirmAsync($":exclamation: A lot of people finished, here is the text for those still typing:\n\n**{Format.Sanitize(CurrentSentence.Replace(" ", " \x200B")).SanitizeMentions()}**").ConfigureAwait(false); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) { _log.Warn(ex); } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private bool Judge(int errors, int textLength) => errors <= textLength / 25; | ||||||
|  |  | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -6,6 +6,7 @@ using NadekoBot.Attributes; | |||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
| using NadekoBot.Services; | using NadekoBot.Services; | ||||||
| using NadekoBot.Services.Database.Models; | using NadekoBot.Services.Database.Models; | ||||||
|  | using NadekoBot.Services.Games; | ||||||
| using NLog; | using NLog; | ||||||
| using System; | using System; | ||||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||||
| @@ -28,89 +29,20 @@ namespace NadekoBot.Modules.Games | |||||||
|         [Group] |         [Group] | ||||||
|         public class PlantPickCommands : NadekoSubmodule |         public class PlantPickCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|             private static ConcurrentHashSet<ulong> generationChannels { get; } |             private readonly CurrencyHandler _ch; | ||||||
|             //channelid/message |             private readonly BotConfig _bc; | ||||||
|             private static ConcurrentDictionary<ulong, List<IUserMessage>> plantedFlowers { get; } = new ConcurrentDictionary<ulong, List<IUserMessage>>(); |             private readonly GamesService _games; | ||||||
|             //channelId/last generation |             private readonly DbHandler _db; | ||||||
|             private static ConcurrentDictionary<ulong, DateTime> lastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>(); |  | ||||||
|  |  | ||||||
|             static PlantPickCommands() |             public PlantPickCommands(BotConfig bc, CurrencyHandler ch, GamesService games, | ||||||
|  |                 DbHandler db) | ||||||
|             { |             { | ||||||
|                 NadekoBot.Client.MessageReceived += PotentialFlowerGeneration; |                 _bc = bc; | ||||||
|                 generationChannels = new ConcurrentHashSet<ulong>(NadekoBot.AllGuildConfigs |                 _ch = ch; | ||||||
|                     .SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId))); |                 _games = games; | ||||||
|  |                 _db = db; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             private static Task PotentialFlowerGeneration(SocketMessage imsg) |  | ||||||
|             { |  | ||||||
|                 var msg = imsg as SocketUserMessage; |  | ||||||
|                 if (msg == null || msg.IsAuthor() || msg.Author.IsBot) |  | ||||||
|                     return Task.CompletedTask; |  | ||||||
|  |  | ||||||
|                 var channel = imsg.Channel as ITextChannel; |  | ||||||
|                 if (channel == null) |  | ||||||
|                     return Task.CompletedTask; |  | ||||||
|  |  | ||||||
|                 if (!generationChannels.Contains(channel.Id)) |  | ||||||
|                     return Task.CompletedTask; |  | ||||||
|  |  | ||||||
|                 var _ = Task.Run(async () => |  | ||||||
|                 { |  | ||||||
|                     try |  | ||||||
|                     { |  | ||||||
|                         var lastGeneration = lastGenerations.GetOrAdd(channel.Id, DateTime.MinValue); |  | ||||||
|                         var rng = new NadekoRandom(); |  | ||||||
|  |  | ||||||
|                         //todo i'm stupid :rofl: wtg kwoth. real async programming :100: :ok_hand: :100: :100: :thumbsup: |  | ||||||
|                         if (DateTime.Now - TimeSpan.FromSeconds(NadekoBot.BotConfig.CurrencyGenerationCooldown) < lastGeneration) //recently generated in this channel, don't generate again |  | ||||||
|                             return; |  | ||||||
|  |  | ||||||
|                         var num = rng.Next(1, 101) + NadekoBot.BotConfig.CurrencyGenerationChance * 100; |  | ||||||
|  |  | ||||||
|                         if (num > 100) |  | ||||||
|                         { |  | ||||||
|                             lastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now); |  | ||||||
|  |  | ||||||
|                             var dropAmount = NadekoBot.BotConfig.CurrencyDropAmount; |  | ||||||
|  |  | ||||||
|                             if (dropAmount > 0) |  | ||||||
|                             { |  | ||||||
|                                 var msgs = new IUserMessage[dropAmount]; |  | ||||||
|                                 var prefix = NadekoBot.ModulePrefixes[typeof(Games).Name]; |  | ||||||
|                                 var toSend = dropAmount == 1  |  | ||||||
|                                     ? GetLocalText(channel, "curgen_sn", NadekoBot.BotConfig.CurrencySign)  |  | ||||||
|                                         + " " + GetLocalText(channel, "pick_sn", prefix) |  | ||||||
|                                     : GetLocalText(channel, "curgen_pl", dropAmount, NadekoBot.BotConfig.CurrencySign) |  | ||||||
|                                         + " " + GetLocalText(channel, "pick_pl", prefix); |  | ||||||
|                                 var file = GetRandomCurrencyImage(); |  | ||||||
|                                 using (var fileStream = file.Value.ToStream()) |  | ||||||
|                                 { |  | ||||||
|                                     var sent = await channel.SendFileAsync( |  | ||||||
|                                         fileStream, |  | ||||||
|                                         file.Key, |  | ||||||
|                                         toSend).ConfigureAwait(false); |  | ||||||
|  |  | ||||||
|                                     msgs[0] = sent; |  | ||||||
|                                 } |  | ||||||
|  |  | ||||||
|                                 plantedFlowers.AddOrUpdate(channel.Id, msgs.ToList(), (id, old) => { old.AddRange(msgs); return old; }); |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     catch (Exception ex) |  | ||||||
|                     { |  | ||||||
|                         LogManager.GetCurrentClassLogger().Warn(ex); |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|                 return Task.CompletedTask; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public static string GetLocalText(ITextChannel channel, string key, params object[] replacements) => |  | ||||||
|                 NadekoTopLevelModule.GetTextStatic(key, |  | ||||||
|                     NadekoBot.Localization.GetCultureInfo(channel.GuildId), |  | ||||||
|                     typeof(Games).Name.ToLowerInvariant(), |  | ||||||
|                     replacements); |  | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             [RequireContext(ContextType.Guild)] |             [RequireContext(ContextType.Guild)] | ||||||
|             public async Task Pick() |             public async Task Pick() | ||||||
| @@ -120,16 +52,15 @@ namespace NadekoBot.Modules.Games | |||||||
|                 if (!(await channel.Guild.GetCurrentUserAsync()).GetPermissions(channel).ManageMessages) |                 if (!(await channel.Guild.GetCurrentUserAsync()).GetPermissions(channel).ManageMessages) | ||||||
|                     return; |                     return; | ||||||
|  |  | ||||||
|                 List<IUserMessage> msgs; |  | ||||||
|  |  | ||||||
|                 try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { } |                 try { await Context.Message.DeleteAsync().ConfigureAwait(false); } catch { } | ||||||
|                 if (!plantedFlowers.TryRemove(channel.Id, out msgs)) |                 if (!_games.PlantedFlowers.TryRemove(channel.Id, out List<IUserMessage> msgs)) | ||||||
|                     return; |                     return; | ||||||
|  |  | ||||||
|                 await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false); |                 await Task.WhenAll(msgs.Where(m => m != null).Select(toDelete => toDelete.DeleteAsync())).ConfigureAwait(false); | ||||||
|  |  | ||||||
|                 await CurrencyHandler.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {NadekoBot.BotConfig.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false); |                 await _ch.AddCurrencyAsync((IGuildUser)Context.User, $"Picked {_bc.CurrencyPluralName}", msgs.Count, false).ConfigureAwait(false); | ||||||
|                 var msg = await ReplyConfirmLocalized("picked", msgs.Count + NadekoBot.BotConfig.CurrencySign) |                 var msg = await ReplyConfirmLocalized("picked", msgs.Count + _bc.CurrencySign) | ||||||
|                     .ConfigureAwait(false); |                     .ConfigureAwait(false); | ||||||
|                 msg.DeleteAfter(10); |                 msg.DeleteAfter(10); | ||||||
|             } |             } | ||||||
| @@ -141,21 +72,21 @@ namespace NadekoBot.Modules.Games | |||||||
|                 if (amount < 1) |                 if (amount < 1) | ||||||
|                     return; |                     return; | ||||||
|  |  | ||||||
|                 var removed = await CurrencyHandler.RemoveCurrencyAsync((IGuildUser)Context.User, $"Planted a {NadekoBot.BotConfig.CurrencyName}", amount, false).ConfigureAwait(false); |                 var removed = await _ch.RemoveCurrencyAsync((IGuildUser)Context.User, $"Planted a {_bc.CurrencyName}", amount, false).ConfigureAwait(false); | ||||||
|                 if (!removed) |                 if (!removed) | ||||||
|                 { |                 { | ||||||
|                     await ReplyErrorLocalized("not_enough", NadekoBot.BotConfig.CurrencySign).ConfigureAwait(false); |                     await ReplyErrorLocalized("not_enough", _bc.CurrencySign).ConfigureAwait(false); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 var imgData = GetRandomCurrencyImage(); |                 var imgData = _games.GetRandomCurrencyImage(); | ||||||
|  |  | ||||||
|                 //todo upload all currency images to transfer.sh and use that one as cdn |                 //todo upload all currency images to transfer.sh and use that one as cdn | ||||||
|                 //and then  |                 //and then  | ||||||
|  |  | ||||||
|                 var msgToSend = GetText("planted", |                 var msgToSend = GetText("planted", | ||||||
|                     Format.Bold(Context.User.ToString()), |                     Format.Bold(Context.User.ToString()), | ||||||
|                     amount + NadekoBot.BotConfig.CurrencySign, |                     amount + _bc.CurrencySign, | ||||||
|                     Prefix); |                     Prefix); | ||||||
|  |  | ||||||
|                 if (amount > 1) |                 if (amount > 1) | ||||||
| @@ -172,7 +103,7 @@ namespace NadekoBot.Modules.Games | |||||||
|                 var msgs = new IUserMessage[amount]; |                 var msgs = new IUserMessage[amount]; | ||||||
|                 msgs[0] = msg; |                 msgs[0] = msg; | ||||||
|  |  | ||||||
|                 plantedFlowers.AddOrUpdate(Context.Channel.Id, msgs.ToList(), (id, old) => |                 _games.PlantedFlowers.AddOrUpdate(Context.Channel.Id, msgs.ToList(), (id, old) => | ||||||
|                 { |                 { | ||||||
|                     old.AddRange(msgs); |                     old.AddRange(msgs); | ||||||
|                     return old; |                     return old; | ||||||
| @@ -190,7 +121,7 @@ namespace NadekoBot.Modules.Games | |||||||
|                 var channel = (ITextChannel)Context.Channel; |                 var channel = (ITextChannel)Context.Channel; | ||||||
|  |  | ||||||
|                 bool enabled; |                 bool enabled; | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     var guildConfig = uow.GuildConfigs.For(channel.Id, set => set.Include(gc => gc.GenerateCurrencyChannelIds)); |                     var guildConfig = uow.GuildConfigs.For(channel.Id, set => set.Include(gc => gc.GenerateCurrencyChannelIds)); | ||||||
|  |  | ||||||
| @@ -198,13 +129,13 @@ namespace NadekoBot.Modules.Games | |||||||
|                     if (!guildConfig.GenerateCurrencyChannelIds.Contains(toAdd)) |                     if (!guildConfig.GenerateCurrencyChannelIds.Contains(toAdd)) | ||||||
|                     { |                     { | ||||||
|                         guildConfig.GenerateCurrencyChannelIds.Add(toAdd); |                         guildConfig.GenerateCurrencyChannelIds.Add(toAdd); | ||||||
|                         generationChannels.Add(channel.Id); |                         _games.GenerationChannels.Add(channel.Id); | ||||||
|                         enabled = true; |                         enabled = true; | ||||||
|                     } |                     } | ||||||
|                     else |                     else | ||||||
|                     { |                     { | ||||||
|                         guildConfig.GenerateCurrencyChannelIds.Remove(toAdd); |                         guildConfig.GenerateCurrencyChannelIds.Remove(toAdd); | ||||||
|                         generationChannels.TryRemove(channel.Id); |                         _games.GenerationChannels.TryRemove(channel.Id); | ||||||
|                         enabled = false; |                         enabled = false; | ||||||
|                     } |                     } | ||||||
|                     await uow.CompleteAsync(); |                     await uow.CompleteAsync(); | ||||||
| @@ -218,14 +149,6 @@ namespace NadekoBot.Modules.Games | |||||||
|                     await ReplyConfirmLocalized("curgen_disabled").ConfigureAwait(false); |                     await ReplyConfirmLocalized("curgen_disabled").ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             private static KeyValuePair<string, ImmutableArray<byte>> GetRandomCurrencyImage() |  | ||||||
|             { |  | ||||||
|                 var rng = new NadekoRandom(); |  | ||||||
|                 var images = NadekoBot.Images.Currency; |  | ||||||
|  |  | ||||||
|                 return images[rng.Next(0, images.Length)]; |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -10,6 +10,7 @@ using System.Linq; | |||||||
| using System.Text; | using System.Text; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using ImageSharp.Processing; | using ImageSharp.Processing; | ||||||
|  | using NadekoBot.Services; | ||||||
|  |  | ||||||
| namespace NadekoBot.Modules.Games | namespace NadekoBot.Modules.Games | ||||||
| { | { | ||||||
| @@ -19,6 +20,12 @@ namespace NadekoBot.Modules.Games | |||||||
|         public class PollCommands : NadekoSubmodule |         public class PollCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|             public static ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>(); |             public static ConcurrentDictionary<ulong, Poll> ActivePolls = new ConcurrentDictionary<ulong, Poll>(); | ||||||
|  |             private readonly DiscordShardedClient _client; | ||||||
|  |  | ||||||
|  |             public PollCommands(DiscordShardedClient client) | ||||||
|  |             { | ||||||
|  |                 _client = client; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             [RequireUserPermission(GuildPermission.ManageMessages)] |             [RequireUserPermission(GuildPermission.ManageMessages)] | ||||||
| @@ -54,7 +61,7 @@ namespace NadekoBot.Modules.Games | |||||||
|                 if (data.Length < 3) |                 if (data.Length < 3) | ||||||
|                     return; |                     return; | ||||||
|  |  | ||||||
|                 var poll = new Poll(Context.Message, data[0], data.Skip(1), isPublic: isPublic); |                 var poll = new Poll(_client, _strings, Context.Message, data[0], data.Skip(1), isPublic: isPublic); | ||||||
|                 if (ActivePolls.TryAdd(channel.Guild.Id, poll)) |                 if (ActivePolls.TryAdd(channel.Guild.Id, poll)) | ||||||
|                 { |                 { | ||||||
|                     await poll.StartPoll().ConfigureAwait(false); |                     await poll.StartPoll().ConfigureAwait(false); | ||||||
| @@ -83,10 +90,16 @@ namespace NadekoBot.Modules.Games | |||||||
|             private string[] answers { get; } |             private string[] answers { get; } | ||||||
|             private readonly ConcurrentDictionary<ulong, int> _participants = new ConcurrentDictionary<ulong, int>(); |             private readonly ConcurrentDictionary<ulong, int> _participants = new ConcurrentDictionary<ulong, int>(); | ||||||
|             private readonly string _question; |             private readonly string _question; | ||||||
|  |             private readonly DiscordShardedClient _client; | ||||||
|  |             private readonly NadekoStrings _strings; | ||||||
|  |  | ||||||
|             public bool IsPublic { get; } |             public bool IsPublic { get; } | ||||||
|  |  | ||||||
|             public Poll(IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false) |             public Poll(DiscordShardedClient client, NadekoStrings strings, IUserMessage umsg, string question, IEnumerable<string> enumerable, bool isPublic = false) | ||||||
|             { |             { | ||||||
|  |                 _client = client; | ||||||
|  |                 _strings = strings; | ||||||
|  |  | ||||||
|                 _originalMessage = umsg; |                 _originalMessage = umsg; | ||||||
|                 _guild = ((ITextChannel)umsg.Channel).Guild; |                 _guild = ((ITextChannel)umsg.Channel).Guild; | ||||||
|                 _question = question; |                 _question = question; | ||||||
| @@ -134,7 +147,7 @@ namespace NadekoBot.Modules.Games | |||||||
|  |  | ||||||
|             public async Task StartPoll() |             public async Task StartPoll() | ||||||
|             { |             { | ||||||
|                 NadekoBot.Client.MessageReceived += Vote; |                 _client.MessageReceived += Vote; | ||||||
|                 var msgToSend = GetText("poll_created", Format.Bold(_originalMessage.Author.Username)) + "\n\n" + Format.Bold(_question) + "\n"; |                 var msgToSend = GetText("poll_created", Format.Bold(_originalMessage.Author.Username)) + "\n\n" + Format.Bold(_question) + "\n"; | ||||||
|                 var num = 1; |                 var num = 1; | ||||||
|                 msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n"); |                 msgToSend = answers.Aggregate(msgToSend, (current, answ) => current + $"`{num++}.` **{answ}**\n"); | ||||||
| @@ -147,7 +160,7 @@ namespace NadekoBot.Modules.Games | |||||||
|  |  | ||||||
|             public async Task StopPoll() |             public async Task StopPoll() | ||||||
|             { |             { | ||||||
|                 NadekoBot.Client.MessageReceived -= Vote; |                 _client.MessageReceived -= Vote; | ||||||
|                 await _originalMessage.Channel.EmbedAsync(GetStats("POLL CLOSED")).ConfigureAwait(false); |                 await _originalMessage.Channel.EmbedAsync(GetStats("POLL CLOSED")).ConfigureAwait(false); | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -205,8 +218,8 @@ namespace NadekoBot.Modules.Games | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             private string GetText(string key, params object[] replacements) |             private string GetText(string key, params object[] replacements) | ||||||
|                 => NadekoTopLevelModule.GetTextStatic(key, |                 => _strings.GetText(key, | ||||||
|                     NadekoBot.Localization.GetCultureInfo(_guild.Id), |                     _guild.Id, | ||||||
|                     typeof(Games).Name.ToLowerInvariant(), |                     typeof(Games).Name.ToLowerInvariant(), | ||||||
|                     replacements); |                     replacements); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -3,14 +3,11 @@ using Discord.Commands; | |||||||
| using Discord.WebSocket; | using Discord.WebSocket; | ||||||
| using NadekoBot.Attributes; | using NadekoBot.Attributes; | ||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
| using NadekoBot.Modules.Games.Commands.Models; | using NadekoBot.Modules.Games.Models; | ||||||
| using NadekoBot.Services; | using NadekoBot.Services.Games; | ||||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||||
| using NLog; |  | ||||||
| using System; | using System; | ||||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Diagnostics; |  | ||||||
| using System.IO; | using System.IO; | ||||||
| using System.Linq; | using System.Linq; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| @@ -19,145 +16,18 @@ namespace NadekoBot.Modules.Games | |||||||
| { | { | ||||||
|     public partial class Games |     public partial class Games | ||||||
|     { |     { | ||||||
|         public class TypingGame |  | ||||||
|         { |  | ||||||
|             public const float WORD_VALUE = 4.5f; |  | ||||||
|             public ITextChannel Channel { get; } |  | ||||||
|             public string CurrentSentence { get; private set; } |  | ||||||
|             public bool IsActive { get; private set; } |  | ||||||
|             private readonly Stopwatch sw; |  | ||||||
|             private readonly List<ulong> finishedUserIds; |  | ||||||
|             private Logger _log { get; } |  | ||||||
|  |  | ||||||
|             public TypingGame(ITextChannel channel) |  | ||||||
|             { |  | ||||||
|                 _log = LogManager.GetCurrentClassLogger(); |  | ||||||
|                 this.Channel = channel; |  | ||||||
|                 IsActive = false; |  | ||||||
|                 sw = new Stopwatch(); |  | ||||||
|                 finishedUserIds = new List<ulong>(); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public async Task<bool> Stop() |  | ||||||
|             { |  | ||||||
|                 if (!IsActive) return false; |  | ||||||
|                 NadekoBot.Client.MessageReceived -= AnswerReceived; |  | ||||||
|                 finishedUserIds.Clear(); |  | ||||||
|                 IsActive = false; |  | ||||||
|                 sw.Stop(); |  | ||||||
|                 sw.Reset(); |  | ||||||
|                 try { await Channel.SendConfirmAsync("Typing contest stopped.").ConfigureAwait(false); } catch (Exception ex) { _log.Warn(ex); } |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public async Task Start() |  | ||||||
|             { |  | ||||||
|                 if (IsActive) return; // can't start running game |  | ||||||
|                 IsActive = true; |  | ||||||
|                 CurrentSentence = GetRandomSentence(); |  | ||||||
|                 var i = (int)(CurrentSentence.Length / WORD_VALUE * 1.7f); |  | ||||||
|                 try |  | ||||||
|                 { |  | ||||||
|                     await Channel.SendConfirmAsync($@":clock2: Next contest will last for {i} seconds. Type the bolded text as fast as you can.").ConfigureAwait(false); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|                     var msg = await Channel.SendMessageAsync("Starting new typing contest in **3**...").ConfigureAwait(false); |  | ||||||
|                     await Task.Delay(1000).ConfigureAwait(false); |  | ||||||
|                     try |  | ||||||
|                     { |  | ||||||
|                         await msg.ModifyAsync(m => m.Content = "Starting new typing contest in **2**...").ConfigureAwait(false); |  | ||||||
|                         await Task.Delay(1000).ConfigureAwait(false); |  | ||||||
|                         await msg.ModifyAsync(m => m.Content = "Starting new typing contest in **1**...").ConfigureAwait(false); |  | ||||||
|                         await Task.Delay(1000).ConfigureAwait(false); |  | ||||||
|                     } |  | ||||||
|                     catch (Exception ex) { _log.Warn(ex); } |  | ||||||
|  |  | ||||||
|                     await msg.ModifyAsync(m => m.Content = Format.Bold(Format.Sanitize(CurrentSentence.Replace(" ", " \x200B")).SanitizeMentions())).ConfigureAwait(false); |  | ||||||
|                     sw.Start(); |  | ||||||
|                     HandleAnswers(); |  | ||||||
|  |  | ||||||
|                     while (i > 0) |  | ||||||
|                     { |  | ||||||
|                         await Task.Delay(1000).ConfigureAwait(false); |  | ||||||
|                         i--; |  | ||||||
|                         if (!IsActive) |  | ||||||
|                             return; |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                 } |  | ||||||
|                 catch { } |  | ||||||
|                 finally |  | ||||||
|                 { |  | ||||||
|                     await Stop().ConfigureAwait(false); |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public string GetRandomSentence() |  | ||||||
|             { |  | ||||||
|                 if (SpeedTypingCommands.TypingArticles.Any()) |  | ||||||
|                     return SpeedTypingCommands.TypingArticles[new NadekoRandom().Next(0, SpeedTypingCommands.TypingArticles.Count)].Text; |  | ||||||
|                 else |  | ||||||
|                     return $"No typing articles found. Use {NadekoBot.ModulePrefixes[typeof(Games).Name]}typeadd command to add a new article for typing."; |  | ||||||
|  |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             private void HandleAnswers() |  | ||||||
|             { |  | ||||||
|                 NadekoBot.Client.MessageReceived += AnswerReceived; |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             private async Task AnswerReceived(SocketMessage imsg) |  | ||||||
|             { |  | ||||||
|                 try |  | ||||||
|                 { |  | ||||||
|                     if (imsg.Author.IsBot) |  | ||||||
|                         return; |  | ||||||
|                     var msg = imsg as SocketUserMessage; |  | ||||||
|                     if (msg == null) |  | ||||||
|                         return; |  | ||||||
|  |  | ||||||
|                     if (this.Channel == null || this.Channel.Id != msg.Channel.Id) return; |  | ||||||
|  |  | ||||||
|                     var guess = msg.Content; |  | ||||||
|  |  | ||||||
|                     var distance = CurrentSentence.LevenshteinDistance(guess); |  | ||||||
|                     var decision = Judge(distance, guess.Length); |  | ||||||
|                     if (decision && !finishedUserIds.Contains(msg.Author.Id)) |  | ||||||
|                     { |  | ||||||
|                         var elapsed = sw.Elapsed; |  | ||||||
|                         var wpm = CurrentSentence.Length / WORD_VALUE / elapsed.TotalSeconds * 60; |  | ||||||
|                         finishedUserIds.Add(msg.Author.Id); |  | ||||||
|                         await this.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() |  | ||||||
|                             .WithTitle($"{msg.Author} finished the race!") |  | ||||||
|                             .AddField(efb => efb.WithName("Place").WithValue($"#{finishedUserIds.Count}").WithIsInline(true)) |  | ||||||
|                             .AddField(efb => efb.WithName("WPM").WithValue($"{wpm:F1} *[{elapsed.TotalSeconds:F2}sec]*").WithIsInline(true)) |  | ||||||
|                             .AddField(efb => efb.WithName("Errors").WithValue(distance.ToString()).WithIsInline(true))) |  | ||||||
|                                 .ConfigureAwait(false); |  | ||||||
|                         if (finishedUserIds.Count % 4 == 0) |  | ||||||
|                         { |  | ||||||
|                             await this.Channel.SendConfirmAsync($":exclamation: A lot of people finished, here is the text for those still typing:\n\n**{Format.Sanitize(CurrentSentence.Replace(" ", " \x200B")).SanitizeMentions()}**").ConfigureAwait(false); |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 catch (Exception ex) { _log.Warn(ex); } |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             private bool Judge(int errors, int textLength) => errors <= textLength / 25; |  | ||||||
|  |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         [Group] |         [Group] | ||||||
|         public class SpeedTypingCommands : NadekoSubmodule |         public class SpeedTypingCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|             public static List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>(); |  | ||||||
|  |  | ||||||
|             private const string _typingArticlesPath = "data/typing_articles2.json"; |  | ||||||
|  |  | ||||||
|             static SpeedTypingCommands() |  | ||||||
|             { |  | ||||||
|                 try { TypingArticles = JsonConvert.DeserializeObject<List<TypingArticle>>(File.ReadAllText(_typingArticlesPath)); } catch { } |  | ||||||
|             } |  | ||||||
|             public static ConcurrentDictionary<ulong, TypingGame> RunningContests = new ConcurrentDictionary<ulong, TypingGame>(); |             public static ConcurrentDictionary<ulong, TypingGame> RunningContests = new ConcurrentDictionary<ulong, TypingGame>(); | ||||||
|  |             private readonly GamesService _games; | ||||||
|  |             private readonly DiscordShardedClient _client; | ||||||
|  |  | ||||||
|  |             public SpeedTypingCommands(DiscordShardedClient client, GamesService games) | ||||||
|  |             { | ||||||
|  |                 _games = games; | ||||||
|  |                 _client = client; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             [RequireContext(ContextType.Guild)] |             [RequireContext(ContextType.Guild)] | ||||||
| @@ -165,7 +35,7 @@ namespace NadekoBot.Modules.Games | |||||||
|             { |             { | ||||||
|                 var channel = (ITextChannel)Context.Channel; |                 var channel = (ITextChannel)Context.Channel; | ||||||
|  |  | ||||||
|                 var game = RunningContests.GetOrAdd(channel.Guild.Id, id => new TypingGame(channel)); |                 var game = RunningContests.GetOrAdd(channel.Guild.Id, id => new TypingGame(_games, _client, channel)); | ||||||
|  |  | ||||||
|                 if (game.IsActive) |                 if (game.IsActive) | ||||||
|                 { |                 { | ||||||
| @@ -202,13 +72,14 @@ namespace NadekoBot.Modules.Games | |||||||
|             { |             { | ||||||
|                 var channel = (ITextChannel)Context.Channel; |                 var channel = (ITextChannel)Context.Channel; | ||||||
|  |  | ||||||
|                 TypingArticles.Add(new TypingArticle |                 _games.TypingArticles.Add(new TypingArticle | ||||||
|                 { |                 { | ||||||
|                     Title = $"Text added on {DateTime.UtcNow} by {Context.User}", |                     Title = $"Text added on {DateTime.UtcNow} by {Context.User}", | ||||||
|                     Text = text.SanitizeMentions(), |                     Text = text.SanitizeMentions(), | ||||||
|                 }); |                 }); | ||||||
|  |  | ||||||
|                 File.WriteAllText(_typingArticlesPath, JsonConvert.SerializeObject(TypingArticles)); |                 //todo move this to service | ||||||
|  |                 File.WriteAllText(_games.TypingArticlesPath, JsonConvert.SerializeObject(_games.TypingArticles)); | ||||||
|  |  | ||||||
|                 await channel.SendConfirmAsync("Added new article for typing game.").ConfigureAwait(false); |                 await channel.SendConfirmAsync("Added new article for typing game.").ConfigureAwait(false); | ||||||
|             } |             } | ||||||
| @@ -222,7 +93,7 @@ namespace NadekoBot.Modules.Games | |||||||
|                 if (page < 1) |                 if (page < 1) | ||||||
|                     return; |                     return; | ||||||
|  |  | ||||||
|                 var articles = TypingArticles.Skip((page - 1) * 15).Take(15).ToArray(); |                 var articles = _games.TypingArticles.Skip((page - 1) * 15).Take(15).ToArray(); | ||||||
|  |  | ||||||
|                 if (!articles.Any()) |                 if (!articles.Any()) | ||||||
|                 { |                 { | ||||||
| @@ -242,13 +113,13 @@ namespace NadekoBot.Modules.Games | |||||||
|                 var channel = (ITextChannel)Context.Channel; |                 var channel = (ITextChannel)Context.Channel; | ||||||
|  |  | ||||||
|                 index -= 1; |                 index -= 1; | ||||||
|                 if (index < 0 || index >= TypingArticles.Count) |                 if (index < 0 || index >= _games.TypingArticles.Count) | ||||||
|                     return; |                     return; | ||||||
|  |  | ||||||
|                 var removed = TypingArticles[index]; |                 var removed = _games.TypingArticles[index]; | ||||||
|                 TypingArticles.RemoveAt(index); |                 _games.TypingArticles.RemoveAt(index); | ||||||
|  |  | ||||||
|                 File.WriteAllText(_typingArticlesPath, JsonConvert.SerializeObject(TypingArticles)); |                 File.WriteAllText(_games.TypingArticlesPath, JsonConvert.SerializeObject(_games.TypingArticles)); | ||||||
|  |  | ||||||
|                 await channel.SendConfirmAsync($"`Removed typing article:` #{index + 1} - {removed.Text.TrimTo(50)}") |                 await channel.SendConfirmAsync($"`Removed typing article:` #{index + 1} - {removed.Text.TrimTo(50)}") | ||||||
|                              .ConfigureAwait(false); |                              .ConfigureAwait(false); | ||||||
|   | |||||||
| @@ -1,8 +1,9 @@ | |||||||
| using Discord; | using Discord; | ||||||
| using Discord.Commands; | using Discord.Commands; | ||||||
|  | using Discord.WebSocket; | ||||||
| using NadekoBot.Attributes; | using NadekoBot.Attributes; | ||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
| using NLog; | using NadekoBot.Services; | ||||||
| using System; | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Text; | using System.Text; | ||||||
| @@ -20,6 +21,12 @@ namespace NadekoBot.Modules.Games | |||||||
|             private static readonly Dictionary<ulong, TicTacToe> _games = new Dictionary<ulong, TicTacToe>(); |             private static readonly Dictionary<ulong, TicTacToe> _games = new Dictionary<ulong, TicTacToe>(); | ||||||
|  |  | ||||||
|             private readonly SemaphoreSlim _sem = new SemaphoreSlim(1, 1); |             private readonly SemaphoreSlim _sem = new SemaphoreSlim(1, 1); | ||||||
|  |             private readonly DiscordShardedClient _client; | ||||||
|  |  | ||||||
|  |             public TicTacToeCommands(DiscordShardedClient client) | ||||||
|  |             { | ||||||
|  |                 _client = client; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             [RequireContext(ContextType.Guild)] |             [RequireContext(ContextType.Guild)] | ||||||
| @@ -39,7 +46,7 @@ namespace NadekoBot.Modules.Games | |||||||
|                         }); |                         }); | ||||||
|                         return; |                         return; | ||||||
|                     } |                     } | ||||||
|                     game = new TicTacToe(channel, (IGuildUser)Context.User); |                     game = new TicTacToe(_strings, _client, channel, (IGuildUser)Context.User); | ||||||
|                     _games.Add(channel.Id, game); |                     _games.Add(channel.Id, game); | ||||||
|                     await ReplyConfirmLocalized("ttt_created").ConfigureAwait(false); |                     await ReplyConfirmLocalized("ttt_created").ConfigureAwait(false); | ||||||
|  |  | ||||||
| @@ -79,10 +86,15 @@ namespace NadekoBot.Modules.Games | |||||||
|  |  | ||||||
|             private IUserMessage _previousMessage; |             private IUserMessage _previousMessage; | ||||||
|             private Timer _timeoutTimer; |             private Timer _timeoutTimer; | ||||||
|  |             private readonly NadekoStrings _strings; | ||||||
|  |             private readonly DiscordShardedClient _client; | ||||||
|  |  | ||||||
|             public TicTacToe(ITextChannel channel, IGuildUser firstUser) |             public TicTacToe(NadekoStrings strings, DiscordShardedClient client, ITextChannel channel, IGuildUser firstUser) | ||||||
|             { |             { | ||||||
|                 _channel = channel; |                 _channel = channel; | ||||||
|  |                 _strings = strings; | ||||||
|  |                 _client = client; | ||||||
|  |  | ||||||
|                 _users = new[] { firstUser, null }; |                 _users = new[] { firstUser, null }; | ||||||
|                 _state = new int?[,] { |                 _state = new int?[,] { | ||||||
|                     { null, null, null }, |                     { null, null, null }, | ||||||
| @@ -95,8 +107,8 @@ namespace NadekoBot.Modules.Games | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             private string GetText(string key, params object[] replacements) => |             private string GetText(string key, params object[] replacements) => | ||||||
|                 NadekoTopLevelModule.GetTextStatic(key, |                 _strings.GetText(key, | ||||||
|                     NadekoBot.Localization.GetCultureInfo(_channel.GuildId), |                     _channel.GuildId, | ||||||
|                     typeof(Games).Name.ToLowerInvariant(), |                     typeof(Games).Name.ToLowerInvariant(), | ||||||
|                     replacements); |                     replacements); | ||||||
|  |  | ||||||
| @@ -206,7 +218,7 @@ namespace NadekoBot.Modules.Games | |||||||
|                     } |                     } | ||||||
|                 }, null, 15000, Timeout.Infinite); |                 }, null, 15000, Timeout.Infinite); | ||||||
|  |  | ||||||
|                 NadekoBot.Client.MessageReceived += Client_MessageReceived; |                 _client.MessageReceived += Client_MessageReceived; | ||||||
|  |  | ||||||
|  |  | ||||||
|                 _previousMessage = await _channel.EmbedAsync(GetEmbed(GetText("game_started"))).ConfigureAwait(false); |                 _previousMessage = await _channel.EmbedAsync(GetEmbed(GetText("game_started"))).ConfigureAwait(false); | ||||||
| @@ -283,14 +295,14 @@ namespace NadekoBot.Modules.Games | |||||||
|                             { |                             { | ||||||
|                                 reason = GetText("ttt_matched_three"); |                                 reason = GetText("ttt_matched_three"); | ||||||
|                                 _winner = _users[_curUserIndex]; |                                 _winner = _users[_curUserIndex]; | ||||||
|                                 NadekoBot.Client.MessageReceived -= Client_MessageReceived; |                                 _client.MessageReceived -= Client_MessageReceived; | ||||||
|                                 OnEnded?.Invoke(this); |                                 OnEnded?.Invoke(this); | ||||||
|                             } |                             } | ||||||
|                             else if (IsDraw()) |                             else if (IsDraw()) | ||||||
|                             { |                             { | ||||||
|                                 reason = GetText("ttt_a_draw"); |                                 reason = GetText("ttt_a_draw"); | ||||||
|                                 _phase = Phase.Ended; |                                 _phase = Phase.Ended; | ||||||
|                                 NadekoBot.Client.MessageReceived -= Client_MessageReceived; |                                 _client.MessageReceived -= Client_MessageReceived; | ||||||
|                                 OnEnded?.Invoke(this); |                                 OnEnded?.Invoke(this); | ||||||
|                             } |                             } | ||||||
|                              |                              | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ using Discord.Net; | |||||||
| using Discord.WebSocket; | using Discord.WebSocket; | ||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
| using NadekoBot.Services; | using NadekoBot.Services; | ||||||
|  | using NadekoBot.Services.Database.Models; | ||||||
| using NLog; | using NLog; | ||||||
| using System; | using System; | ||||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||||
| @@ -18,6 +19,10 @@ namespace NadekoBot.Modules.Games.Trivia | |||||||
|     { |     { | ||||||
|         private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1, 1); |         private readonly SemaphoreSlim _guessLock = new SemaphoreSlim(1, 1); | ||||||
|         private readonly Logger _log; |         private readonly Logger _log; | ||||||
|  |         private readonly NadekoStrings _strings; | ||||||
|  |         private readonly DiscordShardedClient _client; | ||||||
|  |         private readonly BotConfig _bc; | ||||||
|  |         private readonly CurrencyHandler _ch; | ||||||
|  |  | ||||||
|         public IGuild Guild { get; } |         public IGuild Guild { get; } | ||||||
|         public ITextChannel Channel { get; } |         public ITextChannel Channel { get; } | ||||||
| @@ -38,9 +43,15 @@ namespace NadekoBot.Modules.Games.Trivia | |||||||
|  |  | ||||||
|         public int WinRequirement { get; } |         public int WinRequirement { get; } | ||||||
|  |  | ||||||
|         public TriviaGame(IGuild guild, ITextChannel channel, bool showHints, int winReq, bool isPokemon) |         public TriviaGame(NadekoStrings strings, DiscordShardedClient client, BotConfig bc, | ||||||
|  |             CurrencyHandler ch, IGuild guild, ITextChannel channel, | ||||||
|  |             bool showHints, int winReq, bool isPokemon) | ||||||
|         { |         { | ||||||
|             _log = LogManager.GetCurrentClassLogger(); |             _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |             _strings = strings; | ||||||
|  |             _client = client; | ||||||
|  |             _bc = bc; | ||||||
|  |             _ch = ch; | ||||||
|  |  | ||||||
|             ShowHints = showHints; |             ShowHints = showHints; | ||||||
|             Guild = guild; |             Guild = guild; | ||||||
| @@ -50,8 +61,8 @@ namespace NadekoBot.Modules.Games.Trivia | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         private string GetText(string key, params object[] replacements) => |         private string GetText(string key, params object[] replacements) => | ||||||
|             NadekoTopLevelModule.GetTextStatic(key, |             _strings.GetText(key, | ||||||
|                 NadekoBot.Localization.GetCultureInfo(Channel.GuildId), |                 Channel.GuildId, | ||||||
|                 typeof(Games).Name.ToLowerInvariant(), |                 typeof(Games).Name.ToLowerInvariant(), | ||||||
|                 replacements); |                 replacements); | ||||||
|  |  | ||||||
| @@ -99,7 +110,7 @@ namespace NadekoBot.Modules.Games.Trivia | |||||||
|                 //receive messages |                 //receive messages | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     NadekoBot.Client.MessageReceived += PotentialGuess; |                     _client.MessageReceived += PotentialGuess; | ||||||
|  |  | ||||||
|                     //allow people to guess |                     //allow people to guess | ||||||
|                     GameActive = true; |                     GameActive = true; | ||||||
| @@ -128,7 +139,7 @@ namespace NadekoBot.Modules.Games.Trivia | |||||||
|                 finally |                 finally | ||||||
|                 { |                 { | ||||||
|                     GameActive = false; |                     GameActive = false; | ||||||
|                     NadekoBot.Client.MessageReceived -= PotentialGuess; |                     _client.MessageReceived -= PotentialGuess; | ||||||
|                 } |                 } | ||||||
|                 if (!triviaCancelSource.IsCancellationRequested) |                 if (!triviaCancelSource.IsCancellationRequested) | ||||||
|                 { |                 { | ||||||
| @@ -214,9 +225,9 @@ namespace NadekoBot.Modules.Games.Trivia | |||||||
|                     { |                     { | ||||||
|                         // ignored |                         // ignored | ||||||
|                     } |                     } | ||||||
|                     var reward = NadekoBot.BotConfig.TriviaCurrencyReward; |                     var reward = _bc.TriviaCurrencyReward; | ||||||
|                     if (reward > 0) |                     if (reward > 0) | ||||||
|                         await CurrencyHandler.AddCurrencyAsync(guildUser, "Won trivia", reward, true).ConfigureAwait(false); |                         await _ch.AddCurrencyAsync(guildUser, "Won trivia", reward, true).ConfigureAwait(false); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,8 +1,11 @@ | |||||||
| using Discord; | using Discord; | ||||||
| using Discord.Commands; | using Discord.Commands; | ||||||
|  | using Discord.WebSocket; | ||||||
| using NadekoBot.Attributes; | using NadekoBot.Attributes; | ||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
| using NadekoBot.Modules.Games.Trivia; | using NadekoBot.Modules.Games.Trivia; | ||||||
|  | using NadekoBot.Services; | ||||||
|  | using NadekoBot.Services.Database.Models; | ||||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
|  |  | ||||||
| @@ -14,8 +17,19 @@ namespace NadekoBot.Modules.Games | |||||||
|         [Group] |         [Group] | ||||||
|         public class TriviaCommands : NadekoSubmodule |         public class TriviaCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|  |             private readonly CurrencyHandler _ch; | ||||||
|  |             private readonly DiscordShardedClient _client; | ||||||
|  |             private readonly BotConfig _bc; | ||||||
|  |  | ||||||
|             public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias { get; } = new ConcurrentDictionary<ulong, TriviaGame>(); |             public static ConcurrentDictionary<ulong, TriviaGame> RunningTrivias { get; } = new ConcurrentDictionary<ulong, TriviaGame>(); | ||||||
|  |  | ||||||
|  |             public TriviaCommands(DiscordShardedClient client, BotConfig bc, CurrencyHandler ch) | ||||||
|  |             { | ||||||
|  |                 _ch = ch; | ||||||
|  |                 _client = client; | ||||||
|  |                 _bc = bc; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             [RequireContext(ContextType.Guild)] |             [RequireContext(ContextType.Guild)] | ||||||
|             public Task Trivia([Remainder] string additionalArgs = "") |             public Task Trivia([Remainder] string additionalArgs = "") | ||||||
| @@ -35,7 +49,7 @@ namespace NadekoBot.Modules.Games | |||||||
|                 var showHints = !additionalArgs.Contains("nohint"); |                 var showHints = !additionalArgs.Contains("nohint"); | ||||||
|                 var isPokemon = additionalArgs.Contains("pokemon"); |                 var isPokemon = additionalArgs.Contains("pokemon"); | ||||||
|  |  | ||||||
|                 var trivia = new TriviaGame(channel.Guild, channel, showHints, winReq, isPokemon); |                 var trivia = new TriviaGame(_strings, _client, _bc, _ch, channel.Guild, channel, showHints, winReq, isPokemon); | ||||||
|                 if (RunningTrivias.TryAdd(channel.Guild.Id, trivia)) |                 if (RunningTrivias.TryAdd(channel.Guild.Id, trivia)) | ||||||
|                 { |                 { | ||||||
|                     try |                     try | ||||||
|   | |||||||
| @@ -14,19 +14,20 @@ using System.Net.Http; | |||||||
| using ImageSharp; | using ImageSharp; | ||||||
| using NadekoBot.DataStructures; | using NadekoBot.DataStructures; | ||||||
| using NLog; | using NLog; | ||||||
|  | using NadekoBot.Services.Games; | ||||||
|  |  | ||||||
| namespace NadekoBot.Modules.Games | namespace NadekoBot.Modules.Games | ||||||
| { | { | ||||||
|     [NadekoModule("Games", ">")] |  | ||||||
|     public partial class Games : NadekoTopLevelModule |     public partial class Games : NadekoTopLevelModule | ||||||
|     { |     { | ||||||
|         private static readonly ImmutableArray<string> _8BallResponses = NadekoBot.BotConfig.EightBallResponses.Select(ebr => ebr.Text).ToImmutableArray(); |         private readonly GamesService _games; | ||||||
|  |         private readonly IImagesService _images; | ||||||
|  |  | ||||||
|         private static readonly Timer _t = new Timer((_) => |         public Games(GamesService games, IImagesService images) | ||||||
|         { |         { | ||||||
|             _girlRatings.Clear(); |             _games = games; | ||||||
|  |             _images = images; | ||||||
|         }, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1)); |         } | ||||||
|  |  | ||||||
|         [NadekoCommand, Usage, Description, Aliases] |         [NadekoCommand, Usage, Description, Aliases] | ||||||
|         public async Task Choose([Remainder] string list = null) |         public async Task Choose([Remainder] string list = null) | ||||||
| @@ -48,7 +49,7 @@ namespace NadekoBot.Modules.Games | |||||||
|  |  | ||||||
|             await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor) |             await Context.Channel.EmbedAsync(new EmbedBuilder().WithColor(NadekoBot.OkColor) | ||||||
|                                .AddField(efb => efb.WithName("❓ " + GetText("question") ).WithValue(question).WithIsInline(false)) |                                .AddField(efb => efb.WithName("❓ " + GetText("question") ).WithValue(question).WithIsInline(false)) | ||||||
|                                .AddField(efb => efb.WithName("🎱 " + GetText("8ball")).WithValue(_8BallResponses[new NadekoRandom().Next(0, _8BallResponses.Length)]).WithIsInline(false))); |                                .AddField(efb => efb.WithName("🎱 " + GetText("8ball")).WithValue(_games.EightBallResponses[new NadekoRandom().Next(0, _games.EightBallResponses.Length)]).WithIsInline(false))); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         [NadekoCommand, Usage, Description, Aliases] |         [NadekoCommand, Usage, Description, Aliases] | ||||||
| @@ -94,7 +95,7 @@ namespace NadekoBot.Modules.Games | |||||||
|             else if ((pick == 0 && nadekoPick == 1) || |             else if ((pick == 0 && nadekoPick == 1) || | ||||||
|                      (pick == 1 && nadekoPick == 2) || |                      (pick == 1 && nadekoPick == 2) || | ||||||
|                      (pick == 2 && nadekoPick == 0)) |                      (pick == 2 && nadekoPick == 0)) | ||||||
|                 msg = GetText("rps_win", NadekoBot.Client.CurrentUser.Mention, |                 msg = GetText("rps_win", Context.Client.CurrentUser.Mention, | ||||||
|                     getRpsPick(nadekoPick), getRpsPick(pick)); |                     getRpsPick(nadekoPick), getRpsPick(pick)); | ||||||
|             else |             else | ||||||
|                 msg = GetText("rps_win", Context.User.Mention, getRpsPick(pick), |                 msg = GetText("rps_win", Context.User.Mention, getRpsPick(pick), | ||||||
| @@ -103,73 +104,12 @@ namespace NadekoBot.Modules.Games | |||||||
|             await Context.Channel.SendConfirmAsync(msg).ConfigureAwait(false); |             await Context.Channel.SendConfirmAsync(msg).ConfigureAwait(false); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private static readonly ConcurrentDictionary<ulong, GirlRating> _girlRatings = new ConcurrentDictionary<ulong, GirlRating>(); |  | ||||||
|  |  | ||||||
|         public class GirlRating |  | ||||||
|         { |  | ||||||
|             private static readonly Logger _log = LogManager.GetCurrentClassLogger(); |  | ||||||
|  |  | ||||||
|             public double Crazy { get; } |  | ||||||
|             public double Hot { get; } |  | ||||||
|             public int Roll { get; } |  | ||||||
|             public string Advice { get; } |  | ||||||
|             public AsyncLazy<string> Url { get; } |  | ||||||
|  |  | ||||||
|             public GirlRating(double crazy, double hot, int roll, string advice) |  | ||||||
|             { |  | ||||||
|                 Crazy = crazy; |  | ||||||
|                 Hot = hot; |  | ||||||
|                 Roll = roll; |  | ||||||
|                 Advice = advice; // convenient to have it here, even though atm there are only few different ones. |  | ||||||
|  |  | ||||||
|                 Url = new AsyncLazy<string>(async () => |  | ||||||
|                 { |  | ||||||
|                     try |  | ||||||
|                     { |  | ||||||
|                         using (var ms = new MemoryStream(NadekoBot.Images.WifeMatrix.ToArray(), false)) |  | ||||||
|                         using (var img = new ImageSharp.Image(ms)) |  | ||||||
|                         { |  | ||||||
|                             const int minx = 35; |  | ||||||
|                             const int miny = 385; |  | ||||||
|                             const int length = 345; |  | ||||||
|  |  | ||||||
|                             var pointx = (int)(minx + length * (Hot / 10)); |  | ||||||
|                             var pointy = (int)(miny - length * ((Crazy - 4) / 6)); |  | ||||||
|                              |  | ||||||
|                             using (var pointMs = new MemoryStream(NadekoBot.Images.RategirlDot.ToArray(), false)) |  | ||||||
|                             using (var pointImg = new ImageSharp.Image(pointMs)) |  | ||||||
|                             { |  | ||||||
|                                 img.DrawImage(pointImg, 100, default(Size), new Point(pointx - 10, pointy - 10)); |  | ||||||
|                             } |  | ||||||
|  |  | ||||||
|                             string url; |  | ||||||
|                             using (var http = new HttpClient()) |  | ||||||
|                             using (var imgStream = new MemoryStream()) |  | ||||||
|                             { |  | ||||||
|                                 img.Save(imgStream); |  | ||||||
|                                 var byteContent = new ByteArrayContent(imgStream.ToArray()); |  | ||||||
|                                 http.AddFakeHeaders(); |  | ||||||
|  |  | ||||||
|                                 var reponse = await http.PutAsync("https://transfer.sh/img.png", byteContent); |  | ||||||
|                                 url = await reponse.Content.ReadAsStringAsync(); |  | ||||||
|                             } |  | ||||||
|                             return url; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                     catch (Exception ex) |  | ||||||
|                     { |  | ||||||
|                         _log.Warn(ex); |  | ||||||
|                         return null; |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         [NadekoCommand, Usage, Description, Aliases] |         [NadekoCommand, Usage, Description, Aliases] | ||||||
|         [RequireContext(ContextType.Guild)] |         [RequireContext(ContextType.Guild)] | ||||||
|         public async Task RateGirl(IGuildUser usr) |         public async Task RateGirl(IGuildUser usr) | ||||||
|         { |         { | ||||||
|             var gr = _girlRatings.GetOrAdd(usr.Id, GetGirl); |             var gr = _games.GirlRatings.GetOrAdd(usr.Id, GetGirl); | ||||||
|             var img = await gr.Url; |             var img = await gr.Url; | ||||||
|             await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() |             await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() | ||||||
|                 .WithTitle("Girl Rating For " + usr) |                 .WithTitle("Girl Rating For " + usr) | ||||||
| @@ -255,7 +195,7 @@ namespace NadekoBot.Modules.Games | |||||||
|                          "and maybe look at how to replicate that."; |                          "and maybe look at how to replicate that."; | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             return new GirlRating(crazy, hot, roll, advice); |             return new GirlRating(_images, crazy, hot, roll, advice); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         [NadekoCommand, Usage, Description, Aliases] |         [NadekoCommand, Usage, Description, Aliases] | ||||||
|   | |||||||
| @@ -1,184 +0,0 @@ | |||||||
| using System; |  | ||||||
| using Newtonsoft.Json.Linq; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Linq; |  | ||||||
| using System.Net; |  | ||||||
| using System.Net.Http; |  | ||||||
| using System.Threading.Tasks; |  | ||||||
|  |  | ||||||
| namespace NadekoBot.Modules.Searches |  | ||||||
| { |  | ||||||
|     public class GoogleTranslator |  | ||||||
|     { |  | ||||||
|         private static GoogleTranslator _instance; |  | ||||||
|         public static GoogleTranslator Instance = _instance ?? (_instance = new GoogleTranslator()); |  | ||||||
|  |  | ||||||
|         public IEnumerable<string> Languages => _languageDictionary.Keys.OrderBy(x => x); |  | ||||||
|         private readonly Dictionary<string, string> _languageDictionary; |  | ||||||
|  |  | ||||||
|         static GoogleTranslator() { } |  | ||||||
|         private GoogleTranslator() { |  | ||||||
|             _languageDictionary = new Dictionary<string, string>() { |  | ||||||
|                     { "afrikaans", "af"}, |  | ||||||
|                     { "albanian", "sq"}, |  | ||||||
|                     { "arabic", "ar"}, |  | ||||||
|                     { "armenian", "hy"}, |  | ||||||
|                     { "azerbaijani", "az"}, |  | ||||||
|                     { "basque", "eu"}, |  | ||||||
|                     { "belarusian", "be"}, |  | ||||||
|                     { "bengali", "bn"}, |  | ||||||
|                     { "bulgarian", "bg"}, |  | ||||||
|                     { "catalan", "ca"}, |  | ||||||
|                     { "chinese-traditional", "zh-TW"}, |  | ||||||
|                     { "chinese-simplified", "zh-CN"}, |  | ||||||
|                     { "chinese", "zh-CN"}, |  | ||||||
|                     { "croatian", "hr"}, |  | ||||||
|                     { "czech", "cs"}, |  | ||||||
|                     { "danish", "da"}, |  | ||||||
|                     { "dutch", "nl"}, |  | ||||||
|                     { "english", "en"}, |  | ||||||
|                     { "esperanto", "eo"}, |  | ||||||
|                     { "estonian", "et"}, |  | ||||||
|                     { "filipino", "tl"}, |  | ||||||
|                     { "finnish", "fi"}, |  | ||||||
|                     { "french", "fr"}, |  | ||||||
|                     { "galician", "gl"}, |  | ||||||
|                     { "german", "de"}, |  | ||||||
|                     { "georgian", "ka"}, |  | ||||||
|                     { "greek", "el"}, |  | ||||||
|                     { "haitian Creole", "ht"}, |  | ||||||
|                     { "hebrew", "iw"}, |  | ||||||
|                     { "hindi", "hi"}, |  | ||||||
|                     { "hungarian", "hu"}, |  | ||||||
|                     { "icelandic", "is"}, |  | ||||||
|                     { "indonesian", "id"}, |  | ||||||
|                     { "irish", "ga"}, |  | ||||||
|                     { "italian", "it"}, |  | ||||||
|                     { "japanese", "ja"}, |  | ||||||
|                     { "korean", "ko"}, |  | ||||||
|                     { "lao", "lo"}, |  | ||||||
|                     { "latin", "la"}, |  | ||||||
|                     { "latvian", "lv"}, |  | ||||||
|                     { "lithuanian", "lt"}, |  | ||||||
|                     { "macedonian", "mk"}, |  | ||||||
|                     { "malay", "ms"}, |  | ||||||
|                     { "maltese", "mt"}, |  | ||||||
|                     { "norwegian", "no"}, |  | ||||||
|                     { "persian", "fa"}, |  | ||||||
|                     { "polish", "pl"}, |  | ||||||
|                     { "portuguese", "pt"}, |  | ||||||
|                     { "romanian", "ro"}, |  | ||||||
|                     { "russian", "ru"}, |  | ||||||
|                     { "serbian", "sr"}, |  | ||||||
|                     { "slovak", "sk"}, |  | ||||||
|                     { "slovenian", "sl"}, |  | ||||||
|                     { "spanish", "es"}, |  | ||||||
|                     { "swahili", "sw"}, |  | ||||||
|                     { "swedish", "sv"}, |  | ||||||
|                     { "tamil", "ta"}, |  | ||||||
|                     { "telugu", "te"}, |  | ||||||
|                     { "thai", "th"}, |  | ||||||
|                     { "turkish", "tr"}, |  | ||||||
|                     { "ukrainian", "uk"}, |  | ||||||
|                     { "urdu", "ur"}, |  | ||||||
|                     { "vietnamese", "vi"}, |  | ||||||
|                     { "welsh", "cy"}, |  | ||||||
|                     { "yiddish", "yi"}, |  | ||||||
|  |  | ||||||
|                     { "af", "af"}, |  | ||||||
|                     { "sq", "sq"}, |  | ||||||
|                     { "ar", "ar"}, |  | ||||||
|                     { "hy", "hy"}, |  | ||||||
|                     { "az", "az"}, |  | ||||||
|                     { "eu", "eu"}, |  | ||||||
|                     { "be", "be"}, |  | ||||||
|                     { "bn", "bn"}, |  | ||||||
|                     { "bg", "bg"}, |  | ||||||
|                     { "ca", "ca"}, |  | ||||||
|                     { "zh-tw", "zh-TW"}, |  | ||||||
|                     { "zh-cn", "zh-CN"}, |  | ||||||
|                     { "hr", "hr"}, |  | ||||||
|                     { "cs", "cs"}, |  | ||||||
|                     { "da", "da"}, |  | ||||||
|                     { "nl", "nl"}, |  | ||||||
|                     { "en", "en"}, |  | ||||||
|                     { "eo", "eo"}, |  | ||||||
|                     { "et", "et"}, |  | ||||||
|                     { "tl", "tl"}, |  | ||||||
|                     { "fi", "fi"}, |  | ||||||
|                     { "fr", "fr"}, |  | ||||||
|                     { "gl", "gl"}, |  | ||||||
|                     { "de", "de"}, |  | ||||||
|                     { "ka", "ka"}, |  | ||||||
|                     { "el", "el"}, |  | ||||||
|                     { "ht", "ht"}, |  | ||||||
|                     { "iw", "iw"}, |  | ||||||
|                     { "hi", "hi"}, |  | ||||||
|                     { "hu", "hu"}, |  | ||||||
|                     { "is", "is"}, |  | ||||||
|                     { "id", "id"}, |  | ||||||
|                     { "ga", "ga"}, |  | ||||||
|                     { "it", "it"}, |  | ||||||
|                     { "ja", "ja"}, |  | ||||||
|                     { "ko", "ko"}, |  | ||||||
|                     { "lo", "lo"}, |  | ||||||
|                     { "la", "la"}, |  | ||||||
|                     { "lv", "lv"}, |  | ||||||
|                     { "lt", "lt"}, |  | ||||||
|                     { "mk", "mk"}, |  | ||||||
|                     { "ms", "ms"}, |  | ||||||
|                     { "mt", "mt"}, |  | ||||||
|                     { "no", "no"}, |  | ||||||
|                     { "fa", "fa"}, |  | ||||||
|                     { "pl", "pl"}, |  | ||||||
|                     { "pt", "pt"}, |  | ||||||
|                     { "ro", "ro"}, |  | ||||||
|                     { "ru", "ru"}, |  | ||||||
|                     { "sr", "sr"}, |  | ||||||
|                     { "sk", "sk"}, |  | ||||||
|                     { "sl", "sl"}, |  | ||||||
|                     { "es", "es"}, |  | ||||||
|                     { "sw", "sw"}, |  | ||||||
|                     { "sv", "sv"}, |  | ||||||
|                     { "ta", "ta"}, |  | ||||||
|                     { "te", "te"}, |  | ||||||
|                     { "th", "th"}, |  | ||||||
|                     { "tr", "tr"}, |  | ||||||
|                     { "uk", "uk"}, |  | ||||||
|                     { "ur", "ur"}, |  | ||||||
|                     { "vi", "vi"}, |  | ||||||
|                     { "cy", "cy"}, |  | ||||||
|                     { "yi", "yi"}, |  | ||||||
|                 }; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public async Task<string> Translate(string sourceText, string sourceLanguage, string targetLanguage) |  | ||||||
|         { |  | ||||||
|             string text; |  | ||||||
|  |  | ||||||
|             if(!_languageDictionary.ContainsKey(sourceLanguage) ||  |  | ||||||
|                !_languageDictionary.ContainsKey(targetLanguage)) |  | ||||||
|                 throw new ArgumentException(); |  | ||||||
|              |  | ||||||
|  |  | ||||||
|             var url = string.Format("https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}", |  | ||||||
|                                         ConvertToLanguageCode(sourceLanguage), |  | ||||||
|                                         ConvertToLanguageCode(targetLanguage), |  | ||||||
|                                        WebUtility.UrlEncode(sourceText)); |  | ||||||
|             using (var http = new HttpClient()) |  | ||||||
|             { |  | ||||||
|                 http.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"); |  | ||||||
|                 text = await http.GetStringAsync(url).ConfigureAwait(false); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             return (string.Concat(JArray.Parse(text)[0].Select(x => x[0]))); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         private string ConvertToLanguageCode(string language) |  | ||||||
|         { |  | ||||||
|             string mode; |  | ||||||
|             _languageDictionary.TryGetValue(language, out mode); |  | ||||||
|             return mode; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -3,6 +3,7 @@ using NadekoBot.Attributes; | |||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
| using NadekoBot.Modules.Searches.Models; | using NadekoBot.Modules.Searches.Models; | ||||||
| using NadekoBot.Services; | using NadekoBot.Services; | ||||||
|  | using NadekoBot.Services.Searches; | ||||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||||
| using Newtonsoft.Json.Linq; | using Newtonsoft.Json.Linq; | ||||||
| using NLog; | using NLog; | ||||||
| @@ -19,27 +20,11 @@ namespace NadekoBot.Modules.Searches | |||||||
|         [Group] |         [Group] | ||||||
|         public class JokeCommands : NadekoSubmodule |         public class JokeCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|             private static List<WoWJoke> wowJokes { get; } = new List<WoWJoke>(); |             private readonly SearchesService _searches; | ||||||
|             private static List<MagicItem> magicItems { get; } = new List<MagicItem>(); |  | ||||||
|             private new static readonly Logger _log; |  | ||||||
|  |  | ||||||
|             static JokeCommands() |             public JokeCommands(SearchesService searches) | ||||||
|             { |             { | ||||||
|                 _log = LogManager.GetCurrentClassLogger(); |                 _searches = searches; | ||||||
|  |  | ||||||
|                 if (File.Exists("data/wowjokes.json")) |  | ||||||
|                 { |  | ||||||
|                     wowJokes = JsonConvert.DeserializeObject<List<WoWJoke>>(File.ReadAllText("data/wowjokes.json")); |  | ||||||
|                 } |  | ||||||
|                 else |  | ||||||
|                     _log.Warn("data/wowjokes.json is missing. WOW Jokes are not loaded."); |  | ||||||
|  |  | ||||||
|                 if (File.Exists("data/magicitems.json")) |  | ||||||
|                 { |  | ||||||
|                     magicItems = JsonConvert.DeserializeObject<List<MagicItem>>(File.ReadAllText("data/magicitems.json")); |  | ||||||
|                 } |  | ||||||
|                 else |  | ||||||
|                     _log.Warn("data/magicitems.json is missing. Magic items are not loaded."); |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
| @@ -75,24 +60,24 @@ namespace NadekoBot.Modules.Searches | |||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             public async Task WowJoke() |             public async Task WowJoke() | ||||||
|             { |             { | ||||||
|                 if (!wowJokes.Any()) |                 if (!_searches.WowJokes.Any()) | ||||||
|                 { |                 { | ||||||
|                     await ReplyErrorLocalized("jokes_not_loaded").ConfigureAwait(false); |                     await ReplyErrorLocalized("jokes_not_loaded").ConfigureAwait(false); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|                 var joke = wowJokes[new NadekoRandom().Next(0, wowJokes.Count)]; |                 var joke = _searches.WowJokes[new NadekoRandom().Next(0, _searches.WowJokes.Count)]; | ||||||
|                 await Context.Channel.SendConfirmAsync(joke.Question, joke.Answer).ConfigureAwait(false); |                 await Context.Channel.SendConfirmAsync(joke.Question, joke.Answer).ConfigureAwait(false); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             public async Task MagicItem() |             public async Task MagicItem() | ||||||
|             { |             { | ||||||
|                 if (!wowJokes.Any()) |                 if (!_searches.WowJokes.Any()) | ||||||
|                 { |                 { | ||||||
|                     await ReplyErrorLocalized("magicitems_not_loaded").ConfigureAwait(false); |                     await ReplyErrorLocalized("magicitems_not_loaded").ConfigureAwait(false); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|                 var item = magicItems[new NadekoRandom().Next(0, magicItems.Count)]; |                 var item = _searches.MagicItems[new NadekoRandom().Next(0, _searches.MagicItems.Count)]; | ||||||
|  |  | ||||||
|                 await Context.Channel.SendConfirmAsync("✨" + item.Name, item.Description).ConfigureAwait(false); |                 await Context.Channel.SendConfirmAsync("✨" + item.Name, item.Description).ConfigureAwait(false); | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -22,7 +22,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|                 obj["name"].GetHashCode(); |                 obj["name"].GetHashCode(); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         private static string[] trashTalk { get; } = { "Better ban your counters. You are going to carry the game anyway.", |         private static readonly string[] trashTalk = { "Better ban your counters. You are going to carry the game anyway.", | ||||||
|                                                 "Go with the flow. Don't think. Just ban one of these.", |                                                 "Go with the flow. Don't think. Just ban one of these.", | ||||||
|                                                 "DONT READ BELOW! Ban Urgot mid OP 100%. Im smurf Diamond 1.", |                                                 "DONT READ BELOW! Ban Urgot mid OP 100%. Im smurf Diamond 1.", | ||||||
|                                                 "Ask your teammates what would they like to play, and ban that.", |                                                 "Ask your teammates what would they like to play, and ban that.", | ||||||
| @@ -40,7 +40,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|                 using (var http = new HttpClient()) |                 using (var http = new HttpClient()) | ||||||
|                 { |                 { | ||||||
|                     var data = JObject.Parse(await http.GetStringAsync($"http://api.champion.gg/stats/champs/mostBanned?" + |                     var data = JObject.Parse(await http.GetStringAsync($"http://api.champion.gg/stats/champs/mostBanned?" + | ||||||
|                                                     $"api_key={NadekoBot.Credentials.LoLApiKey}&page=1&" + |                                                     $"api_key={_creds.LoLApiKey}&page=1&" + | ||||||
|                                                     $"limit={showCount}") |                                                     $"limit={showCount}") | ||||||
|                                                     .ConfigureAwait(false))["data"] as JArray; |                                                     .ConfigureAwait(false))["data"] as JArray; | ||||||
|                     var dataList = data.Distinct(new ChampionNameComparer()).Take(showCount).ToList(); |                     var dataList = data.Distinct(new ChampionNameComparer()).Take(showCount).ToList(); | ||||||
| @@ -127,7 +127,7 @@ namespace NadekoBot.Modules.Searches | |||||||
| //                                  await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false); | //                                  await e.Channel.SendFile("champ.png", champ.ImageStream).ConfigureAwait(false); | ||||||
| //                                  return; | //                                  return; | ||||||
| //                              } | //                              } | ||||||
| //                          var allData = JArray.Parse(await Classes.http.GetStringAsync($"http://api.champion.gg/champion/{name}?api_key={NadekoBot.Credentials.LOLAPIKey}").ConfigureAwait(false)); | //                          var allData = JArray.Parse(await Classes.http.GetStringAsync($"http://api.champion.gg/champion/{name}?api_key={_creds.LOLAPIKey}").ConfigureAwait(false)); | ||||||
| //                          JToken data = null; | //                          JToken data = null; | ||||||
| //                          if (role != null) | //                          if (role != null) | ||||||
| //                          { | //                          { | ||||||
| @@ -168,7 +168,7 @@ namespace NadekoBot.Modules.Searches | |||||||
| //                                  roles[i] = ">" + roles[i] + "<"; | //                                  roles[i] = ">" + roles[i] + "<"; | ||||||
| //                          } | //                          } | ||||||
| //                          var general = JArray.Parse(await http.GetStringAsync($"http://api.champion.gg/stats/" + | //                          var general = JArray.Parse(await http.GetStringAsync($"http://api.champion.gg/stats/" + | ||||||
| //                                                                                               $"champs/{name}?api_key={NadekoBot.Credentials.LOLAPIKey}") | //                                                                                               $"champs/{name}?api_key={_creds.LOLAPIKey}") | ||||||
| //                                                                                                .ConfigureAwait(false)) | //                                                                                                .ConfigureAwait(false)) | ||||||
| //                                              .FirstOrDefault(jt => jt["role"].ToString() == role)?["general"]; | //                                              .FirstOrDefault(jt => jt["role"].ToString() == role)?["general"]; | ||||||
| //                          if (general == null) | //                          if (general == null) | ||||||
|   | |||||||
| @@ -1,33 +0,0 @@ | |||||||
| using NadekoBot.Extensions; |  | ||||||
| using System.Collections.Generic; |  | ||||||
| using System.Net; |  | ||||||
|  |  | ||||||
| namespace NadekoBot.Modules.Searches.Models |  | ||||||
| { |  | ||||||
|     public class ImdbMovie |  | ||||||
|     { |  | ||||||
|         public bool Status { get; set; } |  | ||||||
|         public string Id { get; set; } |  | ||||||
|         public string Title { get; set; } |  | ||||||
|         public string OriginalTitle { get; set; } |  | ||||||
|         public string Year { get; set; } |  | ||||||
|         public string Rating { get; set; } |  | ||||||
|         public string Plot { get; set; } |  | ||||||
|         public string Poster { get; set; } |  | ||||||
|         public List<string> Genres { get; set; } |  | ||||||
|         public string ImdbURL { get; set; } |  | ||||||
|  |  | ||||||
|         public Dictionary<string, string> Aka { get; set; } |  | ||||||
|  |  | ||||||
|         public override string ToString() => |  | ||||||
| $@"`Title:` {WebUtility.HtmlDecode(Title)} {(string.IsNullOrEmpty(OriginalTitle) ? "" : $"({OriginalTitle})")} |  | ||||||
| `Year:` {Year} |  | ||||||
| `Rating:` {Rating} |  | ||||||
| `Genre:` {GenresAsString} |  | ||||||
| `Link:` <{ImdbURL}> |  | ||||||
| `Plot:` {System.Net.WebUtility.HtmlDecode(Plot.TrimTo(500))} |  | ||||||
| `Poster:` " + NadekoBot.Google.ShortenUrl(Poster).GetAwaiter().GetResult(); |  | ||||||
|         public string GenresAsString => |  | ||||||
|                 string.Join(", ", Genres); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -1,6 +1,7 @@ | |||||||
| using Discord; | using Discord; | ||||||
| using Discord.API; | using Discord.API; | ||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
|  | using NadekoBot.Services; | ||||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||||
| using System; | using System; | ||||||
| using System.Net.Http; | using System.Net.Http; | ||||||
| @@ -12,7 +13,7 @@ namespace NadekoBot.Modules.Searches.Commands.OMDB | |||||||
|     { |     { | ||||||
|         private const string queryUrl =  "http://www.omdbapi.com/?t={0}&y=&plot=full&r=json"; |         private const string queryUrl =  "http://www.omdbapi.com/?t={0}&y=&plot=full&r=json"; | ||||||
|  |  | ||||||
|         public static async Task<OmdbMovie> FindMovie(string name) |         public static async Task<OmdbMovie> FindMovie(string name, IGoogleApiService google) | ||||||
|         { |         { | ||||||
|             using (var http = new HttpClient()) |             using (var http = new HttpClient()) | ||||||
|             { |             { | ||||||
| @@ -20,7 +21,7 @@ namespace NadekoBot.Modules.Searches.Commands.OMDB | |||||||
|                 var movie = JsonConvert.DeserializeObject<OmdbMovie>(res); |                 var movie = JsonConvert.DeserializeObject<OmdbMovie>(res); | ||||||
|                 if (movie?.Title == null) |                 if (movie?.Title == null) | ||||||
|                     return null; |                     return null; | ||||||
|                 movie.Poster = await NadekoBot.Google.ShortenUrl(movie.Poster); |                 movie.Poster = await google.ShortenUrl(movie.Poster); | ||||||
|                 return movie; |                 return movie; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| using Discord.Commands; | using Discord.Commands; | ||||||
| using NadekoBot.Attributes; | using NadekoBot.Attributes; | ||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
|  | using NadekoBot.Services; | ||||||
| using Newtonsoft.Json.Linq; | using Newtonsoft.Json.Linq; | ||||||
| using System; | using System; | ||||||
| using System.Globalization; | using System.Globalization; | ||||||
| @@ -17,6 +18,15 @@ namespace NadekoBot.Modules.Searches | |||||||
|         [Group] |         [Group] | ||||||
|         public class OsuCommands : NadekoSubmodule |         public class OsuCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|  |             private readonly IGoogleApiService _google; | ||||||
|  |             private readonly IBotCredentials _creds; | ||||||
|  |  | ||||||
|  |             public OsuCommands(IGoogleApiService google, IBotCredentials creds) | ||||||
|  |             { | ||||||
|  |                 _google = google; | ||||||
|  |                 _creds = creds; | ||||||
|  |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             public async Task Osu(string usr, [Remainder] string mode = null) |             public async Task Osu(string usr, [Remainder] string mode = null) | ||||||
|             { |             { | ||||||
| @@ -51,7 +61,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             public async Task Osub([Remainder] string map) |             public async Task Osub([Remainder] string map) | ||||||
|             { |             { | ||||||
|                 if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.OsuApiKey)) |                 if (string.IsNullOrWhiteSpace(_creds.OsuApiKey)) | ||||||
|                 { |                 { | ||||||
|                     await ReplyErrorLocalized("osu_api_key").ConfigureAwait(false); |                     await ReplyErrorLocalized("osu_api_key").ConfigureAwait(false); | ||||||
|                     return; |                     return; | ||||||
| @@ -65,7 +75,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|                     using (var http = new HttpClient()) |                     using (var http = new HttpClient()) | ||||||
|                     { |                     { | ||||||
|                         var mapId = ResolveMap(map); |                         var mapId = ResolveMap(map); | ||||||
|                         var reqString = $"https://osu.ppy.sh/api/get_beatmaps?k={NadekoBot.Credentials.OsuApiKey}&{mapId}"; |                         var reqString = $"https://osu.ppy.sh/api/get_beatmaps?k={_creds.OsuApiKey}&{mapId}"; | ||||||
|                         var obj = JArray.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false))[0]; |                         var obj = JArray.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false))[0]; | ||||||
|                         var sb = new System.Text.StringBuilder(); |                         var sb = new System.Text.StringBuilder(); | ||||||
|                         var starRating = Math.Round(double.Parse($"{obj["difficultyrating"]}", CultureInfo.InvariantCulture), 2); |                         var starRating = Math.Round(double.Parse($"{obj["difficultyrating"]}", CultureInfo.InvariantCulture), 2); | ||||||
| @@ -86,7 +96,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|             public async Task Osu5(string user, [Remainder] string mode = null) |             public async Task Osu5(string user, [Remainder] string mode = null) | ||||||
|             { |             { | ||||||
|                 var channel = (ITextChannel)Context.Channel; |                 var channel = (ITextChannel)Context.Channel; | ||||||
|                 if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.OsuApiKey)) |                 if (string.IsNullOrWhiteSpace(_creds.OsuApiKey)) | ||||||
|                 { |                 { | ||||||
|                     await channel.SendErrorAsync("An osu! API key is required.").ConfigureAwait(false); |                     await channel.SendErrorAsync("An osu! API key is required.").ConfigureAwait(false); | ||||||
|                     return; |                     return; | ||||||
| @@ -107,12 +117,12 @@ namespace NadekoBot.Modules.Searches | |||||||
|                             m = ResolveGameMode(mode); |                             m = ResolveGameMode(mode); | ||||||
|                         } |                         } | ||||||
|  |  | ||||||
|                         var reqString = $"https://osu.ppy.sh/api/get_user_best?k={NadekoBot.Credentials.OsuApiKey}&u={Uri.EscapeDataString(user)}&type=string&limit=5&m={m}"; |                         var reqString = $"https://osu.ppy.sh/api/get_user_best?k={_creds.OsuApiKey}&u={Uri.EscapeDataString(user)}&type=string&limit=5&m={m}"; | ||||||
|                         var obj = JArray.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false)); |                         var obj = JArray.Parse(await http.GetStringAsync(reqString).ConfigureAwait(false)); | ||||||
|                         var sb = new System.Text.StringBuilder($"`Top 5 plays for {user}:`\n```xl" + Environment.NewLine); |                         var sb = new System.Text.StringBuilder($"`Top 5 plays for {user}:`\n```xl" + Environment.NewLine); | ||||||
|                         foreach (var item in obj) |                         foreach (var item in obj) | ||||||
|                         { |                         { | ||||||
|                             var mapReqString = $"https://osu.ppy.sh/api/get_beatmaps?k={NadekoBot.Credentials.OsuApiKey}&b={item["beatmap_id"]}"; |                             var mapReqString = $"https://osu.ppy.sh/api/get_beatmaps?k={_creds.OsuApiKey}&b={item["beatmap_id"]}"; | ||||||
|                             var map = JArray.Parse(await http.GetStringAsync(mapReqString).ConfigureAwait(false))[0]; |                             var map = JArray.Parse(await http.GetStringAsync(mapReqString).ConfigureAwait(false))[0]; | ||||||
|                             var pp = Math.Round(double.Parse($"{item["pp"]}", CultureInfo.InvariantCulture), 2); |                             var pp = Math.Round(double.Parse($"{item["pp"]}", CultureInfo.InvariantCulture), 2); | ||||||
|                             var acc = CalculateAcc(item, m); |                             var acc = CalculateAcc(item, m); | ||||||
|   | |||||||
| @@ -29,7 +29,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
|             public async Task Placelist() |             public async Task Placelist() | ||||||
|             { |             { | ||||||
|                 await Context.Channel.SendConfirmAsync(GetText("list_of_place_tags", NadekoBot.ModulePrefixes[typeof(Searches).Name]),  |                 await Context.Channel.SendConfirmAsync(GetText("list_of_place_tags", NadekoBot.Prefix),  | ||||||
|                     typesStr) |                     typesStr) | ||||||
|                              .ConfigureAwait(false); |                              .ConfigureAwait(false); | ||||||
|             } |             } | ||||||
|   | |||||||
| @@ -3,6 +3,7 @@ using Discord.Commands; | |||||||
| using NadekoBot.Attributes; | using NadekoBot.Attributes; | ||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
| using NadekoBot.Modules.Searches.Models; | using NadekoBot.Modules.Searches.Models; | ||||||
|  | using NadekoBot.Services.Searches; | ||||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||||
| using NLog; | using NLog; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| @@ -17,28 +18,14 @@ namespace NadekoBot.Modules.Searches | |||||||
|         [Group] |         [Group] | ||||||
|         public class PokemonSearchCommands : NadekoSubmodule |         public class PokemonSearchCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|             private static Dictionary<string, SearchPokemon> pokemons { get; } = new Dictionary<string, SearchPokemon>(); |             private readonly SearchesService _searches; | ||||||
|             private static Dictionary<string, SearchPokemonAbility> pokemonAbilities { get; } = new Dictionary<string, SearchPokemonAbility>(); |  | ||||||
|  |  | ||||||
|             public const string PokemonAbilitiesFile = "data/pokemon/pokemon_abilities7.json"; |             public Dictionary<string, SearchPokemon> Pokemons => _searches.Pokemons; | ||||||
|  |             public Dictionary<string, SearchPokemonAbility> PokemonAbilities => _searches.PokemonAbilities; | ||||||
|  |  | ||||||
|             public const string PokemonListFile = "data/pokemon/pokemon_list7.json"; |             public PokemonSearchCommands(SearchesService searches) | ||||||
|             private new static readonly Logger _log; |  | ||||||
|  |  | ||||||
|             static PokemonSearchCommands() |  | ||||||
|             { |             { | ||||||
|                 _log = LogManager.GetCurrentClassLogger(); |                 _searches = searches; | ||||||
|  |  | ||||||
|                 if (File.Exists(PokemonListFile)) |  | ||||||
|                 { |  | ||||||
|                     pokemons = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemon>>(File.ReadAllText(PokemonListFile)); |  | ||||||
|                 } |  | ||||||
|                 else |  | ||||||
|                     _log.Warn(PokemonListFile + " is missing. Pokemon abilities not loaded."); |  | ||||||
|                 if (File.Exists(PokemonAbilitiesFile)) |  | ||||||
|                     pokemonAbilities = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemonAbility>>(File.ReadAllText(PokemonAbilitiesFile)); |  | ||||||
|                 else |  | ||||||
|                     _log.Warn(PokemonAbilitiesFile + " is missing. Pokemon abilities not loaded."); |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
| @@ -48,7 +35,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|                 if (string.IsNullOrWhiteSpace(pokemon)) |                 if (string.IsNullOrWhiteSpace(pokemon)) | ||||||
|                     return; |                     return; | ||||||
|  |  | ||||||
|                 foreach (var kvp in pokemons) |                 foreach (var kvp in Pokemons) | ||||||
|                 { |                 { | ||||||
|                     if (kvp.Key.ToUpperInvariant() == pokemon.ToUpperInvariant()) |                     if (kvp.Key.ToUpperInvariant() == pokemon.ToUpperInvariant()) | ||||||
|                     { |                     { | ||||||
| @@ -71,7 +58,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|                 ability = ability?.Trim().ToUpperInvariant().Replace(" ", ""); |                 ability = ability?.Trim().ToUpperInvariant().Replace(" ", ""); | ||||||
|                 if (string.IsNullOrWhiteSpace(ability)) |                 if (string.IsNullOrWhiteSpace(ability)) | ||||||
|                     return; |                     return; | ||||||
|                 foreach (var kvp in pokemonAbilities) |                 foreach (var kvp in PokemonAbilities) | ||||||
|                 { |                 { | ||||||
|                     if (kvp.Key.ToUpperInvariant() == ability) |                     if (kvp.Key.ToUpperInvariant() == ability) | ||||||
|                     { |                     { | ||||||
|   | |||||||
| @@ -1,195 +1,30 @@ | |||||||
| using Discord.Commands; | using Discord.Commands; | ||||||
| using Discord; | using Discord; | ||||||
| using System; |  | ||||||
| using System.Collections.Concurrent; |  | ||||||
| using System.Linq; | using System.Linq; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using NadekoBot.Services; | using NadekoBot.Services; | ||||||
| using System.Threading; | using System.Threading; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using NadekoBot.Services.Database.Models; | using NadekoBot.Services.Database.Models; | ||||||
| using System.Net.Http; |  | ||||||
| using NadekoBot.Attributes; | using NadekoBot.Attributes; | ||||||
| using Microsoft.EntityFrameworkCore; | using Microsoft.EntityFrameworkCore; | ||||||
| using Newtonsoft.Json; |  | ||||||
| using NadekoBot.Extensions; | using NadekoBot.Extensions; | ||||||
|  | using NadekoBot.Services.Searches; | ||||||
|  |  | ||||||
| namespace NadekoBot.Modules.Searches | namespace NadekoBot.Modules.Searches | ||||||
| { | { | ||||||
|     public partial class Searches |     public partial class Searches | ||||||
|     { |     { | ||||||
|         public class StreamStatus |  | ||||||
|         { |  | ||||||
|             public bool IsLive { get; set; } |  | ||||||
|             public string ApiLink { get; set; } |  | ||||||
|             public string Views { get; set; } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public class HitboxResponse { |  | ||||||
|             public bool Success { get; set; } = true; |  | ||||||
|             [JsonProperty("media_is_live")] |  | ||||||
|             public string MediaIsLive { get; set; } |  | ||||||
|             public bool IsLive  => MediaIsLive == "1"; |  | ||||||
|             [JsonProperty("media_views")] |  | ||||||
|             public string Views { get; set; } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public class TwitchResponse |  | ||||||
|         { |  | ||||||
|             public string Error { get; set; } = null; |  | ||||||
|             public bool IsLive => Stream != null; |  | ||||||
|             public StreamInfo Stream { get; set; } |  | ||||||
|  |  | ||||||
|             public class StreamInfo |  | ||||||
|             { |  | ||||||
|                 public int Viewers { get; set; } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public class BeamResponse |  | ||||||
|         { |  | ||||||
|             public string Error { get; set; } = null; |  | ||||||
|  |  | ||||||
|             [JsonProperty("online")] |  | ||||||
|             public bool IsLive { get; set; } |  | ||||||
|             public int ViewersCurrent { get; set; } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public class StreamNotFoundException : Exception |  | ||||||
|         { |  | ||||||
|             public StreamNotFoundException(string message) : base("Stream '" + message + "' not found.") |  | ||||||
|             { |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         [Group] |         [Group] | ||||||
|         public class StreamNotificationCommands : NadekoSubmodule |         public class StreamNotificationCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|             private static readonly Timer _checkTimer; |             private readonly DbHandler _db; | ||||||
|             private static readonly ConcurrentDictionary<string, StreamStatus> _cachedStatuses = new ConcurrentDictionary<string, StreamStatus>(); |             private readonly StreamNotificationService _service; | ||||||
|  |  | ||||||
|             private static bool firstPass { get; set; } = true; |             public StreamNotificationCommands(DbHandler db, StreamNotificationService service) | ||||||
|  |  | ||||||
|             static StreamNotificationCommands() |  | ||||||
|             { |             { | ||||||
|                 _checkTimer = new Timer(async (state) => |                 _db = db; | ||||||
|                 { |                 _service = service; | ||||||
|                     var oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(_cachedStatuses); |  | ||||||
|                     _cachedStatuses.Clear(); |  | ||||||
|                     IEnumerable<FollowedStream> streams; |  | ||||||
|                     using (var uow = DbHandler.UnitOfWork()) |  | ||||||
|                     { |  | ||||||
|                         streams = uow.GuildConfigs.GetAllFollowedStreams(); |  | ||||||
|                     } |  | ||||||
|  |  | ||||||
|                     await Task.WhenAll(streams.Select(async fs => |  | ||||||
|                     { |  | ||||||
|                         try |  | ||||||
|                         { |  | ||||||
|                             var newStatus = await GetStreamStatus(fs).ConfigureAwait(false); |  | ||||||
|                             if (firstPass) |  | ||||||
|                             { |  | ||||||
|                                 return; |  | ||||||
|                             } |  | ||||||
|  |  | ||||||
|                             StreamStatus oldStatus; |  | ||||||
|                             if (oldCachedStatuses.TryGetValue(newStatus.ApiLink, out oldStatus) && |  | ||||||
|                                 oldStatus.IsLive != newStatus.IsLive) |  | ||||||
|                             { |  | ||||||
|                                 var server = NadekoBot.Client.GetGuild(fs.GuildId); |  | ||||||
|                                 var channel = server?.GetTextChannel(fs.ChannelId); |  | ||||||
|                                 if (channel == null) |  | ||||||
|                                     return; |  | ||||||
|                                 try |  | ||||||
|                                 { |  | ||||||
|                                     await channel.EmbedAsync(fs.GetEmbed(newStatus, channel.Guild.Id)).ConfigureAwait(false); |  | ||||||
|                                 } |  | ||||||
|                                 catch |  | ||||||
|                                 { |  | ||||||
|                                     // ignored |  | ||||||
|                                 } |  | ||||||
|                             } |  | ||||||
|                         } |  | ||||||
|                         catch |  | ||||||
|                         { |  | ||||||
|                             // ignored |  | ||||||
|                         } |  | ||||||
|                     })); |  | ||||||
|  |  | ||||||
|                     firstPass = false; |  | ||||||
|                 }, null, TimeSpan.Zero, TimeSpan.FromSeconds(60)); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             private static async Task<StreamStatus> GetStreamStatus(FollowedStream stream, bool checkCache = true) |  | ||||||
|             { |  | ||||||
|                 string response; |  | ||||||
|                 StreamStatus result; |  | ||||||
|                 switch (stream.Type) |  | ||||||
|                 { |  | ||||||
|                     case FollowedStream.FollowedStreamType.Hitbox: |  | ||||||
|                         var hitboxUrl = $"https://api.hitbox.tv/media/status/{stream.Username.ToLowerInvariant()}"; |  | ||||||
|                         if (checkCache && _cachedStatuses.TryGetValue(hitboxUrl, out result)) |  | ||||||
|                             return result; |  | ||||||
|                         using (var http = new HttpClient()) |  | ||||||
|                         { |  | ||||||
|                             response = await http.GetStringAsync(hitboxUrl).ConfigureAwait(false); |  | ||||||
|                         } |  | ||||||
|                         var hbData = JsonConvert.DeserializeObject<HitboxResponse>(response); |  | ||||||
|                         if (!hbData.Success) |  | ||||||
|                             throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]"); |  | ||||||
|                         result = new StreamStatus() |  | ||||||
|                         { |  | ||||||
|                             IsLive = hbData.IsLive, |  | ||||||
|                             ApiLink = hitboxUrl, |  | ||||||
|                             Views = hbData.Views |  | ||||||
|                         }; |  | ||||||
|                         _cachedStatuses.AddOrUpdate(hitboxUrl, result, (key, old) => result); |  | ||||||
|                         return result; |  | ||||||
|                     case FollowedStream.FollowedStreamType.Twitch: |  | ||||||
|                         var twitchUrl = $"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username.ToLowerInvariant())}?client_id=67w6z9i09xv2uoojdm9l0wsyph4hxo6"; |  | ||||||
|                         if (checkCache && _cachedStatuses.TryGetValue(twitchUrl, out result)) |  | ||||||
|                             return result; |  | ||||||
|                         using (var http = new HttpClient()) |  | ||||||
|                         { |  | ||||||
|                             response = await http.GetStringAsync(twitchUrl).ConfigureAwait(false); |  | ||||||
|                         } |  | ||||||
|                         var twData = JsonConvert.DeserializeObject<TwitchResponse>(response); |  | ||||||
|                         if (twData.Error != null) |  | ||||||
|                         { |  | ||||||
|                             throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]"); |  | ||||||
|                         } |  | ||||||
|                         result = new StreamStatus() |  | ||||||
|                         { |  | ||||||
|                             IsLive = twData.IsLive, |  | ||||||
|                             ApiLink = twitchUrl, |  | ||||||
|                             Views = twData.Stream?.Viewers.ToString() ?? "0" |  | ||||||
|                         }; |  | ||||||
|                         _cachedStatuses.AddOrUpdate(twitchUrl, result, (key, old) => result); |  | ||||||
|                         return result; |  | ||||||
|                     case FollowedStream.FollowedStreamType.Beam: |  | ||||||
|                         var beamUrl = $"https://beam.pro/api/v1/channels/{stream.Username.ToLowerInvariant()}"; |  | ||||||
|                         if (checkCache && _cachedStatuses.TryGetValue(beamUrl, out result)) |  | ||||||
|                             return result; |  | ||||||
|                         using (var http = new HttpClient()) |  | ||||||
|                         { |  | ||||||
|                             response = await http.GetStringAsync(beamUrl).ConfigureAwait(false); |  | ||||||
|                         } |  | ||||||
|  |  | ||||||
|                         var bmData = JsonConvert.DeserializeObject<BeamResponse>(response); |  | ||||||
|                         if (bmData.Error != null) |  | ||||||
|                             throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]"); |  | ||||||
|                         result = new StreamStatus() |  | ||||||
|                         { |  | ||||||
|                             IsLive = bmData.IsLive, |  | ||||||
|                             ApiLink = beamUrl, |  | ||||||
|                             Views = bmData.ViewersCurrent.ToString() |  | ||||||
|                         }; |  | ||||||
|                         _cachedStatuses.AddOrUpdate(beamUrl, result, (key, old) => result); |  | ||||||
|                         return result; |  | ||||||
|                     default: |  | ||||||
|                         break; |  | ||||||
|                 } |  | ||||||
|                 return null; |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
| @@ -218,7 +53,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|             public async Task ListStreams() |             public async Task ListStreams() | ||||||
|             { |             { | ||||||
|                 IEnumerable<FollowedStream> streams; |                 IEnumerable<FollowedStream> streams; | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     streams = uow.GuildConfigs |                     streams = uow.GuildConfigs | ||||||
|                                  .For(Context.Guild.Id,  |                                  .For(Context.Guild.Id,  | ||||||
| @@ -260,7 +95,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|                 }; |                 }; | ||||||
|  |  | ||||||
|                 bool removed; |                 bool removed; | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     var config = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(gc => gc.FollowedStreams)); |                     var config = uow.GuildConfigs.For(Context.Guild.Id, set => set.Include(gc => gc.FollowedStreams)); | ||||||
|                     removed = config.FollowedStreams.Remove(fs); |                     removed = config.FollowedStreams.Remove(fs); | ||||||
| @@ -287,7 +122,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|                     return; |                     return; | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     var streamStatus = (await GetStreamStatus(new FollowedStream |                     var streamStatus = (await _service.GetStreamStatus(new FollowedStream | ||||||
|                     { |                     { | ||||||
|                         Username = stream, |                         Username = stream, | ||||||
|                         Type = platform, |                         Type = platform, | ||||||
| @@ -325,7 +160,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|                 StreamStatus status; |                 StreamStatus status; | ||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     status = await GetStreamStatus(fs).ConfigureAwait(false); |                     status = await _service.GetStreamStatus(fs).ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|                 catch |                 catch | ||||||
|                 { |                 { | ||||||
| @@ -333,53 +168,15 @@ namespace NadekoBot.Modules.Searches | |||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 using (var uow = DbHandler.UnitOfWork()) |                 using (var uow = _db.UnitOfWork) | ||||||
|                 { |                 { | ||||||
|                     uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.FollowedStreams)) |                     uow.GuildConfigs.For(channel.Guild.Id, set => set.Include(gc => gc.FollowedStreams)) | ||||||
|                                     .FollowedStreams |                                     .FollowedStreams | ||||||
|                                     .Add(fs); |                                     .Add(fs); | ||||||
|                     await uow.CompleteAsync().ConfigureAwait(false); |                     await uow.CompleteAsync().ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|                 await channel.EmbedAsync(fs.GetEmbed(status, Context.Guild.Id), GetText("stream_tracked")).ConfigureAwait(false); |                 await channel.EmbedAsync(_service.GetEmbed(fs, status, Context.Guild.Id), GetText("stream_tracked")).ConfigureAwait(false); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public static class FollowedStreamExtensions |  | ||||||
|     { |  | ||||||
|         public static EmbedBuilder GetEmbed(this FollowedStream fs, Searches.StreamStatus status, ulong guildId) |  | ||||||
|         { |  | ||||||
|             var embed = new EmbedBuilder().WithTitle(fs.Username) |  | ||||||
|                                           .WithUrl(fs.GetLink()) |  | ||||||
|                                           .AddField(efb => efb.WithName(fs.GetText("status")) |  | ||||||
|                                                             .WithValue(status.IsLive ? "Online" : "Offline") |  | ||||||
|                                                             .WithIsInline(true)) |  | ||||||
|                                           .AddField(efb => efb.WithName(fs.GetText("viewers")) |  | ||||||
|                                                             .WithValue(status.IsLive ? status.Views : "-") |  | ||||||
|                                                             .WithIsInline(true)) |  | ||||||
|                                           .AddField(efb => efb.WithName(fs.GetText("platform")) |  | ||||||
|                                                             .WithValue(fs.Type.ToString()) |  | ||||||
|                                                             .WithIsInline(true)) |  | ||||||
|                                           .WithColor(status.IsLive ? NadekoBot.OkColor : NadekoBot.ErrorColor); |  | ||||||
|  |  | ||||||
|             return embed; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public static string GetText(this FollowedStream fs, string key, params object[] replacements) => |  | ||||||
|             NadekoTopLevelModule.GetTextStatic(key, |  | ||||||
|                 NadekoBot.Localization.GetCultureInfo(fs.GuildId), |  | ||||||
|                 typeof(Searches).Name.ToLowerInvariant(), |  | ||||||
|                 replacements); |  | ||||||
|  |  | ||||||
|         public static string GetLink(this FollowedStream fs) |  | ||||||
|         { |  | ||||||
|             if (fs.Type == FollowedStream.FollowedStreamType.Hitbox) |  | ||||||
|                 return $"http://www.hitbox.tv/{fs.Username}/"; |  | ||||||
|             if (fs.Type == FollowedStream.FollowedStreamType.Twitch) |  | ||||||
|                 return $"http://www.twitch.tv/{fs.Username}/"; |  | ||||||
|             if (fs.Type == FollowedStream.FollowedStreamType.Beam) |  | ||||||
|                 return $"https://beam.pro/{fs.Username}/"; |  | ||||||
|             return "??"; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -7,54 +7,23 @@ using System.Threading.Tasks; | |||||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||||
| using System.Linq; | using System.Linq; | ||||||
| using Discord.WebSocket; | using Discord.WebSocket; | ||||||
|  | using NadekoBot.Services.Searches; | ||||||
|  | using NadekoBot.Services; | ||||||
|  |  | ||||||
| namespace NadekoBot.Modules.Searches | namespace NadekoBot.Modules.Searches | ||||||
| { | { | ||||||
|     public partial class Searches |     public partial class Searches | ||||||
|     { |     { | ||||||
|         public struct UserChannelPair |  | ||||||
|         { |  | ||||||
|             public ulong UserId { get; set; } |  | ||||||
|             public ulong ChannelId { get; set; } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         [Group] |         [Group] | ||||||
|         public class TranslateCommands : NadekoSubmodule |         public class TranslateCommands : NadekoSubmodule | ||||||
|         { |         { | ||||||
|             private static ConcurrentDictionary<ulong, bool> translatedChannels { get; } = new ConcurrentDictionary<ulong, bool>(); |             private readonly SearchesService _searches; | ||||||
|             private static ConcurrentDictionary<UserChannelPair, string> userLanguages { get; } = new ConcurrentDictionary<UserChannelPair, string>(); |             private readonly IGoogleApiService _google; | ||||||
|  |  | ||||||
|             static TranslateCommands() |             public TranslateCommands(SearchesService searches, IGoogleApiService google) | ||||||
|             { |             { | ||||||
|                 NadekoBot.Client.MessageReceived += async (msg) => |                 _searches = searches; | ||||||
|                 { |                 _google = google; | ||||||
|                     try |  | ||||||
|                     { |  | ||||||
|                         var umsg = msg as SocketUserMessage; |  | ||||||
|                         if (umsg == null) |  | ||||||
|                             return; |  | ||||||
|  |  | ||||||
|                         bool autoDelete; |  | ||||||
|                         if (!translatedChannels.TryGetValue(umsg.Channel.Id, out autoDelete)) |  | ||||||
|                             return; |  | ||||||
|                         var key = new UserChannelPair() |  | ||||||
|                         { |  | ||||||
|                             UserId = umsg.Author.Id, |  | ||||||
|                             ChannelId = umsg.Channel.Id, |  | ||||||
|                         }; |  | ||||||
|  |  | ||||||
|                         string langs; |  | ||||||
|                         if (!userLanguages.TryGetValue(key, out langs)) |  | ||||||
|                             return; |  | ||||||
|  |  | ||||||
|                         var text = await TranslateInternal(langs, umsg.Resolve(TagHandling.Ignore)) |  | ||||||
|                                             .ConfigureAwait(false); |  | ||||||
|                         if (autoDelete) |  | ||||||
|                             try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { } |  | ||||||
|                         await umsg.Channel.SendConfirmAsync($"{umsg.Author.Mention} `:` " + text.Replace("<@ ", "<@").Replace("<@! ", "<@!")).ConfigureAwait(false); |  | ||||||
|                     } |  | ||||||
|                     catch { } |  | ||||||
|                 }; |  | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             [NadekoCommand, Usage, Description, Aliases] |             [NadekoCommand, Usage, Description, Aliases] | ||||||
| @@ -63,7 +32,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|                 try |                 try | ||||||
|                 { |                 { | ||||||
|                     await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); |                     await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); | ||||||
|                     var translation = await TranslateInternal(langs, text); |                     var translation = await _searches.Translate(langs, text); | ||||||
|                     await Context.Channel.SendConfirmAsync(GetText("translation") + " " + langs, translation).ConfigureAwait(false); |                     await Context.Channel.SendConfirmAsync(GetText("translation") + " " + langs, translation).ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|                 catch |                 catch | ||||||
| @@ -72,19 +41,6 @@ namespace NadekoBot.Modules.Searches | |||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             private static async Task<string> TranslateInternal(string langs, [Remainder] string text = null) |  | ||||||
|             { |  | ||||||
|                 var langarr = langs.ToLowerInvariant().Split('>'); |  | ||||||
|                 if (langarr.Length != 2) |  | ||||||
|                     throw new ArgumentException(); |  | ||||||
|                 var from = langarr[0]; |  | ||||||
|                 var to = langarr[1]; |  | ||||||
|                 text = text?.Trim(); |  | ||||||
|                 if (string.IsNullOrWhiteSpace(text)) |  | ||||||
|                     throw new ArgumentException(); |  | ||||||
|                 return (await GoogleTranslator.Instance.Translate(text, from, to).ConfigureAwait(false)).SanitizeMentions(); |  | ||||||
|             } |  | ||||||
|  |  | ||||||
|             public enum AutoDeleteAutoTranslate |             public enum AutoDeleteAutoTranslate | ||||||
|             { |             { | ||||||
|                 Del, |                 Del, | ||||||
| @@ -101,18 +57,17 @@ namespace NadekoBot.Modules.Searches | |||||||
|  |  | ||||||
|                 if (autoDelete == AutoDeleteAutoTranslate.Del) |                 if (autoDelete == AutoDeleteAutoTranslate.Del) | ||||||
|                 { |                 { | ||||||
|                     translatedChannels.AddOrUpdate(channel.Id, true, (key, val) => true); |                     _searches.TranslatedChannels.AddOrUpdate(channel.Id, true, (key, val) => true); | ||||||
|                     await ReplyConfirmLocalized("atl_ad_started").ConfigureAwait(false); |                     await ReplyConfirmLocalized("atl_ad_started").ConfigureAwait(false); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  |                  | ||||||
|                 bool throwaway; |                 if (_searches.TranslatedChannels.TryRemove(channel.Id, out var throwaway)) | ||||||
|                 if (translatedChannels.TryRemove(channel.Id, out throwaway)) |  | ||||||
|                 { |                 { | ||||||
|                     await ReplyConfirmLocalized("atl_stopped").ConfigureAwait(false); |                     await ReplyConfirmLocalized("atl_stopped").ConfigureAwait(false); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|                 if (translatedChannels.TryAdd(channel.Id, autoDelete == AutoDeleteAutoTranslate.Del)) |                 if (_searches.TranslatedChannels.TryAdd(channel.Id, autoDelete == AutoDeleteAutoTranslate.Del)) | ||||||
|                 { |                 { | ||||||
|                     await ReplyConfirmLocalized("atl_started").ConfigureAwait(false); |                     await ReplyConfirmLocalized("atl_started").ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
| @@ -130,7 +85,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|  |  | ||||||
|                 if (string.IsNullOrWhiteSpace(langs)) |                 if (string.IsNullOrWhiteSpace(langs)) | ||||||
|                 { |                 { | ||||||
|                     if (userLanguages.TryRemove(ucp, out langs)) |                     if (_searches.UserLanguages.TryRemove(ucp, out langs)) | ||||||
|                         await ReplyConfirmLocalized("atl_removed").ConfigureAwait(false); |                         await ReplyConfirmLocalized("atl_removed").ConfigureAwait(false); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
| @@ -141,13 +96,13 @@ namespace NadekoBot.Modules.Searches | |||||||
|                 var from = langarr[0]; |                 var from = langarr[0]; | ||||||
|                 var to = langarr[1]; |                 var to = langarr[1]; | ||||||
|  |  | ||||||
|                 if (!GoogleTranslator.Instance.Languages.Contains(from) || !GoogleTranslator.Instance.Languages.Contains(to)) |                 if (!_google.Languages.Contains(from) || !_google.Languages.Contains(to)) | ||||||
|                 { |                 { | ||||||
|                     await ReplyErrorLocalized("invalid_lang").ConfigureAwait(false); |                     await ReplyErrorLocalized("invalid_lang").ConfigureAwait(false); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|  |  | ||||||
|                 userLanguages.AddOrUpdate(ucp, langs, (key, val) => langs); |                 _searches.UserLanguages.AddOrUpdate(ucp, langs, (key, val) => langs); | ||||||
|  |  | ||||||
|                 await ReplyConfirmLocalized("atl_set", from, to).ConfigureAwait(false); |                 await ReplyConfirmLocalized("atl_set", from, to).ConfigureAwait(false); | ||||||
|             } |             } | ||||||
| @@ -156,7 +111,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|             [RequireContext(ContextType.Guild)] |             [RequireContext(ContextType.Guild)] | ||||||
|             public async Task Translangs() |             public async Task Translangs() | ||||||
|             { |             { | ||||||
|                 await Context.Channel.SendTableAsync(GoogleTranslator.Instance.Languages, str => $"{str,-15}", 3); |                 await Context.Channel.SendTableAsync(_google.Languages, str => $"{str,-15}", 3); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ using Newtonsoft.Json; | |||||||
| using Newtonsoft.Json.Linq; | using Newtonsoft.Json.Linq; | ||||||
| using System; | using System; | ||||||
| using System.Linq; | using System.Linq; | ||||||
| using System.Text; |  | ||||||
| using System.Net.Http; | using System.Net.Http; | ||||||
| using NadekoBot.Services; | using NadekoBot.Services; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| @@ -17,18 +16,27 @@ using NadekoBot.Modules.Searches.Commands.Models; | |||||||
| using AngleSharp; | using AngleSharp; | ||||||
| using AngleSharp.Dom.Html; | using AngleSharp.Dom.Html; | ||||||
| using AngleSharp.Dom; | using AngleSharp.Dom; | ||||||
| using System.Xml; |  | ||||||
| using Configuration = AngleSharp.Configuration; | using Configuration = AngleSharp.Configuration; | ||||||
| using NadekoBot.Attributes; | using NadekoBot.Attributes; | ||||||
| using Discord.Commands; | using Discord.Commands; | ||||||
| using ImageSharp.Processing.Processors; |  | ||||||
| using ImageSharp; | using ImageSharp; | ||||||
|  | using NadekoBot.Services.Searches; | ||||||
|  |  | ||||||
| namespace NadekoBot.Modules.Searches | namespace NadekoBot.Modules.Searches | ||||||
| { | { | ||||||
|     [NadekoModule("Searches", "~")] |  | ||||||
|     public partial class Searches : NadekoTopLevelModule |     public partial class Searches : NadekoTopLevelModule | ||||||
|     { |     { | ||||||
|  |         private readonly IBotCredentials _creds; | ||||||
|  |         private readonly IGoogleApiService _google; | ||||||
|  |         private readonly SearchesService _searches; | ||||||
|  |  | ||||||
|  |         public Searches(IBotCredentials creds, IGoogleApiService google, SearchesService searches) | ||||||
|  |         { | ||||||
|  |             _creds = creds; | ||||||
|  |             _google = google; | ||||||
|  |             _searches = searches; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         [NadekoCommand, Usage, Description, Aliases] |         [NadekoCommand, Usage, Description, Aliases] | ||||||
|         public async Task Weather([Remainder] string query) |         public async Task Weather([Remainder] string query) | ||||||
|         { |         { | ||||||
| @@ -59,16 +67,16 @@ namespace NadekoBot.Modules.Searches | |||||||
|         [NadekoCommand, Usage, Description, Aliases] |         [NadekoCommand, Usage, Description, Aliases] | ||||||
|         public async Task Time([Remainder] string arg) |         public async Task Time([Remainder] string arg) | ||||||
|         { |         { | ||||||
|             if (string.IsNullOrWhiteSpace(arg) || string.IsNullOrWhiteSpace(NadekoBot.Credentials.GoogleApiKey)) |             if (string.IsNullOrWhiteSpace(arg) || string.IsNullOrWhiteSpace(_creds.GoogleApiKey)) | ||||||
|                 return; |                 return; | ||||||
|  |  | ||||||
|             using (var http = new HttpClient()) |             using (var http = new HttpClient()) | ||||||
|             { |             { | ||||||
|                 var res = await http.GetStringAsync($"https://maps.googleapis.com/maps/api/geocode/json?address={arg}&key={NadekoBot.Credentials.GoogleApiKey}").ConfigureAwait(false); |                 var res = await http.GetStringAsync($"https://maps.googleapis.com/maps/api/geocode/json?address={arg}&key={_creds.GoogleApiKey}").ConfigureAwait(false); | ||||||
|                 var obj = JsonConvert.DeserializeObject<GeolocationResult>(res); |                 var obj = JsonConvert.DeserializeObject<GeolocationResult>(res); | ||||||
|  |  | ||||||
|                 var currentSeconds = DateTime.UtcNow.UnixTimestamp(); |                 var currentSeconds = DateTime.UtcNow.UnixTimestamp(); | ||||||
|                 var timeRes = await http.GetStringAsync($"https://maps.googleapis.com/maps/api/timezone/json?location={obj.results[0].Geometry.Location.Lat},{obj.results[0].Geometry.Location.Lng}×tamp={currentSeconds}&key={NadekoBot.Credentials.GoogleApiKey}").ConfigureAwait(false); |                 var timeRes = await http.GetStringAsync($"https://maps.googleapis.com/maps/api/timezone/json?location={obj.results[0].Geometry.Location.Lat},{obj.results[0].Geometry.Location.Lng}×tamp={currentSeconds}&key={_creds.GoogleApiKey}").ConfigureAwait(false); | ||||||
|                 var timeObj = JsonConvert.DeserializeObject<TimeZoneResult>(timeRes); |                 var timeObj = JsonConvert.DeserializeObject<TimeZoneResult>(timeRes); | ||||||
|  |  | ||||||
|                 var time = DateTime.UtcNow.AddSeconds(timeObj.DstOffset + timeObj.RawOffset); |                 var time = DateTime.UtcNow.AddSeconds(timeObj.DstOffset + timeObj.RawOffset); | ||||||
| @@ -81,7 +89,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|         public async Task Youtube([Remainder] string query = null) |         public async Task Youtube([Remainder] string query = null) | ||||||
|         { |         { | ||||||
|             if (!await ValidateQuery(Context.Channel, query).ConfigureAwait(false)) return; |             if (!await ValidateQuery(Context.Channel, query).ConfigureAwait(false)) return; | ||||||
|             var result = (await NadekoBot.Google.GetVideosByKeywordsAsync(query, 1)).FirstOrDefault(); |             var result = (await _google.GetVideosByKeywordsAsync(query, 1)).FirstOrDefault(); | ||||||
|             if (string.IsNullOrWhiteSpace(result)) |             if (string.IsNullOrWhiteSpace(result)) | ||||||
|             { |             { | ||||||
|                 await ReplyErrorLocalized("no_results").ConfigureAwait(false); |                 await ReplyErrorLocalized("no_results").ConfigureAwait(false); | ||||||
| @@ -97,7 +105,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|             if (!(await ValidateQuery(Context.Channel, query).ConfigureAwait(false))) return; |             if (!(await ValidateQuery(Context.Channel, query).ConfigureAwait(false))) return; | ||||||
|             await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); |             await Context.Channel.TriggerTypingAsync().ConfigureAwait(false); | ||||||
|  |  | ||||||
|             var movie = await OmdbProvider.FindMovie(query); |             var movie = await OmdbProvider.FindMovie(query, _google); | ||||||
|             if (movie == null) |             if (movie == null) | ||||||
|             { |             { | ||||||
|                 await ReplyErrorLocalized("imdb_fail").ConfigureAwait(false); |                 await ReplyErrorLocalized("imdb_fail").ConfigureAwait(false); | ||||||
| @@ -137,7 +145,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|  |  | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
|                 var res = await NadekoBot.Google.GetImageAsync(terms).ConfigureAwait(false); |                 var res = await _google.GetImageAsync(terms).ConfigureAwait(false); | ||||||
|                 var embed = new EmbedBuilder() |                 var embed = new EmbedBuilder() | ||||||
|                     .WithOkColor() |                     .WithOkColor() | ||||||
|                     .WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50)) |                     .WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50)) | ||||||
| @@ -189,7 +197,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|             terms = WebUtility.UrlEncode(terms).Replace(' ', '+'); |             terms = WebUtility.UrlEncode(terms).Replace(' ', '+'); | ||||||
|             try |             try | ||||||
|             { |             { | ||||||
|                 var res = await NadekoBot.Google.GetImageAsync(terms, new NadekoRandom().Next(0, 50)).ConfigureAwait(false); |                 var res = await _google.GetImageAsync(terms, new NadekoRandom().Next(0, 50)).ConfigureAwait(false); | ||||||
|                 var embed = new EmbedBuilder() |                 var embed = new EmbedBuilder() | ||||||
|                     .WithOkColor() |                     .WithOkColor() | ||||||
|                     .WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50)) |                     .WithAuthor(eab => eab.WithName(GetText("image_search_for") + " " + terms.TrimTo(50)) | ||||||
| @@ -239,7 +247,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|             if (string.IsNullOrWhiteSpace(ffs)) |             if (string.IsNullOrWhiteSpace(ffs)) | ||||||
|                 return; |                 return; | ||||||
|  |  | ||||||
|             await Context.Channel.SendConfirmAsync("<" + await NadekoBot.Google.ShortenUrl($"http://lmgtfy.com/?q={ Uri.EscapeUriString(ffs) }") + ">") |             await Context.Channel.SendConfirmAsync("<" + await _google.ShortenUrl($"http://lmgtfy.com/?q={ Uri.EscapeUriString(ffs) }") + ">") | ||||||
|                            .ConfigureAwait(false); |                            .ConfigureAwait(false); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -249,7 +257,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|             if (string.IsNullOrWhiteSpace(arg)) |             if (string.IsNullOrWhiteSpace(arg)) | ||||||
|                 return; |                 return; | ||||||
|  |  | ||||||
|             var shortened = await NadekoBot.Google.ShortenUrl(arg).ConfigureAwait(false); |             var shortened = await _google.ShortenUrl(arg).ConfigureAwait(false); | ||||||
|  |  | ||||||
|             if (shortened == arg) |             if (shortened == arg) | ||||||
|             { |             { | ||||||
| @@ -315,7 +323,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|                 .WithFooter(efb => efb.WithText(totalResults)); |                 .WithFooter(efb => efb.WithText(totalResults)); | ||||||
|  |  | ||||||
|             var desc = await Task.WhenAll(results.Select(async res => |             var desc = await Task.WhenAll(results.Select(async res => | ||||||
|                     $"[{Format.Bold(res?.Title)}]({(await NadekoBot.Google.ShortenUrl(res?.Link))})\n{res?.Text}\n\n")) |                     $"[{Format.Bold(res?.Title)}]({(await _google.ShortenUrl(res?.Link))})\n{res?.Text}\n\n")) | ||||||
|                 .ConfigureAwait(false); |                 .ConfigureAwait(false); | ||||||
|             await Context.Channel.EmbedAsync(embed.WithDescription(string.Concat(desc))).ConfigureAwait(false); |             await Context.Channel.EmbedAsync(embed.WithDescription(string.Concat(desc))).ConfigureAwait(false); | ||||||
|         } |         } | ||||||
| @@ -339,7 +347,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|                     if (items == null || items.Length == 0) |                     if (items == null || items.Length == 0) | ||||||
|                         throw new KeyNotFoundException("Cannot find a card by that name"); |                         throw new KeyNotFoundException("Cannot find a card by that name"); | ||||||
|                     var item = items[new NadekoRandom().Next(0, items.Length)]; |                     var item = items[new NadekoRandom().Next(0, items.Length)]; | ||||||
|                     var storeUrl = await NadekoBot.Google.ShortenUrl(item["store_url"].ToString()); |                     var storeUrl = await _google.ShortenUrl(item["store_url"].ToString()); | ||||||
|                     var cost = item["cost"].ToString(); |                     var cost = item["cost"].ToString(); | ||||||
|                     var desc = item["text"].ToString(); |                     var desc = item["text"].ToString(); | ||||||
|                     var types = string.Join(",\n", item["types"].ToObject<string[]>()); |                     var types = string.Join(",\n", item["types"].ToObject<string[]>()); | ||||||
| @@ -351,7 +359,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|                                     .AddField(efb => efb.WithName(GetText("store_url")).WithValue(storeUrl).WithIsInline(true)) |                                     .AddField(efb => efb.WithName(GetText("store_url")).WithValue(storeUrl).WithIsInline(true)) | ||||||
|                                     .AddField(efb => efb.WithName(GetText("cost")).WithValue(cost).WithIsInline(true)) |                                     .AddField(efb => efb.WithName(GetText("cost")).WithValue(cost).WithIsInline(true)) | ||||||
|                                     .AddField(efb => efb.WithName(GetText("types")).WithValue(types).WithIsInline(true)); |                                     .AddField(efb => efb.WithName(GetText("types")).WithValue(types).WithIsInline(true)); | ||||||
|                     //.AddField(efb => efb.WithName("Store Url").WithValue(await NadekoBot.Google.ShortenUrl(items[0]["store_url"].ToString())).WithIsInline(true)); |                     //.AddField(efb => efb.WithName("Store Url").WithValue(await _google.ShortenUrl(items[0]["store_url"].ToString())).WithIsInline(true)); | ||||||
|  |  | ||||||
|                     await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); |                     await Context.Channel.EmbedAsync(embed).ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
| @@ -369,7 +377,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|             if (string.IsNullOrWhiteSpace(arg)) |             if (string.IsNullOrWhiteSpace(arg)) | ||||||
|                 return; |                 return; | ||||||
|  |  | ||||||
|             if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey)) |             if (string.IsNullOrWhiteSpace(_creds.MashapeKey)) | ||||||
|             { |             { | ||||||
|                 await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false); |                 await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false); | ||||||
|                 return; |                 return; | ||||||
| @@ -379,7 +387,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|             using (var http = new HttpClient()) |             using (var http = new HttpClient()) | ||||||
|             { |             { | ||||||
|                 http.DefaultRequestHeaders.Clear(); |                 http.DefaultRequestHeaders.Clear(); | ||||||
|                 http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey); |                 http.DefaultRequestHeaders.Add("X-Mashape-Key", _creds.MashapeKey); | ||||||
|                 var response = await http.GetStringAsync($"https://omgvamp-hearthstone-v1.p.mashape.com/cards/search/{Uri.EscapeUriString(arg)}") |                 var response = await http.GetStringAsync($"https://omgvamp-hearthstone-v1.p.mashape.com/cards/search/{Uri.EscapeUriString(arg)}") | ||||||
|                     .ConfigureAwait(false); |                     .ConfigureAwait(false); | ||||||
|                 try |                 try | ||||||
| @@ -422,7 +430,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|         [NadekoCommand, Usage, Description, Aliases] |         [NadekoCommand, Usage, Description, Aliases] | ||||||
|         public async Task Yodify([Remainder] string query = null) |         public async Task Yodify([Remainder] string query = null) | ||||||
|         { |         { | ||||||
|             if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey)) |             if (string.IsNullOrWhiteSpace(_creds.MashapeKey)) | ||||||
|             { |             { | ||||||
|                 await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false); |                 await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false); | ||||||
|                 return; |                 return; | ||||||
| @@ -435,7 +443,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|             using (var http = new HttpClient()) |             using (var http = new HttpClient()) | ||||||
|             { |             { | ||||||
|                 http.DefaultRequestHeaders.Clear(); |                 http.DefaultRequestHeaders.Clear(); | ||||||
|                 http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey); |                 http.DefaultRequestHeaders.Add("X-Mashape-Key", _creds.MashapeKey); | ||||||
|                 http.DefaultRequestHeaders.Add("Accept", "text/plain"); |                 http.DefaultRequestHeaders.Add("Accept", "text/plain"); | ||||||
|                 var res = await http.GetStringAsync($"https://yoda.p.mashape.com/yoda?sentence={Uri.EscapeUriString(query)}").ConfigureAwait(false); |                 var res = await http.GetStringAsync($"https://yoda.p.mashape.com/yoda?sentence={Uri.EscapeUriString(query)}").ConfigureAwait(false); | ||||||
|                 try |                 try | ||||||
| @@ -457,7 +465,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|         [NadekoCommand, Usage, Description, Aliases] |         [NadekoCommand, Usage, Description, Aliases] | ||||||
|         public async Task UrbanDict([Remainder] string query = null) |         public async Task UrbanDict([Remainder] string query = null) | ||||||
|         { |         { | ||||||
|             if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey)) |             if (string.IsNullOrWhiteSpace(_creds.MashapeKey)) | ||||||
|             { |             { | ||||||
|                 await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false); |                 await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false); | ||||||
|                 return; |                 return; | ||||||
| @@ -531,7 +539,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|             if (string.IsNullOrWhiteSpace(query)) |             if (string.IsNullOrWhiteSpace(query)) | ||||||
|                 return; |                 return; | ||||||
|  |  | ||||||
|             if (string.IsNullOrWhiteSpace(NadekoBot.Credentials.MashapeKey)) |             if (string.IsNullOrWhiteSpace(_creds.MashapeKey)) | ||||||
|             { |             { | ||||||
|                 await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false); |                 await ReplyErrorLocalized("mashape_api_missing").ConfigureAwait(false); | ||||||
|                 return; |                 return; | ||||||
| @@ -542,7 +550,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|             using (var http = new HttpClient()) |             using (var http = new HttpClient()) | ||||||
|             { |             { | ||||||
|                 http.DefaultRequestHeaders.Clear(); |                 http.DefaultRequestHeaders.Clear(); | ||||||
|                 http.DefaultRequestHeaders.Add("X-Mashape-Key", NadekoBot.Credentials.MashapeKey); |                 http.DefaultRequestHeaders.Add("X-Mashape-Key", _creds.MashapeKey); | ||||||
|                 res = await http.GetStringAsync($"https://tagdef.p.mashape.com/one.{Uri.EscapeUriString(query)}.json").ConfigureAwait(false); |                 res = await http.GetStringAsync($"https://tagdef.p.mashape.com/one.{Uri.EscapeUriString(query)}.json").ConfigureAwait(false); | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -655,7 +663,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|                 usr = (IGuildUser)Context.User; |                 usr = (IGuildUser)Context.User; | ||||||
|  |  | ||||||
|             var avatarUrl = usr.RealAvatarUrl(); |             var avatarUrl = usr.RealAvatarUrl(); | ||||||
|             var shortenedAvatarUrl = await NadekoBot.Google.ShortenUrl(avatarUrl).ConfigureAwait(false); |             var shortenedAvatarUrl = await _google.ShortenUrl(avatarUrl).ConfigureAwait(false); | ||||||
|             await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() |             await Context.Channel.EmbedAsync(new EmbedBuilder().WithOkColor() | ||||||
|                 .AddField(efb => efb.WithName("Username").WithValue(usr.ToString()).WithIsInline(false)) |                 .AddField(efb => efb.WithName("Username").WithValue(usr.ToString()).WithIsInline(false)) | ||||||
|                 .AddField(efb => efb.WithName("Avatar Url").WithValue(shortenedAvatarUrl).WithIsInline(false)) |                 .AddField(efb => efb.WithName("Avatar Url").WithValue(shortenedAvatarUrl).WithIsInline(false)) | ||||||
| @@ -682,7 +690,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|                     var found = items["items"][0]; |                     var found = items["items"][0]; | ||||||
|                     var response = $@"`{GetText("title")}` {found["title"]} |                     var response = $@"`{GetText("title")}` {found["title"]} | ||||||
| `{GetText("quality")}` {found["quality"]} | `{GetText("quality")}` {found["quality"]} | ||||||
| `{GetText("url")}:` {await NadekoBot.Google.ShortenUrl(found["url"].ToString()).ConfigureAwait(false)}"; | `{GetText("url")}:` {await _google.ShortenUrl(found["url"].ToString()).ConfigureAwait(false)}"; | ||||||
|                     await Context.Channel.SendMessageAsync(response).ConfigureAwait(false); |                     await Context.Channel.SendMessageAsync(response).ConfigureAwait(false); | ||||||
|                 } |                 } | ||||||
|                 catch |                 catch | ||||||
| @@ -769,7 +777,7 @@ namespace NadekoBot.Modules.Searches | |||||||
|  |  | ||||||
|             tag = tag?.Trim() ?? ""; |             tag = tag?.Trim() ?? ""; | ||||||
|  |  | ||||||
|             var url = await InternalDapiSearch(tag, type).ConfigureAwait(false); |             var url = await _searches.DapiSearch(tag, type).ConfigureAwait(false); | ||||||
|  |  | ||||||
|             if (url == null) |             if (url == null) | ||||||
|                 await channel.SendErrorAsync(umsg.Author.Mention + " " + GetText("no_results")); |                 await channel.SendErrorAsync(umsg.Author.Mention + " " + GetText("no_results")); | ||||||
|   | |||||||
| @@ -21,6 +21,8 @@ using NadekoBot.Services.Searches; | |||||||
| using NadekoBot.Services.ClashOfClans; | using NadekoBot.Services.ClashOfClans; | ||||||
| using NadekoBot.Services.Music; | using NadekoBot.Services.Music; | ||||||
| using NadekoBot.Services.CustomReactions; | using NadekoBot.Services.CustomReactions; | ||||||
|  | using NadekoBot.Services.Games; | ||||||
|  | using NadekoBot.Services.Administration; | ||||||
|  |  | ||||||
| namespace NadekoBot | namespace NadekoBot | ||||||
| { | { | ||||||
| @@ -73,11 +75,10 @@ namespace NadekoBot | |||||||
|             }); |             }); | ||||||
|  |  | ||||||
|             var google = new GoogleApiService(credentials); |             var google = new GoogleApiService(credentials); | ||||||
|             var strings = new NadekoStrings(); |  | ||||||
|  |  | ||||||
|             var greetSettingsService = new GreetSettingsService(AllGuildConfigs, db); |  | ||||||
|  |  | ||||||
|             var localization = new Localization(BotConfig.Locale, AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale), db); |             var localization = new Localization(BotConfig.Locale, AllGuildConfigs.ToDictionary(x => x.GuildId, x => x.Locale), db); | ||||||
|  |             var strings = new NadekoStrings(localization); | ||||||
|  |  | ||||||
|  |             var greetSettingsService = new GreetSettingsService(Client, AllGuildConfigs, db); | ||||||
|  |  | ||||||
|             var commandService = new CommandService(new CommandServiceConfig() |             var commandService = new CommandService(new CommandServiceConfig() | ||||||
|             { |             { | ||||||
| @@ -97,10 +98,18 @@ namespace NadekoBot | |||||||
|  |  | ||||||
|             //module services |             //module services | ||||||
|             var utilityService = new UtilityService(AllGuildConfigs, Client, BotConfig, db); |             var utilityService = new UtilityService(AllGuildConfigs, Client, BotConfig, db); | ||||||
|             var searchesService = new SearchesService(); |             var searchesService = new SearchesService(Client, google, db); | ||||||
|             var clashService = new ClashOfClansService(Client, db, localization, strings); |             var clashService = new ClashOfClansService(Client, db, localization, strings); | ||||||
|             var musicService = new MusicService(google, strings, localization, db, soundcloud, credentials); |             var musicService = new MusicService(google, strings, localization, db, soundcloud, credentials); | ||||||
|             var crService = new CustomReactionsService(db, Client); |             var crService = new CustomReactionsService(db, Client); | ||||||
|  |             var gamesService = new GamesService(Client, BotConfig, AllGuildConfigs, strings, images); | ||||||
|  | #region administration | ||||||
|  |             var administrationService = new AdministrationService(AllGuildConfigs, commandHandler); | ||||||
|  |             var selfService = new SelfService(this, commandHandler, db, BotConfig); | ||||||
|  |             var vcRoleService = new VcRoleService(Client, AllGuildConfigs); | ||||||
|  |             var vPlusTService = new VplusTService(Client, AllGuildConfigs, strings, db); | ||||||
|  | #endregion | ||||||
|  |  | ||||||
|  |  | ||||||
|             //initialize Services |             //initialize Services | ||||||
|             Services = new NServiceProvider.ServiceProviderBuilder() //todo all Adds should be interfaces |             Services = new NServiceProvider.ServiceProviderBuilder() //todo all Adds should be interfaces | ||||||
| @@ -124,6 +133,10 @@ namespace NadekoBot | |||||||
|                 .Add<MusicService>(musicService) |                 .Add<MusicService>(musicService) | ||||||
|                 .Add<GreetSettingsService>(greetSettingsService) |                 .Add<GreetSettingsService>(greetSettingsService) | ||||||
|                 .Add<CustomReactionsService>(crService) |                 .Add<CustomReactionsService>(crService) | ||||||
|  |                 .Add<GamesService>(gamesService) | ||||||
|  |                 .Add(selfService) | ||||||
|  |                 .Add(vcRoleService) | ||||||
|  |                 .Add(vPlusTService) | ||||||
|                 .Build(); |                 .Build(); | ||||||
|  |  | ||||||
|             commandHandler.AddServices(Services); |             commandHandler.AddServices(Services); | ||||||
|   | |||||||
| @@ -29,21 +29,22 @@ | |||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|     <Compile Remove="data\**\*;credentials.json;credentials_example.json" /> |     <Compile Remove="data\**\*;credentials.json;credentials_example.json" /> | ||||||
|     <Compile Remove="Modules\Administration\**" /> |     <Compile Remove="Modules\Administration\**" /> | ||||||
|     <Compile Remove="Modules\Games\**" /> |  | ||||||
|     <Compile Remove="Modules\NSFW\**" /> |     <Compile Remove="Modules\NSFW\**" /> | ||||||
|     <Compile Remove="Modules\Permissions\**" /> |     <Compile Remove="Modules\Permissions\**" /> | ||||||
|     <Compile Remove="Modules\Searches\**" /> |  | ||||||
|     <EmbeddedResource Remove="Modules\Administration\**" /> |     <EmbeddedResource Remove="Modules\Administration\**" /> | ||||||
|     <EmbeddedResource Remove="Modules\Games\**" /> |  | ||||||
|     <EmbeddedResource Remove="Modules\NSFW\**" /> |     <EmbeddedResource Remove="Modules\NSFW\**" /> | ||||||
|     <EmbeddedResource Remove="Modules\Permissions\**" /> |     <EmbeddedResource Remove="Modules\Permissions\**" /> | ||||||
|     <EmbeddedResource Remove="Modules\Searches\**" /> |  | ||||||
|     <None Remove="Modules\Administration\**" /> |     <None Remove="Modules\Administration\**" /> | ||||||
|     <None Remove="Modules\Games\**" /> |  | ||||||
|     <None Remove="Modules\NSFW\**" /> |     <None Remove="Modules\NSFW\**" /> | ||||||
|     <None Remove="Modules\Permissions\**" /> |     <None Remove="Modules\Permissions\**" /> | ||||||
|     <None Remove="Modules\Searches\**" /> |  | ||||||
|     <Compile Remove="Modules\Gambling\Commands\Lucky7Commands.cs" /> |     <Compile Remove="Modules\Gambling\Commands\Lucky7Commands.cs" /> | ||||||
|  |     <Compile Include="Modules\Administration\Administration.cs" /> | ||||||
|  |     <Compile Include="Modules\Administration\Commands\MuteCommands.cs" /> | ||||||
|  |     <Compile Include="Modules\Administration\Commands\SelfCommands.cs" /> | ||||||
|  |     <Compile Include="Modules\Administration\Commands\ServerGreetCommands.cs" /> | ||||||
|  |     <Compile Include="Modules\Administration\Commands\UserPunishCommands.cs" /> | ||||||
|  |     <Compile Include="Modules\Administration\Commands\VcRoleCommands.cs" /> | ||||||
|  |     <Compile Include="Modules\Administration\Commands\VoicePlusTextCommands.cs" /> | ||||||
|     <None Update="libsodium.dll;opus.dll;libsodium.so;libopus.so"> |     <None Update="libsodium.dll;opus.dll;libsodium.so;libopus.so"> | ||||||
|       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> |       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> | ||||||
|     </None> |     </None> | ||||||
|   | |||||||
| @@ -0,0 +1,49 @@ | |||||||
|  | using Discord; | ||||||
|  | using Discord.Commands; | ||||||
|  | using Discord.WebSocket; | ||||||
|  | using NadekoBot.Services.Database.Models; | ||||||
|  | using NLog; | ||||||
|  | using System; | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Services.Administration | ||||||
|  | { | ||||||
|  |     public class AdministrationService | ||||||
|  |     { | ||||||
|  |         public readonly ConcurrentHashSet<ulong> DeleteMessagesOnCommand; | ||||||
|  |         private readonly Logger _log; | ||||||
|  |  | ||||||
|  |         public AdministrationService(IEnumerable<GuildConfig> gcs, CommandHandler cmdHandler) | ||||||
|  |         { | ||||||
|  |             _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |  | ||||||
|  |             DeleteMessagesOnCommand = new ConcurrentHashSet<ulong>(gcs.Where(g => g.DeleteMessageOnCommand).Select(g => g.GuildId)); | ||||||
|  |             cmdHandler.CommandExecuted += DelMsgOnCmd_Handler; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Task DelMsgOnCmd_Handler(IUserMessage msg, CommandInfo cmd) | ||||||
|  |         { | ||||||
|  |             var _ = Task.Run(async () => | ||||||
|  |             { | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     var channel = msg.Channel as SocketTextChannel; | ||||||
|  |                     if (channel == null) | ||||||
|  |                         return; | ||||||
|  |                     if (DeleteMessagesOnCommand.Contains(channel.Guild.Id) && cmd.Name != "prune" && cmd.Name != "pick") | ||||||
|  |                         await msg.DeleteAsync().ConfigureAwait(false); | ||||||
|  |                 } | ||||||
|  |                 catch (Exception ex) | ||||||
|  |                 { | ||||||
|  |                     _log.Warn("Delmsgoncmd errored..."); | ||||||
|  |                     _log.Warn(ex); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             return Task.CompletedTask; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										275
									
								
								src/NadekoBot/Services/Administration/MuteService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								src/NadekoBot/Services/Administration/MuteService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,275 @@ | |||||||
|  | using Discord; | ||||||
|  | using Discord.WebSocket; | ||||||
|  | using Microsoft.EntityFrameworkCore; | ||||||
|  | using NadekoBot.Services.Database.Models; | ||||||
|  | using NLog; | ||||||
|  | using System; | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Threading; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Services.Administration | ||||||
|  | { | ||||||
|  |     public class MuteService | ||||||
|  |     { | ||||||
|  |         public ConcurrentDictionary<ulong, string> GuildMuteRoles { get; } | ||||||
|  |         public ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>> MutedUsers { get; } | ||||||
|  |         public ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, Timer>> UnmuteTimers { get; } | ||||||
|  |             = new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, Timer>>(); | ||||||
|  |  | ||||||
|  |         public event Action<IGuildUser, MuteType> UserMuted = delegate { }; | ||||||
|  |         public event Action<IGuildUser, MuteType> UserUnmuted = delegate { }; | ||||||
|  |  | ||||||
|  |         private static readonly OverwritePermissions denyOverwrite = new OverwritePermissions(sendMessages: PermValue.Deny, attachFiles: PermValue.Deny); | ||||||
|  |  | ||||||
|  |         private readonly Logger _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |         private readonly DiscordShardedClient _client; | ||||||
|  |         private readonly DbHandler _db; | ||||||
|  |  | ||||||
|  |         public MuteService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs, DbHandler db) | ||||||
|  |         { | ||||||
|  |             _client = client; | ||||||
|  |             _db = db; | ||||||
|  |  | ||||||
|  |             GuildMuteRoles = new ConcurrentDictionary<ulong, string>(gcs | ||||||
|  |                     .Where(c => !string.IsNullOrWhiteSpace(c.MuteRoleName)) | ||||||
|  |                     .ToDictionary(c => c.GuildId, c => c.MuteRoleName)); | ||||||
|  |  | ||||||
|  |             MutedUsers = new ConcurrentDictionary<ulong, ConcurrentHashSet<ulong>>(gcs.ToDictionary( | ||||||
|  |                 k => k.GuildId, | ||||||
|  |                 v => new ConcurrentHashSet<ulong>(v.MutedUsers.Select(m => m.UserId)) | ||||||
|  |             )); | ||||||
|  |  | ||||||
|  |             foreach (var conf in gcs) | ||||||
|  |             { | ||||||
|  |                 foreach (var x in conf.UnmuteTimers) | ||||||
|  |                 { | ||||||
|  |                     TimeSpan after; | ||||||
|  |                     if (x.UnmuteAt - TimeSpan.FromMinutes(2) <= DateTime.UtcNow) | ||||||
|  |                     { | ||||||
|  |                         after = TimeSpan.FromMinutes(2); | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         after = x.UnmuteAt - DateTime.UtcNow; | ||||||
|  |                     } | ||||||
|  |                     StartUnmuteTimer(conf.GuildId, x.UserId, after); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             _client.UserJoined += Client_UserJoined; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private async Task Client_UserJoined(IGuildUser usr) | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 MutedUsers.TryGetValue(usr.Guild.Id, out ConcurrentHashSet<ulong> muted); | ||||||
|  |  | ||||||
|  |                 if (muted == null || !muted.Contains(usr.Id)) | ||||||
|  |                     return; | ||||||
|  |                 await MuteUser(usr).ConfigureAwait(false); | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 LogManager.GetCurrentClassLogger().Warn(ex); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public async Task MuteUser(IGuildUser usr, MuteType type = MuteType.All) | ||||||
|  |         { | ||||||
|  |             if (type == MuteType.All) | ||||||
|  |             { | ||||||
|  |                 await usr.ModifyAsync(x => x.Mute = true).ConfigureAwait(false); | ||||||
|  |                 var muteRole = await GetMuteRole(usr.Guild); | ||||||
|  |                 if (!usr.RoleIds.Contains(muteRole.Id)) | ||||||
|  |                     await usr.AddRoleAsync(muteRole).ConfigureAwait(false); | ||||||
|  |                 StopUnmuteTimer(usr.GuildId, usr.Id); | ||||||
|  |                 using (var uow = _db.UnitOfWork) | ||||||
|  |                 { | ||||||
|  |                     var config = uow.GuildConfigs.For(usr.Guild.Id, | ||||||
|  |                         set => set.Include(gc => gc.MutedUsers) | ||||||
|  |                             .Include(gc => gc.UnmuteTimers)); | ||||||
|  |                     config.MutedUsers.Add(new MutedUserId() | ||||||
|  |                     { | ||||||
|  |                         UserId = usr.Id | ||||||
|  |                     }); | ||||||
|  |                     if (MutedUsers.TryGetValue(usr.Guild.Id, out ConcurrentHashSet<ulong> muted)) | ||||||
|  |                         muted.Add(usr.Id); | ||||||
|  |  | ||||||
|  |                     config.UnmuteTimers.RemoveWhere(x => x.UserId == usr.Id); | ||||||
|  |  | ||||||
|  |                     await uow.CompleteAsync().ConfigureAwait(false); | ||||||
|  |                 } | ||||||
|  |                 UserMuted(usr, MuteType.All); | ||||||
|  |             } | ||||||
|  |             else if (type == MuteType.Voice) | ||||||
|  |             { | ||||||
|  |                 await usr.ModifyAsync(x => x.Mute = true).ConfigureAwait(false); | ||||||
|  |                 UserMuted(usr, MuteType.Voice); | ||||||
|  |             } | ||||||
|  |             else if (type == MuteType.Chat) | ||||||
|  |             { | ||||||
|  |                 await usr.AddRoleAsync(await GetMuteRole(usr.Guild).ConfigureAwait(false)).ConfigureAwait(false); | ||||||
|  |                 UserMuted(usr, MuteType.Chat); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public async Task UnmuteUser(IGuildUser usr, MuteType type = MuteType.All) | ||||||
|  |         { | ||||||
|  |             if (type == MuteType.All) | ||||||
|  |             { | ||||||
|  |                 StopUnmuteTimer(usr.GuildId, usr.Id); | ||||||
|  |                 try { await usr.ModifyAsync(x => x.Mute = false).ConfigureAwait(false); } catch { } | ||||||
|  |                 try { await usr.RemoveRoleAsync(await GetMuteRole(usr.Guild)).ConfigureAwait(false); } catch { /*ignore*/ } | ||||||
|  |                 using (var uow = _db.UnitOfWork) | ||||||
|  |                 { | ||||||
|  |                     var config = uow.GuildConfigs.For(usr.Guild.Id, set => set.Include(gc => gc.MutedUsers) | ||||||
|  |                         .Include(gc => gc.UnmuteTimers)); | ||||||
|  |                     config.MutedUsers.Remove(new MutedUserId() | ||||||
|  |                     { | ||||||
|  |                         UserId = usr.Id | ||||||
|  |                     }); | ||||||
|  |                     if (MutedUsers.TryGetValue(usr.Guild.Id, out ConcurrentHashSet<ulong> muted)) | ||||||
|  |                         muted.TryRemove(usr.Id); | ||||||
|  |  | ||||||
|  |                     config.UnmuteTimers.RemoveWhere(x => x.UserId == usr.Id); | ||||||
|  |  | ||||||
|  |                     await uow.CompleteAsync().ConfigureAwait(false); | ||||||
|  |                 } | ||||||
|  |                 UserUnmuted(usr, MuteType.All); | ||||||
|  |             } | ||||||
|  |             else if (type == MuteType.Voice) | ||||||
|  |             { | ||||||
|  |                 await usr.ModifyAsync(x => x.Mute = false).ConfigureAwait(false); | ||||||
|  |                 UserUnmuted(usr, MuteType.Voice); | ||||||
|  |             } | ||||||
|  |             else if (type == MuteType.Chat) | ||||||
|  |             { | ||||||
|  |                 await usr.RemoveRoleAsync(await GetMuteRole(usr.Guild).ConfigureAwait(false)).ConfigureAwait(false); | ||||||
|  |                 UserUnmuted(usr, MuteType.Chat); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public async Task<IRole> GetMuteRole(IGuild guild) | ||||||
|  |         { | ||||||
|  |             const string defaultMuteRoleName = "nadeko-mute"; | ||||||
|  |  | ||||||
|  |             var muteRoleName = GuildMuteRoles.GetOrAdd(guild.Id, defaultMuteRoleName); | ||||||
|  |  | ||||||
|  |             var muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName); | ||||||
|  |             if (muteRole == null) | ||||||
|  |             { | ||||||
|  |  | ||||||
|  |                 //if it doesn't exist, create it  | ||||||
|  |                 try { muteRole = await guild.CreateRoleAsync(muteRoleName, GuildPermissions.None).ConfigureAwait(false); } | ||||||
|  |                 catch | ||||||
|  |                 { | ||||||
|  |                     //if creations fails,  maybe the name is not correct, find default one, if doesn't work, create default one | ||||||
|  |                     muteRole = guild.Roles.FirstOrDefault(r => r.Name == muteRoleName) ?? | ||||||
|  |                         await guild.CreateRoleAsync(defaultMuteRoleName, GuildPermissions.None).ConfigureAwait(false); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             foreach (var toOverwrite in (await guild.GetTextChannelsAsync())) | ||||||
|  |             { | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     if (!toOverwrite.PermissionOverwrites.Select(x => x.Permissions).Contains(denyOverwrite)) | ||||||
|  |                     { | ||||||
|  |                         await toOverwrite.AddPermissionOverwriteAsync(muteRole, denyOverwrite) | ||||||
|  |                                 .ConfigureAwait(false); | ||||||
|  |  | ||||||
|  |                         await Task.Delay(200).ConfigureAwait(false); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 catch | ||||||
|  |                 { | ||||||
|  |                     // ignored | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return muteRole; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public async Task TimedMute(IGuildUser user, TimeSpan after) | ||||||
|  |         { | ||||||
|  |             await MuteUser(user).ConfigureAwait(false); // mute the user. This will also remove any previous unmute timers | ||||||
|  |             using (var uow = _db.UnitOfWork) | ||||||
|  |             { | ||||||
|  |                 var config = uow.GuildConfigs.For(user.GuildId, set => set.Include(x => x.UnmuteTimers)); | ||||||
|  |                 config.UnmuteTimers.Add(new UnmuteTimer() | ||||||
|  |                 { | ||||||
|  |                     UserId = user.Id, | ||||||
|  |                     UnmuteAt = DateTime.UtcNow + after, | ||||||
|  |                 }); // add teh unmute timer to the database | ||||||
|  |                 uow.Complete(); | ||||||
|  |             } | ||||||
|  |             StartUnmuteTimer(user.GuildId, user.Id, after); // start the timer | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void StartUnmuteTimer(ulong guildId, ulong userId, TimeSpan after) | ||||||
|  |         { | ||||||
|  |             //load the unmute timers for this guild | ||||||
|  |             var userUnmuteTimers = UnmuteTimers.GetOrAdd(guildId, new ConcurrentDictionary<ulong, Timer>()); | ||||||
|  |  | ||||||
|  |             //unmute timer to be added | ||||||
|  |             var toAdd = new Timer(async _ => | ||||||
|  |             { | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     var guild = _client.GetGuild(guildId); // load the guild | ||||||
|  |                     if (guild == null) | ||||||
|  |                     { | ||||||
|  |                         RemoveUnmuteTimerFromDb(guildId, userId); | ||||||
|  |                         return; // if guild can't be found, just remove the timer from db | ||||||
|  |                     } | ||||||
|  |                     // unmute the user, this will also remove the timer from the db | ||||||
|  |                     await UnmuteUser(guild.GetUser(userId)).ConfigureAwait(false); | ||||||
|  |                 } | ||||||
|  |                 catch (Exception ex) | ||||||
|  |                 { | ||||||
|  |                     RemoveUnmuteTimerFromDb(guildId, userId); // if unmute errored, just remove unmute from db | ||||||
|  |                     _log.Warn("Couldn't unmute user {0} in guild {1}", userId, guildId); | ||||||
|  |                     _log.Warn(ex); | ||||||
|  |                 } | ||||||
|  |             }, null, after, Timeout.InfiniteTimeSpan); | ||||||
|  |  | ||||||
|  |             //add it, or stop the old one and add this one | ||||||
|  |             userUnmuteTimers.AddOrUpdate(userId, (key) => toAdd, (key, old) => | ||||||
|  |             { | ||||||
|  |                 old.Change(Timeout.Infinite, Timeout.Infinite); | ||||||
|  |                 return toAdd; | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public void StopUnmuteTimer(ulong guildId, ulong userId) | ||||||
|  |         { | ||||||
|  |             if (!UnmuteTimers.TryGetValue(guildId, out ConcurrentDictionary<ulong, Timer> userUnmuteTimers)) return; | ||||||
|  |  | ||||||
|  |             if (userUnmuteTimers.TryRemove(userId, out Timer removed)) | ||||||
|  |             { | ||||||
|  |                 removed.Change(Timeout.Infinite, Timeout.Infinite); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private void RemoveUnmuteTimerFromDb(ulong guildId, ulong userId) | ||||||
|  |         { | ||||||
|  |             using (var uow = _db.UnitOfWork) | ||||||
|  |             { | ||||||
|  |                 var config = uow.GuildConfigs.For(guildId, set => set.Include(x => x.UnmuteTimers)); | ||||||
|  |                 config.UnmuteTimers.RemoveWhere(x => x.UserId == userId); | ||||||
|  |                 uow.Complete(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public enum MuteType | ||||||
|  |     { | ||||||
|  |         Voice, | ||||||
|  |         Chat, | ||||||
|  |         All | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								src/NadekoBot/Services/Administration/SelfService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/NadekoBot/Services/Administration/SelfService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | using NadekoBot.Services.Database.Models; | ||||||
|  | using NLog; | ||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Services.Administration | ||||||
|  | { | ||||||
|  |     public class SelfService | ||||||
|  |     { | ||||||
|  |         public volatile bool ForwardDMs; | ||||||
|  |         public volatile bool ForwardDMsToAllOwners; | ||||||
|  |  | ||||||
|  |         private readonly Logger _log; | ||||||
|  |         private readonly NadekoBot _bot; | ||||||
|  |         private readonly CommandHandler _cmdHandler; | ||||||
|  |         private readonly DbHandler _db; | ||||||
|  |  | ||||||
|  |         public SelfService(NadekoBot bot, CommandHandler cmdHandler, DbHandler db, | ||||||
|  |             BotConfig bc) | ||||||
|  |         { | ||||||
|  |             _bot = bot; | ||||||
|  |             _cmdHandler = cmdHandler; | ||||||
|  |             _db = db; | ||||||
|  |  | ||||||
|  |             using (var uow = _db.UnitOfWork) | ||||||
|  |             { | ||||||
|  |                 var config = uow.BotConfig.GetOrCreate(); | ||||||
|  |                 ForwardDMs = config.ForwardMessages; | ||||||
|  |                 ForwardDMsToAllOwners = config.ForwardToAllOwners; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             var _ = Task.Run(async () => | ||||||
|  |             { | ||||||
|  |                 while (!bot.Ready) | ||||||
|  |                     await Task.Delay(1000); | ||||||
|  |  | ||||||
|  |                 foreach (var cmd in bc.StartupCommands) | ||||||
|  |                 { | ||||||
|  |                     await cmdHandler.ExecuteExternal(cmd.GuildId, cmd.ChannelId, cmd.CommandText); | ||||||
|  |                     await Task.Delay(400).ConfigureAwait(false); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										102
									
								
								src/NadekoBot/Services/Administration/VcRoleService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/NadekoBot/Services/Administration/VcRoleService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | |||||||
|  | using Discord; | ||||||
|  | using Discord.WebSocket; | ||||||
|  | using NadekoBot.Services.Database.Models; | ||||||
|  | using NLog; | ||||||
|  | using System; | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Services.Administration | ||||||
|  | { | ||||||
|  |     public class VcRoleService | ||||||
|  |     { | ||||||
|  |         private readonly Logger _log; | ||||||
|  |  | ||||||
|  |         public ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>> VcRoles { get; } | ||||||
|  |  | ||||||
|  |         public VcRoleService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs) | ||||||
|  |         { | ||||||
|  |             _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |  | ||||||
|  |             client.UserVoiceStateUpdated += ClientOnUserVoiceStateUpdated; | ||||||
|  |             VcRoles = new ConcurrentDictionary<ulong, ConcurrentDictionary<ulong, IRole>>(); | ||||||
|  |             foreach (var gconf in gcs) | ||||||
|  |             { | ||||||
|  |                 var g = client.GetGuild(gconf.GuildId); | ||||||
|  |                 if (g == null) | ||||||
|  |                     continue; //todo delete everything from db if guild doesn't exist? | ||||||
|  |  | ||||||
|  |                 var infos = new ConcurrentDictionary<ulong, IRole>(); | ||||||
|  |                 VcRoles.TryAdd(gconf.GuildId, infos); | ||||||
|  |                 foreach (var ri in gconf.VcRoleInfos) | ||||||
|  |                 { | ||||||
|  |                     var role = g.GetRole(ri.RoleId); | ||||||
|  |                     if (role == null) | ||||||
|  |                         continue; //todo remove this entry from db | ||||||
|  |  | ||||||
|  |                     infos.TryAdd(ri.VoiceChannelId, role); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Task ClientOnUserVoiceStateUpdated(SocketUser usr, SocketVoiceState oldState, | ||||||
|  |             SocketVoiceState newState) | ||||||
|  |         { | ||||||
|  |  | ||||||
|  |             var gusr = usr as SocketGuildUser; | ||||||
|  |             if (gusr == null) | ||||||
|  |                 return Task.CompletedTask; | ||||||
|  |  | ||||||
|  |             var oldVc = oldState.VoiceChannel; | ||||||
|  |             var newVc = newState.VoiceChannel; | ||||||
|  |             var _ = Task.Run(async () => | ||||||
|  |             { | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     if (oldVc != newVc) | ||||||
|  |                     { | ||||||
|  |                         ulong guildId; | ||||||
|  |                         guildId = newVc?.Guild.Id ?? oldVc.Guild.Id; | ||||||
|  |  | ||||||
|  |                         if (VcRoles.TryGetValue(guildId, out ConcurrentDictionary<ulong, IRole> guildVcRoles)) | ||||||
|  |                         { | ||||||
|  |                             //remove old | ||||||
|  |                             if (oldVc != null && guildVcRoles.TryGetValue(oldVc.Id, out IRole role)) | ||||||
|  |                             { | ||||||
|  |                                 if (gusr.Roles.Contains(role)) | ||||||
|  |                                 { | ||||||
|  |                                     try | ||||||
|  |                                     { | ||||||
|  |                                         await gusr.RemoveRoleAsync(role).ConfigureAwait(false); | ||||||
|  |                                         await Task.Delay(500).ConfigureAwait(false); | ||||||
|  |                                     } | ||||||
|  |                                     catch | ||||||
|  |                                     { | ||||||
|  |                                         await Task.Delay(200).ConfigureAwait(false); | ||||||
|  |                                         await gusr.RemoveRoleAsync(role).ConfigureAwait(false); | ||||||
|  |                                         await Task.Delay(500).ConfigureAwait(false); | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                             //add new | ||||||
|  |                             if (newVc != null && guildVcRoles.TryGetValue(newVc.Id, out role)) | ||||||
|  |                             { | ||||||
|  |                                 if (!gusr.Roles.Contains(role)) | ||||||
|  |                                     await gusr.AddRoleAsync(role).ConfigureAwait(false); | ||||||
|  |                             } | ||||||
|  |  | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 catch (Exception ex) | ||||||
|  |                 { | ||||||
|  |                     _log.Warn(ex); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             return Task.CompletedTask; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										157
									
								
								src/NadekoBot/Services/Administration/VplusTService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								src/NadekoBot/Services/Administration/VplusTService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | |||||||
|  | using Discord; | ||||||
|  | using Discord.WebSocket; | ||||||
|  | using NadekoBot.Extensions; | ||||||
|  | using NadekoBot.Services.Database.Models; | ||||||
|  | using NLog; | ||||||
|  | using System; | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Text; | ||||||
|  | using System.Text.RegularExpressions; | ||||||
|  | using System.Threading; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Services.Administration | ||||||
|  | { | ||||||
|  |     public class VplusTService | ||||||
|  |     { | ||||||
|  |         private readonly Regex _channelNameRegex = new Regex(@"[^a-zA-Z0-9 -]", RegexOptions.Compiled); | ||||||
|  |  | ||||||
|  |         public readonly ConcurrentHashSet<ulong> VoicePlusTextCache; | ||||||
|  |  | ||||||
|  |         private readonly ConcurrentDictionary<ulong, SemaphoreSlim> _guildLockObjects = new ConcurrentDictionary<ulong, SemaphoreSlim>(); | ||||||
|  |         private readonly DiscordShardedClient _client; | ||||||
|  |         private readonly NadekoStrings _strings; | ||||||
|  |         private readonly DbHandler _db; | ||||||
|  |         private readonly Logger _log; | ||||||
|  |  | ||||||
|  |         public VplusTService(DiscordShardedClient client, IEnumerable<GuildConfig> gcs, NadekoStrings strings, | ||||||
|  |             DbHandler db) | ||||||
|  |         { | ||||||
|  |             _client = client; | ||||||
|  |             _strings = strings; | ||||||
|  |             _db = db; | ||||||
|  |             _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |  | ||||||
|  |             VoicePlusTextCache = new ConcurrentHashSet<ulong>(gcs.Where(g => g.VoicePlusTextEnabled).Select(g => g.GuildId)); | ||||||
|  |             _client.UserVoiceStateUpdated += UserUpdatedEventHandler; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Task UserUpdatedEventHandler(SocketUser iuser, SocketVoiceState before, SocketVoiceState after) | ||||||
|  |         { | ||||||
|  |             var user = (iuser as SocketGuildUser); | ||||||
|  |             var guild = user?.Guild; | ||||||
|  |  | ||||||
|  |             if (guild == null) | ||||||
|  |                 return Task.CompletedTask; | ||||||
|  |  | ||||||
|  |             var botUserPerms = guild.CurrentUser.GuildPermissions; | ||||||
|  |  | ||||||
|  |             if (before.VoiceChannel == after.VoiceChannel) | ||||||
|  |                 return Task.CompletedTask; | ||||||
|  |  | ||||||
|  |             if (!VoicePlusTextCache.Contains(guild.Id)) | ||||||
|  |                 return Task.CompletedTask; | ||||||
|  |  | ||||||
|  |             var _ = Task.Run(async () => | ||||||
|  |             { | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |  | ||||||
|  |                     if (!botUserPerms.ManageChannels || !botUserPerms.ManageRoles) | ||||||
|  |                     { | ||||||
|  |                         try | ||||||
|  |                         { | ||||||
|  |                             await guild.Owner.SendErrorAsync( | ||||||
|  |                                 _strings.GetText("vt_exit", | ||||||
|  |                                     guild.Id, | ||||||
|  |                                     "Administration".ToLowerInvariant(), | ||||||
|  |                                     Format.Bold(guild.Name))).ConfigureAwait(false); | ||||||
|  |                         } | ||||||
|  |                         catch | ||||||
|  |                         { | ||||||
|  |                             // ignored | ||||||
|  |                         } | ||||||
|  |                         using (var uow = _db.UnitOfWork) | ||||||
|  |                         { | ||||||
|  |                             uow.GuildConfigs.For(guild.Id, set => set).VoicePlusTextEnabled = false; | ||||||
|  |                             VoicePlusTextCache.TryRemove(guild.Id); | ||||||
|  |                             await uow.CompleteAsync().ConfigureAwait(false); | ||||||
|  |                         } | ||||||
|  |                         return; | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     var semaphore = _guildLockObjects.GetOrAdd(guild.Id, (key) => new SemaphoreSlim(1, 1)); | ||||||
|  |  | ||||||
|  |                     try | ||||||
|  |                     { | ||||||
|  |                         await semaphore.WaitAsync().ConfigureAwait(false); | ||||||
|  |  | ||||||
|  |                         var beforeVch = before.VoiceChannel; | ||||||
|  |                         if (beforeVch != null) | ||||||
|  |                         { | ||||||
|  |                             var beforeRoleName = GetRoleName(beforeVch); | ||||||
|  |                             var beforeRole = guild.Roles.FirstOrDefault(x => x.Name == beforeRoleName); | ||||||
|  |                             if (beforeRole != null) | ||||||
|  |                                 try | ||||||
|  |                                 { | ||||||
|  |                                     _log.Info("Removing role " + beforeRoleName + " from user " + user.Username); | ||||||
|  |                                     await user.RemoveRoleAsync(beforeRole).ConfigureAwait(false); | ||||||
|  |                                     await Task.Delay(200).ConfigureAwait(false); | ||||||
|  |                                 } | ||||||
|  |                                 catch (Exception ex) | ||||||
|  |                                 { | ||||||
|  |                                     _log.Warn(ex); | ||||||
|  |                                 } | ||||||
|  |                         } | ||||||
|  |                         var afterVch = after.VoiceChannel; | ||||||
|  |                         if (afterVch != null && guild.AFKChannel?.Id != afterVch.Id) | ||||||
|  |                         { | ||||||
|  |                             var roleName = GetRoleName(afterVch); | ||||||
|  |                             var roleToAdd = guild.Roles.FirstOrDefault(x => x.Name == roleName) ?? | ||||||
|  |                                               (IRole)await guild.CreateRoleAsync(roleName, GuildPermissions.None).ConfigureAwait(false); | ||||||
|  |  | ||||||
|  |                             ITextChannel textChannel = guild.TextChannels | ||||||
|  |                                                         .FirstOrDefault(t => t.Name == GetChannelName(afterVch.Name).ToLowerInvariant()); | ||||||
|  |                             if (textChannel == null) | ||||||
|  |                             { | ||||||
|  |                                 var created = (await guild.CreateTextChannelAsync(GetChannelName(afterVch.Name).ToLowerInvariant()).ConfigureAwait(false)); | ||||||
|  |  | ||||||
|  |                                 try { await guild.CurrentUser.AddRoleAsync(roleToAdd).ConfigureAwait(false); } catch {/*ignored*/} | ||||||
|  |                                 await Task.Delay(50).ConfigureAwait(false); | ||||||
|  |                                 await created.AddPermissionOverwriteAsync(roleToAdd, new OverwritePermissions( | ||||||
|  |                                     readMessages: PermValue.Allow, | ||||||
|  |                                     sendMessages: PermValue.Allow)) | ||||||
|  |                                         .ConfigureAwait(false); | ||||||
|  |                                 await Task.Delay(50).ConfigureAwait(false); | ||||||
|  |                                 await created.AddPermissionOverwriteAsync(guild.EveryoneRole, new OverwritePermissions( | ||||||
|  |                                     readMessages: PermValue.Deny, | ||||||
|  |                                     sendMessages: PermValue.Deny)) | ||||||
|  |                                         .ConfigureAwait(false); | ||||||
|  |                                 await Task.Delay(50).ConfigureAwait(false); | ||||||
|  |                             } | ||||||
|  |                             _log.Warn("Adding role " + roleToAdd.Name + " to user " + user.Username); | ||||||
|  |                             await user.AddRoleAsync(roleToAdd).ConfigureAwait(false); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     finally | ||||||
|  |                     { | ||||||
|  |                         semaphore.Release(); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 catch (Exception ex) | ||||||
|  |                 { | ||||||
|  |                     _log.Warn(ex); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             return Task.CompletedTask; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public string GetChannelName(string voiceName) => | ||||||
|  |             _channelNameRegex.Replace(voiceName, "").Trim().Replace(" ", "-").TrimTo(90, true) + "-voice"; | ||||||
|  |  | ||||||
|  |         public string GetRoleName(IVoiceChannel ch) => | ||||||
|  |             "nvoice-" + ch.Id; | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -544,7 +544,7 @@ namespace NadekoBot.Services | |||||||
|                     //////int price; |                     //////int price; | ||||||
|                     //////if (Permissions.CommandCostCommands.CommandCosts.TryGetValue(cmd.Aliases.First().Trim().ToLowerInvariant(), out price) && price > 0) |                     //////if (Permissions.CommandCostCommands.CommandCosts.TryGetValue(cmd.Aliases.First().Trim().ToLowerInvariant(), out price) && price > 0) | ||||||
|                     //////{ |                     //////{ | ||||||
|                     //////    var success = await CurrencyHandler.RemoveCurrencyAsync(context.User.Id, $"Running {cmd.Name} command.", price).ConfigureAwait(false); |                     //////    var success = await _ch.RemoveCurrencyAsync(context.User.Id, $"Running {cmd.Name} command.", price).ConfigureAwait(false); | ||||||
|                     //////    if (!success) |                     //////    if (!success) | ||||||
|                     //////    { |                     //////    { | ||||||
|                     //////        return new ExecuteCommandResult(cmd, pc, SearchResult.FromError(CommandError.Exception, $"Insufficient funds. You need {price}{NadekoBot.BotConfig.CurrencySign} to run this command.")); |                     //////        return new ExecuteCommandResult(cmd, pc, SearchResult.FromError(CommandError.Exception, $"Insufficient funds. You need {price}{NadekoBot.BotConfig.CurrencySign} to run this command.")); | ||||||
|   | |||||||
| @@ -19,7 +19,7 @@ namespace NadekoBot.Services | |||||||
|             var optionsBuilder = new DbContextOptionsBuilder(); |             var optionsBuilder = new DbContextOptionsBuilder(); | ||||||
|             optionsBuilder.UseSqlite(creds.Db.ConnectionString); |             optionsBuilder.UseSqlite(creds.Db.ConnectionString); | ||||||
|             options = optionsBuilder.Options; |             options = optionsBuilder.Options; | ||||||
|             //switch (NadekoBot.Credentials.Db.Type.ToUpperInvariant()) |             //switch (_creds.Db.Type.ToUpperInvariant()) | ||||||
|             //{ |             //{ | ||||||
|             //    case "SQLITE": |             //    case "SQLITE": | ||||||
|             //        dbType = typeof(NadekoSqliteContext); |             //        dbType = typeof(NadekoSqliteContext); | ||||||
|   | |||||||
							
								
								
									
										8
									
								
								src/NadekoBot/Services/Games/ChatterBotResponse.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/NadekoBot/Services/Games/ChatterBotResponse.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | namespace NadekoBot.Services.Games | ||||||
|  | { | ||||||
|  |     public class ChatterBotResponse | ||||||
|  |     { | ||||||
|  |         public string Convo_id { get; set; } | ||||||
|  |         public string BotSay { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								src/NadekoBot/Services/Games/ChatterBotSession.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/NadekoBot/Services/Games/ChatterBotSession.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | |||||||
|  | using NadekoBot.Extensions; | ||||||
|  | using Newtonsoft.Json; | ||||||
|  | using System.Collections.Immutable; | ||||||
|  | using System.Net.Http; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Services.Games | ||||||
|  | { | ||||||
|  |     public class ChatterBotSession | ||||||
|  |     { | ||||||
|  |         private static NadekoRandom rng { get; } = new NadekoRandom(); | ||||||
|  |         public string ChatterbotId { get; } | ||||||
|  |         public string ChannelId { get; } | ||||||
|  |         private int _botId = 6; | ||||||
|  |  | ||||||
|  |         public ChatterBotSession(ulong channelId) | ||||||
|  |         { | ||||||
|  |             ChannelId = channelId.ToString().ToBase64(); | ||||||
|  |             ChatterbotId = rng.Next(0, 1000000).ToString().ToBase64(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private string apiEndpoint => "http://api.program-o.com/v2/chatbot/" + | ||||||
|  |                                       $"?bot_id={_botId}&" + | ||||||
|  |                                       "say={0}&" + | ||||||
|  |                                       $"convo_id=nadekobot_{ChatterbotId}_{ChannelId}&" + | ||||||
|  |                                       "format=json"; | ||||||
|  |  | ||||||
|  |         public async Task<string> Think(string message) | ||||||
|  |         { | ||||||
|  |             using (var http = new HttpClient()) | ||||||
|  |             { | ||||||
|  |                 var res = await http.GetStringAsync(string.Format(apiEndpoint, message)).ConfigureAwait(false); | ||||||
|  |                 var cbr = JsonConvert.DeserializeObject<ChatterBotResponse>(res); | ||||||
|  |                 //Console.WriteLine(cbr.Convo_id); | ||||||
|  |                 return cbr.BotSay.Replace("<br/>", "\n"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										206
									
								
								src/NadekoBot/Services/Games/GamesService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								src/NadekoBot/Services/Games/GamesService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,206 @@ | |||||||
|  | using Discord; | ||||||
|  | using Discord.WebSocket; | ||||||
|  | using NadekoBot.Extensions; | ||||||
|  | using NadekoBot.Services.Database.Models; | ||||||
|  | using Newtonsoft.Json; | ||||||
|  | using NLog; | ||||||
|  | using System; | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Collections.Immutable; | ||||||
|  | using System.IO; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Threading; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Services.Games | ||||||
|  | { | ||||||
|  |     public class GamesService | ||||||
|  |     { | ||||||
|  |         private readonly BotConfig _bc; | ||||||
|  |  | ||||||
|  |         public readonly ConcurrentDictionary<ulong, GirlRating> GirlRatings = new ConcurrentDictionary<ulong, GirlRating>(); | ||||||
|  |         public readonly ImmutableArray<string> EightBallResponses; | ||||||
|  |  | ||||||
|  |         private readonly Timer _t; | ||||||
|  |         private readonly DiscordShardedClient _client; | ||||||
|  |         private readonly NadekoStrings _strings; | ||||||
|  |         private readonly IImagesService _images; | ||||||
|  |         private readonly Logger _log; | ||||||
|  |  | ||||||
|  |         public ConcurrentDictionary<ulong, Lazy<ChatterBotSession>> CleverbotGuilds { get; } | ||||||
|  |  | ||||||
|  |         public readonly string TypingArticlesPath = "data/typing_articles2.json"; | ||||||
|  |         public List<TypingArticle> TypingArticles { get; } = new List<TypingArticle>(); | ||||||
|  |  | ||||||
|  |         public GamesService(DiscordShardedClient client, BotConfig bc, IEnumerable<GuildConfig> gcs,  | ||||||
|  |             NadekoStrings strings, IImagesService images) | ||||||
|  |         { | ||||||
|  |             _bc = bc; | ||||||
|  |             _client = client; | ||||||
|  |             _strings = strings; | ||||||
|  |             _images = images; | ||||||
|  |             _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |  | ||||||
|  |             //8ball | ||||||
|  |             EightBallResponses = _bc.EightBallResponses.Select(ebr => ebr.Text).ToImmutableArray(); | ||||||
|  |  | ||||||
|  |             //girl ratings | ||||||
|  |             _t = new Timer((_) => | ||||||
|  |             { | ||||||
|  |                 GirlRatings.Clear(); | ||||||
|  |  | ||||||
|  |             }, null, TimeSpan.FromDays(1), TimeSpan.FromDays(1)); | ||||||
|  |  | ||||||
|  |             //cleverbot | ||||||
|  |             CleverbotGuilds = new ConcurrentDictionary<ulong, Lazy<ChatterBotSession>>( | ||||||
|  |                     gcs.Where(gc => gc.CleverbotEnabled) | ||||||
|  |                         .ToDictionary(gc => gc.GuildId, gc => new Lazy<ChatterBotSession>(() => new ChatterBotSession(gc.GuildId), true))); | ||||||
|  |  | ||||||
|  |             //plantpick | ||||||
|  |             client.MessageReceived += PotentialFlowerGeneration; | ||||||
|  |             GenerationChannels = new ConcurrentHashSet<ulong>(gcs | ||||||
|  |                 .SelectMany(c => c.GenerateCurrencyChannelIds.Select(obj => obj.ChannelId))); | ||||||
|  |  | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 TypingArticles = JsonConvert.DeserializeObject<List<TypingArticle>>(File.ReadAllText(TypingArticlesPath)); | ||||||
|  |             } | ||||||
|  |             catch (Exception ex) | ||||||
|  |             { | ||||||
|  |                 _log.Warn("Error while loading typing articles {0}", ex.ToString()); | ||||||
|  |                 TypingArticles = new List<TypingArticle>(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |         public string PrepareMessage(IUserMessage msg, out ChatterBotSession cleverbot) | ||||||
|  |         { | ||||||
|  |             var channel = msg.Channel as ITextChannel; | ||||||
|  |             cleverbot = null; | ||||||
|  |  | ||||||
|  |             if (channel == null) | ||||||
|  |                 return null; | ||||||
|  |  | ||||||
|  |             Lazy<ChatterBotSession> lazyCleverbot; | ||||||
|  |             if (!CleverbotGuilds.TryGetValue(channel.Guild.Id, out lazyCleverbot)) | ||||||
|  |                 return null; | ||||||
|  |  | ||||||
|  |             cleverbot = lazyCleverbot.Value; | ||||||
|  |  | ||||||
|  |             var nadekoId = _client.CurrentUser.Id; | ||||||
|  |             var normalMention = $"<@{nadekoId}> "; | ||||||
|  |             var nickMention = $"<@!{nadekoId}> "; | ||||||
|  |             string message; | ||||||
|  |             if (msg.Content.StartsWith(normalMention)) | ||||||
|  |             { | ||||||
|  |                 message = msg.Content.Substring(normalMention.Length).Trim(); | ||||||
|  |             } | ||||||
|  |             else if (msg.Content.StartsWith(nickMention)) | ||||||
|  |             { | ||||||
|  |                 message = msg.Content.Substring(nickMention.Length).Trim(); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 return null; | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return message; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public async Task<bool> TryAsk(ChatterBotSession cleverbot, ITextChannel channel, string message) | ||||||
|  |         { | ||||||
|  |             await channel.TriggerTypingAsync().ConfigureAwait(false); | ||||||
|  |  | ||||||
|  |             var response = await cleverbot.Think(message).ConfigureAwait(false); | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 await channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false); | ||||||
|  |             } | ||||||
|  |             catch | ||||||
|  |             { | ||||||
|  |                 await channel.SendConfirmAsync(response.SanitizeMentions()).ConfigureAwait(false); // try twice :\ | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public ConcurrentHashSet<ulong> GenerationChannels { get; } | ||||||
|  |         //channelid/message | ||||||
|  |         public ConcurrentDictionary<ulong, List<IUserMessage>> PlantedFlowers { get; } = new ConcurrentDictionary<ulong, List<IUserMessage>>(); | ||||||
|  |         //channelId/last generation | ||||||
|  |         public ConcurrentDictionary<ulong, DateTime> LastGenerations { get; } = new ConcurrentDictionary<ulong, DateTime>(); | ||||||
|  |          | ||||||
|  |         public KeyValuePair<string, ImmutableArray<byte>> GetRandomCurrencyImage() | ||||||
|  |         { | ||||||
|  |             var rng = new NadekoRandom(); | ||||||
|  |             return _images.Currency[rng.Next(0, _images.Currency.Length)]; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private string GetText(ITextChannel ch, string key, params object[] rep) | ||||||
|  |             => _strings.GetText(key, ch.GuildId, "Games".ToLowerInvariant(), rep); | ||||||
|  |  | ||||||
|  |         private Task PotentialFlowerGeneration(SocketMessage imsg) | ||||||
|  |         { | ||||||
|  |             var msg = imsg as SocketUserMessage; | ||||||
|  |             if (msg == null || msg.Author.IsBot) | ||||||
|  |                 return Task.CompletedTask; | ||||||
|  |  | ||||||
|  |             var channel = imsg.Channel as ITextChannel; | ||||||
|  |             if (channel == null) | ||||||
|  |                 return Task.CompletedTask; | ||||||
|  |  | ||||||
|  |             if (!GenerationChannels.Contains(channel.Id)) | ||||||
|  |                 return Task.CompletedTask; | ||||||
|  |  | ||||||
|  |             var _ = Task.Run(async () => | ||||||
|  |             { | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     var lastGeneration = LastGenerations.GetOrAdd(channel.Id, DateTime.MinValue); | ||||||
|  |                     var rng = new NadekoRandom(); | ||||||
|  |  | ||||||
|  |                     //todo i'm stupid :rofl: wtg kwoth. real async programming :100: :ok_hand: :100: :100: :thumbsup: | ||||||
|  |                     if (DateTime.Now - TimeSpan.FromSeconds(_bc.CurrencyGenerationCooldown) < lastGeneration) //recently generated in this channel, don't generate again | ||||||
|  |                         return; | ||||||
|  |  | ||||||
|  |                     var num = rng.Next(1, 101) + _bc.CurrencyGenerationChance * 100; | ||||||
|  |  | ||||||
|  |                     if (num > 100) | ||||||
|  |                     { | ||||||
|  |                         LastGenerations.AddOrUpdate(channel.Id, DateTime.Now, (id, old) => DateTime.Now); | ||||||
|  |  | ||||||
|  |                         var dropAmount = _bc.CurrencyDropAmount; | ||||||
|  |  | ||||||
|  |                         if (dropAmount > 0) | ||||||
|  |                         { | ||||||
|  |                             var msgs = new IUserMessage[dropAmount]; | ||||||
|  |                             var prefix = NadekoBot.Prefix; | ||||||
|  |                             var toSend = dropAmount == 1 | ||||||
|  |                                 ? GetText(channel, "curgen_sn", _bc.CurrencySign) | ||||||
|  |                                     + " " + GetText(channel, "pick_sn", prefix) | ||||||
|  |                                 : GetText(channel, "curgen_pl", dropAmount, _bc.CurrencySign) | ||||||
|  |                                     + " " + GetText(channel, "pick_pl", prefix); | ||||||
|  |                             var file = GetRandomCurrencyImage(); | ||||||
|  |                             using (var fileStream = file.Value.ToStream()) | ||||||
|  |                             { | ||||||
|  |                                 var sent = await channel.SendFileAsync( | ||||||
|  |                                     fileStream, | ||||||
|  |                                     file.Key, | ||||||
|  |                                     toSend).ConfigureAwait(false); | ||||||
|  |  | ||||||
|  |                                 msgs[0] = sent; | ||||||
|  |                             } | ||||||
|  |  | ||||||
|  |                             PlantedFlowers.AddOrUpdate(channel.Id, msgs.ToList(), (id, old) => { old.AddRange(msgs); return old; }); | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 catch (Exception ex) | ||||||
|  |                 { | ||||||
|  |                     LogManager.GetCurrentClassLogger().Warn(ex); | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             return Task.CompletedTask; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										71
									
								
								src/NadekoBot/Services/Games/GirlRating.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/NadekoBot/Services/Games/GirlRating.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | |||||||
|  | using ImageSharp; | ||||||
|  | using NadekoBot.DataStructures; | ||||||
|  | using NadekoBot.Extensions; | ||||||
|  | using NLog; | ||||||
|  | using System; | ||||||
|  | using System.IO; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Net.Http; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Services.Games | ||||||
|  | { | ||||||
|  |     public class GirlRating | ||||||
|  |     { | ||||||
|  |         private static readonly Logger _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |  | ||||||
|  |         public double Crazy { get; } | ||||||
|  |         public double Hot { get; } | ||||||
|  |         public int Roll { get; } | ||||||
|  |         public string Advice { get; } | ||||||
|  |         public AsyncLazy<string> Url { get; } | ||||||
|  |  | ||||||
|  |         public GirlRating(IImagesService _images, double crazy, double hot, int roll, string advice) | ||||||
|  |         { | ||||||
|  |             Crazy = crazy; | ||||||
|  |             Hot = hot; | ||||||
|  |             Roll = roll; | ||||||
|  |             Advice = advice; // convenient to have it here, even though atm there are only few different ones. | ||||||
|  |  | ||||||
|  |             Url = new AsyncLazy<string>(async () => | ||||||
|  |             { | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     using (var ms = new MemoryStream(_images.WifeMatrix.ToArray(), false)) | ||||||
|  |                     using (var img = new ImageSharp.Image(ms)) | ||||||
|  |                     { | ||||||
|  |                         const int minx = 35; | ||||||
|  |                         const int miny = 385; | ||||||
|  |                         const int length = 345; | ||||||
|  |  | ||||||
|  |                         var pointx = (int)(minx + length * (Hot / 10)); | ||||||
|  |                         var pointy = (int)(miny - length * ((Crazy - 4) / 6)); | ||||||
|  |  | ||||||
|  |                         using (var pointMs = new MemoryStream(_images.RategirlDot.ToArray(), false)) | ||||||
|  |                         using (var pointImg = new ImageSharp.Image(pointMs)) | ||||||
|  |                         { | ||||||
|  |                             img.DrawImage(pointImg, 100, default(Size), new Point(pointx - 10, pointy - 10)); | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                         string url; | ||||||
|  |                         using (var http = new HttpClient()) | ||||||
|  |                         using (var imgStream = new MemoryStream()) | ||||||
|  |                         { | ||||||
|  |                             img.Save(imgStream); | ||||||
|  |                             var byteContent = new ByteArrayContent(imgStream.ToArray()); | ||||||
|  |                             http.AddFakeHeaders(); | ||||||
|  |  | ||||||
|  |                             var reponse = await http.PutAsync("https://transfer.sh/img.png", byteContent); | ||||||
|  |                             url = await reponse.Content.ReadAsStringAsync(); | ||||||
|  |                         } | ||||||
|  |                         return url; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 catch (Exception ex) | ||||||
|  |                 { | ||||||
|  |                     _log.Warn(ex); | ||||||
|  |                     return null; | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| namespace NadekoBot.Modules.Games.Commands.Models | namespace NadekoBot.Services.Games | ||||||
| { | { | ||||||
|     public class TypingArticle |     public class TypingArticle | ||||||
|     { |     { | ||||||
| @@ -1,6 +1,10 @@ | |||||||
| using NadekoBot.Extensions; | using Discord; | ||||||
|  | using Discord.WebSocket; | ||||||
|  | using NadekoBot.DataStructures; | ||||||
|  | using NadekoBot.Extensions; | ||||||
| using NadekoBot.Services; | using NadekoBot.Services; | ||||||
| using NadekoBot.Services.Database.Models; | using NadekoBot.Services.Database.Models; | ||||||
|  | using NLog; | ||||||
| using System; | using System; | ||||||
| using System.Collections.Concurrent; | using System.Collections.Concurrent; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| @@ -15,11 +19,158 @@ namespace NadekoBot.Services | |||||||
|         private readonly DbHandler _db; |         private readonly DbHandler _db; | ||||||
|  |  | ||||||
|         public readonly ConcurrentDictionary<ulong, GreetSettings> GuildConfigsCache; |         public readonly ConcurrentDictionary<ulong, GreetSettings> GuildConfigsCache; | ||||||
|  |         private readonly DiscordShardedClient _client; | ||||||
|  |         private readonly Logger _log; | ||||||
|  |  | ||||||
|         public GreetSettingsService(IEnumerable<GuildConfig> guildConfigs, DbHandler db) |         public GreetSettingsService(DiscordShardedClient client, IEnumerable<GuildConfig> guildConfigs, DbHandler db) | ||||||
|         { |         { | ||||||
|             _db = db; |             _db = db; | ||||||
|  |             _client = client; | ||||||
|  |             _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |  | ||||||
|             GuildConfigsCache = new ConcurrentDictionary<ulong, GreetSettings>(guildConfigs.ToDictionary(g => g.GuildId, GreetSettings.Create)); |             GuildConfigsCache = new ConcurrentDictionary<ulong, GreetSettings>(guildConfigs.ToDictionary(g => g.GuildId, GreetSettings.Create)); | ||||||
|  |  | ||||||
|  |             _client.UserJoined += UserJoined; | ||||||
|  |             _client.UserLeft += UserLeft; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Task UserLeft(IGuildUser user) | ||||||
|  |         { | ||||||
|  |             var _ = Task.Run(async () => | ||||||
|  |             { | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     var conf = GetOrAddSettingsForGuild(user.GuildId); | ||||||
|  |  | ||||||
|  |                     if (!conf.SendChannelByeMessage) return; | ||||||
|  |                     var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.ByeMessageChannelId); | ||||||
|  |  | ||||||
|  |                     if (channel == null) //maybe warn the server owner that the channel is missing | ||||||
|  |                         return; | ||||||
|  |                     CREmbed embedData; | ||||||
|  |                     if (CREmbed.TryParse(conf.ChannelByeMessageText, out embedData)) | ||||||
|  |                     { | ||||||
|  |                         embedData.PlainText = embedData.PlainText?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); | ||||||
|  |                         embedData.Description = embedData.Description?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); | ||||||
|  |                         embedData.Title = embedData.Title?.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); | ||||||
|  |                         try | ||||||
|  |                         { | ||||||
|  |                             var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false); | ||||||
|  |                             if (conf.AutoDeleteByeMessagesTimer > 0) | ||||||
|  |                             { | ||||||
|  |                                 toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         catch (Exception ex) { _log.Warn(ex); } | ||||||
|  |                     } | ||||||
|  |                     else | ||||||
|  |                     { | ||||||
|  |                         var msg = conf.ChannelByeMessageText.Replace("%user%", user.Username).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); | ||||||
|  |                         if (string.IsNullOrWhiteSpace(msg)) | ||||||
|  |                             return; | ||||||
|  |                         try | ||||||
|  |                         { | ||||||
|  |                             var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false); | ||||||
|  |                             if (conf.AutoDeleteByeMessagesTimer > 0) | ||||||
|  |                             { | ||||||
|  |                                 toDelete.DeleteAfter(conf.AutoDeleteByeMessagesTimer); | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         catch (Exception ex) { _log.Warn(ex); } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 catch | ||||||
|  |                 { | ||||||
|  |                     // ignored | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             return Task.CompletedTask; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private Task UserJoined(IGuildUser user) | ||||||
|  |         { | ||||||
|  |             var _ = Task.Run(async () => | ||||||
|  |             { | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     var conf = GetOrAddSettingsForGuild(user.GuildId); | ||||||
|  |  | ||||||
|  |                     if (conf.SendChannelGreetMessage) | ||||||
|  |                     { | ||||||
|  |                         var channel = (await user.Guild.GetTextChannelsAsync()).SingleOrDefault(c => c.Id == conf.GreetMessageChannelId); | ||||||
|  |                         if (channel != null) //maybe warn the server owner that the channel is missing | ||||||
|  |                         { | ||||||
|  |  | ||||||
|  |                             CREmbed embedData; | ||||||
|  |                             if (CREmbed.TryParse(conf.ChannelGreetMessageText, out embedData)) | ||||||
|  |                             { | ||||||
|  |                                 embedData.PlainText = embedData.PlainText?.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); | ||||||
|  |                                 embedData.Description = embedData.Description?.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); | ||||||
|  |                                 embedData.Title = embedData.Title?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); | ||||||
|  |                                 try | ||||||
|  |                                 { | ||||||
|  |                                     var toDelete = await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false); | ||||||
|  |                                     if (conf.AutoDeleteGreetMessagesTimer > 0) | ||||||
|  |                                     { | ||||||
|  |                                         toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer); | ||||||
|  |                                     } | ||||||
|  |                                 } | ||||||
|  |                                 catch (Exception ex) { _log.Warn(ex); } | ||||||
|  |                             } | ||||||
|  |                             else | ||||||
|  |                             { | ||||||
|  |                                 var msg = conf.ChannelGreetMessageText.Replace("%user%", user.Mention).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); | ||||||
|  |                                 if (!string.IsNullOrWhiteSpace(msg)) | ||||||
|  |                                 { | ||||||
|  |                                     try | ||||||
|  |                                     { | ||||||
|  |                                         var toDelete = await channel.SendMessageAsync(msg.SanitizeMentions()).ConfigureAwait(false); | ||||||
|  |                                         if (conf.AutoDeleteGreetMessagesTimer > 0) | ||||||
|  |                                         { | ||||||
|  |                                             toDelete.DeleteAfter(conf.AutoDeleteGreetMessagesTimer); | ||||||
|  |                                         } | ||||||
|  |                                     } | ||||||
|  |                                     catch (Exception ex) { _log.Warn(ex); } | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     if (conf.SendDmGreetMessage) | ||||||
|  |                     { | ||||||
|  |                         var channel = await user.CreateDMChannelAsync(); | ||||||
|  |  | ||||||
|  |                         if (channel != null) | ||||||
|  |                         { | ||||||
|  |                             CREmbed embedData; | ||||||
|  |                             if (CREmbed.TryParse(conf.ChannelGreetMessageText, out embedData)) | ||||||
|  |                             { | ||||||
|  |                                 embedData.PlainText = embedData.PlainText?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); | ||||||
|  |                                 embedData.Description = embedData.Description?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); | ||||||
|  |                                 embedData.Title = embedData.Title?.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); | ||||||
|  |                                 try | ||||||
|  |                                 { | ||||||
|  |                                     await channel.EmbedAsync(embedData.ToEmbed(), embedData.PlainText ?? "").ConfigureAwait(false); | ||||||
|  |                                 } | ||||||
|  |                                 catch (Exception ex) { _log.Warn(ex); } | ||||||
|  |                             } | ||||||
|  |                             else | ||||||
|  |                             { | ||||||
|  |                                 var msg = conf.DmGreetMessageText.Replace("%user%", user.ToString()).Replace("%id%", user.Id.ToString()).Replace("%server%", user.Guild.Name); | ||||||
|  |                                 if (!string.IsNullOrWhiteSpace(msg)) | ||||||
|  |                                 { | ||||||
|  |                                     await channel.SendConfirmAsync(msg).ConfigureAwait(false); | ||||||
|  |                                 } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 catch | ||||||
|  |                 { | ||||||
|  |                     // ignored | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |             return Task.CompletedTask; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public GreetSettings GetOrAddSettingsForGuild(ulong guildId) |         public GreetSettings GetOrAddSettingsForGuild(ulong guildId) | ||||||
| @@ -210,6 +361,23 @@ namespace NadekoBot.Services | |||||||
|                 await uow.CompleteAsync().ConfigureAwait(false); |                 await uow.CompleteAsync().ConfigureAwait(false); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public async Task SetGreetDel(ulong id, int timer) | ||||||
|  |         { | ||||||
|  |             if (timer < 0 || timer > 600) | ||||||
|  |                 return; | ||||||
|  |  | ||||||
|  |             using (var uow = _db.UnitOfWork) | ||||||
|  |             { | ||||||
|  |                 var conf = uow.GuildConfigs.For(id, set => set); | ||||||
|  |                 conf.AutoDeleteGreetMessagesTimer = timer; | ||||||
|  |  | ||||||
|  |                 var toAdd = GreetSettings.Create(conf); | ||||||
|  |                 GuildConfigsCache.AddOrUpdate(id, toAdd, (key, old) => toAdd); | ||||||
|  |  | ||||||
|  |                 await uow.CompleteAsync().ConfigureAwait(false); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public class GreetSettings |     public class GreetSettings | ||||||
|   | |||||||
| @@ -17,7 +17,8 @@ namespace NadekoBot.Services | |||||||
|         string CarbonKey { get; } |         string CarbonKey { get; } | ||||||
|  |  | ||||||
|         DBConfig Db { get; } |         DBConfig Db { get; } | ||||||
|         string SoundCloudClientId { get; set; } |         string SoundCloudClientId { get; } | ||||||
|  |         string OsuApiKey { get; } | ||||||
|  |  | ||||||
|         bool IsOwner(IUser u); |         bool IsOwner(IUser u); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| using System; | using Google.Apis.Customsearch.v1.Data; | ||||||
|  | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
|  |  | ||||||
| @@ -6,12 +7,28 @@ namespace NadekoBot.Services | |||||||
| { | { | ||||||
|     public interface IGoogleApiService |     public interface IGoogleApiService | ||||||
|     { |     { | ||||||
|  |         IEnumerable<string> Languages { get; } | ||||||
|  |  | ||||||
|         Task<IEnumerable<string>> GetVideosByKeywordsAsync(string keywords, int count = 1); |         Task<IEnumerable<string>> GetVideosByKeywordsAsync(string keywords, int count = 1); | ||||||
|         Task<IEnumerable<string>> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1); |         Task<IEnumerable<string>> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1); | ||||||
|         Task<IEnumerable<string>> GetRelatedVideosAsync(string url, int count = 1); |         Task<IEnumerable<string>> GetRelatedVideosAsync(string url, int count = 1); | ||||||
|         Task<IEnumerable<string>> GetPlaylistTracksAsync(string playlistId, int count = 50); |         Task<IEnumerable<string>> GetPlaylistTracksAsync(string playlistId, int count = 50); | ||||||
|         Task<IReadOnlyDictionary<string, TimeSpan>> GetVideoDurationsAsync(IEnumerable<string> videoIds); |         Task<IReadOnlyDictionary<string, TimeSpan>> GetVideoDurationsAsync(IEnumerable<string> videoIds); | ||||||
|  |         Task<ImageResult> GetImageAsync(string query, int start = 1); | ||||||
|  |         Task<string> Translate(string sourceText, string sourceLanguage, string targetLanguage); | ||||||
|  |  | ||||||
|         Task<string> ShortenUrl(string url); |         Task<string> ShortenUrl(string url); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     public struct ImageResult | ||||||
|  |     { | ||||||
|  |         public Result.ImageData Image { get; } | ||||||
|  |         public string Link { get; } | ||||||
|  |  | ||||||
|  |         public ImageResult(Result.ImageData image, string link) | ||||||
|  |         { | ||||||
|  |             this.Image = image; | ||||||
|  |             this.Link = link; | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ namespace NadekoBot.Services.Impl | |||||||
|                     ? "d0bd7768e3a1a2d15430f0dccb871117" |                     ? "d0bd7768e3a1a2d15430f0dccb871117" | ||||||
|                     : _soundcloudClientId; |                     : _soundcloudClientId; | ||||||
|             } |             } | ||||||
|             set { |             private set { | ||||||
|                 _soundcloudClientId = value; |                 _soundcloudClientId = value; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -9,7 +9,9 @@ using Google.Apis.Urlshortener.v1; | |||||||
| using Google.Apis.Urlshortener.v1.Data; | using Google.Apis.Urlshortener.v1.Data; | ||||||
| using NLog; | using NLog; | ||||||
| using Google.Apis.Customsearch.v1; | using Google.Apis.Customsearch.v1; | ||||||
| using Google.Apis.Customsearch.v1.Data; | using System.Net.Http; | ||||||
|  | using System.Net; | ||||||
|  | using Newtonsoft.Json.Linq; | ||||||
|  |  | ||||||
| namespace NadekoBot.Services.Impl | namespace NadekoBot.Services.Impl | ||||||
| { | { | ||||||
| @@ -20,7 +22,7 @@ namespace NadekoBot.Services.Impl | |||||||
|         private YouTubeService yt; |         private YouTubeService yt; | ||||||
|         private UrlshortenerService sh; |         private UrlshortenerService sh; | ||||||
|         private CustomsearchService cs; |         private CustomsearchService cs; | ||||||
|          |  | ||||||
|         private Logger _log { get; } |         private Logger _log { get; } | ||||||
|  |  | ||||||
|         public GoogleApiService(IBotCredentials creds) |         public GoogleApiService(IBotCredentials creds) | ||||||
| @@ -39,6 +41,7 @@ namespace NadekoBot.Services.Impl | |||||||
|             sh = new UrlshortenerService(bcs); |             sh = new UrlshortenerService(bcs); | ||||||
|             cs = new CustomsearchService(bcs); |             cs = new CustomsearchService(bcs); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public async Task<IEnumerable<string>> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1) |         public async Task<IEnumerable<string>> GetPlaylistIdsByKeywordsAsync(string keywords, int count = 1) | ||||||
|         { |         { | ||||||
|             if (string.IsNullOrWhiteSpace(keywords)) |             if (string.IsNullOrWhiteSpace(keywords)) | ||||||
| @@ -190,18 +193,6 @@ namespace NadekoBot.Services.Impl | |||||||
|             return toReturn; |             return toReturn; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         public struct ImageResult |  | ||||||
|         { |  | ||||||
|             public Result.ImageData Image { get; } |  | ||||||
|             public string Link { get; } |  | ||||||
|  |  | ||||||
|             public ImageResult(Result.ImageData image, string link) |  | ||||||
|             { |  | ||||||
|                 this.Image = image; |  | ||||||
|                 this.Link = link; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         public async Task<ImageResult> GetImageAsync(string query, int start = 1) |         public async Task<ImageResult> GetImageAsync(string query, int start = 1) | ||||||
|         { |         { | ||||||
|             if (string.IsNullOrWhiteSpace(query)) |             if (string.IsNullOrWhiteSpace(query)) | ||||||
| @@ -218,5 +209,168 @@ namespace NadekoBot.Services.Impl | |||||||
|  |  | ||||||
|             return new ImageResult(search.Items[0].Image, search.Items[0].Link); |             return new ImageResult(search.Items[0].Image, search.Items[0].Link); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public IEnumerable<string> Languages => _languageDictionary.Keys.OrderBy(x => x); | ||||||
|  |         private readonly Dictionary<string, string> _languageDictionary = new Dictionary<string, string>() { | ||||||
|  |                     { "afrikaans", "af"}, | ||||||
|  |                     { "albanian", "sq"}, | ||||||
|  |                     { "arabic", "ar"}, | ||||||
|  |                     { "armenian", "hy"}, | ||||||
|  |                     { "azerbaijani", "az"}, | ||||||
|  |                     { "basque", "eu"}, | ||||||
|  |                     { "belarusian", "be"}, | ||||||
|  |                     { "bengali", "bn"}, | ||||||
|  |                     { "bulgarian", "bg"}, | ||||||
|  |                     { "catalan", "ca"}, | ||||||
|  |                     { "chinese-traditional", "zh-TW"}, | ||||||
|  |                     { "chinese-simplified", "zh-CN"}, | ||||||
|  |                     { "chinese", "zh-CN"}, | ||||||
|  |                     { "croatian", "hr"}, | ||||||
|  |                     { "czech", "cs"}, | ||||||
|  |                     { "danish", "da"}, | ||||||
|  |                     { "dutch", "nl"}, | ||||||
|  |                     { "english", "en"}, | ||||||
|  |                     { "esperanto", "eo"}, | ||||||
|  |                     { "estonian", "et"}, | ||||||
|  |                     { "filipino", "tl"}, | ||||||
|  |                     { "finnish", "fi"}, | ||||||
|  |                     { "french", "fr"}, | ||||||
|  |                     { "galician", "gl"}, | ||||||
|  |                     { "german", "de"}, | ||||||
|  |                     { "georgian", "ka"}, | ||||||
|  |                     { "greek", "el"}, | ||||||
|  |                     { "haitian Creole", "ht"}, | ||||||
|  |                     { "hebrew", "iw"}, | ||||||
|  |                     { "hindi", "hi"}, | ||||||
|  |                     { "hungarian", "hu"}, | ||||||
|  |                     { "icelandic", "is"}, | ||||||
|  |                     { "indonesian", "id"}, | ||||||
|  |                     { "irish", "ga"}, | ||||||
|  |                     { "italian", "it"}, | ||||||
|  |                     { "japanese", "ja"}, | ||||||
|  |                     { "korean", "ko"}, | ||||||
|  |                     { "lao", "lo"}, | ||||||
|  |                     { "latin", "la"}, | ||||||
|  |                     { "latvian", "lv"}, | ||||||
|  |                     { "lithuanian", "lt"}, | ||||||
|  |                     { "macedonian", "mk"}, | ||||||
|  |                     { "malay", "ms"}, | ||||||
|  |                     { "maltese", "mt"}, | ||||||
|  |                     { "norwegian", "no"}, | ||||||
|  |                     { "persian", "fa"}, | ||||||
|  |                     { "polish", "pl"}, | ||||||
|  |                     { "portuguese", "pt"}, | ||||||
|  |                     { "romanian", "ro"}, | ||||||
|  |                     { "russian", "ru"}, | ||||||
|  |                     { "serbian", "sr"}, | ||||||
|  |                     { "slovak", "sk"}, | ||||||
|  |                     { "slovenian", "sl"}, | ||||||
|  |                     { "spanish", "es"}, | ||||||
|  |                     { "swahili", "sw"}, | ||||||
|  |                     { "swedish", "sv"}, | ||||||
|  |                     { "tamil", "ta"}, | ||||||
|  |                     { "telugu", "te"}, | ||||||
|  |                     { "thai", "th"}, | ||||||
|  |                     { "turkish", "tr"}, | ||||||
|  |                     { "ukrainian", "uk"}, | ||||||
|  |                     { "urdu", "ur"}, | ||||||
|  |                     { "vietnamese", "vi"}, | ||||||
|  |                     { "welsh", "cy"}, | ||||||
|  |                     { "yiddish", "yi"}, | ||||||
|  |  | ||||||
|  |                     { "af", "af"}, | ||||||
|  |                     { "sq", "sq"}, | ||||||
|  |                     { "ar", "ar"}, | ||||||
|  |                     { "hy", "hy"}, | ||||||
|  |                     { "az", "az"}, | ||||||
|  |                     { "eu", "eu"}, | ||||||
|  |                     { "be", "be"}, | ||||||
|  |                     { "bn", "bn"}, | ||||||
|  |                     { "bg", "bg"}, | ||||||
|  |                     { "ca", "ca"}, | ||||||
|  |                     { "zh-tw", "zh-TW"}, | ||||||
|  |                     { "zh-cn", "zh-CN"}, | ||||||
|  |                     { "hr", "hr"}, | ||||||
|  |                     { "cs", "cs"}, | ||||||
|  |                     { "da", "da"}, | ||||||
|  |                     { "nl", "nl"}, | ||||||
|  |                     { "en", "en"}, | ||||||
|  |                     { "eo", "eo"}, | ||||||
|  |                     { "et", "et"}, | ||||||
|  |                     { "tl", "tl"}, | ||||||
|  |                     { "fi", "fi"}, | ||||||
|  |                     { "fr", "fr"}, | ||||||
|  |                     { "gl", "gl"}, | ||||||
|  |                     { "de", "de"}, | ||||||
|  |                     { "ka", "ka"}, | ||||||
|  |                     { "el", "el"}, | ||||||
|  |                     { "ht", "ht"}, | ||||||
|  |                     { "iw", "iw"}, | ||||||
|  |                     { "hi", "hi"}, | ||||||
|  |                     { "hu", "hu"}, | ||||||
|  |                     { "is", "is"}, | ||||||
|  |                     { "id", "id"}, | ||||||
|  |                     { "ga", "ga"}, | ||||||
|  |                     { "it", "it"}, | ||||||
|  |                     { "ja", "ja"}, | ||||||
|  |                     { "ko", "ko"}, | ||||||
|  |                     { "lo", "lo"}, | ||||||
|  |                     { "la", "la"}, | ||||||
|  |                     { "lv", "lv"}, | ||||||
|  |                     { "lt", "lt"}, | ||||||
|  |                     { "mk", "mk"}, | ||||||
|  |                     { "ms", "ms"}, | ||||||
|  |                     { "mt", "mt"}, | ||||||
|  |                     { "no", "no"}, | ||||||
|  |                     { "fa", "fa"}, | ||||||
|  |                     { "pl", "pl"}, | ||||||
|  |                     { "pt", "pt"}, | ||||||
|  |                     { "ro", "ro"}, | ||||||
|  |                     { "ru", "ru"}, | ||||||
|  |                     { "sr", "sr"}, | ||||||
|  |                     { "sk", "sk"}, | ||||||
|  |                     { "sl", "sl"}, | ||||||
|  |                     { "es", "es"}, | ||||||
|  |                     { "sw", "sw"}, | ||||||
|  |                     { "sv", "sv"}, | ||||||
|  |                     { "ta", "ta"}, | ||||||
|  |                     { "te", "te"}, | ||||||
|  |                     { "th", "th"}, | ||||||
|  |                     { "tr", "tr"}, | ||||||
|  |                     { "uk", "uk"}, | ||||||
|  |                     { "ur", "ur"}, | ||||||
|  |                     { "vi", "vi"}, | ||||||
|  |                     { "cy", "cy"}, | ||||||
|  |                     { "yi", "yi"}, | ||||||
|  |                 }; | ||||||
|  |  | ||||||
|  |         public async Task<string> Translate(string sourceText, string sourceLanguage, string targetLanguage) | ||||||
|  |         { | ||||||
|  |             string text; | ||||||
|  |  | ||||||
|  |             if (!_languageDictionary.ContainsKey(sourceLanguage) || | ||||||
|  |                !_languageDictionary.ContainsKey(targetLanguage)) | ||||||
|  |                 throw new ArgumentException(); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |             var url = string.Format("https://translate.googleapis.com/translate_a/single?client=gtx&sl={0}&tl={1}&dt=t&q={2}", | ||||||
|  |                                         ConvertToLanguageCode(sourceLanguage), | ||||||
|  |                                         ConvertToLanguageCode(targetLanguage), | ||||||
|  |                                        WebUtility.UrlEncode(sourceText)); | ||||||
|  |             using (var http = new HttpClient()) | ||||||
|  |             { | ||||||
|  |                 http.DefaultRequestHeaders.Add("user-agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"); | ||||||
|  |                 text = await http.GetStringAsync(url).ConfigureAwait(false); | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return (string.Concat(JArray.Parse(text)[0].Select(x => x[0]))); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private string ConvertToLanguageCode(string language) | ||||||
|  |         { | ||||||
|  |             string mode; | ||||||
|  |             _languageDictionary.TryGetValue(language, out mode); | ||||||
|  |             return mode; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -7,6 +7,7 @@ using NLog; | |||||||
| using System.Diagnostics; | using System.Diagnostics; | ||||||
| using Newtonsoft.Json; | using Newtonsoft.Json; | ||||||
| using System; | using System; | ||||||
|  | using Discord; | ||||||
|  |  | ||||||
| namespace NadekoBot.Services | namespace NadekoBot.Services | ||||||
| { | { | ||||||
| @@ -20,10 +21,13 @@ namespace NadekoBot.Services | |||||||
|         /// Used as failsafe in case response key doesn't exist in the selected or default language. |         /// Used as failsafe in case response key doesn't exist in the selected or default language. | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         private readonly CultureInfo _usCultureInfo = new CultureInfo("en-US"); |         private readonly CultureInfo _usCultureInfo = new CultureInfo("en-US"); | ||||||
|  |         private readonly ILocalization _localization; | ||||||
|  |  | ||||||
|         public NadekoStrings() |         public NadekoStrings(ILocalization loc) | ||||||
|         { |         { | ||||||
|             _log = LogManager.GetCurrentClassLogger(); |             _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |             _localization = loc; | ||||||
|  |  | ||||||
|             var sw = Stopwatch.StartNew(); |             var sw = Stopwatch.StartNew(); | ||||||
|             var allLangsDict = new Dictionary<string, ImmutableDictionary<string, string>>(); // lang:(name:value) |             var allLangsDict = new Dictionary<string, ImmutableDictionary<string, string>>(); // lang:(name:value) | ||||||
|             foreach (var file in Directory.GetFiles(stringsPath)) |             foreach (var file in Directory.GetFiles(stringsPath)) | ||||||
| @@ -58,6 +62,9 @@ namespace NadekoBot.Services | |||||||
|             return val; |             return val; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|  |         public string GetText(string key, ulong guildId, string lowerModuleTypeName, params object[] replacements) => | ||||||
|  |             GetText(key, _localization.GetCultureInfo(guildId), lowerModuleTypeName); | ||||||
|  |  | ||||||
|         public string GetText(string key, CultureInfo cultureInfo, string lowerModuleTypeName) |         public string GetText(string key, CultureInfo cultureInfo, string lowerModuleTypeName) | ||||||
|         { |         { | ||||||
|             var text = GetString(lowerModuleTypeName + "_" + key, cultureInfo); |             var text = GetString(lowerModuleTypeName + "_" + key, cultureInfo); | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| namespace NadekoBot.Modules.Searches.Models | namespace NadekoBot.Services.Searches | ||||||
| { | { | ||||||
|     class MagicItem |     public class MagicItem | ||||||
|     { |     { | ||||||
|         public string Name { get; set; } |         public string Name { get; set; } | ||||||
|         public string Description { get; set; } |         public string Description { get; set; } | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| 
 | 
 | ||||||
| namespace NadekoBot.Modules.Searches.Models | namespace NadekoBot.Services.Searches | ||||||
| { | { | ||||||
|     public class SearchPokemon |     public class SearchPokemon | ||||||
|     { |     { | ||||||
| @@ -1,4 +1,12 @@ | |||||||
| using NadekoBot.Extensions; | using Discord; | ||||||
|  | using Discord.WebSocket; | ||||||
|  | using NadekoBot.Extensions; | ||||||
|  | using Newtonsoft.Json; | ||||||
|  | using NLog; | ||||||
|  | using System; | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.IO; | ||||||
| using System.Net.Http; | using System.Net.Http; | ||||||
| using System.Threading.Tasks; | using System.Threading.Tasks; | ||||||
| using System.Xml; | using System.Xml; | ||||||
| @@ -7,6 +15,101 @@ namespace NadekoBot.Services.Searches | |||||||
| { | { | ||||||
|     public class SearchesService |     public class SearchesService | ||||||
|     { |     { | ||||||
|  |         private readonly DiscordShardedClient _client; | ||||||
|  |         private readonly IGoogleApiService _google; | ||||||
|  |         private readonly DbHandler _db; | ||||||
|  |         private readonly Logger _log; | ||||||
|  |  | ||||||
|  |         public ConcurrentDictionary<ulong, bool> TranslatedChannels { get; } = new ConcurrentDictionary<ulong, bool>(); | ||||||
|  |         public ConcurrentDictionary<UserChannelPair, string> UserLanguages { get; } = new ConcurrentDictionary<UserChannelPair, string>(); | ||||||
|  |          | ||||||
|  |         public readonly string PokemonAbilitiesFile = "data/pokemon/pokemon_abilities7.json"; | ||||||
|  |         public readonly string PokemonListFile = "data/pokemon/pokemon_list7.json"; | ||||||
|  |         public Dictionary<string, SearchPokemon> Pokemons { get; } = new Dictionary<string, SearchPokemon>(); | ||||||
|  |         public Dictionary<string, SearchPokemonAbility> PokemonAbilities { get; } = new Dictionary<string, SearchPokemonAbility>(); | ||||||
|  |  | ||||||
|  |         public List<WoWJoke> WowJokes { get; } = new List<WoWJoke>(); | ||||||
|  |         public List<MagicItem> MagicItems { get; } = new List<MagicItem>(); | ||||||
|  |  | ||||||
|  |         public SearchesService(DiscordShardedClient client, IGoogleApiService google, DbHandler db) | ||||||
|  |         { | ||||||
|  |             _client = client; | ||||||
|  |             _google = google; | ||||||
|  |             _db = db; | ||||||
|  |             _log = LogManager.GetCurrentClassLogger(); | ||||||
|  |  | ||||||
|  |             //translate commands | ||||||
|  |             _client.MessageReceived += async (msg) => | ||||||
|  |             { | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     var umsg = msg as SocketUserMessage; | ||||||
|  |                     if (umsg == null) | ||||||
|  |                         return; | ||||||
|  |  | ||||||
|  |                     if (!TranslatedChannels.TryGetValue(umsg.Channel.Id, out var autoDelete)) | ||||||
|  |                         return; | ||||||
|  |  | ||||||
|  |                     var key = new UserChannelPair() | ||||||
|  |                     { | ||||||
|  |                         UserId = umsg.Author.Id, | ||||||
|  |                         ChannelId = umsg.Channel.Id, | ||||||
|  |                     }; | ||||||
|  |  | ||||||
|  |                     string langs; | ||||||
|  |                     if (!UserLanguages.TryGetValue(key, out langs)) | ||||||
|  |                         return; | ||||||
|  |  | ||||||
|  |                     var text = await Translate(langs, umsg.Resolve(TagHandling.Ignore)) | ||||||
|  |                                         .ConfigureAwait(false); | ||||||
|  |                     if (autoDelete) | ||||||
|  |                         try { await umsg.DeleteAsync().ConfigureAwait(false); } catch { } | ||||||
|  |                     await umsg.Channel.SendConfirmAsync($"{umsg.Author.Mention} `:` " + text.Replace("<@ ", "<@").Replace("<@! ", "<@!")).ConfigureAwait(false); | ||||||
|  |                 } | ||||||
|  |                 catch { } | ||||||
|  |             }; | ||||||
|  |  | ||||||
|  |             //pokemon commands | ||||||
|  |             if (File.Exists(PokemonListFile)) | ||||||
|  |             { | ||||||
|  |                 Pokemons = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemon>>(File.ReadAllText(PokemonListFile)); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |                 _log.Warn(PokemonListFile + " is missing. Pokemon abilities not loaded."); | ||||||
|  |             if (File.Exists(PokemonAbilitiesFile)) | ||||||
|  |                 PokemonAbilities = JsonConvert.DeserializeObject<Dictionary<string, SearchPokemonAbility>>(File.ReadAllText(PokemonAbilitiesFile)); | ||||||
|  |             else | ||||||
|  |                 _log.Warn(PokemonAbilitiesFile + " is missing. Pokemon abilities not loaded."); | ||||||
|  |  | ||||||
|  |             //joke commands | ||||||
|  |             if (File.Exists("data/wowjokes.json")) | ||||||
|  |             { | ||||||
|  |                 WowJokes = JsonConvert.DeserializeObject<List<WoWJoke>>(File.ReadAllText("data/wowjokes.json")); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |                 _log.Warn("data/wowjokes.json is missing. WOW Jokes are not loaded."); | ||||||
|  |  | ||||||
|  |             if (File.Exists("data/magicitems.json")) | ||||||
|  |             { | ||||||
|  |                 MagicItems = JsonConvert.DeserializeObject<List<MagicItem>>(File.ReadAllText("data/magicitems.json")); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |                 _log.Warn("data/magicitems.json is missing. Magic items are not loaded."); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public async Task<string> Translate(string langs, string text = null) | ||||||
|  |         { | ||||||
|  |             var langarr = langs.ToLowerInvariant().Split('>'); | ||||||
|  |             if (langarr.Length != 2) | ||||||
|  |                 throw new ArgumentException(); | ||||||
|  |             var from = langarr[0]; | ||||||
|  |             var to = langarr[1]; | ||||||
|  |             text = text?.Trim(); | ||||||
|  |             if (string.IsNullOrWhiteSpace(text)) | ||||||
|  |                 throw new ArgumentException(); | ||||||
|  |             return (await _google.Translate(text, from, to).ConfigureAwait(false)).SanitizeMentions(); | ||||||
|  |         } | ||||||
|  |  | ||||||
|         public async Task<string> DapiSearch(string tag, DapiSearchType type) |         public async Task<string> DapiSearch(string tag, DapiSearchType type) | ||||||
|         { |         { | ||||||
|             tag = tag?.Replace(" ", "_"); |             tag = tag?.Replace(" ", "_"); | ||||||
| @@ -65,4 +168,18 @@ namespace NadekoBot.Services.Searches | |||||||
|         Rule34, |         Rule34, | ||||||
|         Yandere |         Yandere | ||||||
|     } |     } | ||||||
|  |      | ||||||
|  |     public struct UserChannelPair | ||||||
|  |     { | ||||||
|  |         public ulong UserId { get; set; } | ||||||
|  |         public ulong ChannelId { get; set; } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |     public class StreamStatus | ||||||
|  |     { | ||||||
|  |         public bool IsLive { get; set; } | ||||||
|  |         public string ApiLink { get; set; } | ||||||
|  |         public string Views { get; set; } | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										11
									
								
								src/NadekoBot/Services/Searches/StreamNotFoundException.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/NadekoBot/Services/Searches/StreamNotFoundException.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | |||||||
|  | using System; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Services.Searches | ||||||
|  | { | ||||||
|  |     public class StreamNotFoundException : Exception | ||||||
|  |     { | ||||||
|  |         public StreamNotFoundException(string message) : base($"Stream '{message}' not found.") | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										190
									
								
								src/NadekoBot/Services/Searches/StreamNotificationService.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								src/NadekoBot/Services/Searches/StreamNotificationService.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,190 @@ | |||||||
|  | using Discord; | ||||||
|  | using Discord.WebSocket; | ||||||
|  | using NadekoBot.Extensions; | ||||||
|  | using NadekoBot.Services.Database.Models; | ||||||
|  | using Newtonsoft.Json; | ||||||
|  | using System; | ||||||
|  | using System.Collections.Concurrent; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Linq; | ||||||
|  | using System.Net.Http; | ||||||
|  | using System.Threading; | ||||||
|  | using System.Threading.Tasks; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Services.Searches | ||||||
|  | { | ||||||
|  |     public class StreamNotificationService | ||||||
|  |     { | ||||||
|  |         private readonly Timer _streamCheckTimer; | ||||||
|  |         private bool firstStreamNotifPass { get; set; } = true; | ||||||
|  |         private readonly ConcurrentDictionary<string, StreamStatus> _cachedStatuses = new ConcurrentDictionary<string, StreamStatus>(); | ||||||
|  |  | ||||||
|  |         private readonly DbHandler _db; | ||||||
|  |         private readonly DiscordShardedClient _client; | ||||||
|  |         private readonly NadekoStrings _strings; | ||||||
|  |  | ||||||
|  |         public StreamNotificationService(DbHandler db, DiscordShardedClient client, NadekoStrings strings) | ||||||
|  |         { | ||||||
|  |             _db = db; | ||||||
|  |             _client = client; | ||||||
|  |             _strings = strings; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public StreamNotificationService() | ||||||
|  |         { | ||||||
|  |             _streamCheckTimer = new Timer(async (state) => | ||||||
|  |             { | ||||||
|  |                 var oldCachedStatuses = new ConcurrentDictionary<string, StreamStatus>(_cachedStatuses); | ||||||
|  |                 _cachedStatuses.Clear(); | ||||||
|  |                 IEnumerable<FollowedStream> streams; | ||||||
|  |                 using (var uow = _db.UnitOfWork) | ||||||
|  |                 { | ||||||
|  |                     streams = uow.GuildConfigs.GetAllFollowedStreams(); | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 await Task.WhenAll(streams.Select(async fs => | ||||||
|  |                 { | ||||||
|  |                     try | ||||||
|  |                     { | ||||||
|  |                         var newStatus = await GetStreamStatus(fs).ConfigureAwait(false); | ||||||
|  |                         if (firstStreamNotifPass) | ||||||
|  |                         { | ||||||
|  |                             return; | ||||||
|  |                         } | ||||||
|  |  | ||||||
|  |                         StreamStatus oldStatus; | ||||||
|  |                         if (oldCachedStatuses.TryGetValue(newStatus.ApiLink, out oldStatus) && | ||||||
|  |                             oldStatus.IsLive != newStatus.IsLive) | ||||||
|  |                         { | ||||||
|  |                             var server = _client.GetGuild(fs.GuildId); | ||||||
|  |                             var channel = server?.GetTextChannel(fs.ChannelId); | ||||||
|  |                             if (channel == null) | ||||||
|  |                                 return; | ||||||
|  |                             try | ||||||
|  |                             { | ||||||
|  |                                 await channel.EmbedAsync(GetEmbed(fs, newStatus, channel.Guild.Id)).ConfigureAwait(false); | ||||||
|  |                             } | ||||||
|  |                             catch | ||||||
|  |                             { | ||||||
|  |                                 // ignored | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                     } | ||||||
|  |                     catch | ||||||
|  |                     { | ||||||
|  |                         // ignored | ||||||
|  |                     } | ||||||
|  |                 })); | ||||||
|  |  | ||||||
|  |                 firstStreamNotifPass = false; | ||||||
|  |             }, null, TimeSpan.Zero, TimeSpan.FromSeconds(60)); | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public async Task<StreamStatus> GetStreamStatus(FollowedStream stream, bool checkCache = true) | ||||||
|  |         { | ||||||
|  |             string response; | ||||||
|  |             StreamStatus result; | ||||||
|  |             switch (stream.Type) | ||||||
|  |             { | ||||||
|  |                 case FollowedStream.FollowedStreamType.Hitbox: | ||||||
|  |                     var hitboxUrl = $"https://api.hitbox.tv/media/status/{stream.Username.ToLowerInvariant()}"; | ||||||
|  |                     if (checkCache && _cachedStatuses.TryGetValue(hitboxUrl, out result)) | ||||||
|  |                         return result; | ||||||
|  |                     using (var http = new HttpClient()) | ||||||
|  |                     { | ||||||
|  |                         response = await http.GetStringAsync(hitboxUrl).ConfigureAwait(false); | ||||||
|  |                     } | ||||||
|  |                     var hbData = JsonConvert.DeserializeObject<HitboxResponse>(response); | ||||||
|  |                     if (!hbData.Success) | ||||||
|  |                         throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]"); | ||||||
|  |                     result = new StreamStatus() | ||||||
|  |                     { | ||||||
|  |                         IsLive = hbData.IsLive, | ||||||
|  |                         ApiLink = hitboxUrl, | ||||||
|  |                         Views = hbData.Views | ||||||
|  |                     }; | ||||||
|  |                     _cachedStatuses.AddOrUpdate(hitboxUrl, result, (key, old) => result); | ||||||
|  |                     return result; | ||||||
|  |                 case FollowedStream.FollowedStreamType.Twitch: | ||||||
|  |                     var twitchUrl = $"https://api.twitch.tv/kraken/streams/{Uri.EscapeUriString(stream.Username.ToLowerInvariant())}?client_id=67w6z9i09xv2uoojdm9l0wsyph4hxo6"; | ||||||
|  |                     if (checkCache && _cachedStatuses.TryGetValue(twitchUrl, out result)) | ||||||
|  |                         return result; | ||||||
|  |                     using (var http = new HttpClient()) | ||||||
|  |                     { | ||||||
|  |                         response = await http.GetStringAsync(twitchUrl).ConfigureAwait(false); | ||||||
|  |                     } | ||||||
|  |                     var twData = JsonConvert.DeserializeObject<TwitchResponse>(response); | ||||||
|  |                     if (twData.Error != null) | ||||||
|  |                     { | ||||||
|  |                         throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]"); | ||||||
|  |                     } | ||||||
|  |                     result = new StreamStatus() | ||||||
|  |                     { | ||||||
|  |                         IsLive = twData.IsLive, | ||||||
|  |                         ApiLink = twitchUrl, | ||||||
|  |                         Views = twData.Stream?.Viewers.ToString() ?? "0" | ||||||
|  |                     }; | ||||||
|  |                     _cachedStatuses.AddOrUpdate(twitchUrl, result, (key, old) => result); | ||||||
|  |                     return result; | ||||||
|  |                 case FollowedStream.FollowedStreamType.Beam: | ||||||
|  |                     var beamUrl = $"https://beam.pro/api/v1/channels/{stream.Username.ToLowerInvariant()}"; | ||||||
|  |                     if (checkCache && _cachedStatuses.TryGetValue(beamUrl, out result)) | ||||||
|  |                         return result; | ||||||
|  |                     using (var http = new HttpClient()) | ||||||
|  |                     { | ||||||
|  |                         response = await http.GetStringAsync(beamUrl).ConfigureAwait(false); | ||||||
|  |                     } | ||||||
|  |  | ||||||
|  |                     var bmData = JsonConvert.DeserializeObject<BeamResponse>(response); | ||||||
|  |                     if (bmData.Error != null) | ||||||
|  |                         throw new StreamNotFoundException($"{stream.Username} [{stream.Type}]"); | ||||||
|  |                     result = new StreamStatus() | ||||||
|  |                     { | ||||||
|  |                         IsLive = bmData.IsLive, | ||||||
|  |                         ApiLink = beamUrl, | ||||||
|  |                         Views = bmData.ViewersCurrent.ToString() | ||||||
|  |                     }; | ||||||
|  |                     _cachedStatuses.AddOrUpdate(beamUrl, result, (key, old) => result); | ||||||
|  |                     return result; | ||||||
|  |                 default: | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  |             return null; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public EmbedBuilder GetEmbed(FollowedStream fs, StreamStatus status, ulong guildId) | ||||||
|  |         { | ||||||
|  |             var embed = new EmbedBuilder().WithTitle(fs.Username) | ||||||
|  |                                           .WithUrl(GetLink(fs)) | ||||||
|  |                                           .AddField(efb => efb.WithName(GetText(fs, "status")) | ||||||
|  |                                                             .WithValue(status.IsLive ? "Online" : "Offline") | ||||||
|  |                                                             .WithIsInline(true)) | ||||||
|  |                                           .AddField(efb => efb.WithName(GetText(fs, "viewers")) | ||||||
|  |                                                             .WithValue(status.IsLive ? status.Views : "-") | ||||||
|  |                                                             .WithIsInline(true)) | ||||||
|  |                                           .AddField(efb => efb.WithName(GetText(fs, "platform")) | ||||||
|  |                                                             .WithValue(fs.Type.ToString()) | ||||||
|  |                                                             .WithIsInline(true)) | ||||||
|  |                                           .WithColor(status.IsLive ? NadekoBot.OkColor : NadekoBot.ErrorColor); | ||||||
|  |  | ||||||
|  |             return embed; | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         public string GetText(FollowedStream fs, string key, params object[] replacements) => | ||||||
|  |             _strings.GetText(key, | ||||||
|  |                 fs.GuildId, | ||||||
|  |                 "Searches".ToLowerInvariant(), | ||||||
|  |                 replacements); | ||||||
|  |  | ||||||
|  |         public string GetLink(FollowedStream fs) | ||||||
|  |         { | ||||||
|  |             if (fs.Type == FollowedStream.FollowedStreamType.Hitbox) | ||||||
|  |                 return $"http://www.hitbox.tv/{fs.Username}/"; | ||||||
|  |             if (fs.Type == FollowedStream.FollowedStreamType.Twitch) | ||||||
|  |                 return $"http://www.twitch.tv/{fs.Username}/"; | ||||||
|  |             if (fs.Type == FollowedStream.FollowedStreamType.Beam) | ||||||
|  |                 return $"https://beam.pro/{fs.Username}/"; | ||||||
|  |             return "??"; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								src/NadekoBot/Services/Searches/StreamResponses.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/NadekoBot/Services/Searches/StreamResponses.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | using Newtonsoft.Json; | ||||||
|  |  | ||||||
|  | namespace NadekoBot.Services.Searches | ||||||
|  | { | ||||||
|  |     public class HitboxResponse | ||||||
|  |     { | ||||||
|  |         public bool Success { get; set; } = true; | ||||||
|  |         [JsonProperty("media_is_live")] | ||||||
|  |         public string MediaIsLive { get; set; } | ||||||
|  |         public bool IsLive => MediaIsLive == "1"; | ||||||
|  |         [JsonProperty("media_views")] | ||||||
|  |         public string Views { get; set; } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public class TwitchResponse | ||||||
|  |     { | ||||||
|  |         public string Error { get; set; } = null; | ||||||
|  |         public bool IsLive => Stream != null; | ||||||
|  |         public StreamInfo Stream { get; set; } | ||||||
|  |  | ||||||
|  |         public class StreamInfo | ||||||
|  |         { | ||||||
|  |             public int Viewers { get; set; } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public class BeamResponse | ||||||
|  |     { | ||||||
|  |         public string Error { get; set; } = null; | ||||||
|  |  | ||||||
|  |         [JsonProperty("online")] | ||||||
|  |         public bool IsLive { get; set; } | ||||||
|  |         public int ViewersCurrent { get; set; } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| namespace NadekoBot.Modules.Searches.Models | namespace NadekoBot.Services.Searches | ||||||
| { | { | ||||||
|     public class WoWJoke |     public class WoWJoke | ||||||
|     { |     { | ||||||
		Reference in New Issue
	
	Block a user